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

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