【STM32】I2C练习,HAL库读取MPU6050角度陀螺仪

时间:2024-10-30 08:33:08

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值计算出实际加速度。