【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050

打印 上一主题 下一主题

主题 1898|帖子 1898|积分 5694

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. MPU6050简介



  • MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加快度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等必要检测自身姿态的场景
  • 3轴加快度计(Accelerometer):测量X、Y、Z轴的加快度
  • 3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
      
     

1.1 重要参数



  • 16位ADC采集传感器的模拟信号,量化范围:-32768~32767
  • 加快度计满量程选择:±2、±4、±8、±16(g)
  • 陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
  • 可设置的数字低通滤波器
  • 可设置的时钟源
  • 可设置的采样分频
I2C从机地点:1101000(AD0=0)、1101001(AD0=1)
如果在程序中用十六进制表现,一般有两种表现方式,以1101000地点为例。第一种,直接把7位二进制数转换为十六进制,110 1000就是0x68,以是可以说MPU6050的从机地点是0x68。在I2C通信时序中,第一个字节的高7位是从机地点,最低位是读写位,以是如果以为0x68是从机地点,在发送第一个字节时,必要先把0x68左移1位,再按位或上读写位。第二种,把0x68左移1位后的数据当做从机地点。0x68左移1位后是0xD0,则MPU6050的从机地点就是0xD0,这时在实际发送第一个字节时,如果必要写,就直接把0xD0当作第一个字节,如果必要读,就或上0x01,即0xD1当作第一个字节。
1.2 硬件电路

   
     MPU6050模块原理图   

XCL、XDA:MPU6050的6轴传感器不够用,必要举行扩展时利用,通常用于外接磁力计或者气压计,MPU6050的主机接口可以直接访问这些扩展芯片的数据,在MPU6050内有DMP单元举行数据融合和姿态解算。
1.3 模块框图


2. 软件I2C读写MPU6050

2.1 接线图

SCL接PB10、SDA接PB11。由于本节代码利用的是软件I2C,即用平凡的GPIO口手动翻转电平,不必要STM32内部的外设资源支持,以是这里端口可以恣意指定,也可以SCL接PA8,SDA接PA9等。
根据I2C协议的硬件规定,SCL和SDA均应外挂上拉电阻,由于模块内部自带上拉电阻,以是不必要外接。XCL和XDA用于扩展的接口,不利用。AD0引脚修改从机地点的最低位,由于模块内接了下拉电阻,以是引脚悬空相当于接地。INT中断信号输出脚,不利用。

2.2 代码

程序的整体架构:

  • 起首,创建I2C通信层的.c和.h模块。在通信层中,写好I2C底层的GPIO初始化和6个时序基本单元(起始、停止、发送一个字节、接收一个字节、发送应答、接收应答)。
  • 再创建MPU6050的.c的.h模块。在此层,基于I2C通信模块实现指定地点读、指定地点写,再实现写寄存器对芯片举行设置、读寄存器得到传感器数据。
  • 最后在main.c中调用MPU6050模块,初始化,获取数据,显示数据。
