国产risc-v微控制器读取MPU6050
MPU6050是一个6轴姿态传感器(3轴加速度计和3轴陀螺仪传感器),可以测量芯片自身X、Y、Z轴的加速度、角度参数,通过数据融合,可以得到姿态角。
mpu6050在运动控制、车辆和飞行器控制等方面应用非常的多。
MPU6050作为一个6轴姿态传感器,其中3轴加速度计为x、y、z三个方向的加速度,3轴陀螺仪传感器为围绕x、y、z旋转的加速度。
在MPU6050中,X、Y、Z轴都具有一个加速度计,假设芯片里有6个测力的秤组成一个正方体,正方体内部放一个大小正好的单位质量小球,小球压在一个面上,就会产生对应轴的数据输出。如果压在上面为正值,压在下面为负值,6个面测的力就是3个轴的加速度值。
MPU6050芯片内部的陀螺仪测量的实时角速度,分别表示了此时芯片绕X、Y、Z轴旋转的角速度。对角速度进行积分,就可以得到角度。
本帖最后由 点赞 于 2025-7-27 20:34 编辑
MPU6050参数:
一、加速度计
量程:±2g、±4g、±8g、±16g(默认±2g)。
分辨率:16位ADC。
二、陀螺仪
量程:±250°/s、±500°/s、±1000°/s、±2000°/s(默认±250°/s)。
灵敏度误差:±1%。
三、其他特性
内置温度传感器(用于漂移补偿)。
数字运动处理器(DMP):支持四元数/欧拉角输出,减轻主控负担。
采样率:加速度计1kHz,陀螺仪8kHz(DMP融合输出200Hz)。
四、电气特性
工作电压:2.375V~3.46V(模块支持3.3V/5V输入)。功耗:6轴模式≤3mA,睡眠模式5μA。接口:I2C(400kHz,默认地址0x68/0x69),SPI(仅MPU6000支持)
电路原理图
MPU6050 模块内部自带稳压电路,可兼容 3.3V/5V 的供电电压,采用先进的数字滤波技术,提高精度同时抑制了测量噪声。通讯方面,MPU6050 保留了 IIC 接口,高级用户能够采样底层测量数据。值得一提的是,芯片集成了 DMP (Digital Motion Processor)数字动态处理器(以后会用到,实现平衡小车姿体平衡)从陀螺仪、加速度计以及外接的传感器接收并处理数据,处理结果可以从 DMP 寄存器读出,或通过 FIFO(First In First Out) 缓冲。下图为模块的原理图:
本帖最后由 点赞 于 2025-7-27 20:52 编辑
HPM5361与MPU6050的连接:
使用的HPM5361开发板P1接口有两组I2C接口,这里使用I2C0接口,及PB03和PB02引脚。
这两个引脚,在开发板上已经有了10K的上拉电阻,使用的mpu6050上也有4.7k的上拉电阻,这里可以共存,不需要特殊处理。
本帖最后由 点赞 于 2025-7-27 21:16 编辑
工程创建及资源配置:
一、新建工程
打开RT-Thread Studio,在菜单选:文件-->新建-->RT-Thread 项目,在打开的“新建项目”选项卡,填写项目名称,选择“基于开发板”,其他选项默认,点“完成”按钮。
二、配置工程资源
双击工程下的“RT-Thread Setting”,打开RT-Thread Setting选项卡。
在软件包项目下,点“添加软件包”,在打开的搜索页面搜索“mpu”,在出现的项目中点“mpu6xxx“软件包的”添加“按钮。
我这里已经添加完毕。
在”软件包“标签页,配置mpu6xxx软件包:
在”组件“标签页打开”使用I2C设备驱动程序“
在”硬件“标签页,打开”Enable I2C“选项,并打开I2C0。
本帖最后由 点赞 于 2025-7-27 21:34 编辑
主要代码:
mpu6xxx.c文件:
写寄存器:
static rt_err_t mpu6xxx_write_reg(struct mpu6xxx_device *dev, rt_uint8_t reg, rt_uint8_t data)
{
rt_int8_t res = 0;
#ifdef RT_USING_I2C
struct rt_i2c_msg msgs;
rt_uint8_t buf = {reg, data};
#endif
if (dev->bus->type == RT_Device_Class_I2CBUS)
{
#ifdef RT_USING_I2C
msgs.addr= dev->i2c_addr; /* slave address */
msgs.flags = RT_I2C_WR; /* write flag */
msgs.buf = buf; /* Send data pointer */
msgs.len = 2;
if (rt_i2c_transfer((struct rt_i2c_bus_device *)dev->bus, &msgs, 1) == 1)
{
res = RT_EOK;
}
else
{
res = -RT_ERROR;
}
#endif
}
else
{
#ifdef RT_USING_SPI
res = rt_spi_send_then_send((struct rt_spi_device *)dev->bus, ®, 1, &data, 1);
#endif
}
return res;
}读寄存器:
static rt_err_t mpu6xxx_read_regs(struct mpu6xxx_device *dev, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf)
{
rt_int8_t res = 0;
#ifdef RT_USING_I2C
struct rt_i2c_msg msgs;
#endif
#ifdef RT_USING_SPI
rt_uint8_t tmp;
#endif
if (dev->bus->type == RT_Device_Class_I2CBUS)
{
#ifdef RT_USING_I2C
msgs.addr= dev->i2c_addr; /* Slave address */
msgs.flags = RT_I2C_WR; /* Write flag */
msgs.buf = ® /* Slave register address */
msgs.len = 1; /* Number of bytes sent */
msgs.addr= dev->i2c_addr; /* Slave address */
msgs.flags = RT_I2C_RD; /* Read flag */
msgs.buf = buf; /* Read data pointer */
msgs.len = len; /* Number of bytes read */
if (rt_i2c_transfer((struct rt_i2c_bus_device *)dev->bus, msgs, 2) == 2)
{
res = RT_EOK;
}
else
{
res = -RT_ERROR;
}
#endif
}
else
{
#ifdef RT_USING_SPI
//The first bit of the first byte contains the Read/Write bit and indicates the Read (1) or Write (0) operation.
tmp = reg | 0x80;
res = rt_spi_send_then_recv((struct rt_spi_device *)dev->bus, &tmp, 1, buf, len);
#endif
}
return res;
}写寄存器的bit位:
static rt_err_t mpu6xxx_write_bit(struct mpu6xxx_device *dev, rt_uint8_t reg, rt_uint8_t bit, rt_uint8_t data)
{
rt_uint8_t byte;
rt_err_t res;
res = mpu6xxx_read_regs(dev, reg, 1, &byte);
if (res != RT_EOK)
{
return res;
}
byte = (data != 0) ? (byte | (1 << bit)) : (byte & ~(1 << bit));
return mpu6xxx_write_reg(dev, reg, byte);
}读寄存器的bit位:
static rt_err_t mpu6xxx_read_bit(struct mpu6xxx_device *dev, rt_uint8_t reg, rt_uint8_t bit, rt_uint8_t *data)
{
rt_uint8_t byte;
rt_err_t res;
res = mpu6xxx_read_regs(dev, reg, 1, &byte);
if (res != RT_EOK)
{
return res;
}
*data = byte & (1 << bit);
return RT_EOK;
}写寄存器的多个bit位:
static rt_err_t mpu6xxx_write_bits(struct mpu6xxx_device *dev, rt_uint8_t reg, rt_uint8_t start_bit, rt_uint8_t len, rt_uint8_t data)
{
rt_uint8_t byte, mask;
rt_err_t res;
res = mpu6xxx_read_regs(dev, reg, 1, &byte);
if (res != RT_EOK)
{
return res;
}
mask = ((1 << len) - 1) << (start_bit - len + 1);
data <<= (start_bit - len + 1); // shift data into correct position
data &= mask; // zero all non-important bits in data
byte &= ~(mask); // zero all important bits in existing byte
byte |= data; // combine data with existing byte
return mpu6xxx_write_reg(dev, reg, byte);
}读寄存器的多个bit位:
static rt_err_t mpu6xxx_read_bits(struct mpu6xxx_device *dev, rt_uint8_t reg, rt_uint8_t start_bit, rt_uint8_t len, rt_uint8_t *data)
{
rt_uint8_t byte, mask;
rt_err_t res;
res = mpu6xxx_read_regs(dev, reg, 1, &byte);
if (res != RT_EOK)
{
return res;
}
mask = ((1 << len) - 1) << (start_bit - len + 1);
byte &= mask;
byte >>= (start_bit - len + 1);
*data = byte;
return RT_EOK;
}获取加速度原始数据:
static rt_err_t mpu6xxx_get_accel_raw(struct mpu6xxx_device *dev, struct mpu6xxx_3axes *accel)
{
rt_uint8_t buffer;
rt_err_t res;
res = mpu6xxx_read_regs(dev, MPU6XXX_RA_ACCEL_XOUT_H, 6, buffer);
if (res != RT_EOK)
{
return res;
}
accel->x = ((rt_uint16_t)buffer << 8) + buffer;
accel->y = ((rt_uint16_t)buffer << 8) + buffer;
accel->z = ((rt_uint16_t)buffer << 8) + buffer;
return RT_EOK;
}获取陀螺仪原始数据:
static rt_err_t mpu6xxx_get_gyro_raw(struct mpu6xxx_device *dev, struct mpu6xxx_3axes *gyro)
{
rt_uint8_t buffer;
rt_err_t res;
res = mpu6xxx_read_regs(dev, MPU6XXX_RA_GYRO_XOUT_H, 6, buffer);
if (res != RT_EOK)
{
return res;
}
gyro->x = ((rt_uint16_t)buffer << 8) + buffer;
gyro->y = ((rt_uint16_t)buffer << 8) + buffer;
gyro->z = ((rt_uint16_t)buffer << 8) + buffer;
return RT_EOK;
}获取mpu6050内部温度传感器原始数据:
static rt_err_t mpu6xxx_get_temp_raw(struct mpu6xxx_device *dev, rt_int16_t *temp)
{
rt_uint8_t buffer;
rt_err_t res;
res = mpu6xxx_read_regs(dev, MPU6XXX_RA_TEMP_OUT_H, 2, buffer);
if (res != RT_EOK)
{
return res;
}
*temp = ((rt_uint16_t)buffer << 8) + buffer;
return RT_EOK;
}获取mpu6050的配置参数:
static rt_err_t mpu6xxx_get_param(struct mpu6xxx_device *dev, enum mpu6xxx_cmd cmd, rt_uint16_t *param)
{
rt_uint8_t data = 0;
rt_err_t res = RT_EOK;
RT_ASSERT(dev);
switch (cmd)
{
case MPU6XXX_GYRO_RANGE:/* Gyroscope full scale range */
res = mpu6xxx_read_bits(dev, MPU6XXX_RA_GYRO_CONFIG, MPU6XXX_GCONFIG_FS_SEL_BIT, MPU6XXX_GCONFIG_FS_SEL_LENGTH, &data);
*param = data;
break;
case MPU6XXX_ACCEL_RANGE: /* Accelerometer full scale range */
res = mpu6xxx_read_bits(dev, MPU6XXX_RA_ACCEL_CONFIG, MPU6XXX_ACONFIG_AFS_SEL_BIT, MPU6XXX_ACONFIG_AFS_SEL_LENGTH, &data);
*param = data;
break;
case MPU6XXX_DLPF_CONFIG: /* Digital Low Pass Filter */
res = mpu6xxx_read_bits(dev, MPU6XXX_RA_CONFIG, MPU6XXX_CFG_DLPF_CFG_BIT, MPU6XXX_CFG_DLPF_CFG_LENGTH, &data);
*param = data;
break;
case MPU6XXX_SAMPLE_RATE: /* Sample Rate */
/* Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV) */
res = mpu6xxx_read_bits(dev, MPU6XXX_RA_CONFIG, MPU6XXX_CFG_DLPF_CFG_BIT, MPU6XXX_CFG_DLPF_CFG_LENGTH, &data);
if (res != RT_EOK)
{
break;
}
if (data == 0 || data == 7) /* dlpf is disable */
{
res = mpu6xxx_read_regs(dev, MPU6XXX_RA_SMPLRT_DIV, 1, &data);
*param = 8000 / (data + 1);
}
else /* dlpf is enable */
{
res = mpu6xxx_read_regs(dev, MPU6XXX_RA_SMPLRT_DIV, 1, &data);
*param = 1000 / (data + 1);
}
break;
case MPU6XXX_SLEEP: /* sleep mode */
res = mpu6xxx_read_bit(dev, MPU6XXX_RA_PWR_MGMT_1, MPU6XXX_PWR1_SLEEP_BIT, &data);
*param = data;
break;
}
return res;
}配置mpu6050参数:
rt_err_t mpu6xxx_set_param(struct mpu6xxx_device *dev, enum mpu6xxx_cmd cmd, rt_uint16_t param)
{
rt_uint8_t data = 0;
rt_err_t res = RT_EOK;
RT_ASSERT(dev);
switch (cmd)
{
case MPU6XXX_GYRO_RANGE:/* Gyroscope full scale range */
res = mpu6xxx_write_bits(dev, MPU6XXX_RA_GYRO_CONFIG, MPU6XXX_GCONFIG_FS_SEL_BIT, MPU6XXX_GCONFIG_FS_SEL_LENGTH, param);
dev->config.gyro_range = param;
break;
case MPU6XXX_ACCEL_RANGE: /* Accelerometer full scale range */
res = mpu6xxx_write_bits(dev, MPU6XXX_RA_ACCEL_CONFIG, MPU6XXX_ACONFIG_AFS_SEL_BIT, MPU6XXX_ACONFIG_AFS_SEL_LENGTH, param);
dev->config.accel_range = param;
break;
case MPU6XXX_DLPF_CONFIG: /* Digital Low Pass Filter */
res = mpu6xxx_write_bits(dev, MPU6XXX_RA_CONFIG, MPU6XXX_CFG_DLPF_CFG_BIT, MPU6XXX_CFG_DLPF_CFG_LENGTH, param);
break;
case MPU6XXX_SAMPLE_RATE: /* Sample Rate = 16-bit unsigned value.
Sample Rate = HZ when dlpf is enable
Sample Rate = HZ when dlpf is disable */
//Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
res = mpu6xxx_read_bits(dev, MPU6XXX_RA_CONFIG, MPU6XXX_CFG_DLPF_CFG_BIT, MPU6XXX_CFG_DLPF_CFG_LENGTH, &data);
if (res != RT_EOK)
{
break;
}
if (data == 0 || data == 7) /* dlpf is disable */
{
if (param > 8000)
data = 0;
else if (param < 32)
data = 0xFF;
else
data = 8000 / param - 1;
}
else /* dlpf is enable */
{
if (param > 1000)
data = 0;
else if (param < 4)
data = 0xFF;
else
data = 1000 / param - 1;
}
res = mpu6xxx_write_reg(dev, MPU6XXX_RA_SMPLRT_DIV, data);
break;
case MPU6XXX_SLEEP: /* Configure sleep mode */
res = mpu6xxx_write_bit(dev, MPU6XXX_RA_PWR_MGMT_1, MPU6XXX_PWR1_SLEEP_BIT, param);
break;
}
return res;
}获取加速度值:
rt_err_t mpu6xxx_get_accel(struct mpu6xxx_device *dev, struct mpu6xxx_3axes *accel)
{
struct mpu6xxx_3axes tmp;
rt_uint16_t sen;
rt_err_t res;
res = mpu6xxx_get_accel_raw(dev, &tmp);
if (res != RT_EOK)
{
return res;
}
sen = MPU6XXX_ACCEL_SEN >> dev->config.accel_range;
accel->x = (rt_int32_t)tmp.x * 1000 / sen;
accel->y = (rt_int32_t)tmp.y * 1000 / sen;
accel->z = (rt_int32_t)tmp.z * 1000 / sen;
return RT_EOK;
}获取陀螺仪数据:
rt_err_t mpu6xxx_get_gyro(struct mpu6xxx_device *dev, struct mpu6xxx_3axes *gyro)
{
struct mpu6xxx_3axes tmp;
rt_uint16_t sen;
rt_err_t res;
res = mpu6xxx_get_gyro_raw(dev, &tmp);
if (res != RT_EOK)
{
return res;
}
sen = MPU6XXX_GYRO_SEN >> dev->config.gyro_range;
gyro->x = (rt_int32_t)tmp.x * 100 / sen;
gyro->y = (rt_int32_t)tmp.y * 100 / sen;
gyro->z = (rt_int32_t)tmp.z * 100 / sen;
return RT_EOK;
}获取温度:
rt_err_t mpu6xxx_get_temp(struct mpu6xxx_device *dev, float *temp)
{
rt_int16_t tmp;
rt_err_t res;
res = mpu6xxx_get_temp_raw(dev, &tmp);
if (res != RT_EOK)
{
return res;
}
if (dev->id == MPU6050_WHO_AM_I)
{
/* mpu60x0: Temperature in degrees C = (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */
*temp = (double)tmp / MPU60X0_TEMP_SEN + MPU60X0_TEMP_OFFSET;
}
else
{
/* mpu6500:((TEMP_OUT - RoomTemp_Offset)/Temp_Sensitivity)+ 21degC */
*temp = (double)tmp / MPU6500_TEMP_SEN + MPU6500_TEMP_OFFSET;
}
return RT_EOK;
}mpu6050初始化:
struct mpu6xxx_device *mpu6xxx_init(const char *dev_name, rt_uint8_t param)
{
struct mpu6xxx_device *dev = RT_NULL;
rt_uint8_t reg = 0xFF, res = RT_EOK;
RT_ASSERT(dev_name);
dev = rt_calloc(1, sizeof(struct mpu6xxx_device));
if (dev == RT_NULL)
{
LOG_E("Can't allocate memory for mpu6xxx device on '%s' ", dev_name);
goto __exit;
}
dev->bus = rt_device_find(dev_name);
if (dev->bus == RT_NULL)
{
LOG_E("Can't find device:'%s'", dev_name);
goto __exit;
}
if (dev->bus->type == RT_Device_Class_I2CBUS)
{
if (param != RT_NULL)
{
dev->i2c_addr = param;
}
else
{
/* find mpu6xxx device at address: 0x68 */
dev->i2c_addr = MPU6XXX_ADDRESS_AD0_LOW;
if (mpu6xxx_read_regs(dev, MPU6XXX_RA_WHO_AM_I, 1, ®) != RT_EOK)
{
/* find mpu6xxx device at address 0x69 */
dev->i2c_addr = MPU6XXX_ADDRESS_AD0_HIGH;
if (mpu6xxx_read_regs(dev, MPU6XXX_RA_WHO_AM_I, 1, ®) != RT_EOK)
{
LOG_E("Can't find device at '%s'!", dev_name);
goto __exit;
}
}
LOG_D("Device i2c address is:'0x%x'!", dev->i2c_addr);
}
}
else if (dev->bus->type == RT_Device_Class_SPIDevice)
{
#ifdef RT_USING_SPI
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
cfg.max_hz = MPU60X0_SPI_MAX_SPEED; /* Set spi max speed */
rt_spi_configure((struct rt_spi_device *)dev->bus, &cfg);
#endif
}
else
{
LOG_E("Unsupported device:'%s'!", dev_name);
goto __exit;
}
if (mpu6xxx_read_regs(dev, MPU6XXX_RA_WHO_AM_I, 1, ®) != RT_EOK)
{
LOG_E("Failed to read device id!");
goto __exit;
}
dev->id = reg;
switch (dev->id)
{
case MPU6050_WHO_AM_I:
LOG_I("Find device: mpu6050!");
break;
case MPU6500_WHO_AM_I:
LOG_I("Find device: mpu6500!");
break;
case MPU9250_WHO_AM_I:
LOG_I("Find device: mpu9250!");
break;
case ICM20608G_WHO_AM_I:
case ICM20608D_WHO_AM_I:
LOG_I("Find device: icm20608!");
break;
case 0xFF:
LOG_E("No device connection!");
goto __exit;
default:
LOG_W("Unknown device id: 0x%x!", reg);
}
res += mpu6xxx_get_param(dev, MPU6XXX_ACCEL_RANGE, &dev->config.accel_range);
res += mpu6xxx_get_param(dev, MPU6XXX_GYRO_RANGE, &dev->config.gyro_range);
res += mpu6xxx_write_bits(dev, MPU6XXX_RA_PWR_MGMT_1, MPU6XXX_PWR1_CLKSEL_BIT, MPU6XXX_PWR1_CLKSEL_LENGTH, MPU6XXX_CLOCK_PLL_XGYRO);
res += mpu6xxx_set_param(dev, MPU6XXX_GYRO_RANGE, MPU6XXX_GYRO_RANGE_250DPS);
res += mpu6xxx_set_param(dev, MPU6XXX_ACCEL_RANGE, MPU6XXX_ACCEL_RANGE_2G);
res += mpu6xxx_set_param(dev, MPU6XXX_SLEEP, MPU6XXX_SLEEP_DISABLE);
#ifdef PKG_USING_MPU6XXX_MAG
mpu6xxx_write_reg(dev, MPU6XXX_RA_USER_CTRL, 0x20);
mpu92_mag_write_reg(dev, AK8963_REG_CNTL2, 0x01); /* Reset Device */
rt_thread_mdelay(1);
mpu92_mag_write_reg(dev, AK8963_REG_CNTL1, 0x00); /* Power-down mode */
mpu92_mag_write_reg(dev, AK8963_REG_CNTL1, 0x0F); /* Fuse ROM access mode */
mpu92_mag_write_reg(dev, AK8963_REG_CNTL1, 0x00); /* Power-down mode */
rt_thread_mdelay(1); // 100us
mpu92_mag_write_reg(dev, AK8963_REG_CNTL1, 0x16); /* 16bits and Continuous measurement mode 2 */
/* config mpu9250 i2c */
rt_thread_mdelay(2);
mpu6xxx_write_reg(dev, MPU6XXX_RA_I2C_MST_CTRL, 0x5D);
rt_thread_mdelay(2);
mpu6xxx_write_reg(dev, MPU6XXX_RA_I2C_SLV0_ADDR, AK8963_I2C_ADDR | 0x80);
rt_thread_mdelay(2);
mpu6xxx_write_reg(dev, MPU6XXX_RA_I2C_SLV0_REG, AK8963_REG_ST1);
rt_thread_mdelay(2);
mpu6xxx_write_reg(dev, MPU6XXX_RA_I2C_SLV0_CTRL, MPU6500_I2C_SLVx_EN | 8);
rt_thread_mdelay(2);
mpu6xxx_write_reg(dev, MPU6XXX_RA_I2C_SLV4_CTRL, 0x09);
rt_thread_mdelay(2);
mpu6xxx_write_reg(dev, MPU6XXX_RA_I2C_MST_DELAY_CTRL, 0x81);
#endif
if (res == RT_EOK)
{
LOG_I("Device init succeed!");
}
else
{
LOG_W("Error in device initialization!");
}
return dev;
__exit:
if (dev != RT_NULL)
{
rt_free(dev);
}
return RT_NULL;
}
效果验证:
在串口调试工具,输入:
效果验证:
在串口调试工具,输入:mpu6xxx_test会打印mpu6050的加速度和陀螺仪数据如下:
老哥求例程 谢谢分享,大家一起进步~!谢谢分享,大家一起进步~! MPU6050的数据融合技术确实在很多领域都有应用,特别是在需要精确姿态控制的场合。
页:
[1]