LIS3DH 从入门到精通 ------ 一款"老而弥坚"的三轴加速度计
关键词:LIS3DH、三轴加速度计、MEMS、姿态检测、低功耗、I²C、SPI、自由落体检测
一、前言
在众多 MEMS 加速度计中选一颗"万金油"芯片,LIS3DH 一定排在前三。
这颗来自 ST(意法半导体)的三轴加速度计,从 2010 年左右推向市场到现在,已经卖了十几亿颗。你手上的智能手环、TWS 耳机、电子标签、共享单车的智能锁、车辆防盗器、智能水杯......里面大概率都有一颗 LIS3DH 或者它的变种。
它这么能打,三个字:够用、便宜、省心。
这篇文章会从应用场景、硬件设计、寄存器配置、驱动代码、低功耗策略、进阶功能到常见调试坑,一条龙讲透彻。无论你是第一次接触 MEMS 传感器的新人,还是想深入研究的老手,都能有所收获。
二、LIS3DH 是谁?------ 一张图看懂
2.1 基本参数
| 参数 | 规格 | 解读 |
|---|---|---|
| 测量轴 | 3 轴(X, Y, Z) | 可独立使能任意轴 |
| 量程 | ±2g / ±4g / ±8g / ±16g | 软件可选,寄存器配置 |
| 分辨率 | 16-bit 数据输出 | 精度足够绝大多数应用 |
| 输出数据率(ODR) | 1 Hz ~ 5.376 kHz | 从超低功耗到高速采样全覆盖 |
| 接口 | I²C(最高 400 kHz)+ SPI(最高 10 MHz) | 两种任选 |
| 工作电压 | 1.71V ~ 3.6V | 一颗纽扣电池就能跑 |
| 工作电流 | 低至 2 μA(低功耗模式,1 Hz ODR) | 真正的"钮扣电池吃一年"级别 |
| FIFO | 32 级,每级 10-bit | 配合 MCU 间歇读取,进一步省电 |
| 中断 | 2 路独立可编程中断引脚(INT1, INT2) | 自由落体、唤醒、单击/双击、6D 方向...... |
| 封装 | LGA-16,3×3×1mm | 比一粒芝麻还小 |
| 温度范围 | -40°C ~ +85°C | 工业级,户外随便用 |
2.2 芯片内部框图
┌─────────────────────────────────────┐
│ LIS3DH │
│ │
X ──→ │ ┌─────┐ │
Y ──→ │ │MEMS │ ┌──────┐ ┌──────────┐ │
Z ──→ │ │传感 │→ │电荷放大│→ │16-bit ADC│ │
│ │单元 │ └──────┘ └────┬─────┘ │
│ └─────┘ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ 数字滤波 │ │
│ └────┬─────┘ │
│ │ │
│ ┌────────────┼──────┐ │
│ │ 控制逻辑 │ │
│ │ ┌───┴────┐ │ │
│ │ │ FIFO │ │ │
│ │ │ (32级) │ │ │
│ │ └────────┘ │ │
│ │ ┌────────┐ │ │
│ │ │ 中断 │→ INT1/INT2
│ │ │ 发生器 │ │ │
│ │ └────────┘ │ │
│ └──────┬──────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ I²C / SPI │ │
│ │ 从机接口 │ │
│ └─────────────┘ │
└─────────────────────────────────────┘
三、LIS3DH 用在哪些产品上?------ 16 个真实落地场景
这是读者最关心的问题,我把它放前面讲。
3.1 消费电子
| 产品 | LIS3DH 做什么 | 具体实现 |
|---|---|---|
| TWS 耳机 | 佩戴检测、敲击控制 | 检测放入/取出耳机的加速度特征,双击/三击切歌 |
| 智能手环/手表 | 抬腕亮屏、计步、睡眠监测 | 加速度变化的持续检测和模式识别 |
| 智能手机 | 屏幕旋转、摇一摇、计步 | 这是加速度计的"基本盘"应用 |
| 平板/电子书 | 屏幕方向切换 | 重力方向检测 |
| 游戏手柄 | 姿态感知、体感操控 | 倾斜角对应游戏角色运动 |
| 遥控器 | 空中飞鼠、语音键抬起唤醒 | 拿起遥控器自动唤醒语音识别 |
3.2 物联网 & 工业
| 产品 | LIS3DH 做什么 | 具体实现 |
|---|---|---|
| 共享单车智能锁 | 车辆倾倒检测、震动报警 | 持续监测倾角,异常移动推送告警 |
| 电动车/摩托车防盗器 | 震动触发报警、拖车检测 | 极低功耗待机,震动唤醒后 GPS 定位上报 |
| 智能水杯 | 喝水动作检测、倾倒检测 | 杯子倾斜角对应喝水动作;倾倒 90° 防漏水提醒 |
| 物流追踪标签 | 货物倾倒/撞击记录 | 全程记录加速度异常事件 |
| 贵重资产监控 | 移动检测、防拆告警 | 任何未经授权的移动都会触发中断通知 |
| 桥梁/建筑结构监测 | 低频振动监测 | 长期监测结构的微小倾斜和振动频率变化 |
3.3 汽车 & 安防
| 产品 | LIS3DH 做什么 | 具体实现 |
|---|---|---|
| 车载 OBD/T-Box | 急加速/急刹车/碰撞检测 | 加速度阈值判断 + 行车数据上传 |
| 行车记录仪 | 碰撞锁定视频、停车监控 | 检测到碰撞时自动锁定当前视频文件 |
| 安防门窗传感器 | 开/关门检测 | 门的加速度变化特征判断开关状态 |
| 保险箱 | 移动/倾斜报警 | 任何异常姿态变化立即触发告警 |
一句话总结 :只要你需要检测运动、震动、倾斜、自由落体、敲击、休眠唤醒中的任何一个,LIS3DH 就是最优先考虑的那颗料。
四、硬件设计 ------ 原理图到 PCB 一条龙
4.1 典型 I²C 应用电路
VDD (1.71V ~ 3.6V)
│
├──────────────────────────────────┐
│ │
10μF│ ┌───────────────┼──────────┐
│ ┌───100nF──┐ │ │ │
│ │ │ │ │ │
GND GND GND │ │ │
│ LIS3DH │
┌──────────────┤ 1 (VDD) 12 ├─ GND
│ ┌────────┤ 2 (VDD_IO) 11 ├─ GND
│ │ ┌─────┤ 3 (NC) INT1 10 ├─→ MCU GPIO (中断)
│ │ │ ┌──┤ 4 (NC / SDO/SA0) 9 ├─→ MCU GPIO (I²C地址选择)
│ │ │ │ │ │
│ │ │ │ │ 5 (SCL/SPC) ────────┼─→ MCU SCL (加10kΩ上拉到VDD)
│ │ │ │ │ 6 (SDA/SDI/SDO) ─────┼─→ MCU SDA (加10kΩ上拉到VDD)
│ │ │ │ │ 7 (SDO/SA0) ─────────┼─→ MCU GPIO (备用地址)
│ │ │ │ │ 8 (CS) ──────────────┤─→ GND (I²C模式拉低)
└─────┴──┴──┴──┤ │
└─────────────────────────┘
4.2 关键设计要点
4.2.1 电源去耦 ------ 不能省!
VDD ──┬── 10μF (钽电容或大容量MLCC) ── GND
│
└── 100nF (靠近芯片1脚,距离<5mm) ── GND
VDD_IO ── 100nF (靠近芯片2脚) ── GND
LIS3DH 内部有电荷泵和 ADC,瞬间电流可达几 mA。 去耦电容没做好,加速度数据会出现周期性噪声(50Hz/60Hz 尖峰),怎么滤波都滤不干净------因为问题出在硬件而不是软件。
4.2.2 I²C 地址选择
| SA0 引脚 (Pin 4) 电平 | 7-bit I²C 地址 | 8-bit 写地址 |
|---|---|---|
| 接 GND | 0x18 | 0x30 |
| 接 VDD | 0x19 | 0x32 |
注意:如果你买的是 LIS3DH 模块(如 GY-521 那种小板),厂家通常已经帮你拉好了,看模块上的丝印或量一下 SA0 电平。
4.2.3 CS 引脚 ------ I²C vs SPI 模式选择
| CS (Pin 8) | 模式 |
|---|---|
| 接 GND | I²C 模式 |
| 接 VDD | SPI 模式 |
4.2.4 PCB Layout 注意事项
- 不要放在板边:板边的机械应力最大,MEMS 对这种应力非常敏感,会出现 DC 偏置漂移
- 远离发热源:远离 LDO、功率电阻、MCU 等发热器件。温度梯度会导致 MEMS 产生热应力,零点偏移可达几十 mg
- 底部铺铜做 GND:芯片正下方铺大面积 GND,减少 EMI 干扰
- 不要手动焊接 QFN/LGA 封装:回流焊才能保证焊接平整。手动焊容易导致芯片倾斜,Z 轴会有 1g 的"旋转投影误差"
- 焊盘设计:严格按数据手册中的推荐焊盘尺寸(0.45mm × 0.35mm),不要自作主张改大改小
五、寄存器地图 ------ 你真正需要关心的就这 10 个
LIS3DH 有 50 多个寄存器,但 90% 的应用只需要掌握以下 10 个:
| 寄存器名 | 地址 | 作用 | 一句话 |
|---|---|---|---|
| WHO_AM_I | 0x0F | 芯片 ID | 读出来永远是 0x33------通信验证第一步 |
| CTRL_REG1 | 0x20 | 数据率 + 三轴使能 | ODR + XYZ 开关,上电必配 |
| CTRL_REG2 | 0x21 | 高通滤波器 | 一般不用,默认就行 |
| CTRL_REG3 | 0x22 | INT1 中断源配置 | 哪个事件触发 INT1 引脚 |
| CTRL_REG4 | 0x23 | 量程 + 数据格式 + 自检 | 选 ±2/4/8/16g,选大端还是小端 |
| CTRL_REG5 | 0x24 | INT1 锁存 + 4D 使能 | 中断引脚的电气行为 |
| CTRL_REG6 | 0x25 | INT2 中断源配置 | 和 CTRL_REG3 功能一样,控制 INT2 |
| OUT_X_L / OUT_X_H | 0x28-0x29 | X 轴数据 | 16-bit,左对齐,需要右移 |
| OUT_Y_L / OUT_Y_H | 0x2A-0x2B | Y 轴数据 | 同上 |
| OUT_Z_L / OUT_Z_H | 0x2C-0x2D | Z 轴数据 | 同上 |
| INT1_SRC | 0x31 | 中断源状态 | 读这个就知道是谁触发了中断 |
5.1 核心寄存器详解
CTRL_REG1 (0x20) ------ 上电第一个要配的寄存器
Bit 7 6 5 4 3 2 1 0
ODR3 ODR2 ODR1 ODR0 LPen Zen Yen Xen
| Bits 7:4 | 数据率 (ODR) | 典型功耗 |
|---|---|---|
| 0000 | 省电模式 | < 1 μA |
| 0001 | 1 Hz | 2 μA |
| 0010 | 10 Hz | --- |
| 0011 | 25 Hz | --- |
| 0100 | 50 Hz | --- |
| 0101 | 100 Hz | --- |
| 0110 | 200 Hz | --- |
| 0111 | 400 Hz | --- |
| 1000 | 1.620 kHz (低功耗 5.376 kHz) | --- |
| Bits 2:0 | 功能 |
|---|---|
| Xen / Yen / Zen | 1 = 使能该轴,0 = 关闭 |
最常用的几个初始化值:
c
// 低功耗计步:1 Hz,三轴全开
CTRL_REG1 = 0x17; // 0001 0111
// 正常模式:100 Hz,三轴全开(最常用)
CTRL_REG1 = 0x57; // 0101 0111
// 高速采样:400 Hz,三轴全开
CTRL_REG1 = 0x77; // 0111 0111
CTRL_REG4 (0x23) ------ 量程与数据格式
Bit 7 6 5 4 3 2 1 0
BDU BLE FS1 FS0 HR ST1 ST0 SIM
| FS1:0 (Bits 5:4) | 量程 | 灵敏度 (mg/LSB) |
|---|---|---|
| 00 | ±2g | 1 mg/LSB |
| 01 | ±4g | 2 mg/LSB |
| 10 | ±8g | 4 mg/LSB |
| 11 | ±16g | 12 mg/LSB |
经验之谈:
- 计步、姿态检测、抬腕亮屏 → ±2g,精度最高
- 碰撞检测、自由落体 → ±8g 或 ±16g,不会饱和
- 不确定用哪个?先上 ±4g,跑一圈看数据峰值,再收窄
BLE (Bit 6) :1 = 先读低字节(Little-Endian),这是大多数 MCU 的默认习惯。建议始终置 1。
BDU (Bit 7) :1 = 数据在读取期间锁定,防止高低字节分属两个采样点。建议始终置 1。
六、驱动代码实战
6.1 I²C 通信基础(以 STM32 HAL 为例)
c
#define LIS3DH_I2C_ADDR 0x18 // SA0 接 GND
#define LIS3DH_WHO_AM_I 0x0F
#define LIS3DH_CTRL_REG1 0x20
#define LIS3DH_CTRL_REG4 0x23
#define LIS3DH_OUT_X_L 0x28
#define LIS3DH_OUT_X_H 0x29
#define LIS3DH_OUT_Y_L 0x2A
#define LIS3DH_OUT_Y_H 0x2B
#define LIS3DH_OUT_Z_L 0x2C
#define LIS3DH_OUT_Z_H 0x2D
// 写单个寄存器
void lis3dh_write_reg(uint8_t reg, uint8_t val) {
uint8_t buf[2] = {reg, val};
HAL_I2C_Master_Transmit(&hi2c1, LIS3DH_I2C_ADDR << 1, buf, 2, 100);
}
// 读单个寄存器
uint8_t lis3dh_read_reg(uint8_t reg) {
uint8_t val;
HAL_I2C_Master_Transmit(&hi2c1, LIS3DH_I2C_ADDR << 1, ®, 1, 100);
HAL_I2C_Master_Receive(&hi2c1, LIS3DH_I2C_ADDR << 1, &val, 1, 100);
return val;
}
// 读多个连续寄存器(自动递增)
void lis3dh_read_regs(uint8_t reg, uint8_t *buf, uint8_t len) {
// LIS3DH 支持地址自动递增,设置 bit 7 使能
reg |= 0x80; // 使能多字节读取模式
HAL_I2C_Master_Transmit(&hi2c1, LIS3DH_I2C_ADDR << 1, ®, 1, 100);
HAL_I2C_Master_Receive(&hi2c1, LIS3DH_I2C_ADDR << 1, buf, len, 100);
}
6.2 芯片初始化和自检
c
int lis3dh_init(void) {
uint8_t whoami;
// 第一步:验证通信
whoami = lis3dh_read_reg(LIS3DH_WHO_AM_I);
if (whoami != 0x33) {
printf("LIS3DH not found! WHO_AM_I = 0x%02X\r\n", whoami);
return -1;
}
printf("LIS3DH detected!\r\n");
// 第二步:配置数据率和使能轴
// 100 Hz ODR,X/Y/Z 全开
lis3dh_write_reg(LIS3DH_CTRL_REG1, 0x57);
// 第三步:配置量程与数据格式
// BDU=1(锁存), BLE=1(小端), FS=00(±2g), HR=1(高分辨率)
lis3dh_write_reg(LIS3DH_CTRL_REG4, 0x88);
// 等待传感器稳定(上电后需要至少 5ms)
HAL_Delay(10);
return 0;
}
6.3 读取原始数据并转换
c
void lis3dh_read_accel(int16_t *x, int16_t *y, int16_t *z) {
uint8_t buf[6];
// 从 OUT_X_L 开始连续读 6 个字节
lis3dh_read_regs(LIS3DH_OUT_X_L, buf, 6);
// 组合高低字节(小端格式,BLE=1)
*x = (int16_t)((buf[1] << 8) | buf[0]);
*y = (int16_t)((buf[3] << 8) | buf[2]);
*z = (int16_t)((buf[5] << 8) | buf[4]);
// LIS3DH 是左对齐的 16-bit,实际有效数据在高 10~12 bits
// 需要右移以对齐:
// ±2g: 12-bit有效 → 右移4位
// ±4g: 12-bit有效 → 右移4位
// ±8g: 10-bit有效 → 右移6位
// ±16g: 10-bit有效 → 右移6位
// 此处以 ±2g 为例:
*x >>= 4;
*y >>= 4;
*z >>= 4;
}
// 转换为 mg 单位 (以 ±2g, 灵敏度 1mg/LSB 为例)
void lis3dh_read_accel_mg(float *x_mg, float *y_mg, float *z_mg) {
int16_t x, y, z;
lis3dh_read_accel(&x, &y, &z);
*x_mg = (float)x; // ±2g: 1 LSB = 1 mg
*y_mg = (float)y;
*z_mg = (float)z;
}
6.4 计算姿态角(倾斜角)
c
// 计算俯仰角 (Pitch) 和翻滚角 (Roll),单位:度
void lis3dh_calc_angles(float x_mg, float y_mg, float z_mg,
float *pitch, float *roll) {
// Pitch: 绕 Y 轴旋转的角度
*pitch = atan2f(-x_mg, sqrtf(y_mg * y_mg + z_mg * z_mg)) * 180.0f / M_PI;
// Roll: 绕 X 轴旋转的角度
*roll = atan2f(y_mg, z_mg) * 180.0f / M_PI;
}
// 简单倾斜检测(用于水杯倾倒、设备倾斜告警等)
uint8_t lis3dh_is_tilted(float pitch, float roll, float threshold_deg) {
return (fabsf(pitch) > threshold_deg || fabsf(roll) > threshold_deg);
}
七、中断功能 ------ LIS3DH 的"灵魂"
如果只用 LIS3DH 读加速度数据,你只用了它 30% 的功能。中断才是这颗芯片的精髓------它让 MCU 从"轮询的奴隶"变成"事件的响应者"。
7.1 支持的中断事件
| 中断类型 | 功能描述 | 典型应用 |
|---|---|---|
| INT_OVERRUN | 数据覆盖(FIFO 溢出) | 高速连续采样 |
| INT_WTM | FIFO 水位线到达 | 攒够一批数据再通知 MCU 读取 |
| INT_IA1 / IA2 | 惯性中断(加速度超阈值) | 震动检测、敲击检测、碰撞检测 |
| INT_ZYXDA | 新数据就绪 | 最简单的数据通知 |
| INT_Click | 单击 / 双击 | TWS 耳机敲击、遥控器按键 |
| INT_FreeFall | 自由落体 | 硬盘保护、跌落记录 |
| INT_6D | 6D 方向检测 | 翻转屏幕、倾倒检测 |
| INT_Sleep | 静止检测 | 设备休眠 |
7.2 实战:震动/敲击检测
c
void lis3dh_config_click_interrupt(void) {
// --- 第一步:配置中断引脚 ---
// CTRL_REG3: 使能 INT1 上的 CLICK 中断
lis3dh_write_reg(0x22, 0x80); // I1_CLICK = 1
// --- 第二步:配置单击参数 ---
// CLICK_CFG (0x38): 使能 Z 轴单击检测
// (实际应用根据需要选择轴,TWS耳机通常用单轴)
lis3dh_write_reg(0x38, 0x10); // ZS = 1 (只检测Z轴)
// CLICK_SRC (0x39): 只读,触发后读这个看结果
// CLICK_THS (0x3A): 单击阈值
// 1 LSB = (量程/128)g,±2g 下 1 LSB ≈ 15.6mg
// 设 16 → 阈值约 250mg
lis3dh_write_reg(0x3A, 16);
// TIME_LIMIT (0x3B): 单击最大时间窗口
// 1 LSB = 1/ODR,100Hz ODR 下 1 LSB = 10ms
// 设 10 → 时间窗口 100ms(超过这个时间不算"单击")
lis3dh_write_reg(0x3B, 10);
// TIME_LATENCY (0x3C): 两次单击之间的延迟(双击用)
// 不用双击的话设 0 即可
lis3dh_write_reg(0x3C, 0);
// TIME_WINDOW (0x3D): 双击检测总时间窗口
list3dh_write_reg(0x3D, 0);
}
c
// 中断引脚 GPIO 的回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == LIS3DH_INT1_PIN) {
// 读取中断源,确认是谁触发的
uint8_t int1_src = lis3dh_read_reg(0x31);
uint8_t click_src = lis3dh_read_reg(0x39);
if (int1_src & 0x40) { // IA bit
// 惯性中断(震动/运动)
}
if (click_src & 0x40) { // IA bit in CLICK_SRC
printf("Click detected!\r\n");
// TWS 切换歌曲、水杯点亮屏幕......
}
}
}
7.3 实战:自由落体检测
c
void lis3dh_config_freefall(void) {
// INT1 上输出自由落体中断
lis3dh_write_reg(0x22, 0x10); // I1_IA1 = 1
// INT1_THS (0x32): 自由落体阈值
// 自由落体时三轴合成加速度 ≈ 0g
// 设 0x10 → 约 250mg 以下触发(越小越灵敏,但容易误触发)
lis3dh_write_reg(0x32, 0x10);
// INT1_DURATION (0x33): 持续时间
// 1 LSB = 1/ODR,100Hz ODR 下 1 LSB = 10ms
// 设 10 → 持续 100ms 低于阈值才触发(滤除短时抖动)
lis3dh_write_reg(0x33, 10);
// INT1_CFG (0x30): 使能中断逻辑和事件
// 0x95 = AOI(与逻辑) + ZHIE(使能Z高) + YLIE(使能Y低) + XLIE(使能X低) + XHIE(使能X高)
// 三轴加速度绝对值都低于阈值时触发
lis3dh_write_reg(0x30, 0x95);
}
八、低功耗设计 ------ 一颗电池撑一年
8.1 三种工作模式对比
| 模式 | 功耗 | ODR | 适用场景 |
|---|---|---|---|
| 掉电模式 | ~0.5 μA | 无 | 长期不用的存储/运输状态 |
| 低功耗模式 (LP) | 2 μA @ 1Hz | 1 ~ 5.376 kHz | 间歇性检测(30s一次够用了) |
| 正常模式 | 6 μA ~ 300 μA | 1 ~ 5.376 kHz | 需要高精度、低噪声的场景 |
| 高分辨率模式 (HR) | 同上 + 额外滤波 | 1 ~ 1.344 kHz | 精度要求极高的场景 |
8.2 极致省电方案:FIFO + 间歇唤醒
这是我在智能水杯、物流追踪器上的实战方案:
┌──────────────────────────────────────────────┐
│ 系统工作周期(如 30 秒) │
│ │
│ ┌── 睡眠 (MCU STOP) ──┐ ┌── 唤醒工作 ──┐ │
│ │ │ │ │ │
│ │ LIS3DH: 低功耗模式 │ │ LIS3DH: │ │
│ │ ODR = 10 Hz │ │ 读 FIFO │ │
│ │ FIFO 持续灌数据 │ │ 处理数据 │ │
│ │ │ │ 决定是否报警 │ │
│ │ MCU: 深度睡眠 │ │ 上报/存日志 │ │
│ │ (0.5 μA) │ │ (几十ms) │ │
│ └─────────────────────┘ └──────────────┘ │
│ │
│ 29.9 秒 0.1 秒 │
└──────────────────────────────────────────────┘
关键代码:
c
void lis3dh_setup_fifo_stream(void) {
// FIFO_CTRL_REG (0x2E): Stream 模式
// 写 0x80 = Stream 模式(满了就盖最老的)
// 写 0x40 = FIFO 模式(满了停止,防覆盖)
// 写 0x00 = Bypass 模式(FIFO 不用)
lis3dh_write_reg(0x2E, 0x80); // Stream 模式
// 设置 FIFO 水印中断(比如攒到 25 个点通知 MCU)
lis3dh_write_reg(0x2E, 0x80 | 25); // Stream + WTM = 25
// CTRL_REG3: INT1 输出 FIFO 水印中断
lis3dh_write_reg(0x22, 0x04); // I1_WTM = 1
// 设置低功耗模式 + 低 ODR
// CTRL_REG1: LP mode + 10 Hz + XYZ 全开
lis3dh_write_reg(0x20, 0x27); // 0010 0111 (ODR=10Hz, LPen=1)
}
// 唤醒后读 FIFO
void lis3dh_read_fifo(uint8_t *buf, uint8_t len) {
// FIFO_SRC_REG (0x2F)
uint8_t fifo_src = lis3dh_read_reg(0x2F);
uint8_t stored = fifo_src & 0x1F; // 存了多少个 sample
// 每个 sample 5 字节:(OUT_X_L, OUT_X_H, OUT_Y_L, OUT_Y_H, OUT_Z_L, OUT_Z_H)
// 不对......LIS3DH FIFO 中,每轴两个字节,一共 6 字节/sample
// 实际读取时,读 OUT_X_L ~ OUT_Z_H 这个连续区域
for (int i = 0; i < stored; i++) {
lis3dh_read_regs(0x28, &buf[i * 6], 6);
}
}
功耗计算结果:
| 工作状态 | 电流 | 占比 |
|---|---|---|
| LIS3DH 低功耗模式 (10 Hz) | 10 μA | 持续 |
| MCU 深度睡眠 | 0.5 μA | 持续 |
| MCU 唤醒工作(30s一次,100ms) | 5 mA | 0.33% 占空比 |
| 总平均 | ≈ 27 μA |
一颗 200mAh 的 CR2032 纽扣电池,平均 27μA → 约 308 天。实际上加上 BLE 广播还能撑 6 个月。这就是为什么共享单车的智能锁能用 2 年不换电池------方案对,功耗就是低。
九、数字信号处理 ------ 数据不是读出来就能用的
9.1 原始数据的噪声特征
LIS3DH 的噪声密度在正常模式下约为 220 μg/√Hz。这意味着:
- 100 Hz 带宽下,RMS 噪声约为 ( 220 \times \sqrt{100 \times 1.6} \approx 2.8 \text{ mg} )
- 实际数据会有 ±10 mg 左右的抖动(再加上 PCB 振动、电源纹波等)
9.2 常用滤波方法
c
// 1. 滑动平均(最简单,最常用)
#define AVG_WINDOW 8
static float x_buf[AVG_WINDOW];
static uint8_t buf_idx = 0;
float lis3dh_moving_avg(float new_val) {
static float sum = 0;
sum -= x_buf[buf_idx]; // 减去最老的值
x_buf[buf_idx] = new_val; // 存入新值
sum += new_val;
buf_idx = (buf_idx + 1) % AVG_WINDOW;
return sum / AVG_WINDOW;
}
// 2. 一阶低通滤波器(IIR)
// y[n] = α * x[n] + (1-α) * y[n-1]
// α 越小,滤波越强,但响应越慢
float lis3dh_low_pass(float input, float *prev_output, float alpha) {
*prev_output = alpha * input + (1.0f - alpha) * (*prev_output);
return *prev_output;
}
// 典型 α 值:
// 计步:α = 0.05 (平滑稳定)
// 碰撞检测:α = 0.5 (响应快)
// 姿态检测:α = 0.1 (适中)
// 3. 静止检测(基于方差)
bool lis3dh_is_stationary(float *buf, int len, float threshold_mg) {
float mean = 0, variance = 0;
for (int i = 0; i < len; i++) mean += buf[i];
mean /= len;
for (int i = 0; i < len; i++) variance += (buf[i] - mean) * (buf[i] - mean);
variance /= len;
return (variance < threshold_mg * threshold_mg);
}
十、校准 ------ 让你的数据真正准确
10.1 零偏校准(Offset Calibration)
MEMS 加速度计出厂时就有零偏误差,加上焊接应力,实际零点可能偏几十甚至上百 mg。
6 面校准法(最精确):
把芯片依次放置于 6 个面,每个面读 100 次取平均:
面 1 (Z 向上):理论值 [0, 0, +1g]
面 2 (Z 向下):理论值 [0, 0, -1g]
面 3 (X 向上):理论值 [+1g, 0, 0]
面 4 (X 向下):理论值 [-1g, 0, 0]
面 5 (Y 向上):理论值 [0, +1g, 0]
面 6 (Y 向下):理论值 [0, -1g, 0]
对 X 轴:
实际+1g 读数 = X_plus
实际-1g 读数 = X_minus
→ offset_X = (X_plus + X_minus) / 2
→ scale_X = (X_plus - X_minus) / 2 / 1000 (mg/LSB, 理论值=1)
c
typedef struct {
float offset_x, offset_y, offset_z;
float scale_x, scale_y, scale_z;
} lis3dh_calib_t;
void lis3dh_apply_calibration(int16_t *x, int16_t *y, int16_t *z,
lis3dh_calib_t *cal) {
*x = (int16_t)((*x - cal->offset_x) / cal->scale_x);
*y = (int16_t)((*y - cal->offset_y) / cal->scale_y);
*z = (int16_t)((*z - cal->offset_z) / cal->scale_z);
}
10.2 生产线上怎么快速校准?
- 工装夹具:做一个六面翻转工装,自动走位
- 省流版:只做 2 面(Z 轴向上和向下),因为大多数设备主要用 Z 轴的姿态检测
- 自校准:如果设备可以旋转(如手机),用户日常翻转就是天然的校准数据
十一、常见坑和调试大全
坑1:WHO_AM_I 读到 0xFF 或 0x00
原因:
- I²C 地址不对(0x18 vs 0x19?用万用表量 SA0 电平)
- I²C 上拉电阻太弱(4.7kΩ 是最佳值,10kΩ 勉强,不要用 100kΩ)
- 芯片没供电(量 VDD 和 VDD_IO)
- CS 引脚浮空(必须接 GND 才能进 I²C 模式!)
坑2:数据正常但总是有 ±100mg 的固定偏移
原因:
- PCB 倾斜或弯曲(板子翘了 1°,Z 轴就多 cos(1°)×1g ≈ 略小于 1g,但可能出现 X/Y 轴有几十 mg 的分量)
- 焊接应力(回流焊时不均匀的降温)
- 芯片附近有磁铁(扬声器、马达磁铁、耳机磁钢)
对策:必须做零偏校准(见第十章)。
坑3:Z 轴数据开立方根式抖动
原因:电源去耦不够。LIS3DH 内部有电荷泵,电感的瞬间电流会通过 GND 反弹回来。
对策:
- 100nF + 10μF 去耦必须靠近芯片
- I²C 走线远离 DC-DC 电感和 PWM 走线
- 必要时给 LIS3DH 独立供电(一个小的 LDO 单独给它)
坑4:低功耗模式数据完全不更新
原因:LP 模式(CTRL_REG1 bit3)必须和对应的 ODR 配合使用,某些 ODR 值在 LP 模式下不支持,数据会"卡住"。
对策:严格按数据手册的 ODR 表选择参数。不确定的话,先在正常模式下跑通,再切低功耗。
坑5:中断引脚反复触发,MCU 被"淹死"
原因:
- 中断没有及时清除(读相关状态寄存器才会清除)
- 中断触发条件设得太宽(震动阈值太低,环境噪声就能触发)
- 中断配置了"锁存"模式,需要读 INT1_SRC 清除
对策:
c
// 中断服务函数中务必读取状态寄存器
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == LIS3DH_INT1_PIN) {
// 读 INT1_SRC 会清除锁存器
uint8_t src = lis3dh_read_reg(0x31);
// 根据 src 的 bit 判断是什么事件
if (src & 0x40) { /* IA 中断 */ }
if (src & 0x08) { /* 数据就绪 */ }
// 如果还配了双击中断,也要读 CLICK_SRC
// uint8_t click = lis3dh_read_reg(0x39);
}
}
坑6:加速度值在量程边界"饱和"------数据不增长
现象:用手摇设备,读数卡在 +2g 不往上涨了。
原因:量程设得太小(±2g)。摇晃时加速度可能达到 3~4g。
对策:
- 碰撞/震动类应用 → 至少 ±8g
- 如果 ±16g 还不够用 → LIS3DH 不适合你,去看 LIS2DW12(±16g)或 IIS3DWB(±128g 超大量程)
坑7:批量生产时一致性差
现象:同一条产线,A 台读数 ±10mg 以内,B 台偏了 80mg。
原因:
- 焊接应力每片板子不一样(回流焊的升降温曲线、PCB 板材批号差异)
- 不同批次的 LIS3DH 零偏初始值不一样
对策:
- 产线上每台都做 1 点校准(至少 Z 轴向上校准一次,读出 offset_z 存入 Flash)
- PCB 拼板时在芯片下方加"应力释放槽"(一条窄槽沿芯片轮廓边缘)
十二、替代选型速查
| 型号 | 厂家 | vs LIS3DH | 适用 |
|---|---|---|---|
| LIS2DW12 | ST | 功耗更低(0.38μA@1Hz)、噪声更低、FIFO更大(512级) | 新一代低功耗设计 |
| LIS2DH12 | ST | 与 LIS3DH 软件兼容,封装兼容,功耗略低 | LIS3DH 的"顺位替代" |
| ADXL345 | ADI | 功耗高但精度更高、量程±16g | 工业/高精度场景 |
| MMA8451Q | NXP | 类似规格,寄存器更简单 | 北美供应链首选 |
| KXTJ3-1057 | Kionix | 超小封装 2×2mm,极低功耗 | 真无线耳机、极致空间 |
| BMI160 | Bosch | 6 轴(Accel+Gyro),惯性导航 | 需要角速度的场景 |
| QMA6100P | 矽睿科技 | 国产替代,性价比极高 | 成本敏感、国产化要求 |
十三、总结与心法
LIS3DH 是一颗"老派"的芯片------它没有追最新最先进的指标,也没有动不动 μA 以下的神仙功耗。但它的稳定性、可靠性、供货能力、生态成熟度,是市面上大多数新品比不了的。
给年轻工程师的三条心法:
-
先读 WHO_AM_I,再谈其他。通信不通,后面全是白费劲。0x33 读到的那一刻,项目就成功了一半。
-
善用中断,别死轮询。LIS3DH 的精华就是那两颗中断脚------新数据、震动、自由落体、敲击、翻转......全部可以用中断通知 MCU。MCU 安心睡觉,有事才唤醒。这才是低功耗系统的正确打开方式。
-
功耗大头永远不在传感器。LIS3DH 最低能跑到 2μA,你的 MCU、你的蓝牙、你的显示屏才是耗电大户。先优化系统架构(谁该睡、谁该醒、多久醒一次),再抠传感器的细节。
核心要点回顾:
- 应用场景:TWS耳机、手环手表、共享单车锁、车载T-Box、智能水杯、安防防盗、跌倒检测------凡是需要"感知运动和姿势"的地方,都能见到它的身影
- 接口:I²C(地址 0x18/0x19)+ SPI
- 量程:±2/4/8/16g 可选,根据应用场景定
- 功耗:最低 2μA @ 1Hz,纽扣电池能撑一年
- 中断:INT1 + INT2,支持新数据就绪、震动/敲击、自由落体、6D方向、FIFO水印等多种事件
- FIFO:32级,配合低功耗间歇采样,进一步省电
- 校准:批量生产时必须做零偏校准,焊接应力不可忽视
- 滤波:滑动平均 + 低通滤波组合,消除噪声同时保持响应速度
参考与延伸阅读:
- ST LIS3DH 官方数据手册(Doc ID: CD00274266)
- ST AN3308:LIS3DH 应用笔记(中断配置、6D 检测)
- ST AN4508:LIS3DH FIFO 使用指南
- ST MEMS 校准指南(6 面校准详细步骤)
- ST GitHub:STMems_Standard_C_drivers(官方驱动库,直接拿来用)