由于利用的是软件I2C,以是stm32的I2C库函数就不必要看了,只必要利用GPIO的读写函数。 
MyI2C.c(防止和库函数的函数重名)
  1. #include "stm32f10x.h"                  // Device header
  2. #include "Delay.h"
  3. void MyI2C_Init(void)
  4. {
  5.     //  需要完成2个任务:
  6.     //  ① 把SCL和SDA均初始化为开漏输出模式;
  7.     //  ② 把SCL和SDA置高电平。
  8.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  9.        
  10.         GPIO_InitTypeDef GPIO_InitStructure;
  11.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//        开漏输出(虽然名字是"输出",但开漏输出模式仍然可以输入)
  12.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;//  SCL是PB10,SDA是PB11
  13.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  14.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  15.    
  16.     GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);//   置SCL和SDA高电平
  17. }
  18. //  对操作端口的库函数进行封装
  19. void MyI2C_W_SCL(uint8_t BitValue)//    写SCL。参数给1或0,就可以释放或拉低SCL
  20. {
  21.     GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
  22.     Delay_us(10);// 如果单片机主频较快,可以加延时,防止从机跟不上
  23. }
  24. void MyI2C_W_SDA(uint8_t BitValue)//    写SDA。参数给1或0,就可以释放或拉低SDA
  25. {
  26.     GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
  27.     Delay_us(10);
  28. }
  29. uint8_t MyI2C_R_SDA(void)// 读SDA
  30. {
  31.     uint8_t BitValue;
  32.     BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
  33.     Delay_us(10);
  34.     return BitValue;
  35. }
  36. //  接下来完成I2C的6个时序基本单元
  37. //
  38. //  1、起始条件
  39. //  首先确保SCL和SDA释放,然后先拉低SDA,再拉低SCL
  40. void MyI2C_Start(void)
  41. {
  42.     MyI2C_W_SDA(1);//   释放SDA
  43.     MyI2C_W_SCL(1);//   释放SCL
  44.    
  45.     MyI2C_W_SDA(0);//   先拉低SDA
  46.     MyI2C_W_SCL(0);//   再拉低SCL
  47. }
  48. //  2、终止条件
  49. //  SCL高电平期间,SDA从低电平切换到高电平
  50. void MyI2C_Stop(void)
  51. {
  52.     MyI2C_W_SDA(0);//   先拉低SDA,避免SDA之前为高电平时释放无法产生上升沿
  53.     MyI2C_W_SCL(1);//   再释放SCL
  54.     MyI2C_W_SDA(1);//   再释放SDA
  55. }
  56. //  3、发送一个字节
  57. //  SCL低电平,SDA变换数据;SCL高电平,SDA保持数据稳定。放完1位后,释放SCL,拉低SCL,驱动时钟运转
  58. void MyI2C_SendByte(uint8_t Byte)
  59. {
  60.     uint8_t i;
  61.     for(i = 0; i < 8; i++)
  62.     {
  63.         MyI2C_W_SDA(Byte & (0x80 >> i));//  首先趁SCL低电平,把Byte的最高位放在SDA线上。下一轮就是把次高位放在SDA线上
  64.         //  取Byte最高位为Byte&0x80,取次高位为Byte&0x40...在循环中,使用右移i位即可实现按位与数值的变换
  65.         MyI2C_W_SCL(1);//   再释放SCL。释放后从机会立刻把SDA的数据读走
  66.         MyI2C_W_SCL(0);//   拉低SCL
  67.     }
  68. }
  69. //  4、接收一个字节
  70. //  SCL低电平,从机把数据放到SDA上,为了防止主机干扰从机写入数据,主机需要先释放SDA,释放SDA也相当于切换为输入模式
  71. //  然后主机在SCL高电平期间读取SDA,再拉低SCL。这样重复8次,主机就能读到一个字节了。
  72. uint8_t MyI2C_ReceiveByte(void)
  73. {
  74.     uint8_t i, Byte = 0x00;
  75.     MyI2C_W_SDA(1);//   主机释放SDA
  76.     for(i = 0; i < 8; i++)
  77.     {
  78.         MyI2C_W_SCL(1);//   主机释放SCL
  79.         if(MyI2C_R_SDA() == 1)//    主机读取数据
  80.         {
  81.             Byte |= (0x80 >> i);// 如果读取到1,就从高到低把Byte对应位置1
  82.         }
  83.         MyI2C_W_SCL(0);//   读取1位后拉低SCL,这时从机会把下一位数据放到SDA上
  84.     }
  85.     return Byte;
  86. }
  87. //  5、发送应答
  88. //  其实就是发送一个字节的简化
  89. void MyI2C_SendAck(uint8_t AckBit)
  90. {
  91.     MyI2C_W_SDA(AckBit);//  函数进入时,SCL低电平,主机把AckBit放到SDA上
  92.     MyI2C_W_SCL(1);//   再释放SCL。从机读取应答
  93.     MyI2C_W_SCL(0);//   拉低SCL,进入下一个时序单元
  94. }
  95. //  6、接收应答
  96. //  其实就是接收一个字节的简化
  97. uint8_t MyI2C_ReceiveAck(void)
  98. {
  99.     uint8_t AckBit;
  100.     MyI2C_W_SDA(1);//   函数进入时SCL低电平,主机释放SDA防止干扰从机。同时,从机把应答位放在SDA上
  101.     MyI2C_W_SCL(1);//   主机释放SCL,读取应答位
  102.     AckBit = MyI2C_R_SDA();
  103.     MyI2C_W_SCL(0);//   读取后拉低SCL,进入下一个时序单元
  104.     return AckBit;
  105. }
