当前位置:   article > 正文

STM32学习笔记(10_3)- 软件I2C读写MPU6050

STM32学习笔记(10_3)- 软件I2C读写MPU6050

无人问津也好,技不如人也罢,都应静下心来,去做该做的事。

最近在学STM32,所以也开贴记录一下主要内容,省的过目即忘。视频教程为江科大(改名江协科技),网站jiangxiekeji.com

本期开始学习 软件模拟I2C控制MPU6050,分为两部分,第一部分,我们完成软件I2C协议的时序;第二部分,基于软件I2C协议,读写寄存器, 来操控MPU6050。

软件I2C就是用普通的GPIO口,手动翻转电平实现的协议,它并不需要STM32内部的外设资源支持。所以这里的端口, 其实可以任意指定

I2C:高位先行,只有在SCL高电平时才会读取SDA的电平

MPU6050的读地址是0xD1,写地址是0xD0

本期的内容是软件I2CMPU6050芯片内部的寄存器进行读写,写入到配置寄存器,就可以对外挂的这个模块进行配置;读出数据寄存器,就可以获取外挂模块的数据。最终,我们读出的数据会显示在这个OLED上。而STM32对MPU6050的读写是通过I2C来进行的。

 

 I2C的完整时序

主要有指定地址写、当前地址读和指定地址读这3种。I2C指定地址写简单来说就是SCL高电平时、SDA下降沿开始传输,传输的第一个字节为从机的7位地址+1位读写位(置0表示要写入),然后是1位应答位(RA,置0表示应答),传输的第二个字节为从机的寄存器地址或者是指令控制字,然后也是1位应答位,传输的第三个字节为主机想写入从机的数据,然后也是1位应答位,这时若不想传输了,则SCL高电平时、SDA上升沿停止传输。

当前地址读就是SCL高电平时、SDA下降沿作为起始条件开始传输,传输的第一个字节为从机的7位地址+1位读写位(这里置1表示读取),然后是1位应答位(RA,置0表示应答),传输的第二个字节为从机的数据,然后是1位非应答位(SA,置1),这时SCL高电平、SDA上升沿作为停止条件停止传输。

指定地址读就是指定地址写去掉写数据的部分+当前地址读,SCL高电平时、SDA下降沿作为起始条件开始传输,传输的第一个字节为从机的7位地址+1位读写位(置0表示要写入),然后是1位应答位(RA,置0表示应答),传输的第二个字节为从机的寄存器地址或者是指令控制字,然后也是1位应答位(置0),然后接重复起始条件Sr(即再来一次起始条件),传输的第三个字节为从机的7位地址+1位读写位(置1表示要读取),然后再接收1位应答位(置0),传输的第四个字节为从机的数据,然后1位非应答位(SA,置1),然后SCL高电平、SDA上升沿作为停止条件停止传输。

软件 I2C读写MPU6050程序

对MPU6050芯片内部的寄存器进行读写,写入到配置寄存器,就可以对外挂的这个模块进行配置;读出数据寄存器,就可以获取外挂模块的数据。最终,我们读出的数据会显示在这个OLED上。最上面的是ID号,图中这个MPU6050的ID号固定为0x68,可以读取这个ID号用来测试 I2C读取数据的功能是不是正常,不同批次的MPU6050,ID号会有所不同。然后下面,左边3个,是加速度传感器的输出数据,分别是X轴、Y轴和Z轴的加速度,右边3个,是陀螺仪传感器的输出数据,分别是X轴、Y轴和Z轴的角速度。

接线图

MPU6050的VCC、GND接供电,SCL接PB10,SDA接PB11。

软件I2C就是用普通的GPIO口,手动翻转电平实现的协议,它并不需要STM32内部的外设资源支持。所以这里的端口, 其实可以任意指定。

初始化步骤

