STM32F103+BMI160实战:从零搭建六轴传感器数据采集系统(附完整代码)
·
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/PB8 | SCL | I2C时钟线 |
| PB7/PB9 | SDA | I2C数据线 |
| 3.3V | VCC | 电源正极 |
| GND | GND | 电源地 |
注意:部分BMI160模块板载电平转换电路,可直接连接5V系统。若模块无此功能,需确保STM32与BMI160工作电压匹配。
2.2 电路设计要点
- 电源滤波:在BMI160的VCC引脚附近放置0.1μF去耦电容,减少电源噪声影响
- I2C上拉电阻:SCL和SDA线需接4.7kΩ上拉电阻至3.3V(部分模块已内置)
- 地址选择: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初始化流程
- 硬件复位:通过发送0xB6到CMD寄存器实现软复位
- 传感器配置:
- 加速度计量程和带宽配置(ACC_RANGE和ACC_CONF寄存器)
- 陀螺仪量程和带宽配置(GYR_RANGE和GYR_CONF寄存器)
- 电源模式设置:
- 加速度计设置为正常工作模式(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°/s | 16384 | 262.4 |
| ±4g/±250°/s | 8192 | 131.2 |
| ±8g/±500°/s | 4096 | 65.6 |
| ±16g/±1000°/s | 2048 | 32.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 常见通信问题排查
-
无应答信号:
- 检查硬件连接是否正确
- 确认I2C地址是否正确(0x68或0x69)
- 测量SCL/SDA线电压是否正常(应有上拉)
-
数据异常:
- 降低I2C时钟频率(可尝试100kHz)
- 增加读写操作后的延时
- 检查电源稳定性,确保无毛刺
-
通信中断:
- 避免在中断服务程序中执行长时间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 传感器校准方法
-
加速度校准:
- 将传感器水平静止放置,Z轴应显示约1g
- 记录各轴偏移量,后续数据中减去
-
陀螺仪校准:
- 保持传感器完全静止,记录各轴输出作为零偏
- 多次采样取平均,提高校准精度
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 性能优化技巧
- 降低采样率:根据应用需求选择合适的数据输出率
- 使用FIFO:BMI160内置1024字节FIFO,可减少主机干预
- 中断驱动:配置数据就绪中断,替代轮询方式
- 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
};
更多推荐



所有评论(0)