尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

stm32-硬件I2C

stm32-硬件I2C 简介多主机分为固定多主机和可变多主机。固定多主机的主机是不变的可变多主机中没有主机任何设备都可以在总线空闲的时候跳出来当主机去和别的设备通信通信完毕之后退出主机。STM32支持的是可变多主机虽然常用一主多从基本没有别的设备需要当主机但仍需遵循跳出来当主机通信完后回去的规则。两个I2C都在APB1总线上7位地址可以挂载128个设备10位地址就可挂载1024个。根据起始后发送的第一个字节的前五位来分辨是哪一种10位地址的话前五位是11110第六七位加上第二个字节的全八位是地址而剩下的第八位是读/写位。对于7位地址前五位不会出现11110。SMBus是系统管理总线和I2C很相似。I2C结构I2C数据收发过程和串口很相似只不过串口是全双工I2C是半双工移位是向左移位收发都使用同一套寄存器。写入数据到数据寄存器DR如果数据移位寄存器中没有数据就会将DR中的数据移入移位寄存器中同时置标志位TXE然后移位发送接收时数据位进入移位寄存器同时置标志位RXNE最后一起移入DR。比较器和寄存器是设计给STM32作为从机时使用它可以同时响应两个从机地址所以有两个地址寄存器可以设置它的从机地址当有主机寻址时比较器会比较寻找的地址和自身地址是否相同。帧错误校验就是CRC校验类似于串口的奇偶校验它会根据前面的数据进行计算然后在末尾添加一位校验位。流程主机发送序列图可以把事件看成是置标志位发送一个事件就是置了一个或者多个标志位。事件5起始条件生成成功事件6地址发送成功事件8_1两个寄存器都空可以写入数据了事件8有数据在移位但是DR寄存器空可以写入数据事件8_2移位寄存器中已经移位完成但是DR寄存器中没有继续写入数据表示数据发送结束了。主机接收序列图事件7有数据发送到寄存器可以读取了。事件6_1:没有置标志位用于只接收一个字节。如果接收多个字节事件6之后就等待事件7然后读取数据循环步骤不想要数据时在最后一个字节接收之前将应答ACK置0表示非应答并置终止条件不然在接收完之后才置会导致多收一个字节才停止只接收一个字节就要等待事件7之前置ACK为0和置终止条件。硬件I2C代码不需要自己设置时序高低电平了直接使用库函数就不需要IIC.c和.h文件了。MPU6050.c文件#include stm32f10x.h // Device header #include MPU6050_REG.h //用while循环等待事件如果一个事件一直没有发生会导致后面程序都不能运行造成阻塞所以这里将给的判断事件函数封装变成等待超时就退出循环执行后面程序的函数 void I2C_waitevent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) { uint32_t timeout10000; while(I2C_CheckEvent( I2Cx, I2C_EVENT)!SUCCESS) { timeout--; if(timeout0) { break;//等待到timeout已经减到0了都还在等就直接break退出当前函数 } } } //指定地址写时序 void mpu6050_write(uint8_t regaddress,uint8_t data) { I2C_GenerateSTART(I2C2,ENABLE);//生成起始条件同时STM32就跳出来变成主机了 I2C_waitevent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//根据序列图发送起始条件成功后会产生事件5所以这里要等待事件5产生 //接下来发送从机地址以及接收应答 I2C_Send7bitAddress(I2C2,0XD0,I2C_Direction_Transmitter);//使用I2C2从机地址0XD0进行发送发送和接收数据的函数内部都已经自带了接收和发送应答不需要自己再处理 I2C_waitevent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//直接等待事件6事件8_1不用等待这是让你往DR写数据的 //写入数据,对于MPU6050就是写入寄存器地址 I2C_SendData(I2C2,regaddress); I2C_waitevent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);//等待事件8 //继续写入数据 I2C_SendData(I2C2,data); I2C_waitevent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//如果这不是最后一个数据可以继续等待事件8是最后一个数据就要置终止条件了要等待数据8_2 I2C_GenerateSTOP(I2C2,ENABLE);//终止 } //指定地址读 uint8_t mpu6050_read(uint8_t regaddress) { uint8_t byte; I2C_GenerateSTART(I2C2,ENABLE);//生成起始条件同时STM32就跳出来变成主机了 I2C_waitevent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//根据序列图发送起始条件成功后会产生事件5所以这里要等待事件5产生 //接下来发送从机地址以及接收应答 I2C_Send7bitAddress(I2C2,0XD0,I2C_Direction_Transmitter);//使用I2C2从机地址0XD0发送写数据的指令发送数据的函数内部都已经自带了接收应答不需要自己再处理 I2C_waitevent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//直接等待事件6 I2C_SendData(I2C2,regaddress); I2C_waitevent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);//等待事件8 //以上都是指定地址读里的指定地址接下来就发送重复起始条件并读序列图也是从这里开始的 I2C_GenerateSTART(I2C2,ENABLE); I2C_waitevent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//根据序列图等待事件5 I2C_Send7bitAddress(I2C2,0XD0,I2C_Direction_Receiver);//发送读数据的指令 I2C_waitevent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//直接等待事件6,注意发送和接收数据的事件6名字不一样 //如果接收多个字节接下来就等待事件7然后读取数据循环步骤不想要数据时在最后一个字节接收之前将应答ACK置0表示非应答并置终止条件不然在接收完之后才置会导致多收一个字节才停止 //这里只接收一个字节所以等待事件7之前就置ACK为0和置终止条件 I2C_AcknowledgeConfig(I2C2,DISABLE);//非应答这样在后面接收字节函数中自动发送应答时就发送的是非应答了 I2C_GenerateSTOP(I2C2,ENABLE);//终止 I2C_waitevent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//等待事件7 byteI2C_ReceiveData(I2C2);//读取数据 I2C_AcknowledgeConfig(I2C2,ENABLE); return byte; } void mpu6050_init(void)//配置mpu6050的寄存器 { // 初始化IIC和GPIO,使用I2C2 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitSturcture; GPIO_InitSturcture.GPIO_ModeGPIO_Mode_AF_OD;//复用开漏输出复用即GPIO控制权交给片上外设 GPIO_InitSturcture.GPIO_PinGPIO_Pin_10|GPIO_Pin_11; GPIO_InitSturcture.GPIO_SpeedGPIO_Speed_50MHz; GPIO_Init(GPIOB,GPIO_InitSturcture); I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_AckI2C_Ack_Enable;//要给从机应答 I2C_InitStructure.I2C_AcknowledgedAddressI2C_AcknowledgedAddress_7bit;//STM32作为从机使用7位地址 I2C_InitStructure.I2C_ClockSpeed500000;//SCL时钟频率不高于4000KHZ越大传输数据越快 I2C_InitStructure.I2C_DutyCycleI2C_DutyCycle_2;//SCL占空比给快速时使用的频率越高就越要给SCL低电平多些时间来改变SDA电平 I2C_InitStructure.I2C_ModeI2C_Mode_I2C;//使用I2C模式。还有SMBus模式 I2C_InitStructure.I2C_OwnAddress10x00;//STM32作为从机的自身地址上面选择了七位这里就给七位十位类似。 I2C_Init(I2C2,I2C_InitStructure); //使能I2C I2C_Cmd(I2C2,ENABLE); mpu6050_write(MPU6050_PWR_MGMT_1, 0x01); mpu6050_write(MPU6050_PWR_MGMT_2, 0x00); mpu6050_write(MPU6050_SMPLRT_DIV, 0x09); mpu6050_write(MPU6050_CONFIG, 0x06); mpu6050_write(MPU6050_GYRO_CONFIG, 0x18); mpu6050_write(MPU6050_ACCEL_CONFIG, 0x18); } uint8_t MPU6050_GetID(void) { return mpu6050_read(MPU6050_WHO_AM_I); } void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)//寄存器中的值为16位 { uint8_t DataH, DataL; DataH mpu6050_read(MPU6050_ACCEL_XOUT_H);//获取高八位 DataL mpu6050_read(MPU6050_ACCEL_XOUT_L);//获取低八位 *AccX (DataH 8) | DataL; DataH mpu6050_read(MPU6050_ACCEL_YOUT_H); DataL mpu6050_read(MPU6050_ACCEL_YOUT_L); *AccY (DataH 8) | DataL; DataH mpu6050_read(MPU6050_ACCEL_ZOUT_H); DataL mpu6050_read(MPU6050_ACCEL_ZOUT_L); *AccZ (DataH 8) | DataL; DataH mpu6050_read(MPU6050_GYRO_XOUT_H); DataL mpu6050_read(MPU6050_GYRO_XOUT_L); *GyroX (DataH 8) | DataL; DataH mpu6050_read(MPU6050_GYRO_YOUT_H); DataL mpu6050_read(MPU6050_GYRO_YOUT_L); *GyroY (DataH 8) | DataL; DataH mpu6050_read(MPU6050_GYRO_ZOUT_H); DataL mpu6050_read(MPU6050_GYRO_ZOUT_L); *GyroZ (DataH 8) | DataL; }其他代码和软件I2C都一样。
返回列表