基于STM32的LIS2DH12加速度传感器I²C驱动实现

在智能手环自动亮屏、工业设备振动告警、物流包裹跌落记录这些看似不相关的场景背后,其实都依赖同一个关键元件——三轴加速度传感器。当工程师选型时, LIS2DH12 往往会进入候选名单:它功耗极低,支持高达5.376kHz的采样率,还能通过硬件中断减轻主控负担。而搭配 STM32系列MCU 使用,则能充分发挥其性能优势,尤其是借助HAL库快速搭建稳定可靠的I²C通信链路。

但实际开发中,很多人卡在第一步:明明接好了线, WHO_AM_I 却读不出正确值;或者数据跳变剧烈,无法用于角度计算。问题往往出在细节上——地址配置错误、寄存器位定义混淆、数据组合方式不对。本文不讲大道理,只聚焦一个目标:让你用最短时间跑通 LIS2DH12 的基础功能,并理解每一步背后的逻辑。


先看硬件连接。LIS2DH12 支持 I²C 和 SPI 两种接口,这里选择 I²C 是因为它只需要两根信号线(SCL/SDA),适合引脚资源紧张的设计。典型接法如下:

  • VDD 接 3.3V 电源
  • GND 共地
  • SCL SDA 分别接到 STM32 的 I²C 引脚(如 PB6/PB7 对应 I2C1)
  • 外加上拉电阻(4.7kΩ 推荐)
  • SA0 决定从机地址:接地为 0x30 (写)/ 0x31 (读),接电源为 0x32 / 0x33

注意:HAL 库中的函数使用的是 7位地址 ,所以传参时直接填 0x30 0x32 即可,不需要左移一位再处理。

接下来是软件初始化。很多初学者忽略了一个重要步骤—— 设备存在性检测 。直接写配置寄存器很容易导致程序卡死,建议先读取 WHO_AM_I 寄存器(地址 0x0F ),确认返回值是否为 0x33 。这一步能帮你快速判断是硬件没连好,还是代码逻辑有误。

#define REG_WHO_AM_I      0x0F
#define LIS2DH12_I2C_ADDR 0x30  // SA0 = GND

uint8_t who_am_i;
if (HAL_I2C_Mem_Read(&hi2c1, LIS2DH12_I2C_ADDR, REG_WHO_AM_I, 
                     I2C_MEMADD_SIZE_8BIT, &who_am_i, 1, 100) != HAL_OK) {
    // I2C通信失败,检查线路或电源
    return -1;
}
if (who_am_i != 0x33) {
    // 不是LIS2DH12,可能是地址错或芯片损坏
    return -2;
}

一旦确认设备在线,就可以开始配置工作模式了。核心控制寄存器有两个: CTRL_REG1 CTRL_REG4