复制代码
MyI2C.h
  1. #ifndef __MYI2C_H
  2. #define __MYI2C_H
  3. void MyI2C_Init(void);
  4. void MyI2C_Start(void);
  5. void MyI2C_Stop(void);
  6. void MyI2C_SendByte(uint8_t Byte);
  7. uint8_t MyI2C_ReceiveByte(void);
  8. void MyI2C_SendAck(uint8_t AckBit);
  9. uint8_t MyI2C_ReceiveAck(void);
  10. #endif
复制代码
MPU6050.c
  1. #include "stm32f10x.h"                  // Device header
  2. #include "MyI2C.h"
  3. #include "MPU6050_Reg.h"
  4. #define MPU6050_ADDRESS 0xD0
  5. // 指定地址写
  6. void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
  7. {
  8.     MyI2C_Start();
  9.     MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址+读写位
  10.     MyI2C_ReceiveAck();//   接收应答
  11.     MyI2C_SendByte(RegAddress);//   指定寄存器地址
  12.     MyI2C_ReceiveAck();
  13.     MyI2C_SendByte(Data);//   发送写入指定寄存器地址下的数据
  14.     MyI2C_ReceiveAck();
  15.     MyI2C_Stop();
  16. }
  17. //  指定地址读
  18. uint8_t MPU6050_ReadReg(uint8_t RegAddress)
  19. {
  20.     uint8_t Data;
  21.    
  22.     MyI2C_Start();
  23.     MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址+读写位
  24.     MyI2C_ReceiveAck();//   接收应答
  25.     MyI2C_SendByte(RegAddress);//   指定寄存器地址
  26.     MyI2C_ReceiveAck();
  27.    
  28.     MyI2C_Start();//    重复起始条件
  29.     MyI2C_SendByte(MPU6050_ADDRESS | 0xD1);//   读写位1,读出数据
  30.     MyI2C_ReceiveAck();
  31.     Data = MyI2C_ReceiveByte();//   从机发送数据,主机接收数据
  32.     MyI2C_SendAck(1);//  这里只想要1个字节,所以不给从机应答
  33.     MyI2C_Stop();
  34.    
  35.     return Data;
  36. }
  37. void MPU6050_Init(void)
  38. {
  39.     MyI2C_Init();
  40.     MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//   配置PWR_MGMT_1寄存器,解除睡眠,选择X轴陀螺仪时钟
  41.     MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
  42.     MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//   采样率分频,决定了数据输出的快慢。10分频
  43.     MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//   数字低通滤波器给110
  44.     MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//  陀螺仪配置,选择最大量程11
  45.     MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);// 加速度计配置,选择最大量程11
  46.    
  47. }
  48. uint8_t MPU6050_GetID(void)
  49. {
  50.     return MPU6050_ReadReg(MPU6050_WHO_AM_I);
  51. }
  52. //  由于需要返回6个变量,使用指针的地址传递
  53. void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
  54.                         int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
  55. {
  56.     uint8_t Data_H, Data_L;
  57.    
  58.     Data_H = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//    加速度寄存器X轴高8位
  59.     Data_L = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
  60.     *AccX = (Data_H << 8) | Data_L;//   加速度计X轴的16位数据
  61.    
  62.     Data_H = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
  63.     Data_L = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
  64.     *AccY = (Data_H << 8) | Data_L;
  65.    
  66.     Data_H = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
  67.     Data_L = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
  68.     *AccZ = (Data_H << 8) | Data_L;
  69.    
  70.     Data_H = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);//    陀螺仪寄存器X轴高8位
  71.     Data_L = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
  72.     *GyroX = (Data_H << 8) | Data_L;//   陀螺仪X轴的16位数据
  73.    
  74.     Data_H = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
  75.     Data_L = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
  76.     *GyroY = (Data_H << 8) | Data_L;
  77.    
  78.     Data_H = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
  79.     Data_L = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
  80.     *GyroZ = (Data_H << 8) | Data_L;
  81. }
