基于STM32F103驱动BMI160读取三轴加速度和三轴陀螺仪数据
BMI160是一款低功耗6轴惯性测量单元(3轴加速度+3轴陀螺仪),适用于移动设备、可穿戴设备、AR/VR等场景。本文介绍了BMI160的主要特性、引脚功能、数据流框图、I2C通信协议、关键寄存器配置等。并通过STM32F103C8T6测试BMI160,读取三轴加速度和三轴陀螺仪的数据于OLED屏幕展现。实现了BMI160六轴数据的采集与显示测试。
文章目录
一、BMI160简介
BMI160 是 一颗低功耗 6-轴惯性测量单元(3 轴加速度 + 3 轴陀螺),面向移动设备、可穿戴、AR/VR、相机OIS、步数/手势检测等场景。体积小(2.5×3.0×0.83 mm LGA),电源范围宽(VDD 1.71–3.6 V,VDDIO 1.2–3.6 V),并内建 FIFO、时间戳与中断引擎,可实现低功耗的运动检测与自主事件触发。当加速度计和陀螺仪处于完全工作模式时,典型工作电流约 925 µA。
主要特性
- 供电电源:3.3V / 5V
- 高性能加速度计和陀螺仪(硬件同步)
- 超低功耗:典型值925 uA(加速度计和陀螺仪全速运行)
- 兼容Android Lollipop:支持大幅度运动和步数检测器/计步器(每个5 μA)
- 内置电源管理单元 (PMU),可实现高级电源管理
- 陀螺仪快速启动模式,节省功耗
- 1024 字节可分配 FIFO 缓冲区(可处理外部传感器数据)
- 硬件传感器时间戳,用于精确的传感器数据融合
- 集成中断,用于增强自主运动检测
- 灵活的数字主接口,可通过 I2C 或 SPI 连接到主机
- 扩展 I2C 模式,时钟频率高达 1 MHz
- 额外的二级高速接口,用于 OIS 应用
应用场景
- 室内导航
- 3D扫描/室内地图绘制
- 高级手势识别
- 沉浸式游戏
- 9轴运动检测
- 空中鼠标应用和指针
- 计步器/步数计数
- 移动应用的高级系统电源管理

二、引脚说明
| VIN | 5V供电 |
|---|---|
| 3V3 | VIN接了5V,该引脚输出3.3V;如果3.3V供电,接该引脚,则VIN不需要接 |
| GND | 地 |
| SCL | 12C串行时钟线/SPI串行时钟端口 |
| SDA | 12C串行数据线/4线SPI串行数据输入/3线SPI串行数据输入/输出 |
| CS | SPI模式选择引脚 |
| SA0 | SPI串行数据输出/12C地址选择引脚 |
| OCS | OIS接口 |
| INT1 | 中断引脚1 |
| INT2 | 中断引脚2 |
| SCX | 磁力计接口 |
| SDX | 磁力计接口 |
三、数据流框图和I2C读写访问

从上面的框图中,可以看到,BMI160与外部进行双向数据传输的方式有两种:SPI和I2C。以I2C与外部进行通信的方式说明。当BMI160通过I2C与外部进行通信时,BMI160将作为I2C从设备挂到主控芯片(主设备)的I2C总线上,所以,主控芯片在配置其对应的I2C驱动时就需要知道BMI160的从设备地址。
对于不同的硬件设计,BMI160的从设备地址是不同的。从数据手册可知,从设备地址可根据SDO进行选择,SDO接低电平,从设备地址为0x68,SDO接高电平,从设备地址为0x69。店铺的BMI160模块的从设备地址默认为0x69。
I2C读写访问