CTRL_REG1 (地址 0x20 )决定传感器的基本运行状态:
- 高4位设置输出数据速率(ODR)。比如要设为100Hz,对应二进制 0101xxxx
- 第3~1位分别使能X/Y/Z轴,全开就是 xxx111xx
- 最低位开启正常工作模式( xxxxxxx1

把这些拼起来,得到 0x57 —— 这个魔法数字的由来现在清楚了吧?不是随便写的,而是按位组合的结果。

另一个关键寄存器是 CTRL_REG4 (地址 0x23 ),主要设置量程和分辨率:
- FS1 FS0 控制满量程:都置1表示 ±8g
- HR 位启用高分辨率模式(16位输出)

所以 0x30 就代表: 00110000 → FS1=1, FS0=1, HR=1。如果你需要更高精度但更低量程的应用(比如人体姿态识别),可以改为 0x00 (±2g,灵敏度1mg/LSB)。

完整的初始化函数应该包含错误反馈机制,方便调试:

uint8_t LIS2DH12_Init(void) {
    uint8_t who_am_i;

    if (LIS2DH12_ReadReg(REG_WHO_AM_I, &who_am_i, 1) != HAL_OK) return 1;
    if (who_am_i != 0x33) return 2;

    if (LIS2DH12_WriteReg(0x20, 0x57) != HAL_OK) return 3;  // ODR=100Hz, XYZ enable, normal mode
    if (LIS2DH12_WriteReg(0x23, 0x30) != HAL_OK) return 4;  // ±8g, high resolution

    return 0;  // success
}

其中读写函数封装也很讲究。不要每次都调用底层 HAL_I2C_Master_Transmit ,那样代码冗长且易错。推荐封装成类似下面的形式:

uint8_t LIS2DH12_ReadReg(uint8_t reg, uint8_t *data, uint16_t len) {
    return HAL_I2C_Mem_Read(&hi2c1, LIS2DH12_I2C_ADDR, reg, 
                            I2C_MEMADD_SIZE_8BIT, data, len, 100);
}

uint8_t LIS2DH12_WriteReg(uint8_t reg, uint8_t data) {
    return HAL_I2C_Mem_Write(&hi2c1, LIS2DH12_I2C_ADDR, reg, 
                             I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
}

这样不仅简洁,还统一了超时时间和地址模式,减少出错概率。


真正让数据“活”起来的是原始值的解析。LIS2DH12 的 X、Y、Z 轴数据分别存储在连续的6个寄存器中( OUT_X_L , OUT_X_H , …, OUT_Z_H ),每个轴占两个字节,采用小端格式(低字节在前)。最高效的做法是一次性读取全部6字节:

typedef struct {
    int16_t x, y, z;
} lis2dh12_data_t;

uint8_t LIS2DH12_Read_Accel(lis2dh12_data_t *data) {
    uint8_t buf[6];

    // 使用自动递增地址功能(REG | 0x80)
    if (LIS2DH12_ReadReg(0x28 | 0x80, buf, 6) != HAL_OK) {
        return 1;
    }

    data->x = (int16_t)((buf[1] << 8) | buf[0]);
    data->y = (int16_t)((buf[3] << 8) | buf[2]);
    data->z = (int16_t)((buf[5] << 8) | buf[4]);

    return 0;
}

注意这里的 0x28 OUT_X_L 的地址, | 0x80 表示开启地址自动递增,这是I²C批量读取的标准做法。如果不加这个标志,就得反复发起单字节读操作,效率低下且容易出错。

原始数据是16位补码形式,可以直接转为有符号整数。但要转换为物理单位(g),还需要根据当前量程查表换算:

量程 每 LSB 对应的 mg 数
±2g 1
±4g 2
±8g 4
±16g 12

例如,在 ±8g 模式下:

float ax_g = data.x * 4.0f / 1000.0f;  // 单位:g
float ay_g = data.y * 4.0f / 1000.0f;
float az_g = data.z * 4.0f / 1000.0f;

有了以 g 为单位的数据,后续处理就自由了:
- 计算倾斜角: pitch = atan2(ay_g, sqrt(ax_g*ax_g + az_g*az_g)) * 57.3
- 判断静止状态:总加速度接近 1g
- 检测冲击事件:瞬时加速度超过阈值(如 3g)


实际项目中常见几个“坑”,值得特别提醒:

  1. I²C地址搞反
    很多人把读地址当作设备地址传入。记住:HAL库用的是7位地址,写操作时硬件自动添加最低位为0。所以 SA0=GND 时,传 0x30 就行。

  2. 忘记上拉电阻
    I²C是开漏输出,必须外接上拉。没有的话通信必然失败。如果板子已焊死,可用万用表测SCL/SDA对地电阻是否在几kΩ级别。

  3. 数据噪声过大
    如果发现读数频繁抖动,除了检查电源稳定性,还可以启用内置滤波器。通过 CTRL_REG2 设置抗混叠滤波带宽,或开启FIFO缓冲做滑动平均。

  4. 功耗异常高
    默认配置下电流可能达几百μA。若用于电池供电设备,务必切换到低功耗模式( CTRL_REG1 设为 0x17 可降至约10μA @ 10Hz)。

  5. 中断未响应
    若使用INT1/INT2引脚触发中断(如自由落体检测),需额外配置 CTRL_REG3 CLICK_CFG 等寄存器,并在MCU端启用外部中断线。


最后说点工程经验。虽然轮询方式简单直观,但在实时性要求高的系统中并不理想。更好的做法是结合中断与状态机:

  • 配置 INT1 输出运动检测信号
  • MCU睡眠时由中断唤醒
  • 唤醒后读取FIFO中的批量数据进行分析

这种方式能让主控大部分时间处于低功耗模式,显著延长续航。此外,在多传感器共存的系统中,建议将I²C操作封装成模块化接口,避免地址冲突和总线竞争。

这种高度集成的MEMS传感器正在改变嵌入式系统的交互方式。从简单的“有没有动作”到复杂的“是什么动作”,LIS2DH12 提供了扎实的基础能力。只要掌握正确的驱动方法,就能让它在你的项目中稳定可靠地工作。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