我们首先建立I2C通信层的.c和.h模块,在通信层里,写好I2C底层的GPIO初始化和6个时序基本单元。也就是起始、终止、发送一个字节、接收一个字节、发送应答和接收应答。写好I2C通信层后,我们再建立MPU6050的.c和.h模块,基于I2C通信模块来实现指定地址读、指定地址写,再实现写寄存器对芯片进行配置,读寄存器得到传感器数据。最终在main.c里,调用MPU6050的模块,初始化,拿到数据,显示数据。

软件I2C,只需要用gpio的读写函数就行了,不需要用到库函数中的I2C函数。

一、I2C初始化

1、把SCL和SDA都初始化为开漏输出模式

2、把SCL和SDA置高电平

  1. /**
  2. * 函 数:I2C初始化
  3. * 参 数:无
  4. * 返 回 值:无
  5. * 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化
  6. */
  7. void MyI2C_Init(void)
  8. {
  9. /*开启时钟*/
  10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
  11. /*GPIO初始化*/
  12. GPIO_InitTypeDef GPIO_InitStructure;
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
  14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
  15. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  16. GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB10和PB11引脚初始化为开漏输出
  17. /*设置默认电平*/
  18. GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); //设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
  19. }

二、I2C的6个时序基本单元

  1. /*引脚配置层*/
  2. /**
  3. * 函 数:I2C写SCL引脚电平
  4. * 参 数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  5. * 返 回 值:无
  6. * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  7. */
  8. void MyI2C_W_SCL(uint8_t BitValue)
  9. {
  10. GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue); //根据BitValue,设置SCL引脚的电平
  11. Delay_us(10); //延时10us,防止时序频率超过要求
  12. }
  13. /**
  14. * 函 数:I2C写SDA引脚电平
  15. * 参 数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF
  16. * 返 回 值:无
  17. * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平
  18. */
  19. void MyI2C_W_SDA(uint8_t BitValue)
  20. {
  21. GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue); //根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
  22. Delay_us(10); //延时10us,防止时序频率超过要求
  23. }
  24. /**
  25. * 函 数:I2C读SDA引脚电平
  26. * 参 数:无
  27. * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  28. * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  29. */
  30. uint8_t MyI2C_R_SDA(void)
  31. {
  32. uint8_t BitValue;
  33. BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); //读取SDA电平
  34. Delay_us(10); //延时10us,防止时序频率超过要求
  35. return BitValue; //返回SDA电平
  36. }
  37. /*协议层*/
  38. /**
  39. * 函 数:I2C起始
  40. * 参 数:无
  41. * 返 回 值:无
  42. */
  43. void MyI2C_Start(void)
  44. {
  45. MyI2C_W_SDA(1); //释放SDA,确保SDA为高电平
  46. MyI2C_W_SCL(1); //释放SCL,确保SCL为高电平
  47. MyI2C_W_SDA(0); //在SCL高电平期间,拉低SDA,产生起始信号
  48. MyI2C_W_SCL(0); //起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
  49. }
  50. /**
  51. * 函 数:I2C终止
  52. * 参 数:无
  53. * 返 回 值:无
  54. */
  55. void MyI2C_Stop(void)
  56. {
  57. MyI2C_W_SDA(0); //拉低SDA,确保SDA为低电平
  58. MyI2C_W_SCL(1); //释放SCL,使SCL呈现高电平
  59. MyI2C_W_SDA(1); //在SCL高电平期间,释放SDA,产生终止信号
  60. }
  61. /**
  62. * 函 数:I2C发送一个字节
  63. * 参 数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  64. * 返 回 值:无
  65. */
  66. void MyI2C_SendByte(uint8_t Byte)
  67. {
  68. uint8_t i;
  69. for (i = 0; i < 8; i ++) //循环8次,主机依次发送数据的每一位
  70. {
  71. MyI2C_W_SDA(Byte & (0x80 >> i)); //使用掩码的方式取出Byte的指定一位数据并写入到SDA线
  72. MyI2C_W_SCL(1); //释放SCL,从机在SCL高电平期间读取SDA
  73. MyI2C_W_SCL(0); //拉低SCL,主机开始发送下一位数据
  74. }
  75. }
  76. /**
  77. * 函 数:I2C接收一个字节
  78. * 参 数:无
  79. * 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
  80. */
  81. uint8_t MyI2C_ReceiveByte(void)
  82. {
  83. uint8_t i, Byte = 0x00; //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
  84. MyI2C_W_SDA(1); //接收前,主机先确保释放SDA,避免干扰从机的数据发送
  85. for (i = 0; i < 8; i ++) //循环8次,主机依次接收数据的每一位
  86. {
  87. MyI2C_W_SCL(1); //释放SCL,主机机在SCL高电平期间读取SDA
  88. if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);} //读取SDA数据,并存储到Byte变量
  89. //当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
  90. MyI2C_W_SCL(0); //拉低SCL,从机在SCL低电平期间写入SDA
  91. }
  92. return Byte; //返回接收到的一个字节数据
  93. }
  94. /**
  95. * 函 数:I2C发送应答位
  96. * 参 数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
  97. * 返 回 值:无
  98. */
  99. void MyI2C_SendAck(uint8_t AckBit)
  100. {
  101. MyI2C_W_SDA(AckBit); //主机把应答位数据放到SDA线
  102. MyI2C_W_SCL(1); //释放SCL,从机在SCL高电平期间,读取应答位
  103. MyI2C_W_SCL(0); //拉低SCL,开始下一个时序模块
  104. }
  105. /**
  106. * 函 数:I2C接收应答位
  107. * 参 数:无
  108. * 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
  109. */
  110. uint8_t MyI2C_ReceiveAck(void)
  111. {
  112. uint8_t AckBit; //定义应答位变量
  113. MyI2C_W_SDA(1); //接收前,主机先确保释放SDA,避免干扰从机的数据发送
  114. MyI2C_W_SCL(1); //释放SCL,主机机在SCL高电平期间读取SDA
  115. AckBit = MyI2C_R_SDA(); //将应答位存储到变量里
  116. MyI2C_W_SCL(0); //拉低SCL,开始下一个时序模块
  117. return AckBit; //返回定义应答位变量
  118. }

 三、MPU6050基于I2C通信模块来实现指定地址写

  1. #define MPU6050_ADDRESS 0xD0 //MPU6050的I2C从机地址
  2. /**
  3. * 函 数:MPU6050写寄存器
  4. * 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  5. * 参 数:Data 要写入寄存器的数据,范围:0x00~0xFF
  6. * 返 回 值:无
  7. */
  8. void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
  9. {
  10. MyI2C_Start(); //I2C起始
  11. MyI2C_SendByte(MPU6050_ADDRESS); //发送从机地址,读写位为0,表示即将写入
  12. MyI2C_ReceiveAck(); //接收应答
  13. MyI2C_SendByte(RegAddress); //发送寄存器地址
  14. MyI2C_ReceiveAck(); //接收应答
  15. MyI2C_SendByte(Data); //发送要写入寄存器的数据
  16. MyI2C_ReceiveAck(); //接收应答
  17. MyI2C_Stop(); //I2C终止
  18. }

