NRF52832通过I²C读取MPU9250原始数据

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, &reg, 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, &reg, 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, &reg, 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. 加速度计数据解析

2. 陀螺仪数据解析

3. 磁力计数据解析

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, &reg, 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通信失败

2. 磁力计数据无效

3. 数据漂移严重

八、项目资源

1. 开发环境

2. 关键参数

参数
I²C时钟频率 100kHz
加速度计量程 ±16g
陀螺仪量程 ±2000°/s
磁力计量程 ±4800 μT
采样率 100Hz
数据分辨率 16位

3. 参考资料

  1. MPU9250数据手册(InvenSense)
  2. AK8963数据手册(Asahi Kasei)
  3. Nordic nRF52832 Product Specification
  4. nRF5 SDK文档(I²C驱动部分)

九、总结

本项目实现了nRF52832通过I²C读取MPU9250九轴传感器数据的完整流程,包括:

  1. I²C接口初始化与配置
  2. MPU9250和磁力计的初始化
  3. 加速度计、陀螺仪和磁力计原始数据读取
  4. 数据解析与物理量转换
  5. 传感器校准与姿态解算基础

专注于matlab/simulink,电子电路,编程