复制代码
MPU6050.h
  1. #ifndef __MPU6050_H
  2. #define __MPU6050_H
  3. void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
  4. uint8_t MPU6050_ReadReg(uint8_t RegAddress);
  5. void MPU6050_Init(void);
  6. void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
  7.                         int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);
  8. uint8_t MPU6050_GetID(void);
  9. #endif
复制代码
MPU6050_Reg.h
  1. #ifndef __MPU6050_REG_H
  2. #define __MPU6050_REG_H
  3. #define        MPU6050_SMPLRT_DIV                0x19
  4. #define        MPU6050_CONFIG                        0x1A
  5. #define        MPU6050_GYRO_CONFIG                0x1B
  6. #define        MPU6050_ACCEL_CONFIG        0x1C
  7. #define        MPU6050_ACCEL_XOUT_H        0x3B
  8. #define        MPU6050_ACCEL_XOUT_L        0x3C
  9. #define        MPU6050_ACCEL_YOUT_H        0x3D
  10. #define        MPU6050_ACCEL_YOUT_L        0x3E
  11. #define        MPU6050_ACCEL_ZOUT_H        0x3F
  12. #define        MPU6050_ACCEL_ZOUT_L        0x40
  13. #define        MPU6050_TEMP_OUT_H                0x41
  14. #define        MPU6050_TEMP_OUT_L                0x42
  15. #define        MPU6050_GYRO_XOUT_H                0x43
  16. #define        MPU6050_GYRO_XOUT_L                0x44
  17. #define        MPU6050_GYRO_YOUT_H                0x45
  18. #define        MPU6050_GYRO_YOUT_L                0x46
  19. #define        MPU6050_GYRO_ZOUT_H                0x47
  20. #define        MPU6050_GYRO_ZOUT_L                0x48
  21. #define        MPU6050_PWR_MGMT_1                0x6B
  22. #define        MPU6050_PWR_MGMT_2                0x6C
  23. #define        MPU6050_WHO_AM_I                0x75
  24. #endif
复制代码
main.c
  1. #include "stm32f10x.h"                  // Device
  2. #include "Delay.h"
  3. #include "MPU6050.h"
  4. int16_t AX, AY, AZ, GX, GY, GZ;
  5. int main(void)
  6. {
  7.     OLED_Init();
  8.     MPU6050_Init();
  9.    
  10.     OLED_ShowString(1, 1, "ID:");
  11.     OLED_ShowHexNum(1, 4, MPU6050_GetID(), 2);
  12.    
  13.     OLED_ShowString(1, 7, "Acc|Gyro");
  14.     OLED_ShowString(2, 1, "X       |");
  15.     OLED_ShowString(3, 1, "Y       |");
  16.     OLED_ShowString(4, 1, "Z       |");
  17.    
  18.     while(1)
  19.     {
  20.         MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
  21.         OLED_ShowSignedNum(2, 3, AX, 5);
  22.         OLED_ShowSignedNum(3, 3, AY, 5);
  23.         OLED_ShowSignedNum(4, 3, AZ, 5);
  24.         OLED_ShowSignedNum(2, 10, GX, 5);
  25.         OLED_ShowSignedNum(3, 10, GY, 5);
  26.         OLED_ShowSignedNum(4, 10, GZ, 5);
  27.     }
  28. }
复制代码
其他引用的头文件和c代码可在此处查阅:Delay.h(【江协STM32】3-2 LED闪耀&LED流水灯&蜂鸣器,第1.3节) 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

花瓣小跑

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表