四、主要寄存器说明
CHIPID(0x00):设备ID寄存器,BMI160的ID号为0xD1。
ERR_REG(0x02):错误寄存器,用于调试配置。
PMU_STATUS(0x03):状态寄存器,用于读取当前模式。其中Normal为正常工作模式。
DATA(0x04~0x17):数据寄存器,加速度,陀螺仪,若连接了磁力计,也可以读取磁力计数据。读取数据时尽量一次读取完,以保证 accel/gyro 的时间同步,即连续读。
ACC_CONF(0x40):设置加速度计采样频率与带宽。
ACC_RANGE(0x41):选择加速度量程,Bit3-0:选择量程(±2g / ±4g / ±8g / ±16g),其他位没有用到,置0即可。
GYR_CONF(0x42):设置陀螺仪采样频率和带宽。
GYR_RANGE(0x43):选择陀螺仪量程,Bit2-0:选择量程(±125°/s / ±250°/s / ±500°/s / ±1000°/s / ±2000°/s),其他位没有用到,置0即可。
CMD(0x7E):控制寄存器,用该地址写入不同值控制加速度或陀螺仪的工作模式。其中,通过该地址写入0x11,设置加速度为正常工作模式,写入0x15,设置陀螺仪为正常工作模式。写入0xB6,为软复位。
注意:CMD只能接受一个命令一次,写入一次后,必须等待命令执行完毕后再写入,缺少等待会导致后续配置/读取异常。
五、原始数据到物理量的换算
从寄存器读到的是有符号 16-bit 原始值,需要按所选量程除以灵敏度得到物理量。
加速度(以 g 或 m/s²单位 表示),常见灵敏度(LSB / g):
±2 g :16384 LSB/g
±4 g :8192 LSB/g
±8 g :4096 LSB/g
±16 g:2048 LSB/g
转为g单位:accel_g = raw_accel_signed / S_accel,如:raw=16384,FS=±2g → accel_g = 16384/16384 = 1 g
转为m/s²单位:accel_m_s2 = accel_g * 9.80665
陀螺仪(以 °/s 或 rad/s单位 表示),常见灵敏度(LSB / (°/s)):
±2000 °/s → 16.4 LSB/(°/s)
±1000 °/s → 32.8 LSB/(°/s)
±500 °/s → 65.6 LSB/(°/s)
±250 °/s → 131.2 LSB/(°/s)
±125 °/s → 262.4 LSB/(°/s)
转为°/s单位:gyro_dps = raw_gyro_signed / S_gyro
转为rad/s单位:gyro_rad_s = gyro_dps * (π / 180)
六、STM32F103驱动BMI160读取六轴数据
准备工作
STM32F103C8T6开发板,OLED屏,BMI160模块
接线说明
| STM32F103C8T6 | BMI160 |
|---|---|
| PB10 | SCL |
| PB11 | SDA |
| PB8 | OLED->SCL |
| PB9 | OLED->SDA |
| 5V | VIN |
| GND | GND |

