10-3I2C通信外设&硬件I2C读写MPU6050

I2C外设简介

•STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
•支持多主机模型
•支持7位(最常见,7位地址+读写位)/10位地址模式(为了挂载128个以上的设备)
•支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
•支持DMA(读写多字节时,使用DMA自动转运数据)
•兼容SMBus协议(I2C改进的协议)

•STM32F103C8T6 硬件I2C资源:I2C1、I2C2

I2C框图

I2C框图
左侧的SDA和SCL是I2C通信引脚,下方的SMBALERT是smbus使用的,I2C用不到。

上方是SDA,数据控制部分。
数据收发的核心,是数据寄存器(DATA REGISTER)和数据移位寄存器,需要发送数据时,把这个数据写道数据寄存器DR,当移位寄存器没有数据移位时,这个数据寄存器的值就会转到移位寄存器里,在移位过程中,可以把下一个数据放到数据寄存器DR中等待,前一个数据移位完成,下一个数据直接到移位寄存器。
当数据由数据寄存器转移到数据移位寄存器时,置状态寄存器的TXE位为1,表示发送寄存器为空。
接收数据时,数据一位一位地从引脚到移位寄存器中,一个字节的数据收集好之后,数据整体从移位寄存器转移到数据寄存器,置标志位RXNE,表示接收寄存器非空,此时可以从数据寄存器读出数据。

下方为SCL,时钟控制部分。

I2C基本结构

I2C基本结构
移位寄存器和数据寄存器DR配合,是通信的核心。
由于I2C是高位先行,移位寄存器是向左移位,从高位到低位移位。一个SCL时钟移位一次,八次把一个字节传输完成。
接收时,数据从GPIO口移进来,移位8次,一个字节就接收完成了。
使用硬件I2C时,对应的2个GPIO口配置为复用开漏输出模式

主机发送

主机发送

主机接收

主机接收

常用函数

12
void I2C_DeInit(I2C_TypeDef* I2Cx); 

I2C恢复缺省配置

1
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);

I2C初始化

1
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);

I2C结构体初始化

1
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

I2C使能

1
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);

I2C生成起始条件,调用此函数生成起始条件。

1
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

I2C生成终止条件。

1
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

配置在收到一个字节之后,是否给从机应答

1
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);

I2C发送数据,写入数据到数据寄存器DR。

1
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);

读取DR的数据作为返回值,接收数据

1
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

发送7位地址

硬件I2C读写MPU6050

MPU6050.c

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
#include "stm32f10x.h"                  // Device header#include "MPU6050_REG.h" #define MPU6050_ADDRESS 0xD0 void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT){	uint32_t Timeout;//超时退出while,避免死循环	Timeout = 10000;	while(I2C_CheckEvent(I2Cx,I2C_EVENT) != SUCCESS)//检测事件是否发送	{		Timeout--;		if(Timeout == 0)//超时退出		{			break;		}	}}  void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)//指定地址写{ 	I2C_GenerateSTART(I2C2,ENABLE);//生成起始条件	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//监测EV5事件是否发送	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);//发送从机地址	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//监测EV6事件是否发送 	I2C_SendData(I2C2,RegAddress);//发送寄存器地址	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);//监测EV8事件是否发送 	I2C_SendData(I2C2,Data);//发送数据	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//监测EV8_2事件是否发送 	I2C_GenerateSTOP(I2C2,ENABLE);//产生停止条件} uint8_t MPU6050_ReadReg(uint8_t RegAddress){	uint8_t Data; 	I2C_GenerateSTART(I2C2,ENABLE);//生成起始条件	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//监测EV5事件是否发送 	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);//发送从机地址	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//监测EV6事件是否发送 	I2C_SendData(I2C2,RegAddress);//发送寄存器地址	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//监测EV8事件是否发送 	I2C_GenerateSTART(I2C2,ENABLE);//生成重复的起始条件	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//监测EV5事件是否发送 	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver);//发送从机地址,读写位为读	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//监测EV6事件是否发送 	I2C_AcknowledgeConfig(I2C2,DISABLE);//只需要读取一个字节,在EV6事件之后,立刻把ACK位置0	I2C_GenerateSTOP(I2C2,ENABLE);//申请产生终止条件 	MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//监测EV7事件是否发送 	Data = I2C_ReceiveData(I2C2); 	I2C_AcknowledgeConfig(I2C2,ENABLE);//把ACK置1,默认状态下ACK应该为1 	return Data;} void MPU6050_Init(void){	//MyI2C_Init(); 	//硬件I2C初始化	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);//开启I2C2的时钟,I2C1和I2C2都是APB1的外设	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟	//GPIO初始化	GPIO_InitTypeDef GPIO_InitStructure;	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//此处必须设置为复用开漏模式,开漏是I2C协议的设计要求,复用是把GPIO的控制权交给硬件外设。	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	GPIO_Init(GPIOB, &GPIO_InitStructure);	GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);	//I2C初始化	I2C_InitTypeDef I2C_InitStructure;	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;	I2C_InitStructure.I2C_ClockSpeed = 50000;	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_16_9;//占空比,16_9就是低电平和高电平时间是16:9的关系	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//应答位配置	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//STM32作为从机,可以相应几位地址,这里配置成7位	I2C_InitStructure.I2C_OwnAddress1 = 0x00;//STM32作为从机时使用,自身地址	I2C_Init(I2C2,&I2C_InitStructure); 	I2C_Cmd(I2C2,ENABLE);//I2C2使能 	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);	MPU6050_WriteReg(MPU6050_CONFIG,0x06);	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);} void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ){	uint8_t DataH,DataL; 	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);	*AccX = (DataH << 8) | DataL; 	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);	*AccY = (DataH << 8) | DataL; 	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);	*AccZ = (DataH << 8) | DataL; 	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);	*GyroX = (DataH << 8) | DataL; 	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);	*GyroY = (DataH << 8) | DataL; 	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);	*GyroZ = (DataH << 8) | DataL;} 
🔍 ×