STM32F103+BMI160六轴传感器数据采集系统实战指南

1. 项目概述与硬件选型

在物联网和智能硬件快速发展的今天,运动数据采集已成为许多嵌入式系统的核心需求。STM32F103作为一款经典的中低端ARM Cortex-M3微控制器,以其出色的性价比和丰富的外设资源,成为嵌入式开发者的首选。而Bosch Sensortec推出的BMI160则是一款集成了3轴加速度计和3轴陀螺仪的6轴惯性测量单元(IMU),具有低功耗、高精度和小尺寸等特点。

硬件选型考虑因素:

  • 处理器性能:STM32F103C8T6主频72MHz,内置64KB Flash和20KB SRAM,完全满足传感器数据采集和处理需求
  • 传感器精度:BMI160提供16位分辨率的加速度和角速度测量,加速度计量程可配置为±2g/±4g/±8g/±16g,陀螺仪量程可配置为±125°/s至±2000°/s
  • 接口兼容性:BMI160支持I2C和SPI接口,STM32F103硬件I2C或软件模拟均可实现稳定通信
  • 电源管理:BMI160工作电压1.71-3.6V,典型工作电流仅925μA,适合电池供电应用

2. 硬件连接与电路设计

2.1 引脚连接说明

STM32F103引脚BMI160模块引脚功能说明
PB6/PB8SCLI2C时钟线
PB7/PB9SDAI2C数据线
3.3VVCC电源正极
GNDGND电源地

注意:部分BMI160模块板载电平转换电路,可直接连接5V系统。若模块无此功能,需确保STM32与BMI160工作电压匹配。

2.2 电路设计要点

  1. 电源滤波:在BMI160的VCC引脚附近放置0.1μF去耦电容,减少电源噪声影响
  2. I2C上拉电阻:SCL和SDA线需接4.7kΩ上拉电阻至3.3V(部分模块已内置)
  3. 地址选择:BMI160的SDO引脚电平决定I2C地址(接地为0x68,接VCC为0x69)
// BMI160 I2C地址定义
#define BMI160_ADDR 0x68 // SDO接地时的地址
// #define BMI160_ADDR 0x69 // SDO接VCC时的地址

3. 软件架构与初始化配置

3.1 系统软件架构

├── 应用层
│   ├── 数据解析与处理
│   └── 用户界面/通信接口
├── 驱动层
│   ├── BMI160驱动程序
│   └── I2C通信驱动
└── 硬件抽象层
    ├── STM32硬件初始化
    └── 系统时钟配置

3.2 BMI160初始化流程

  1. 硬件复位:通过发送0xB6到CMD寄存器实现软复位
  2. 传感器配置:
    • 加速度计量程和带宽配置(ACC_RANGE和ACC_CONF寄存器)
    • 陀螺仪量程和带宽配置(GYR_RANGE和GYR_CONF寄存器)
  3. 电源模式设置:
    • 加速度计设置为正常工作模式(CMD寄存器写入0x11)
    • 陀螺仪设置为正常工作模式(CMD寄存器写入0x15)
void BMI160_Init(void) {
    // 软复位
    BMI160_WriteReg(BMI160_REG_CMD, 0xB6);
    HAL_Delay(50);
    
    // 设置加速度计为正常模式
    BMI160_WriteReg(BMI160_REG_CMD, 0x11);
    HAL_Delay(50);
    
    // 设置陀螺仪为正常模式
    BMI160_WriteReg(BMI160_REG_CMD, 0x15);
    HAL_Delay(50);
    
    // 配置加速度计: 100Hz输出数据率, ±8g量程
    BMI160_WriteReg(BMI160_ACC_CONF, 0x28);
    BMI160_WriteReg(BMI160_ACC_RANGE, 0x08);
    
    // 配置陀螺仪: 100Hz输出数据率, ±500°/s量程
    BMI160_WriteReg(BMI160_GYR_CONF, 0x28);
    BMI160_WriteReg(BMI160_GYR_RANGE, 0x04);
    
    HAL_Delay(100);
}

4. 数据采集与处理

4.1 原始数据读取

BMI160的传感器数据存储在以下寄存器中:

  • 加速度数据:0x12(LSB)-0x17(MSB)
  • 陀螺仪数据:0x0C(LSB)-0x11(MSB)

推荐操作:一次性读取所有6轴数据(12字节),保证数据同步性。