四、MPU6050基于I2C通信模块来实现指定地址读

  1. /**
  2. * 函 数:MPU6050读寄存器
  3. * 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  4. * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  5. */
  6. uint8_t MPU6050_ReadReg(uint8_t RegAddress)
  7. {
  8. uint8_t Data;
  9. MyI2C_Start(); //I2C起始
  10. MyI2C_SendByte(MPU6050_ADDRESS); //发送从机地址,读写位为0,表示即将写入
  11. MyI2C_ReceiveAck(); //接收应答
  12. MyI2C_SendByte(RegAddress); //发送寄存器地址
  13. MyI2C_ReceiveAck(); //接收应答
  14. MyI2C_Start(); //I2C重复起始
  15. MyI2C_SendByte(MPU6050_ADDRESS | 0x01); //发送从机地址,读写位为1,表示即将读取
  16. MyI2C_ReceiveAck(); //接收应答
  17. Data = MyI2C_ReceiveByte(); //接收指定寄存器的数据
  18. MyI2C_SendAck(1); //发送应答,给从机非应答,终止从机的数据输出
  19. MyI2C_Stop(); //I2C终止
  20. return Data;
  21. }

五、MPU6050初始化、读寄存器得到传感器数据

  1. /**
  2. * 函 数:MPU6050初始化
  3. * 参 数:无
  4. * 返 回 值:无
  5. */
  6. void MPU6050_Init(void)
  7. {
  8. MyI2C_Init(); //先初始化底层的I2C
  9. /*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
  10. MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); //电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
  11. MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); //电源管理寄存器2,保持默认值0,所有轴均不待机
  12. MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); //采样率分频寄存器,配置采样率
  13. MPU6050_WriteReg(MPU6050_CONFIG, 0x06); //配置寄存器,配置DLPF
  14. MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器,选择满量程为±2000°/s
  15. MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); //加速度计配置寄存器,选择满量程为±16g
  16. }
  17. /**
  18. * 函 数:MPU6050获取ID号
  19. * 参 数:无
  20. * 返 回 值:MPU6050的ID号
  21. */
  22. uint8_t MPU6050_GetID(void)
  23. {
  24. return MPU6050_ReadReg(MPU6050_WHO_AM_I); //返回WHO_AM_I寄存器的值
  25. }
  26. /**
  27. * 函 数:MPU6050获取数据
  28. * 参 数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  29. * 参 数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  30. * 返 回 值:无
  31. */
  32. void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
  33. int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
  34. {
  35. uint8_t DataH, DataL; //定义数据高8位和低8位的变量
  36. DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); //读取加速度计X轴的高8位数据
  37. DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); //读取加速度计X轴的低8位数据
  38. *AccX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
  39. DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); //读取加速度计Y轴的高8位数据
  40. DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); //读取加速度计Y轴的低8位数据
  41. *AccY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
  42. DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); //读取加速度计Z轴的高8位数据
  43. DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); //读取加速度计Z轴的低8位数据
  44. *AccZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
  45. DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); //读取陀螺仪X轴的高8位数据
  46. DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); //读取陀螺仪X轴的低8位数据
  47. *GyroX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
  48. DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); //读取陀螺仪Y轴的高8位数据
  49. DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); //读取陀螺仪Y轴的低8位数据
  50. *GyroY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
  51. DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); //读取陀螺仪Z轴的高8位数据
  52. DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); //读取陀螺仪Z轴的低8位数据
  53. *GyroZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
  54. }

 六、main函数

  1. uint8_t ID; //定义用于存放ID号的变量
  2. int16_t AX, AY, AZ, GX, GY, GZ; //定义用于存放各个数据的变量
  3. int main(void)
  4. {
  5. /*模块初始化*/
  6. OLED_Init(); //OLED初始化
  7. MPU6050_Init(); //MPU6050初始化
  8. /*显示ID号*/
  9. OLED_ShowString(1, 1, "ID:"); //显示静态字符串
  10. ID = MPU6050_GetID(); //获取MPU6050的ID号
  11. OLED_ShowHexNum(1, 4, ID, 2); //OLED显示ID号
  12. while (1)
  13. {
  14. MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); //获取MPU6050的数据
  15. OLED_ShowSignedNum(2, 1, AX, 5); //OLED显示数据
  16. OLED_ShowSignedNum(3, 1, AY, 5);
  17. OLED_ShowSignedNum(4, 1, AZ, 5);
  18. OLED_ShowSignedNum(2, 8, GX, 5);
  19. OLED_ShowSignedNum(3, 8, GY, 5);
  20. OLED_ShowSignedNum(4, 8, GZ, 5);
  21. }
  22. }

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/352256
推荐阅读
相关标签
  

闽ICP备14008679号