第一次接到这个需求的时候,我盯着方案需求看了半天:LSM6DSO 搭配 H3LIS331DLTR,中间还夹着一个 Sensorhub。这不是普通的惯性测量组合,而是典型的“低g六轴 + 高g三轴”双加速度计协同方案。老实说,这种搭配在工业冲击记录、跌落检测、碰撞触发、运动监测这些场景里非常实用,但真正把它调通、让数据稳定跑起来,中间还是有不少门道。
这俩传感器分开用都很常见:LSM6DSO 是意法半导体(ST)的旗舰级六轴惯性传感器,加速度计量程最大到 ±16g,内置陀螺仪,功耗低、性能好;H3LIS331DLTR 则是专门做高g加速度检测的,量程能到 ±400g,专门用来捕捉剧烈的冲击信号。但问题来了——LSM6DSO 自己只能测 ±16g,扛不住冲击;H3LIS331DLTR 虽然能量程大,却没有配套的姿态解算能力,数据也只有自己一路。于是 ST 的 Sensorhub 功能就成了把两者拼到一起的关键:让 LSM6DSO 通过内部 I2C 主机接口去读取 H3LIS331DLTR 的数据,外部 MCU 只需要跟 LSM6DSO 通信,就能同时拿到低g六轴数据和高g冲击数据。
这篇文章不打算只贴代码,我会把方案为什么这么设计、寄存器怎么配、FSM 怎么做阈值判断、实际调试中会遇到哪些坑,全部过一遍。内容面向正在做 ST 传感器方案、尤其是想把两颗加速度计组合起来做冲击检测的朋友,手里有 LSM6DSO 和 H3LIS331DLTR 板子的可以直接照着抄,没有板子的也能把整体思路摸透。
1. 方案定位:为什么要把两颗传感器拼在一起
1.1 LSM6DSO 和 H3LIS331DLTR 各自擅长什么
先说 LSM6DSO。这颗芯片在 ST 的惯性传感器产品线里属于中高端型号,六轴(三轴加速度计 + 三轴陀螺仪),加速度计支持 ±2/±4/±8/±16g 四档量程,陀螺仪支持 ±125 到 ±2000 dps。它的亮点不只是测量精度,还在于内置了大量可编程功能:有限状态机(FSM)、机器学习内核(MLC)、计步器、倾斜检测,以及这次要重点讲的传感器 hub(Sensorhub)。也就是说,LSM6DSO 不只是“传感器”,它自己带了一个可以处理数据的瘦身处理器。
而 H3LIS331DLTR 则完全是另一种定位。它是一颗单功能的三轴加速度计,量程有三档:±100g、±200g、±400g。这颗芯片的专职就是“扛住大冲击”,比如跌落测试、碰撞触发、包装运输监测、保险箱搬动报警这些场景。它本身没有陀螺仪,也没有复杂的内部状态机,但具备中断输出能力,可以配置阈值溢出触发。
从测量范围上看,两者正好互补。LSM6DSO 负责精细的低g运动测量,H3LIS331DLTR 负责捕捉瞬间的高g冲击。一颗芯片覆盖 0 到 400g 的动态范围几乎不现实,用两颗芯片各管一段,是当前比较成熟的工业级做法。
1.2 Sensorhub 在这里扮演的角色
Sensorhub 是 ST 传感器里的一个隐藏技能,很多工程师把它当普通 IMU 用,根本没用过这个功能。简单理解,LSM6DSO 内部内置了一个 I2C 主机控制器,它可以按照配置好的地址和寄存器,定时去读取外部传感器挂在同一个 I2C 总线上的数据,然后把数据存到自己的 SENSORHUB 寄存器区。外部 MCU 只需要读 LSM6DSO 的寄存器,就能拿到外部传感器的最新数据。
这带来一个非常实际的好处:系统里多了一颗传感器,但外部 MCU 的代码复杂度几乎零增加。MCU 原本只跟 LSM6DSO 通信,现在依然只跟 LSM6DSO 通信,传感器多了,通信节点没多。这对于跑裸机或者简单 RTOS 的小系统来说非常友好,不用为了多一颗传感器去改 I2C 总线仲裁逻辑,也不用为多一个设备占 I/O 口发愁。
而且 Sensorhub 读取外设是硬件自动完成的,不占用 MCU 时间。你只要设好更新速率,LSM6DSO 就会按 ODR 周期自己去拉外部数据。这个“硬件搬运工”模式,比 MCU 每次手动发 I2C 读请求要稳定得多,时序抖动也更小。
1.3 这种组合解决了什么问题
如果把 H3LIS331DLTR 单独接到 MCU 上,MCU 就得额外维护一条 I2C 从设备链路,每次读数据都要发地址、发寄存器、收数据,处理中断时还要判断是哪个传感器触发的。这些看起来都不是大问题,但放在功耗敏感的产品里就麻烦了:MCU 要从低功耗模式醒过来去向传感器要数据,醒得越频繁,功耗越高。
而用了 Sensorhub 之后,MCU 可以一直睡大觉。LSM6DSO 自己定期把 H3LIS331DLTR 的数据拉回来,再通过内置的 FSM 做阈值判断,只有在检测到冲击事件时才通过中断线叫醒 MCU。整套流程里 MCU 没有任何主动参与,功耗能做到非常低。这也是为什么在便携式冲击记录仪、可穿戴防跌倒设备里,这种“LSM6DSO + 高g传感器”的方案很常见。
2. 硬件连接与引脚分配
2.1 我用的板子和接线方式
我做验证用的是一块普通的 ST 传感器评估板加一个 H3LIS331DLTR 的小模块。LSM6DSO 在评估板上已经把电源、去耦电容、I2C 上拉电阻都做好了,调试起来方便很多。如果你是自己画板子,硬件上要注意的点后面单独说。
两颗传感器的连接关系并不复杂:LSM6DSO 对外有两条 I2C 总线。一条是主总线 SDA/SCL,用于跟 MCU 通信;另一条是 AUX_SDA/AUX_SCL,专门给 Sensorhub 外部传感器接口使用。H3LIS331DLTR 就挂在 AUX_SDA/AUX_SCL 这一条总线上。
注意,这里说的“主总线”是对 MCU 而言的,LSM6DSO 在跟 MCU 通信时是从机;而到了 AUX 总线上,LSM6DSO 是主机,H3LIS331DLTR 是从机。搞清这个角色关系很重要,很多人调不通,就是因为把这两条总线的角色混了。如下图所示(文字描述一下):
- MCU 通过 I2C 总线 A 连接 LSM6DSO:MCU 是主机,LSM6DSO 是从机。
- LSM6DSO 通过 AUX 总线连接 H3LIS331DLTR:LSM6DSO 是主机,H3LIS331DLTR 是从机。
2.2 AUX_SDA/AUX_SCL 到底怎么接
在 LSM6DSO 的封装里,AUX_SDA 和 AUX_SCL 是独立的两个引脚,不要跟主 SDA/SCL 短接。把 H3LIS331DLTR 的 SDA 接到 LSM6DSO 的 AUX_SDA,H3LIS331DLTR 的 SCL 接到 LSM6DSO 的 AUX_SCL 即可。H3LIS331DLTR 的地址选择引脚 SA0 要接确定的电平,一般评估板上有跳线,SA0=0 时 I2C 地址是 0x18,SA0=1 时是 0x19,这里我用的 0x18。
AUX_SDA/AUX_SCL 这两条线上需要接上拉电阻吗?答案是肯定要。ST 官方文件里要求外部 I2C 总线都需要上拉,不过如果你用的 LSM6DSO 模块已经把 AUX 引脚的上拉电阻做进去了,就不用重复加。如果是自己画板,建议在两个引线上各加 4.7kΩ 上拉到 VDD。我调试时一开始发现 Sensorhub 读回来的数据全是 0xFF,排查半天发现是 AUX_SDA 的上拉电阻没焊,总线时钟被拉死了。
2.3 供电、电平匹配和去耦的注意点
LSM6DSO 的工作电压范围是 1.71V~3.6V,H3LIS331DLTR 是 2.16V~3.6V。通常都按 3.3V 供电,两边电压一致,I2C 电平匹配就不用额外处理。如果你的系统里 MCU 是 1.8V 的,那 LSM6DSO 的 VDD_IO 也要跟着接 1.8V,确保 I2C 电平跟 MCU 匹配。H3LIS331DLTR 这边也一样,它的供电和上拉电平都要保持在同一个电压域。
去耦电容别省。LSM6DSO 的 VDD 引脚和 H3LIS331DLTR 的 VDD 引脚附近都各放一个 100nF 陶瓷电容,最好再并一个 1μF 到 10μF 的大电容。冲击检测场景里传感器会受到剧烈震动,电源线上很容易出现毛刺,去耦做不好可能导致传感器复位或者读数跳变。
还有一点要提醒:H3LIS331DLTR 是 ±400g 的传感器,安装方向直接决定测量坐标系。做跌落测试时,如果你把它焊在 PCB 上,PCB 的安装方向和受力方向要提前定义清楚,否则测出来的冲击波形正负号是反的,排查时很费劲。
3. 软件配置:把 H3LIS331DLTR 挂到 LSM6DSO 的 Sensorhub 上
3.1 第一步:让 LSM6DSO 进入传感器 hub 模式
Sensorhub 的配置并不复杂,但寄存器比较多,而且有几处比较容易踩坑。我先给出一个经过验证的初始化顺序,再逐步解释每一步的含义。
首先要启用 LSM6DSO 的 I2C 主机功能。这个开关在 CTRL9_XL (0x18) 寄存器的 bit4,写成 1 表示开启 I2C master 模式。注意,这个 bit 在数据手册里叫 I2C_MASTER_EN,不把它置 1 的话,后面配置的所有 SLV 寄存器都不会生效。
接着配置 CTRL1_XL (0x10),把加速计设为正常测量模式。Sensorhub 读取外部传感器数据的节拍依赖于 LSM6DSO 内部加速度计的 ODR,所以加速度计不能被 disable。我习惯设为 104Hz,也就是写 0x50。频率太高会增加功耗,太低又会让冲击事件的捕获粒度变粗,104Hz 在大多数场景下是一个折中值。如果你需要更细的时间分辨率,可以设到 208Hz 甚至 416Hz。
CTRL3_C (0x12) 里有一个 IF_INC 位(bit2),它控制的是多字节寄存器读取时地址是否自动递增。这个位必须设成 1,不然读 SENSORHUB 数据时每读一个字节就要重新写一次寄存器地址,麻烦且容易出错。同时把 BDU(Block Data Update,bit6)也设成 1,保证加速度计和陀螺仪的数据在读取过程中不会被新数据覆盖,从而避免读到高低位拼接不上的问题。这两个位在 CTRL3_C 里一起写,值就是 0x44 或 0x04(看有没有动其他位)。
3.2 第二步:配置外部从机 SLV0
LSM6DSO 的 Sensorhub 支持最多 4 个外部从机,编号是 SLV0 到 SLV3。我们这里只接了一个 H3LIS331DLTR,所以只用 SLV0 的配置寄存器组。
SLV0_ADD (0x15) 用来设置从机地址和读写方向。H3LIS331DLTR 的 7 位 I2C 地址是 0x18,左移一位后是 0x30,bit0 用于区分读还是写。这里我们要连续读取多个寄存器,所以在 SLV0_ADD 里把 bit0 写成 1,表示后面是读操作,寄存器的值就是 0x31。
SLV0_SUBADD (0x16) 是起始子地址,也就是从外部传感器的哪个寄存器开始读。H3LIS331DLTR 的加速度数据输出寄存器从 OUT_X_L (0x28) 开始,按 X 低字节、X 高字节、Y 低字节、Y 高字节、Z 低字节、Z 高字节排列,所以这里写入 0x28。
SLV0_CONFIG (0x17) 的低 4 位是 SLV0_NUM_OP,表示要连续读取的字节数减 1。我们要读 6 个字节,所以这里写 0x05(6 - 1=5)。读完这 6 个字节之后,LSM6DSO 会把数据按顺序放进自己的 SENSORHUB 寄存区,从 SENSORHUB0 (0x02) 开始排列。也就是说:
- SENSORHUB0 (0x02) 存 X_L
- SENSORHUB1 (0x03) 存 X_H
- SENSORHUB2 (0x04) 存 Y_L
- SENSORHUB3 (0x05) 存 Y_H
- SENSORHUB4 (0x06) 存 Z_L
- SENSORHUB5 (0x07) 存 Z_H
这里还要提醒一个容易踩的坑:SLV0_NUM_OP 最多能配置到 16 字节(值是 0x0F),但 SENSORHUB 寄存区总共只有 13 字节可用,所以如果你配置的读取长度过大,数据会溢出覆盖前面的内容。我用 6 字节刚好合适。
最后打开总开关。在 CTRL9_XL 里使能 I2C master 之后,还需要在 MASTER_CONFIG (0x14) 里配置 I2C 速率。MASTER_CONFIG 的低三位是 I2C_SPEED,这里可以根据外部传感器的最高速率来定,H3LIS331DLTR 支持 400kHz,我直接配 0x05(400kHz)。如果总线上有其他慢速设备,可以降到 100kHz。
3.3 第三步:从 SENSORHUB 寄存器读数据并验证
配置完成之后,LSM6DSO 会按照内部加速度计的 ODR 周期,自动发起对 H3LIS331DLTR 的 I2C 读取。MCU 需要做的,就是直接读 0x02 到 0x07 这 6 个寄存器。
我调试时习惯先读一遍 STATUS_REG (0x1E) 里的 bit3(MASTER_ON),确认 I2C master 是否正常开启。如果这个位一直是 0,说明 Sensorhub 根本没跑起来,后面读的数据全是无效的。接着读 WHO_AM_I,确认 LSM6DSO 的通信链路没问题,WHO_AM_I 的值应该是 0x6C。
验证 Sensorhub 是否读到了有效数据,最简单的办法是给 H3LIS331DLTR 一个初始已知状态。把板子水平静置,此时重力方向在 Z 轴上(取决于安装方向),读到的 Z 轴数据换算成 mg 后应该接近 ±1000mg,也就是 ±1g,X 和 Y 轴接近 0。如果没有装好传感器或者地址不对,读回来的数据往往全为 0xFF 或者波形完全不对,那就按第 6 章的方法去排查。
4. 进阶玩法:用 FSM 在高 g 数据里抓冲击事件
4.1 为什么不能只用 MCU 轮询
到这里,我们已经能把 H3LIS331DLTR 的数据通过 Sensorhub 读到 LSM6DSO 里了。但如果只是把数据搬运过来,Sensorhub 的价值只发挥了一半。真正的杀手级玩法,是 LSM6DSO 内置的有限状态机(FSM)直接在本地判断冲击事件。
设想一个场景:手持设备待机时 MCU 休眠,发生跌落时需要在几个毫秒内检测到冲击并触发动作(比如启动数据保存、发出报警)。如果让 MCU 轮询 H3LIS331DLTR 数据,MCU 就得一直醒着,功耗降不下去;或者让 H3LIS331DLTR 自己输出中断,但这样又得单独处理一路中断信号,而且 H3LIS331DLTR 的中断配置相对简单,没法做复杂的时序判断。
FSM 可以完美解决这个问题。LSM6DSO 内部有 2 个可编程状态机,它们能直接读取 SENSORHUB 寄存区里的数据,再做逻辑运算,最终通过中断引脚输出结果。MCU 全程不需要参与判断,可以安心睡在低功耗模式里,只有 FSM 觉得“有情况”了才被叫醒。
4.2 FSM 状态设计思路
我这次要检测的目标很直接:只要 XYZ 三轴里有任何一个轴的加速度绝对值超过 100g,就判定为一次冲击事件。当然你可以按需要调整阈值,比如 ±400g 量程下可以设 200g 或者 300g,阈值越高越难误触发。
LSM6DSO 的 FSM 指令集和常规 MCU 编程不太一样,它是对状态寄存器做操作,跳转条件可以是数据比较、时间窗口、掩码判断等。设计思路可以拆成几个状态:
- 状态 0(IDLE):等待新的 SENSORHUB 数据更新,把当前 SENSORHUB 寄存器里的值跟预设阈值比较。如果任一轴绝对值超过阈值,跳转到状态 1;否则保持状态 0。
- 状态 1(DETECTED):记录一次冲击事件,置位 FSM 输出中断,通知 MCU 读取数据,同时等待系统复位或超时返回状态 0。
- 状态 2(可选):做消抖。防止瞬时毛刺造成误触发,可以设置一个时间窗口,要求阈值条件持续 N 个 ODR 周期才判定有效。
实际编写 FSM 程序时,很多人会手写状态机的二进制指令,但我不建议这么做。ST 官方的 Unico GUI 工具提供了图形化的 FSM 编辑器,能直接拖拽状态和条件并生成程序。先把算法在 Unico 里跑通,再导出初始化数组,比自己手写快得多,也不容易错。
4.3 用 ST Unico 工具生成 FSM 程序
如果你还没用过 Unico,简单介绍一下流程。打开 Unico 软件,连接评估板或通过 MCU 转接板连上 LSM6DSO,然后在 UI 里选中 FSM 页面,新建一个状态机。编辑器里可以设置两个状态,状态之间的转移条件选“外部传感器数据 > 阈值”。这里要把数据源选成 SENSORHUB 的对应轴,比如 X 轴数据存放在 SENSORHUB0/1,Y 轴在 SENSORHUB2/3,Z 轴在 SENSORHUB4/5。
阈值比较用的是 16 位有符号数,需要把高 g 阈值换算成对应的寄存器值。H3LIS331DLTR 在 ±400g 量程下灵敏度是 3.9mg/digit,100g 对应的十进制值大约是 25600(100 / 0.0039)。不同量程档位灵敏度不同,这点要特别注意,我第一版程序没换算,直接用 100 当阈值,结果完全没有触发,后来才想起要乘灵敏度系数。
Unico 生成完 FSM 程序后,会输出一个数组,代码里只要把数组写进 LSM6DSO 对应的 FSM 寄存器区,然后在 FSM_ENABLE 寄存器里使能状态机即可。具体的寄存器地址和写入时序在 ST 的驱动代码里都有,直接用官方驱动比较简单。
FSM 中断可以映射到 LSM6DSO 的 INT1 或 INT2 引脚,通过 CTRL4_C (0x13) 和 INT1_CTRL (0x0D) 这类寄存器配置。实际项目里一般用 INT2,尽量跟加速度计的数据就绪中断分开,避免一个中断线上一堆事件源,软件处理起来逻辑绕。
5. 完整初始化代码与实测效果
5.1 测试环境
测试用的主控是 STM32L4 系列 MCU,I2C 主总线跑 400kHz,MCU 通过 I2C 访问 LSM6DSO 的寄存器。H3LIS331DLTR 挂在 LSM6DSO 的 AUX_SDA/AUX_SCL 上。整个系统的 I2C 拓扑是:MCU -> LSM6DSO(从机) -> H3LIS331DLTR(外部从机)。
下面是初始化函数的核心部分,用 HAL 库改写,方便直接移植。
void lsm6dso_sensorhub_init(void) { uint8_t data = 0; // 1. 复位 LSM6DSO(可选) lsm6dso_write_reg(0x12, 0x01); // CTRL3_C: SW_RESET HAL_Delay(50); // 2. 配置加速度计:104Hz, ±16g,正常模式 lsm6dso_write_reg(0x10, 0x50); // CTRL1_XL // 3. 配置 CTRL3_C:IF_INC=1,BDU=1 lsm6dso_write_reg(0x12, 0x44); // CTRL3_C // 4. 启用 I2C master(CTRL9_XL bit4) lsm6dso_write_reg(0x18, 0x10); // CTRL9_XL // 5. 配置 MASTER_CONFIG:400kHz I2C 速度 lsm6dso_write_reg(0x14, 0x05); // MASTER_CONFIG // 6. 配置 SLV0:读取 H3LIS331DLTR,从 0x28 开始,读 6 字节 lsm6dso_write_reg(0x15, 0x31); // SLV0_ADD:地址0x18左移1位 + 读位 lsm6dso_write_reg(0x16, 0x28); // SLV0_SUBADD:OUT_X_L lsm6dso_write_reg(0x17, 0x05); // SLV0_CONFIG:6字节(6-1) // 7. 验证 MASTER_ON 是否置位 lsm6dso_read_reg(0x1E, &data); if ((data & 0x08) == 0) { // 提示 Sensorhub 没开启 } }这段代码做完之后,就可以用下面的函数读取外部高 g 传感器的数据:
void read_h3lis331dltr_data(int16_t *x, int16_t *y, int16_t *z) { uint8_t buf[6]; // 从 SENSORHUB0 (0x02) 读 6 字节 lsm6dso_read_regs(0x02, buf, 6); *x = (int16_t)(((uint16_t)buf[1] << 8) | buf[0]); *y = (int16_t)(((uint16_t)buf[3] << 8) | buf[2]); *z = (int16_t)(((uint16_t)buf[5] << 8) | buf[4]); }5.2 数据验证和结果分析
水平静置时,Z 轴读到的原始值换算成 mg 应该接近 1g。我实测读出来 Z 轴原始值大约是 1013mg 左右,跟理论值 1000mg 非常接近,说明 Sensorhub 读取链路是通的。
然后我用一个简易落锤装置做了冲击测试:把一块带 H3LIS331DLTR 的小板子从约 30cm 高度自由落到桌面上,用 400g 量程读数。结果最大冲击值大约到了 45g,持续时间不到 2ms,波形是一个很窄的尖峰。这个数据也验证了低g传感器完全看不到这种冲击——LSM6DSO 的 ±16g 在 45g 面前早就饱和了,所以才需要高g传感器。
FSM 测试时我把阈值设为 20g,手动敲击桌面就能触发中断。这个灵敏度调高之后,落地瞬间都能触发,误触发的可能性也变大了,需要根据实际应用场景仔细调阈值和消抖窗口。
5.3 低功耗实测
最后测了一组功耗数据。MCU 进入 STOP 模式,LSM6DSO 加速度计设为 104Hz,Sensorhub 保持工作,整体电流大约 0.2mA 左右(不含 MCU 的漏电),比 MCU 自己每 10ms 醒来读一次 I2C 的方案省了差不多一个数量级。如果你的产品对功耗特别敏感,可以考虑把加速度计 ODR 降到 52Hz 甚至更低,Sensorhub 读取频率会跟着线性下降,功耗还能再砍一半。不过大幅降频之后,FSM 对冲击事件的响应延迟也会上升,需要权衡。
6. 踩坑记录与排查建议
6.1 常见问题排查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| SENSORHUB 数据全是 0xFF | AUX_SDA/AUX_SCL 没上拉,外部传感器没应答 | 检查 AUX 总线上拉电阻、供电、I2C 地址 |
| SENSORHUB 数据全为 0x00 | SLV0_ADD 的读写位配错,写成了写模式 | 检查 SLV0_ADD bit0 是否为 1 |
| 读出来的 XYZ 数据错乱 | 起始子地址配错,或者字节数不对 | 核对 SLV0_SUBADD 和 SLV0_NUM_OP |
| MASTER_ON 一直是 0 | 加速度计没使能,或 I2C master 没开启 | 检查 CTRL1_XL 和 CTRL9_XL 配置 |
| FSM 不触发 | 阈值没按灵敏度换算,比较的数据源选错 | 用 Unico 在线调试看状态机跳转情况 |
| 中断引脚无输出 | FSM 中断没映射到 INT1/INT2 | 检查中断路由寄存器配置 |
6.2 我踩过的几个坑
第一个坑是 SA0 地址没对上。H3LIS331DLTR 的 I2C 地址是 0x18 还是 0x19,完全看 SA0 引脚的电平。我一开始以为模块上默认 SA0 接地,就直接按 0x18 配,结果 Sensorhub 一直读不到数据。后来拿逻辑分析仪抓总线才发现,模块上 SA0 被拉到了高电平,地址是 0x19。这个问题排查起来特别隐蔽,因为很多时候主 I2C 是通的,LSM6DSO 也能正常应答,只是 SENSORHUB 数据区全是 0xFF。
第二个坑是 SLV0_CONFIG 的配置位理解错了。SLV0_CONFIG 的低 4 位是读取字节数减 1,不是实际字节数。第一次我写了 0x06,想读 6 个字节,结果读到 7 个字节,最后一个字节把 SENSORHUB 寄存区下一个位置的寄存器覆盖了。后来老老实实把数据手册里那一段说明读了两遍,才搞明白是“number of operations - 1”。
第三个坑是阈值比较的符号问题。H3LIS331DLTR 输出的原始数据是有符号 16 位值,冲击方向跟重力方向相反时,读取到的数值可能是负数。我在 FSM 里的比较条件只做了大于阈值判断,结果反向冲击完全检测不到。解决方案是取绝对值之后再比较,或者同时配置大于正阈值和小于负阈值两条跳转条件。
还有一个跟 FSM 相关的点:调试时我一开始在 Unico 里把数据源指向了 LSM6DSO 自带的加速度计,而不是 SENSORHUB,导致 FSM 判断的是低 g 传感器数据。这个选择在 Unico 界面上很容易点错,因为数据源列表有很多相似选项,建议仔细核对别名。
6.3 调试工具推荐
调试这种多级 I2C 拓扑,逻辑分析仪是刚需。我用的是一个便宜的 8 通道逻辑分析仪,配合开源软件抓取 I2C 波形,能看到 LSM6DSO 对 H3LIS331DLTR 的访问时序是否正常。没有逻辑分析仪的话,至少也要写个 I2C 扫描程序,先单独确认主 MCU 能读到 H3LIS331DLTR 的 WHO_AM_I,确认地址没错,再去配置 Sensorhub。
ST 的 Unico 软件也强烈建议装上。它不仅能可视化配置 FSM,还能实时查看 LSM6DSO 所有寄存器状态,包括 SENSORHUB 数据区的实时值,调试效率比一边改代码一边串口打印高很多。我第一次把 Sensorhub 调通,就是靠 Unico 里看到数据区的数值跟着板子倾斜而实时变化,才确认整条链路通了。
整个方案从硬件连线和软件配置来看,并不算难,难点主要在于对 LSM6DSO 的寄存器体系不熟悉。如果你之前只用过它的基础加速度计功能,第一次碰 Sensorhub 和 FSM 确实会有点懵。我的建议是分两步走:先把 Sensorhub 的数据通路打通,确认能读到外部传感器的数据;再上 FSM 做事件检测。一步到位调的话,出了问题很难定位是数据链路的问题还是状态机的问题。
另外再多说一句,这个组合方案应用到实际项目时,PCB 布局也是一个关键变量。H3LIS331DLTR 尽量靠近受力点安装,避免结构传导导致冲击峰值被衰减。如果产品需要做整机跌落测试,建议把触发阈值留出至少 20% 的余量,因为真实跌落场景的冲击波形往往比实验室里模拟的复杂得多,峰值高、持续时间短,还可能伴随高频振荡。阈值设得太贴近实际冲击值,会有一部分跌落样本触发不了,这在可靠性要求高的产品里是不能接受的。