void BMI160_Read6AxisData(int16_t *accel, int16_t *gyro) {
    uint8_t data[12];
    
    // 从陀螺仪数据寄存器开始连续读取12字节
    BMI160_ReadMulti(BMI160_GYR_DATA_ADDR, data, 12);
    
    // 解析陀螺仪数据 (X,Y,Z)
    gyro[0] = (int16_t)((data[1] << 8) | data[0]);
    gyro[1] = (int16_t)((data[3] << 8) | data[2]);
    gyro[2] = (int16_t)((data[5] << 8) | data[4]);
    
    // 解析加速度数据 (X,Y,Z)
    accel[0] = (int16_t)((data[7] << 8) | data[6]);
    accel[1] = (int16_t)((data[9] << 8) | data[8]);
    accel[2] = (int16_t)((data[11] << 8) | data[10]);
}

4.2 物理量转换

原始数据需根据配置的量程转换为实际物理量:

加速度转换公式:

实际加速度(g) = 原始数据 / 灵敏度(LSB/g)

陀螺仪转换公式:

实际角速度(°/s) = 原始数据 / 灵敏度(LSB/(°/s))

各量程对应的灵敏度值:

量程加速度灵敏度(LSB/g)陀螺仪灵敏度(LSB/(°/s))
±2g/±125°/s16384262.4
±4g/±250°/s8192131.2
±8g/±500°/s409665.6
±16g/±1000°/s204832.8
-/±2000°/s-16.4
void ConvertToPhysicalValues(int16_t raw[3], float physical[3], float sensitivity) {
    for(int i=0; i<3; i++) {
        physical[i] = raw[i] / sensitivity;
    }
}

5. 通信协议实现

5.1 I2C通信基础函数

// I2C写单个寄存器
void BMI160_WriteReg(uint8_t reg, uint8_t value) {
    HAL_I2C_Mem_Write(&hi2c1, BMI160_ADDR<<1, reg, 
                     I2C_MEMADD_SIZE_8BIT, &value, 1, 100);
}

// I2C读单个寄存器
uint8_t BMI160_ReadReg(uint8_t reg) {
    uint8_t value;
    HAL_I2C_Mem_Read(&hi2c1, BMI160_ADDR<<1, reg,
                    I2C_MEMADD_SIZE_8BIT, &value, 1, 100);
    return value;
}

// I2C连续读多个寄存器
void BMI160_ReadMulti(uint8_t reg, uint8_t *data, uint16_t len) {
    HAL_I2C_Mem_Read(&hi2c1, BMI160_ADDR<<1, reg,
                    I2C_MEMADD_SIZE_8BIT, data, len, 100);
}

5.2 常见通信问题排查

  1. 无应答信号:

    • 检查硬件连接是否正确
    • 确认I2C地址是否正确(0x68或0x69)
    • 测量SCL/SDA线电压是否正常(应有上拉)
  2. 数据异常:

    • 降低I2C时钟频率(可尝试100kHz)
    • 增加读写操作后的延时
    • 检查电源稳定性,确保无毛刺
  3. 通信中断:

    • 避免在中断服务程序中执行长时间I2C操作
    • 添加超时重试机制

6. 数据滤波与校准

6.1 简单移动平均滤波

#define FILTER_WINDOW_SIZE 5

typedef struct {
    float buffer[FILTER_WINDOW_SIZE][3];
    uint8_t index;
} SensorFilter;

void ApplyFilter(SensorFilter *filter, float newData[3], float filtered[3]) {
    // 更新缓冲区
    for(int i=0; i<3; i++) {
        filter->buffer[filter->index][i] = newData[i];
    }
    filter->index = (filter->index + 1) % FILTER_WINDOW_SIZE;
    
    // 计算平均值
    for(int i=0; i<3; i++) {
        float sum = 0;
        for(int j=0; j<FILTER_WINDOW_SIZE; j++) {
            sum += filter->buffer[j][i];
        }
        filtered[i] = sum / FILTER_WINDOW_SIZE;
    }
}

6.2 传感器校准方法

  1. 加速度校准:

    • 将传感器水平静止放置,Z轴应显示约1g
    • 记录各轴偏移量,后续数据中减去
  2. 陀螺仪校准:

    • 保持传感器完全静止,记录各轴输出作为零偏
    • 多次采样取平均,提高校准精度
void CalibrateBMI160(float accelBias[3], float gyroBias[3], uint16_t samples) {
    int32_t accelSum[3] = {0}, gyroSum[3] = {0};
    int16_t rawAccel[3], rawGyro[3];
    
    for(uint16_t i=0; i<samples; i++) {
        BMI160_Read6AxisData(rawAccel, rawGyro);
        
        for(int j=0; j<3; j++) {
            accelSum[j] += rawAccel[j];
            gyroSum[j] += rawGyro[j];
        }
        HAL_Delay(10);
    }
    
    for(int j=0; j<3; j++) {
        accelBias[j] = (float)accelSum[j] / samples;
        gyroBias[j] = (float)gyroSum[j] / samples;
    }
    
    // 特殊处理Z轴加速度(减去1g)
    accelBias[2] -= (1.0f * GetAccelSensitivity());
}

