2、原始数据采集:I2C/SPI接口读取、原始数据格式、单位换算(LSB/g)、数据速率设置
好,咱们正式开始动手了。上一章聊完了加速度计的基本原理,这一章咱们要真刀真枪地把数据从芯片里读出来。你想想看,传感器再牛,数据读不出来也是白搭。
我个人习惯把原始数据采集分成四步走:选接口 → 读寄存器 → 做换算 → 设速率。咱们一个一个来。
2.1 接口选择:I2C 还是 SPI?
现在的加速度计芯片,基本都同时支持 I2C 和 SPI 两种接口。我刚开始做手环项目时,也纠结过到底用哪个。后来踩过坑才明白——选接口,主要看你的数据量和引脚资源。
| 对比项 | I2C | SPI |
|---|---|---|
| 引脚数 | 2 根线(SDA、SCL) | 4 根线(CS、SCLK、MOSI、MISO) |
| 速度 | 标准 100kHz / 快速 400kHz | 最高可达 10MHz 以上 |
| 多设备支持 | 总线式,可挂多个设备 | 片选式,每个设备需独立 CS |
| 适合场景 | 引脚紧张、速率要求不高 | 高速采集、实时性要求高 |
2.2 原始数据格式:16位有符号整数
不管用 I2C 还是 SPI,读回来的数据格式是一样的。绝大多数加速度计的输出寄存器都是 16 位,以二进制补码形式存储。
举个例子,ADXL345 的 X 轴数据存在两个寄存器里:
0x32(DATAX0):低 8 位0x33(DATAX1):高 8 位
读的时候要注意顺序。我记得第一次写驱动时,直接把高低字节拼起来,结果数据全是乱的。后来才发现——先读低字节,再读高字节,这是芯片的硬件设计决定的。
// 伪代码示例:读取 X 轴原始值
uint8_t low = read_register(0x32);
uint8_t high = read_register(0x33);
int16_t raw = (int16_t)((high << 8) | low);
2.3 单位换算:LSB/g 到底是个啥?
读回来的 raw 值,单位是 LSB(Least Significant Bit),不是 g。你得把它换算成物理单位。
每个加速度计都有个关键参数叫 灵敏度,单位就是 LSB/g。比如你设了 ±2g 量程,灵敏度可能是 16384 LSB/g。意思是:每 1g 的加速度,对应 16384 个 LSB 的数值变化。
换算公式很简单:
加速度(g) = 原始值(LSB) / 灵敏度(LSB/g)
举个例子:
- 你读到的 raw 值是 16384
- 灵敏度是 16384 LSB/g
- 那么加速度 = 16384 / 16384 = 1.0 g
嗯,这里要注意——不同量程下灵敏度不一样。我整理了一张常用表:
| 量程 | 灵敏度(LSB/g) | 典型芯片 |
|---|---|---|
| ±2g | 16384 | ADXL345、MPU6050 |
| ±4g | 8192 | ADXL345、LSM6DS3 |
| ±8g | 4096 | ADXL345、BMI160 |
| ±16g | 2048 | ADXL345、ICM-20948 |
2.4 数据速率设置:别让 CPU 累死
加速度计的输出数据速率(ODR,Output Data Rate)是可以配置的。说白了就是芯片每秒给你送多少次数据。
常见的 ODR 范围从 1Hz 到 3200Hz 不等。你想想看,如果你设了 3200Hz,MCU 每秒要处理 3200 次中断,CPU 直接跑满。但如果你设 10Hz,又可能漏掉关键动作。
我个人建议:
- 计步场景:50Hz ~ 100Hz 足够了
- 手势识别:100Hz ~ 200Hz
- 振动检测:200Hz ~ 400Hz
- 高精度运动分析:400Hz 以上
配置 ODR 通常是通过写芯片的控制寄存器。以 ADXL345 为例:
// 设置 ODR 为 100Hz
// BW_RATE 寄存器地址 0x2C
// 100Hz 对应的值为 0x0A
write_register(0x2C, 0x0A);
2.5 完整初始化流程
最后,我把整个原始数据采集的流程串起来,给你一个完整的初始化示例:
// 加速度计初始化伪代码
void accel_init() {
// 1. 检查芯片 ID
uint8_t id = read_register(0x00);
if (id != 0xE5) { // ADXL345 的 ID
error_handler("芯片通信失败");
return;
}
// 2. 设置量程为 ±2g
// DATA_FORMAT 寄存器 0x31,全量程 00 表示 ±2g
write_register(0x31, 0x00);
// 3. 设置 ODR 为 100Hz
write_register(0x2C, 0x0A);
// 4. 进入测量模式
// POWER_CTL 寄存器 0x2D,bit3 置 1
write_register(0x2D, 0x08);
// 5. 读取原始数据
uint8_t x_low = read_register(0x32);
uint8_t x_high = read_register(0x33);
int16_t x_raw = (int16_t)((x_high << 8) | x_low);
// 6. 换算为 g 值
float x_g = (float)x_raw / 16384.0f;
}
嗯,到这里,原始数据采集这块就讲完了。说白了就是三步:选接口 → 读寄存器 → 做换算。下一章咱们聊聊怎么对这些原始数据做滤波处理,把噪声干掉。