I2C练习
- MPU6050简介
- 寄存器查询表格
- STM32CubeMx配置
- 代码文件
- 文件
- 文件
- 文件
- 总结
- 2024-10-06补充
- 地址问题:0X68还是0x69?
- 为什么发送指令的时候是发送地址位是0xd1、0xd0呢?
- 地址读到0xd1(DEC 209)
- 相关寄存器解析
MPU6050简介
MPU-6000(6050)为全球首例整合性6轴运动处理组件,相较于多组件方案,免除了组合陀螺仪与加速器时间轴之差的问题,减少了大量的封装空间。当连接到三轴磁强计时,MPU-60X0提供完整的9轴运动融合输出到其主I2C或SPI端口(SPI仅在MPU-6000上可用)。
寄存器查询表格
寄存器地址 | 寄存器内容 |
---|---|
0X3B | X轴加速度测量值高位 |
0X3C | X轴加速度测量值低位 |
0X3D | Y轴加速度测量值高位 |
0X3E | Y轴加速度测量值低位 |
0X3F | Z轴加速度测量值高位 |
0X40 | Z轴加速度测量值低位 |
0X41 | 温度测量值高位 |
0X42 | 温度测量值低位 |
0X43 | X轴角度测量值高位 |
0X34 | X轴角度测量值低位 |
0X45 | Y轴角度测量值高位 |
0X46 | Y轴角度测量值低位 |
0X47 | Z轴角度测量值高位 |
0X48 | Z轴角度测量值低位 |
0X6B | 电源管理,典型值:0x00(正常启用) |
STM32CubeMx配置
配置使用外部高速时钟
配置调试方式
配置I2C,这里只需要选择I2C即可其余配置默认,记住自己选择的I2C引脚。
配置时钟
生成工程
代码文件
文件
#ifndef __MPU6050_H
#define __MPU6050_H
#include ""
#define SMPLRT_DIV 0x19 // 采样率分频,典型值:0x07(125Hz) */
#define CONFIG 0x1A // 低通滤波频率,典型值:0x06(5Hz) */
#define GYRO_CONFIG 0x1B // 陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s) */
#define ACCEL_CONFIG 0x1C // 加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz) */
#define ACCEL_XOUT_H 0x3B // 存储最近的X轴、Y轴、Z轴加速度感应器的测量值 */
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41 // 存储的最近温度传感器的测量值 */
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43 // 存储最近的X轴、Y轴、Z轴陀螺仪感应器的测量值 */
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B // 电源管理,典型值:0x00(正常启用) */
#define WHO_AM_I 0x75 // IIC地址寄存器(默认数值0x68,只读) */
// HAL库的读写只需要使用7位地址
#define MPU6050_ADDR_AD0_LOW 0x68 // AD0低电平时7位地址为0X68 iic写时许发送0XD0
#define MPU6050_ADDR_AD0_HIGH 0x69
typedef struct{
// 角速度
float Accel_X;
float Accel_Y;
float Accel_Z;
// 角度
float Gyro_X;
float Gyro_Y;
float Gyro_Z;
// 温度
float Temp;
}MPU6050DATATYPE;
extern MPU6050DATATYPE Mpu6050_Data;
extern I2C_HandleTypeDef hi2c1;
extern I2C_HandleTypeDef hi2c2;
int16_t Sensor_I2C2_Serch(void);
int8_t MPU6050_Init();
int8_t Sensor_I2C2_ReadOneByte(uint16_t DevAddr, uint16_t MemAddr, uint8_t *oData);
int8_t Sensor_I2C2_WriteOneByte(uint16_t DevAddr, uint16_t MemAddr, uint8_t *iData);
void MPU6050_Read_Accel(void);
void MPU6050_Read_Gyro(void);
void MPU6050_Read_Temp(void);
#endif
文件
#include ""
static int16_t Mpu6050Addr = 0x69;
MPU6050DATATYPE Mpu6050_Data;
int8_t Sensor_I2C2_Read(uint16_t DevAddr, uint16_t MemAddr, uint8_t *oData, uint8_t DataLen)
{
return HAL_I2C_Mem_Read(&hi2c2,(DevAddr<<1)|0,MemAddr,1,oData,DataLen,1000);
}
int8_t Sensor_I2C2_Write(uint16_t DevAddr, uint16_t MemAddr, uint8_t *iData, uint8_t DataLen)
{
return HAL_I2C_Mem_Write(&hi2c2,(DevAddr<<1)|1,MemAddr,1,iData,DataLen,1000);
}
int16_t Sensor_I2C2_Serch(void)
{
for(uint8_t i = 1; i < 255; i++)
{
if(HAL_I2C_IsDeviceReady(&hi2c2, i, 1, 1000) == HAL_OK)
{
Mpu6050Addr = i>>1;
return i>>1;
}
}
return -1; // 搜索失败
}
int8_t MPU6050_Init()
{
uint8_t check;
check = 0x80;
Sensor_I2C2_Write(Mpu6050Addr,PWR_MGMT_1,&check, 1); // 复位
HAL_Delay(100);
check = 0x00;
Sensor_I2C2_Write(Mpu6050Addr,PWR_MGMT_1,&check, 1); // 唤醒
if(Sensor_I2C2_Serch() != -1) // 确认设备用 地址寄存器
{
printf("Sensor_I2C2_Serch:%x\r\n", Mpu6050Addr);
check = 0x07;
Sensor_I2C2_Write(Mpu6050Addr,SMPLRT_DIV,&check, 1); // 1Khz的速率
check = 0x00;
Sensor_I2C2_Write(Mpu6050Addr,ACCEL_CONFIG,&check, 1); // 加速度配置
check = 0x00;
Sensor_I2C2_Write(Mpu6050Addr,GYRO_CONFIG,&check, 1); // 陀螺配置
HAL_Delay(100);
return 0;
}
return -1;
}
void MPU6050_Read_Accel(void)
{
uint8_t Read_Buf[6];
// 寄存器依次是加速度X高 - 加速度X低 - 加速度Y高位 - 加速度Y低位 - 加速度Z高位 - 加速度度Z低位
if(HAL_OK != Sensor_I2C2_Read(Mpu6050Addr, ACCEL_XOUT_H, Read_Buf, 6))
{
printf("Sensor_I2C2_Read Mpu6050Addr, GYRO_XOUT_H Error!\r\n");
}
Mpu6050_Data.Accel_X = (int16_t)(Read_Buf[0] << 8 | Read_Buf[1]);
Mpu6050_Data.Accel_Y = (int16_t)(Read_Buf[2] << 8 | Read_Buf[3]);
Mpu6050_Data.Accel_Z = (int16_t)(Read_Buf[4] << 8 | Read_Buf[5]);
Mpu6050_Data.Accel_X = Mpu6050_Data.Accel_X / 16384.0f;
Mpu6050_Data.Accel_Y = Mpu6050_Data.Accel_Y / 16384.0f;
Mpu6050_Data.Accel_Z = Mpu6050_Data.Accel_Z / 16384.0f;
}
void MPU6050_Read_Gyro(void)
{
uint8_t Read_Buf[6];
// 寄存器依次是角度X高 - 角度X低 - 角度Y高位 - 角度Y低位 - 角度Z高位 - 角度Z低位
if(HAL_OK != Sensor_I2C2_Read(Mpu6050Addr, GYRO_XOUT_H, Read_Buf, 6))
{
printf("Sensor_I2C2_Read Mpu6050Addr, GYRO_XOUT_H Error!\r\n");
return;
}
Mpu6050_Data.Gyro_X = (int16_t)(Read_Buf[0] << 8 | Read_Buf[1]);
Mpu6050_Data.Gyro_Y = (int16_t)(Read_Buf[2] << 8 | Read_Buf[3]);
Mpu6050_Data.Gyro_Z = (int16_t)(Read_Buf[4] << 8 | Read_Buf[5]);
Mpu6050_Data.Gyro_X = Mpu6050_Data.Gyro_X / 131.0f;
Mpu6050_Data.Gyro_Y = Mpu6050_Data.Gyro_Y / 131.0f;
Mpu6050_Data.Gyro_Z = Mpu6050_Data.Gyro_Z / 131.0f;
}
void MPU6050_Read_Temp(void)
{
uint8_t Read_Buf[2];
int ret = Sensor_I2C2_Read(Mpu6050Addr, TEMP_OUT_H, Read_Buf, 2);
if(HAL_OK != ret)
{
printf("Sensor_I2C2_Read Mpu6050Addr, TEMP_OUT_H Error! %d\r\n", ret);
return;
}
Mpu6050_Data.Temp = (int16_t)(Read_Buf[0] << 8 | Read_Buf[1]);
Mpu6050_Data.Temp = 36.53f + (Mpu6050_Data.Temp / 340.0f);
}
文件
if(MPU6050_Init() != -1);
while(1)
{
HAL_Delay(500);
MPU6050_Read_Accel();
MPU6050_Read_Gyro();
MPU6050_Read_Temp();
}
总结
MPU6050的数据的读取非常的简单,但是这个数据还无法直接使用,这里我只是用来联系I2C的读取,对于六轴的算法还有很多,可以转移到别的博主文章进行学习参考。
2024-10-06补充
最近有很多反馈MPU6050初始化失败的情况,在这里做一个集合补充
地址问题:0X68还是0x69?
从数据手册上来看AD0引脚表示的是I2C Slave Address的地址低位,如果引脚为低电平地址为0x68如果为高电平地址为0x68;
为什么发送指令的时候是发送地址位是0xd1、0xd0呢?
这里因为I2C中地址有一位(最低位)是属于读写方向,例如这里slaves地址位0x68(1101000)其实是指的7位地址为0x68,那如果我们是发送写寄存器指令的实际操作就是地址左移一位再或上0或则1实现寄存器读写(0表示写,1表示读),如图所示
地址读到0xd1(DEC 209)
通过ID寄存器读到的地址为0xd1,并且越过ID校验数据读取也是有问题的,这种情况通过一些资料查询有可能是以下原因导致:
1、OpenEdv论坛:
本人前由于用的杜邦线都是一条一条单散的,导致初始化一直不成功,ID读到一直为 0XD1,换了几个 6050
都是一样。最后换了一排完好的杜邦线(4跳线粘在一起的)结果就成功了。 本人用的杜邦线长达
30cm,挺长的,但依然没问题,所以用一排完好的杜邦线很重要。
杜邦线的质量问题
原文链接:/posts/list/0/
2、博客园:
连线不稳导致,在很多通信上都有这种情况导致的问题,链接一但不稳定或则链接不好都可以会导致该情况的发生
原文链接:/harper-blogs/p/
3、****
1、首先检查一下电路是否连接正确,特别是第20引脚的电荷泵电容。
MPU6050电路问题,在一些MPU60XX的中文手册中电容的电荷泵电容标注有问题,应按照官方的手册进行修改;
原文链接:/qq_24025329/article/details/120230711
相关寄存器解析
3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
ADC和数据存储
16位ADC:MPU6050集成了16位的ADC,用于将模拟信号转换为数字信号,量化范围为-32768到32767。
量化过程:ADC将模拟信号转换为数字信号,并以两个字节进行存储。
加速度计满量程示例:
±16g:
读取的ADC值为最大值32768时,对应实际加速度为16g。
ADC值为32768的一半(16384)时,对应加速度为8g。
±2g:
读取的ADC值为最大值32768时,对应实际加速度为2g。
ADC值为32768的一半(16384)时,对应加速度为1g。
满量程选择
剧烈运动:选择较大的满量程,确保测量范围足够大。
平缓运动:选择较小的满量程,提升测量分辨率精度。
测量分辨率:
满量程越小,测量分辨率越高,测量越精细。
满量程越大,测量范围越广。
ADC值与加速度值呈线性关系,可以通过乘一个系数从ADC值计算出实际加速度。