7. 应用实例与性能优化

7.1 姿态估计基础

通过加速度计和陀螺仪数据融合,可实现简单的姿态估计:

void UpdateOrientation(float accel[3], float gyro[3], float dt, float angle[3]) {
    // 加速度计姿态估计(俯仰和横滚)
    float pitch_acc = atan2(accel[1], sqrt(accel[0]*accel[0] + accel[2]*accel[2]));
    float roll_acc = atan2(-accel[0], accel[2]);
    
    // 互补滤波
    angle[0] = 0.98 * (angle[0] + gyro[0] * dt) + 0.02 * roll_acc;
    angle[1] = 0.98 * (angle[1] + gyro[1] * dt) + 0.02 * pitch_acc;
}

7.2 性能优化技巧

  1. 降低采样率:根据应用需求选择合适的数据输出率
  2. 使用FIFO:BMI160内置1024字节FIFO,可减少主机干预
  3. 中断驱动:配置数据就绪中断,替代轮询方式
  4. DMA传输:STM32的I2C+DMA可大幅降低CPU负载
// 配置BMI160 FIFO
void ConfigureFIFO(void) {
    // 启用加速度和陀螺仪数据存入FIFO
    BMI160_WriteReg(BMI160_FIFO_CONFIG_1, 0x03);
    
    // 设置FIFO模式为流模式
    BMI160_WriteReg(BMI160_FIFO_CONFIG_0, 0x80);
    
    // 启用FIFO
    BMI160_WriteReg(BMI160_FIFO_CONFIG_0, 0x80 | 0x40);
}

8. 完整代码实现

8.1 主程序框架

#include "stm32f1xx_hal.h"
#include "bmi160.h"
#include <math.h>

// 全局变量
float accelBias[3], gyroBias[3];
SensorFilter accelFilter, gyroFilter;

int main(void) {
    // HAL初始化
    HAL_Init();
    SystemClock_Config();
    
    // 外设初始化
    MX_I2C1_Init();
    BMI160_Init();
    
    // 传感器校准
    CalibrateBMI160(accelBias, gyroBias, 100);
    
    // 主循环
    while (1) {
        int16_t rawAccel[3], rawGyro[3];
        float accel[3], gyro[3], filteredAccel[3], filteredGyro[3];
        
        // 读取原始数据
        BMI160_Read6AxisData(rawAccel, rawGyro);
        
        // 转换为物理量并去除偏移
        for(int i=0; i<3; i++) {
            accel[i] = (rawAccel[i] - accelBias[i]) / GetAccelSensitivity();
            gyro[i] = (rawGyro[i] - gyroBias[i]) / GetGyroSensitivity();
        }
        
        // 应用滤波
        ApplyFilter(&accelFilter, accel, filteredAccel);
        ApplyFilter(&gyroFilter, gyro, filteredGyro);
        
        // 应用处理(如姿态估计)
        // ...
        
        HAL_Delay(10); // 控制循环频率
    }
}

8.2 关键寄存器定义

// BMI160寄存器地址定义
#define BMI160_CHIP_ID_ADDR     0x00
#define BMI160_ERR_REG_ADDR     0x02
#define BMI160_PMU_STATUS_ADDR  0x03
#define BMI160_GYR_DATA_ADDR    0x0C
#define BMI160_ACC_DATA_ADDR    0x12
#define BMI160_STATUS_ADDR      0x1B
#define BMI160_ACC_CONF_ADDR    0x40
#define BMI160_ACC_RANGE_ADDR   0x41
#define BMI160_GYR_CONF_ADDR    0x42
#define BMI160_GYR_RANGE_ADDR   0x43
#define BMI160_FIFO_CONFIG_0    0x46
#define BMI160_FIFO_CONFIG_1    0x47
#define BMI160_CMD_ADDR         0x7E

// 加速度计量程定义
enum {
    ACC_RANGE_2G = 0x03,
    ACC_RANGE_4G = 0x05,
    ACC_RANGE_8G = 0x08,
    ACC_RANGE_16G = 0x0C
};

// 陀螺仪量程定义
enum {
    GYR_RANGE_125DPS = 0x04,
    GYR_RANGE_250DPS = 0x03,
    GYR_RANGE_500DPS = 0x02,
    GYR_RANGE_1000DPS = 0x01,
    GYR_RANGE_2000DPS = 0x00
};

更多推荐