NRF52832通过I²C读取MPU9250原始数据
一、项目概述
本项目使用Nordic nRF52832蓝牙SoC通过I²C接口读取MPU9250九轴传感器的原始数据,包括加速度计、陀螺仪和磁力计数据。项目包含完整的硬件连接、寄存器配置和数据读取实现,适用于运动追踪、姿态检测等应用。
二、系统架构
graph TD
A[nRF52832] -->|I²C| B[MPU9250]
B -->|加速度计| C[3轴数据]
B -->|陀螺仪| D[3轴数据]
B -->|磁力计| E[3轴数据]
A -->|UART| F[PC/终端]
A -->|BLE| G[手机APP]
三、硬件连接
| nRF52832引脚 | MPU9250引脚 | 功能 | 备注 |
|---|---|---|---|
| P0.24 (SDA) | SDA | I²C数据线 | 需4.7KΩ上拉电阻 |
| P0.25 (SCL) | SCL | I²C时钟线 | 需4.7KΩ上拉电阻 |
| 3V3 | VCC | 电源 | 3.3V供电 |
| GND | GND | 地 | 共地 |
| P0.13 | INT | 中断信号 | 数据就绪中断(可选) |
四、软件实现
1. 主程序框架
#include "nrf.h"
#include "nrf_drv_twi.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "boards.h"
#include "bsp.h"
#include "nrf_delay.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
// I²C配置
#define MPU9250_ADDR 0x68 // AD0接地时为0x68
#define AK8963_ADDR 0x0C // 磁力计I²C地址
#define TWI_INSTANCE_ID 0
// MPU9250寄存器定义
#define WHO_AM_I 0x75
#define PWR_MGMT_1 0x6B
#define ACCEL_XOUT_H 0x3B
#define GYRO_XOUT_H 0x43
#define USER_CTRL 0x6A
#define I2C_MST_CTRL 0x24
#define EXT_SENS_DATA_00 0x49
#define AK8963_CNTL 0x0A
// 全局变量
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);
static volatile bool twi_tx_done = false;
static volatile bool twi_rx_done = false;
static uint8_t m_sample[6];
// 函数声明
void twi_handler(nrf_drv_twi_evt_t const *p_event, void *p_context);
ret_code_t mpu9250_init(void);
ret_code_t mpu9250_read_reg(uint8_t reg, uint8_t *data, uint8_t len);
ret_code_t mpu9250_write_reg(uint8_t reg, uint8_t data);
ret_code_t ak8963_init(void);
ret_code_t read_accel(short *accel);
ret_code_t read_gyro(short *gyro);
ret_code_t read_mag(short *mag);
void print_data(short *accel, short *gyro, short *mag);
2. I²C初始化
// TWI事件处理函数
void twi_handler(nrf_drv_twi_evt_t const *p_event, void *p_context) {
switch (p_event->type) {
case NRF_DRV_TWI_EVT_DONE:
switch (p_event->xfer_desc.type) {
case NRF_DRV_TWI_XFER_TX:
twi_tx_done = true;
break;
case NRF_DRV_TWI_XFER_RX:
twi_rx_done = true;
break;
case NRF_DRV_TWI_XFER_TXRX:
twi_rx_done = true;
break;
default:
break;
}
break;
case NRF_DRV_TWI_EVT_ADDRESS_NACK:
NRF_LOG_INFO("Address NACK");
break;
case NRF_DRV_TWI_EVT_DATA_NACK:
NRF_LOG_INFO("Data NACK");
break;
default:
break;
}
}
// I²C初始化
void twi_init(void) {
ret_code_t err_code;
const nrf_drv_twi_config_t twi_mpu9250_config = {
.scl = ARDUINO_SCL_PIN,
.sda = ARDUINO_SDA_PIN,
.frequency = NRF_DRV_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = false
};
err_code = nrf_drv_twi_init(&m_twi, &twi_mpu9250_config, twi_handler, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi);
}
3. MPU9250初始化
// MPU9250初始化
ret_code_t mpu9250_init(void) {
ret_code_t err_code;
uint8_t who_am_i;
// 检查设备ID
err_code = mpu9250_read_reg(WHO_AM_I, &who_am_i, 1);
if (err_code != NRF_SUCCESS || who_am_i != 0x71) {
NRF_LOG_ERROR("MPU9250 not found! ID: 0x%02X", who_am_i);
return NRF_ERROR_NOT_FOUND;
}
// 唤醒设备
err_code = mpu9250_write_reg(PWR_MGMT_1, 0x00);
if (err_code != NRF_SUCCESS) return err_code;
// 设置采样率分频器
err_code = mpu9250_write_reg(0x19, 0x07);
if (err_code != NRF_SUCCESS) return err_code;
// 设置DLPF
err_code = mpu9250_write_reg(0x1A, 0x06);
if (err_code != NRF_SUCCESS) return err_code;
// 设置陀螺仪量程 (±2000dps)
err_code = mpu9250_write_reg(0x1B, 0x18);
if (err_code != NRF_SUCCESS) return err_code;
// 设置加速度计量程 (±16g)
err_code = mpu9250_write_reg(0x1C, 0x18);
if (err_code != NRF_SUCCESS) return err_code;
// 启用磁力计旁路模式
err_code = mpu9250_write_reg(USER_CTRL, 0x00);
if (err_code != NRF_SUCCESS) return err_code;
nrf_delay_ms(10);
// 配置I²C主控制器
err_code = mpu9250_write_reg(I2C_MST_CTRL, 0x0D);
if (err_code != NRF_SUCCESS) return err_code;
return NRF_SUCCESS;
}
4. 磁力计初始化
// 磁力计初始化
ret_code_t ak8963_init(void) {
ret_code_t err_code;
uint8_t mag_id;
// 检查磁力计ID
err_code = mpu9250_read_reg(0x00, &mag_id, 1);
if (err_code != NRF_SUCCESS) return err_code;
NRF_LOG_INFO("AK8963 ID: 0x%02X", mag_id);
// 设置磁力计为连续测量模式1 (8Hz)
err_code = mpu9250_write_reg(AK8963_CNTL, 0x12);
if (err_code != NRF_SUCCESS) return err_code;
nrf_delay_ms(10);
return NRF_SUCCESS;
}
5. 数据读取函数
// 读取加速度计数据
ret_code_t read_accel(short *accel) {
ret_code_t err_code;
uint8_t reg = ACCEL_XOUT_H;
twi_tx_done = false;
nrf_drv_twi_xfer_desc_t tx_desc = NRF_DRV_TWI_XFER_DESC_TX(MPU9250_ADDR, ®, 1);
err_code = nrf_drv_twi_xfer(&m_twi, &tx_desc, NRF_DRV_TWI_FLAG_TX_POSTINC);
if (err_code != NRF_SUCCESS) return err_code;
while (!twi_tx_done) { /* 等待传输完成 */ }
twi_rx_done = false;
nrf_drv_twi_xfer_desc_t rx_desc = NRF_DRV_TWI_XFER_DESC_RX(MPU9250_ADDR, m_sample, 6);
err_code = nrf_drv_twi_xfer(&m_twi, &rx_desc, 0);
if (err_code != NRF_SUCCESS) return err_code;
while (!twi_rx_done) { /* 等待接收完成 */ }
// 组合数据
accel[0] = (m_sample[0] << 8) | m_sample[1];
accel[1] = (m_sample[2] << 8) | m_sample[3];
accel[2] = (m_sample[4] << 8) | m_sample[5];
return NRF_SUCCESS;
}
// 读取陀螺仪数据
ret_code_t read_gyro(short *gyro) {
ret_code_t err_code;
uint8_t reg = GYRO_XOUT_H;
twi_tx_done = false;
nrf_drv_twi_xfer_desc_t tx_desc = NRF_DRV_TWI_XFER_DESC_TX(MPU9250_ADDR, ®, 1);
err_code = nrf_drv_twi_xfer(&m_twi, &tx_desc, NRF_DRV_TWI_FLAG_TX_POSTINC);
if (err_code != NRF_SUCCESS) return err_code;
while (!twi_tx_done) { /* 等待传输完成 */ }
twi_rx_done = false;
nrf_drv_twi_xfer_desc_t rx_desc = NRF_DRV_TWI_XFER_DESC_RX(MPU9250_ADDR, m_sample, 6);
err_code = nrf_drv_twi_xfer(&m_twi, &rx_desc, 0);
if (err_code != NRF_SUCCESS) return err_code;
while (!twi_rx_done) { /* 等待接收完成 */ }
// 组合数据
gyro[0] = (m_sample[0] << 8) | m_sample[1];
gyro[1] = (m_sample[2] << 8) | m_sample[3];
gyro[2] = (m_sample[4] << 8) | m_sample[5];
return NRF_SUCCESS;
}
// 读取磁力计数据
ret_code_t read_mag(short *mag) {
ret_code_t err_code;
uint8_t reg = EXT_SENS_DATA_00;
// 先读取状态寄存器1
uint8_t st1;
err_code = mpu9250_read_reg(0x02, &st1, 1);
if (err_code != NRF_SUCCESS) return err_code;
if (st1 & 0x01) { // 数据就绪
twi_tx_done = false;
nrf_drv_twi_xfer_desc_t tx_desc = NRF_DRV_TWI_XFER_DESC_TX(MPU9250_ADDR, ®, 1);
err_code = nrf_drv_twi_xfer(&m_twi, &tx_desc, NRF_DRV_TWI_FLAG_TX_POSTINC);
if (err_code != NRF_SUCCESS) return err_code;
while (!twi_tx_done) { /* 等待传输完成 */ }
twi_rx_done = false;
nrf_drv_twi_xfer_desc_t rx_desc = NRF_DRV_TWI_XFER_DESC_RX(MPU9250_ADDR, m_sample, 7);
err_code = nrf_drv_twi_xfer(&m_twi, &rx_desc, 0);
if (err_code != NRF_SUCCESS) return err_code;
while (!twi_rx_done) { /* 等待接收完成 */ }
// 检查状态寄存器2
if (!(m_sample[6] & 0x08)) { // 检查HOFL位
// 组合数据
mag[0] = (m_sample[1] << 8) | m_sample[0];
mag[1] = (m_sample[3] << 8) | m_sample[2];
mag[2] = (m_sample[5] << 8) | m_sample[4];
} else {
NRF_LOG_WARNING("Magnetic sensor overflow!");
}
}
return NRF_SUCCESS;
}
6. 主函数
int main(void) {
ret_code_t err_code;
short accel[3], gyro[3], mag[3];
// 初始化
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
twi_init();
NRF_LOG_INFO("MPU9250 Reader Starting...");
// 初始化MPU9250
err_code = mpu9250_init();
if (err_code != NRF_SUCCESS) {
NRF_LOG_ERROR("MPU9250 initialization failed!");
while (1);
}
// 初始化磁力计
err_code = ak8963_init();
if (err_code != NRF_SUCCESS) {
NRF_LOG_ERROR("AK8963 initialization failed!");
}
NRF_LOG_INFO("MPU9250 initialized successfully");
// 主循环
while (1) {
// 读取数据
err_code = read_accel(accel);
if (err_code == NRF_SUCCESS) {
err_code = read_gyro(gyro);
if (err_code == NRF_SUCCESS) {
err_code = read_mag(mag);
if (err_code == NRF_SUCCESS) {
// 打印数据
print_data(accel, gyro, mag);
}
}
}
nrf_delay_ms(100); // 100ms采样间隔
}
}
7. 数据打印函数
void print_data(short *accel, short *gyro, short *mag) {
NRF_LOG_RAW_INFO("Accel: X=%6d, Y=%6d, Z=%6d ", accel[0], accel[1], accel[2]);
NRF_LOG_RAW_INFO("Gyro: X=%6d, Y=%6d, Z=%6d ", gyro[0], gyro[1], gyro[2]);
NRF_LOG_RAW_INFO("Mag: X=%6d, Y=%6d, Z=%6d\r\n", mag[0], mag[1], mag[2]);
// 转换为实际物理量
float accel_g[3] = {
accel[0] / 2048.0f, // ±16g: 32768/16=2048
accel[1] / 2048.0f,
accel[2] / 2048.0f
};
float gyro_dps[3] = {
gyro[0] / 16.4f, // ±2000dps: 32768/2000≈16.4
gyro[1] / 16.4f,
gyro[2] / 16.4f
};
float mag_uT[3] = {
mag[0] * 0.15f, // 16-bit, ±4800uT: 32768/4800≈6.83, 但需校准
mag[1] * 0.15f,
mag[2] * 0.15f
};
NRF_LOG_RAW_INFO("Accel: X=%.2f g, Y=%.2f g, Z=%.2f g ",
accel_g[0], accel_g[1], accel_g[2]);
NRF_LOG_RAW_INFO("Gyro: X=%.2f °/s, Y=%.2f °/s, Z=%.2f °/s ",
gyro_dps[0], gyro_dps[1], gyro_dps[2]);
NRF_LOG_RAW_INFO("Mag: X=%.2f uT, Y=%.2f uT, Z=%.2f uT\r\n",
mag_uT[0], mag_uT[1], mag_uT[2]);
}
参考代码 NRF52832通过IIC读取mpu9250的原始数据 www.youwenfan.com/contentcns/182596.html
五、数据解析与校准
1. 加速度计数据解析
-
量程:±2g, ±4g, ±8g, ±16g
-
灵敏度:
- ±2g: 16384 LSB/g
- ±4g: 8192 LSB/g
- ±8g: 4096 LSB/g
- ±16g: 2048 LSB/g
-
公式:加速度(g) = 原始值 / 灵敏度
2. 陀螺仪数据解析
-
量程:±250, ±500, ±1000, ±2000 °/s
-
灵敏度:
- ±250: 131 LSB/°/s
- ±500: 65.5 LSB/°/s
- ±1000: 32.8 LSB/°/s
- ±2000: 16.4 LSB/°/s
-
公式:角速度(°/s) = 原始值 / 灵敏度
3. 磁力计数据解析
- 量程:±4800 μT
- 灵敏度:0.6 μT/LSB (14位模式)
- 公式:磁场(μT) = 原始值 × 0.6
4. 传感器校准
// 加速度计和陀螺仪校准
void calibrate_imu(short *accel_bias, short *gyro_bias) {
// 收集样本
for (int i = 0; i < 1000; i++) {
short accel[3], gyro[3];
read_accel(accel);
read_gyro(gyro);
accel_bias[0] += accel[0];
accel_bias[1] += accel[1];
accel_bias[2] += accel[2] - 2048; // 假设Z轴有1g重力
gyro_bias[0] += gyro[0];
gyro_bias[1] += gyro[1];
gyro_bias[2] += gyro[2];
nrf_delay_ms(2);
}
// 计算平均值
accel_bias[0] /= 1000;
accel_bias[1] /= 1000;
accel_bias[2] /= 1000;
gyro_bias[0] /= 1000;
gyro_bias[1] /= 1000;
gyro_bias[2] /= 1000;
}
// 磁力计校准(硬铁和软铁校准)
void calibrate_mag(float *mag_bias, float *mag_scale) {
// 在实际应用中,需要使用椭圆拟合或最小二乘法进行校准
// 这里简化处理,仅设置默认值
mag_bias[0] = 0;
mag_bias[1] = 0;
mag_bias[2] = 0;
mag_scale[0] = 1.0f;
mag_scale[1] = 1.0f;
mag_scale[2] = 1.0f;
}
六、优化与扩展
1. 使用DMA传输
// 使用DMA进行批量数据传输
void read_all_data_dma(short *accel, short *gyro, short *mag) {
uint8_t reg = ACCEL_XOUT_H;
uint8_t buffer[14]; // 加速度计6 + 温度2 + 陀螺仪6
// 读取加速度计和陀螺仪数据
twi_tx_done = false;
nrf_drv_twi_xfer_desc_t tx_desc = NRF_DRV_TWI_XFER_DESC_TX(MPU9250_ADDR, ®, 1);
nrf_drv_twi_xfer(&m_twi, &tx_desc, NRF_DRV_TWI_FLAG_TX_POSTINC);
while (!twi_tx_done);
twi_rx_done = false;
nrf_drv_twi_xfer_desc_t rx_desc = NRF_DRV_TWI_XFER_DESC_RX(MPU9250_ADDR, buffer, 14);
nrf_drv_twi_xfer(&m_twi, &rx_desc, 0);
while (!twi_rx_done);
// 解析数据
accel[0] = (buffer[0] << 8) | buffer[1];
accel[1] = (buffer[2] << 8) | buffer[3];
accel[2] = (buffer[4] << 8) | buffer[5];
gyro[0] = (buffer[8] << 8) | buffer[9];
gyro[1] = (buffer[10] << 8) | buffer[11];
gyro[2] = (buffer[12] << 8) | buffer[13];
// 读取磁力计数据(同上)
}
2. 姿态解算(欧拉角)
// 简易姿态解算(互补滤波)
void calculate_attitude(float *accel, float *gyro, float dt, float *roll, float *pitch, float *yaw) {
static float roll_angle = 0, pitch_angle = 0, yaw_angle = 0;
// 加速度计计算角度
float accel_roll = atan2(accel[1], accel[2]) * RAD_TO_DEG;
float accel_pitch = atan2(-accel[0], sqrt(accel[1]*accel[1] + accel[2]*accel[2])) * RAD_TO_DEG;
// 陀螺仪积分
roll_angle += gyro[0] * dt;
pitch_angle += gyro[1] * dt;
yaw_angle += gyro[2] * dt;
// 互补滤波融合
*roll = 0.98 * roll_angle + 0.02 * accel_roll;
*pitch = 0.98 * pitch_angle + 0.02 * accel_pitch;
*yaw = yaw_angle; // 无磁力计修正
}
3. BLE数据传输
// BLE服务初始化
void ble_stack_init(void) {
// 初始化SoftDevice和BLE堆栈
// ...
// 添加自定义服务
ble_uuid_t service_uuid = {SERVICE_UUID_BASE, BLE_UUID_TYPE_VENDOR_BEGIN};
sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &service_uuid, NULL);
// 添加特征(加速度计、陀螺仪、磁力计)
add_characteristic(ACC_CHAR_UUID, 6 * sizeof(int16_t)); // XYZ三轴
add_characteristic(GYRO_CHAR_UUID, 6 * sizeof(int16_t));
add_characteristic(MAG_CHAR_UUID, 6 * sizeof(int16_t));
}
// 发送传感器数据
void send_sensor_data(short *accel, short *gyro, short *mag) {
uint8_t buffer[18]; // 6轴 × 3字节(实际用2字节)
// 打包数据
memcpy(buffer, accel, 6);
memcpy(buffer+6, gyro, 6);
memcpy(buffer+12, mag, 6);
// 通过BLE发送
ble_nus_string_send(&m_nus, buffer, sizeof(buffer));
}
七、常见问题解决
1. I²C通信失败
-
问题:无法读取WHO_AM_I寄存器
-
解决方案:
- 检查硬件连接(SDA/SCL是否接反)
- 确认上拉电阻(4.7kΩ)
- 降低I²C频率(尝试10kHz)
- 检查电源电压(3.3V)
2. 磁力计数据无效
-
问题:读取的状态寄存器HOFL位始终为1
-
解决方案:
- 确保磁力计初始化正确(CNTL寄存器设置)
- 增加采样间隔时间(磁力计转换时间较长)
- 检查是否存在强磁场干扰
3. 数据漂移严重
-
问题:静止时传感器数据不为零
-
解决方案:
- 实现传感器校准(零偏校准)
- 使用温度补偿(MPU9250内置温度传感器)
- 应用数字滤波器(卡尔曼滤波)
八、项目资源
1. 开发环境
- IDE:Segger Embedded Studio / Keil MDK / VS Code + PlatformIO
- SDK:nRF5 SDK 17.1.0
- 调试工具:J-Link EDU Mini / nRF52 DK
2. 关键参数
| 参数 | 值 |
|---|---|
| I²C时钟频率 | 100kHz |
| 加速度计量程 | ±16g |
| 陀螺仪量程 | ±2000°/s |
| 磁力计量程 | ±4800 μT |
| 采样率 | 100Hz |
| 数据分辨率 | 16位 |
3. 参考资料
- MPU9250数据手册(InvenSense)
- AK8963数据手册(Asahi Kasei)
- Nordic nRF52832 Product Specification
- nRF5 SDK文档(I²C驱动部分)
九、总结
本项目实现了nRF52832通过I²C读取MPU9250九轴传感器数据的完整流程,包括:
- I²C接口初始化与配置
- MPU9250和磁力计的初始化
- 加速度计、陀螺仪和磁力计原始数据读取
- 数据解析与物理量转换
- 传感器校准与姿态解算基础