老哥们,最近一直在折腾一个比较典型的低功耗振动监测节点,用的就是ST的IIS3DWB10IS这颗带ISPU的高带宽加速度计。项目本身不复杂——磁吸安装在设备外壳上,电池供电,平时主控睡大觉,隔一段时间醒来采集一次振动数据,判断有没有异常。但做到一半我们卡在了一个绕不开的问题上:ISPU这个内核没有NVM,它的程序只能放在RAM里,掉电就丢,而整机为了省电偏偏还要做power gating,每次唤醒都是冷启动。那ISPU里跑的FFT和异常检测算法代码到底放哪?每次上电怎么恢复?整个电池节点的功耗账该怎么算?
这篇文章就是我处理这个问题时踩出来的完整思路。我会先讲清楚ISPU没有NVM到底卡在哪,再给出我认为最合理的几种方案和选型逻辑,然后详细算一遍冷启动的时间与功耗账,最后把调试中遇到的加载失败、掉电乱序、Flash寿命这些坑都列出来。如果你手上也在做IIS3DWB10IS或类似带ISPU传感器的电池节点,这篇应该能帮你少走很多弯路。
1. 先说清楚:ISPU没有NVM,到底卡在哪个环节
1.1 IIS3DWB10IS在电池节点里的定位
IIS3DWB10IS不是一颗普通的加速度计。它本身的振动测量带宽很高,噪声也低,适合轴承故障、齿轮箱、泵和风机这类旋转机械的状态监测。但真正让它区别于普通传感器的,是这个集成的ISPU内核——一颗可以在传感器内部跑轻量级信号处理和机器学习推理的可编程单元。
在电池供电的振动节点里,ISPU的价值非常直接:原始振动数据不用一路传到主控MCU,ISPU直接在传感器内部做FFT、RMS、峰值提取,甚至跑一个异常检测模型,最后只把诊断结果通过FIFO送出来。主控MCU大部分时间可以保持深度睡眠,甚至整机断电。这样整个系统的平均功耗可以压到非常低,一节电池用好几年的节点就是这么设计出来的。
不过,ISPU虽然能算,但它本身没有非易失存储。这就引出一个所有做低功耗节点的人都要面对的问题——算法代码在掉电之后去哪了。
1.2 “ISPU没有NVM”到底意味着什么
先打个比方。ISPU就像一台没有硬盘的电脑,你写好的算法程序放在它内部的SRAM里,掉电以后连操作系统带应用程序全部清空。每次机器开机,你都得先从外部U盘把系统重新灌进去,灌完才能开始干活。
放在IIS3DWB10IS上就是:主控MCU每次上电后,都需要通过SPI接口把ISPU的算法二进制文件推送到ISPU内部的程序区,然后再让ISPU复位启动,它才能正常跑起来。这个过程不是可选项,而是必须做的,因为ISPU自己没有任何可以持久化保存代码的空间。
所以“power-gated battery node”和“ISPU无NVM”这两个条件放一起,就构成了一个矛盾:省电要求整机频繁断电,但每次断电再上电,ISPU都处于“空系统”状态,必须重新加载算法。而加载算法本身要花时间、要耗电流,这个开销在设计功耗预算时很容易被忽略,但恰恰会决定节点的唤醒频率上限和电池寿命。
很多第一次用这颗芯片的朋友会下意识问:ST为什么不在ISPU里面放一点Flash?其实从成本、晶圆面积和功耗的角度看,这种设计是合理的,毕竟ISPU的定位是辅助处理器,算法代码由主机管理本来就是常见形态。但这就逼着系统设计者必须认真对待“算法从哪来、怎么加载、加载多快”这几个问题。
2. 推荐方案:算法代码应该放在哪里
2.1 几个方案先摆出来对比一下
针对ISPU没有NVM的问题,我见过的主流做法有三类,各有各的适用场景,先用一张表说清楚。
| 方案 | 算法代码存放位置 | 启动时序 | 复杂度 | 适合场景 |
|---|---|---|---|---|
| A:主控Flash存储,每次冷启动加载 | 放主控MCU片内Flash或外挂NOR Flash | 上电后由MCU通过SPI写入ISPU | 低 | 大多数电池振动节点,唤醒不频繁 |
| B:保持传感器供电,不彻底断电 | 算法留在ISPU RAM中不丢失 | 无需重新加载,ISPU直接续跑 | 低 | 唤醒频率较高,待机电流可以接受 |
| C:外挂SPI Flash统一管理算法包 | 放独立SPI Flash,主控只负责搬运 | 比A多一次Flash读操作 | 中高 | 算法体积大、版本多、需要OTA升级 |
先说结论:绝大多数低功耗、间歇唤醒的振动节点,我都推荐方案A,也就是主控NVM存算法、上电后快速加载。方案B适合唤醒频繁但又能接受“不彻底关断”的场景,方案C是算法体积和升级需求大到主控Flash装不下时的备选。
2.2 我最推荐的组合:主控Flash加SPI DMA加载
方案A的具体做法其实不复杂,但有几个细节决定能不能稳定运行。算法工程在ST的ISE环境里编译之后,会生成一个二进制文件,这个文件会嵌到主控MCU的固件里,作为常量数组存在MCU的片内Flash中。注意,这里说的Flash是主控的Flash,不是ISPU的——ISPU没有,但主控一定有。
上电加载流程我一般是这样安排的:
- 主控上电,初始化自己的时钟、GPIO和SPI外设。
- 拉高传感器的电源域,等待传感器内部上电稳定。
- 对传感器做一次软复位,确保它处于已知状态。
- 配置SPI接口,把ISPU算法二进制分块写入传感器的程序区。
- 全部写完后,发送启动或运行指令,让ISPU程序真正跑起来。
- 通过状态寄存器或者中断信号确认ISPU已经正常运行,然后再初始化业务逻辑。
这个流程里,步骤4最容易被忽视的是“分块写入”和“传输校验”。我见过有人直接把数千字节的数组一次性往SPI上怼,结果因为主机缓冲不够、或者SPI时钟边沿余量不足,导致写进去的数据错位,ISPU跑出来的完全是垃圾结果。正确的做法是把算法文件按256字节或512字节一块,一块一块地写入,每块写完都有明确的应答或寄存器状态可查,最后做一次整体校验。
在传输手段上,强烈建议用DMA,不要用CPU一个字节一个字节地搬。加载算法期间主控可以继续做别的事,或者干脆进入一个低功耗等待状态,直到DMA传输完成中断再醒来。我用DMA加载40KB左右的算法,实际CPU占用几乎可以忽略,整个过程由硬件自动完成,这对功耗控制帮助很大。
2.3 什么时候才需要外挂SPI Flash
方案C我平时不太建议一上来就用,除非你明确遇到了下面这些情况:
- 主控MCU片内Flash太小,塞不下多版本算法或一套完整的升级镜像。
- 算法体积已经大到几十上百KB,而且还在不断膨胀。
- 产品需要支持现场OTA升级算法,需要在本地暂存新版本固件。
- 主控MCU过于低端,内部Flash本身就非常有限。
外挂SPI Flash的缺点也很明显,成本增加、PCB面积增加、加载前要多一次“从Flash读出来再写进ISPU”的搬运,启动时间比方案A更久,而且SPI Flash本身也是NVM,一样要考虑擦写寿命。所以我的建议是:预算和技术指标都允许的情况下,先用主控Flash,等确实不够了再考虑外挂Flash。
3. 启动时序和功耗的账,要会算
3.1 一次完整的冷启动需要多长时间
讨论“推荐做法”的时候,不能回避一个核心指标:冷启动到底要多久。这个指标直接影响节点是否能够在可接受的窗口里完成一次数据采集,也直接决定平均功耗。
完整的冷启动时间可以拆成几段看:
- 传感器电源域上电稳定:通常几毫秒到十几毫秒,取决于电源开关器件和去耦电容。
- 主控上电和SPI初始化:几毫秒。
- 传感器软复位与会话建立:几毫秒。
- ISPU算法加载:这是大头,取决于算法体积和SPI时钟。
- ISPU启动、初始化数据通路、FIFO开始填充:几毫秒。
其中第4步是可以量化计算的。假设算法bin的实际大小在40KB左右,SPI时钟按10MHz跑,理论传输时间大概是:
40 × 1024 × 8 / 10,000,000 ≈ 32.8毫秒
但实际操作中有命令开销、块间切换、应答等待,还要算上偶发的重传,所以实际加载时间落在40到60毫秒是很正常的。如果SPI时钟能跑到20MHz,理论时间可以砍半,传输部分能做到16毫秒左右,实际也就20到30毫秒。
如果你按40KB算出来的加载时间,比传感器本身的测量周期还长,那就要警惕了,这种场景下每次唤醒都重载算法的代价可能比你想象的高得多。
3.2 用数字说话:加载一次ISPU到底值多少功耗
功耗账不能只看“时间短”,要看“平均电流”。假设整个加载过程传感器和主控都处于工作状态,整机电流取8mA左右,加载时间按70毫秒算,那么一次加载消耗的电量就是:
8mA × 0.07秒 ≈ 0.56毫安秒
再换算成库仑,约等于0.56mAs。这个数字单独看不吓人,但放到整个工作周期里算平均电流就很直观了:
- 如果节点每60秒唤醒一次:平均电流约 0.56mAs / 60s ≈ 9.3µA。
- 如果节点每5秒唤醒一次:平均电流约 0.56mAs / 5s ≈ 112µA。
- 如果节点每1秒唤醒一次:平均电流约 0.56mAs / 1s ≈ 560µA。
注意,这还只是加载算法这一件事的贡献,没有算传感器配置、数据采集、无线发射的电流。也就是说,在唤醒不频繁的节点里,重新加载算法的功耗完全可以接受;但唤醒频率一旦上去,加载本身就会变成最大的耗电黑洞。
这也是为什么我说“推荐方案”不能脱离场景。低频唤醒的轴承监测节点,方案A非常好,70毫秒加载时间换来极低的待机电流,电池寿命完全达标;但如果你的节点需要每秒都判断一次设备状态,那方案A就没那么香了,要么拉长休眠周期,要么改用方案B,让ISPU一直保持供电。
3.3 降低加载开销的几个实操技巧
如果确认走方案A,有几个小技巧可以帮你把加载功耗压下来。
一是优先用DMA传输。CPU在加载期间不用一直跑,可以进入低功耗等待状态,等DMA完成中断再唤醒,这样峰值电流的时间和幅度都更可控。
二是按需初始化硬件。SPI外设、GPIO、DMA通道这些尽量在加载前一次性配置好,不要在加载过程中反复开关外设时钟,那会引入额外的电流尖峰。
三是检查算法bin体积。每次编译完都看一眼生成的二进制文件大小,别让它在不知不觉中膨胀。关掉调试输出、缩减环形缓冲、精简模型结构,能省下几十KB,就相当于省下了几十毫秒加载时间和对应的功耗。
四是把校准参数和用户配置交给主控统一管理。传感器内部的用户寄存器在掉电后同样会丢,所以像偏移校准、阈值设置、滤波器系数这些,都应该存在主控Flash里,每次加载算法后重新写一遍。别指望ISPU自己能记住这些配置,它没有NVM,做不到。
3.4 唤醒频率高时,也许不该断电
这一点值得单独拎出来说。不少团队一提到低功耗就条件反射地想到“全断”,但全断并不是唯一的省电路径,甚至不是最优路径。
如果节点唤醒频率超过每5秒一次,或者更极端地需要持续监测,那每次唤醒都重新加载几十毫秒的算法,反而比让ISPU保持在一个低功耗状态更费电。这种场景我建议改用“伪power gating”:传感器电源不断,让ISPU进入低功耗或睡眠模式,中断唤醒后它可以直接从RAM里继续执行程序,不需要重新加载。
代价是待机电流不再是零,可能是几十微安级别,但换来的是微秒级的响应和完全不需要加载算法。在故障检测类应用里,很多时候“睡得浅一点但能快速响应”比“彻底关机但要花几十毫秒才能醒”更划算。这个取舍一定要写在方案评审里,别等到代码写完才改架构。
4. 加载失败、掉电时序、Flash寿命:这些坑我替你们踩过了
4.1 ISPU程序加载失败的常见原因
ISPU程序加载失败,通常不会有什么明显的报错,表现就是ISPU没有按预期输出结果,或者传感器状态寄存器里的标志位不对。我排查过几次,最后定位到的主要原因就这几个:
- 传感器还没就绪就开始写程序。上电后立刻发SPI命令,传感器内部还在复位,命令全部被丢弃。
- SPI时钟太快或信号完整性问题。飞线过长、上拉电阻不对、时钟边沿采样点配置错误,都可能导致写入的数据错位。
- 加载过程中电源电压跌落。电池节点上电瞬间电流冲击大,电源没有稳定,导致加载中途失败。
- 算法bin版本与ISPU运行环境不匹配。比如用旧版本的IDE编译的算法,或者字节序配置错误。
- 加载后没有正确触发启动,或者复位时序错误,导致ISPU虽然收到了程序但并没有运行起来。
这些问题的排查方式,我建议从软件和硬件两头同时下手。软件上在加载流程里加CRC校验和状态检查,每次写一块都确认写成功再写下一块;硬件上用逻辑分析仪抓一次SPI波形,看看时钟和数据是否对齐,电源轨有没有明显跌落。
4.2 MCU软复位后的状态一致性问题
还有一个容易被忽略的坑:MCU自己跑飞了,软件看门狗复位,但传感器的电源没有断过。这时候ISPU的程序其实还在RAM里,还在继续跑,而主控MCU的SPI配置、DMA状态、业务逻辑全部重新初始化了。两边处于“都知道对方存在,但不知道对方状态”的尴尬境地,表现出来就是数据错乱、中断唤醒异常、莫名其妙多出很多垃圾中断。
所以我的建议是,MCU的启动流程里必须包含一个“传感器复位并重新加载”的步骤,即使传感器看起来还在正常运行,也要先把它软复位掉,然后重新加载ISPU算法,保证整个系统回到一个确定性的初始状态。这样做虽然会让启动时间多几十毫秒,但换来的是系统状态的一致性和可预期性,在低功耗节点里这个代价是值得的。
4.3 掉电顺序与电源管理电路的配合
既然做了power gating,就绕不开掉电顺序。最怕的情况是:系统正在采集数据,ISPU正在跑FFT,主控突然把传感器电源关了,ISPU跑到一半掉电。下次上电时,传感器可能处于一个未定义状态,SPI通信拉不起来,加载算法也迟迟不成功。
合理的做法是在硬件设计时就规划好掉电时序。传感器的电源开关要保证有明确的上下电延迟,不能和主控的reset共用同一个毛刺源;软件上,主控在关断传感器电源之前,应先把ISPU置于复位状态,或者至少让它停止输出中断信号,避免掉电瞬间ISPU给主控留下一个虚假的中断。
另外,中断线上最好加一个小的RC滤波或下拉,避免掉电期间传感器电源上的残压耦合出干扰脉冲,把主控从低功耗状态误唤醒。这种问题在实验室里很难复现,一到现场就会变成“电池掉得特别快”的诡异Bug,排查起来非常头疼。
4.4 Flash擦写寿命、算法版本升级与现场维护
如果算法放在主控Flash里,就绕不开Flash寿命问题。算法本身是只读的,不会频繁擦写,但OT A升级时,如果设计得不好,频繁在同一个Flash分区写入,几年下来就可能把片内Flash写穿。这不是危言耸听,现场的工程设备往往要服役五到十年。
我的建议是,算法存储区与主程序存储区在逻辑上分开,算法区域使用双备份结构。当前正在运行的算法放一块区域,新升级的算法先写入另一块区域,校验通过后再切换激活。万一升级过程中掉电,下次启动还能用旧版本算法继续跑,不会变砖。这套逻辑不复杂,但对现场维护的可靠性提升非常大。
给算法文件本身也建议带一个版本号和CRC字段,主控加载前读出来校验一遍,避免Flash区域数据出错,ISPU跑了一个完全错误的算法。震动节点平时没人看管,一旦数据异常几乎没法远程定位,所以所有能自愈的机制都要尽量做在前面。
4.5 程序保密性:算法裸露在Flash里的风险
还有一个很多人不会第一时间想到的点:算法二进制放在主控Flash里,本质上就是明文存放的。如果现场的设备被人拆走,通过调试接口把Flash读出来,你的振动诊断算法就完全暴露了。对很多做设备健康管理的公司来说,算法模型本身就是核心资产。
如果在意这个问题,常见的做法是在主控侧对算法二进制做加密存储,上电后解密再传给ISPU。但要注意,解密会占用主控RAM和CPU时间,在超低功耗节点上需要评估是否划算。对于比较敏感的工业客户,这个权衡值得提前和产品经理对齐,别等量产了再改加密方案。
5. 回到标题:我的recommended approach是什么
5.1 一句话结论
如果一定要用一句话回答标题里的问题,我的答案是:默认把ISPU算法二进制放在主控MCU的Flash里,每次冷启动通过SPI DMA分块写入ISPU,配合CRC校验和版本管理,把加载时间控制在几十毫秒量级;当唤醒频率高到重新加载的电量开销无法接受时,再切换为ISPU保电加低功耗运行的“伪power gating”方案;只有当算法体积大到主控Flash放不下、或者需要频繁OTA升级时,才去引入外部SPI Flash。
这个思路的出发点很简单:在绝大多数电池供电的振动监测场景里,唤醒频率并不高,加载几十毫秒的算法换来的低待机功耗才是收益最大的。而真正决定成败的,不是上电加载这个动作本身,而是你为它设计的时序、校验、掉电保护和版本管理机制。
5.2 选型时真正该留意的两件事
第一件事是唤醒频率。在项目最早期就要把“平均多久醒一次”这个参数定下来,因为它直接决定ISPU的供电策略。唤醒频率低于每30秒一次,方案A几乎无脑用;唤醒频率高于每5秒一次,就要认真考虑方案B。第二件事是算法体积。哪怕现在只有20KB,也要按未来增加故障类型、增加模型复杂度后的体积做评估,看你选的主控Flash够不够用,别等算法优化到一半发现塞不下了再换芯片。
5.3 一点经验
做这类低功耗传感器节点,我个人的体会是:不要怕ISPU没有NVM,真正怕的是系统设计时没有把“冷启动加载”当成一个一等公民来对待。只要你在架构阶段就给加载流程留好时序预算、功耗预算和异常恢复路径,这个“缺点”完全不会成为瓶颈。
另外再分享一个小建议:在固件里保留一个加载耗时的性能探针,每次上电都记录一次从电源开启到ISPU开始运行的毫秒数。这个数字在调试时价值极大,能帮你快速识别是电源不稳、SPI通信异常还是算法文件体积膨胀导致的问题。我在好几个项目里都是靠这个数据定位到硬件问题的,强烈建议你加上。