代码示例
BMI160.c
#include "bmi160.h"
void BMI160_WriteReg(uint8_t RegAddress, uint8_t Data)
{
MyI2C_Start();
MyI2C_SendByte(BMI160_ADDR_WRITE);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_SendByte(Data);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
uint8_t BMI160_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start();
MyI2C_SendByte(BMI160_ADDR_WRITE);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_Start();
MyI2C_SendByte(BMI160_ADDR_READ); //指定地址读
MyI2C_ReceiveAck();
Data = MyI2C_ReceiveByte();
MyI2C_SendAck(1);
MyI2C_Stop();
return Data;
}
void BMI160_ReadMulti(uint8_t RegAddress, uint8_t *buf, uint8_t len)
{
uint8_t i;
MyI2C_Start();
MyI2C_SendByte(BMI160_ADDR_WRITE);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_Start();
MyI2C_SendByte(BMI160_ADDR_READ);
MyI2C_ReceiveAck();
for (i = 0; i < len; i++)
{
buf[i] = MyI2C_ReceiveByte();
if (i == (len - 1)) MyI2C_SendAck(1);
else MyI2C_SendAck(0);
}
MyI2C_Stop();
}
void BMI160_Get6AxisRaw(int16_t *ax, int16_t *ay, int16_t *az, int16_t *gx, int16_t *gy, int16_t *gz)
{
uint8_t buf[12];
int16_t raw;
BMI160_ReadMulti(BMI160_REG_GYR_LSB, buf, 12);
*gx = (int16_t)((buf[1]<<8) | buf[0]);
*gy = (int16_t)((buf[3]<<8) | buf[2]);
*gz = (int16_t)((buf[5]<<8) | buf[4]);
*ax = (int16_t)((buf[7]<<8) | buf[6]);
*ay = (int16_t)((buf[9]<<8) | buf[8]);
*az = (int16_t)((buf[11]<<8) | buf[10]);
}
void BMI160_ConvAcc(int16_t ax_raw, int16_t ay_raw, int16_t az_raw, float *ax_g, float *ay_g, float *az_g)
{
*ax_g = (float)ax_raw / BMI160_ACCEL_SENS_16G * 9.80665f; // 输出单位 m/s^2
*ay_g = (float)ay_raw / BMI160_ACCEL_SENS_16G * 9.80665f;
*az_g = (float)az_raw / BMI160_ACCEL_SENS_16G * 9.80665f;
}
void BMI160_ConvGyro(int16_t gx_raw, int16_t gy_raw, int16_t gz_raw, float *gx_dps, float *gy_dps, float *gz_dps)
{
*gx_dps = (float)gx_raw / BMI160_GYRO_SENS_2000; // 输出单位 °/s
*gy_dps = (float)gy_raw / BMI160_GYRO_SENS_2000;
*gz_dps = (float)gz_raw / BMI160_GYRO_SENS_2000;
}
void BMI160_Init(void)
{
MyI2C_Init();
BMI160_WriteReg(BMI160_REG_CMD, 0xB6);
Delay_ms(100);
BMI160_WriteReg(BMI160_REG_CMD, 0x15); // gyro normal
Delay_ms(100);
BMI160_WriteReg(BMI160_REG_CMD, 0x11); // acc normal
Delay_ms(100);
// 设置 ODR
BMI160_WriteReg(BMI160_ACC_CONF, 0x28); // acc 100Hz
BMI160_WriteReg(BMI160_GYR_CONF, 0x28); // gyro 100Hz
// 设置量程
BMI160_WriteReg(BMI160_REG_ACC_RANGE, 0x0C); // ±16g
BMI160_WriteReg(BMI160_REG_GYR_RANGE, 0x00); // ±2000°/s
Delay_ms(100);
}
main.c
#include "stm32f10x.h" // Device header
#include "bmi160.h"
#include "oled.h"
#include "KEY.h"
uint8_t ID, key;
int16_t ax, ay, az, gx, gy, gz;
float ax_g, ay_g, az_g, gx_dps, gy_dps, gz_dps;
int main(void)
{
OLED_Init();
BMI160_Init();
Key_Init();
OLED_ShowString(1,1,"ID:");
ID = BMI160_ReadReg(0x00);
OLED_ShowHexNum(1,4,ID,2);
while (1)
{
BMI160_Get6AxisRaw(&ax, &ay, &az, &gx, &gy, &gz);
key = Key_Scan();
if(key)
{
OLED_ShowString(1,7,"m/s^2 o/s");
BMI160_ConvAcc(ax, ay, az, &ax_g, &ay_g, &az_g);
BMI160_ConvGyro(gx, gy, gz, &gx_dps, &gy_dps, &gz_dps);
OLED_ShowFloat(2, 1, ax_g, 2, 2);
OLED_ShowFloat(3, 1, ay_g, 2, 2);
OLED_ShowFloat(4, 1, az_g, 2, 2);
OLED_ShowFloat(2, 9, gx_dps, 2, 2);
OLED_ShowFloat(3, 9, gy_dps, 2, 2);
OLED_ShowFloat(4, 9, gz_dps, 2, 2);
}else{
OLED_ShowString(1,7," ");
OLED_ShowSignedNum(2, 1, ax, 5);
OLED_ShowSignedNum(3, 1, ay, 5);
OLED_ShowSignedNum(4, 1, az, 5);
OLED_ShowSignedNum(2, 9, gx, 5);
OLED_ShowSignedNum(3, 9, gy, 5);
OLED_ShowSignedNum(4, 9, gz, 5);
}
}
}
效果展示

总结
BMI160 是一颗体积小、功耗低、功能丰富的 6-DoF IMU,适合大多数需要加速度与角速度数据的移动/穿戴/相机稳定场景;使用时优先保证硬件与通信可靠、理解 FIFO 与 CMD 时序、并在系统层面做好校准与滤波。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)