无线应急灯这个品类,看起来简单,真做起来坑不少。我前后经手过三四个版本的应急灯方案,从最早的纯硬件定时器方案,到后来加MCU做状态管理,再到最近这个基于 LoRa1276-C1-915 模组的无线版本,每一步都踩过实实在在的坑。这次要聊的这套方案,核心是用 SX1276 射频芯片做 915 MHz 频段的无线通信,配合 SPI 接口跟主控打交道,同时把低功耗设计贯穿到整个系统里。它解决的不是"灯亮不亮"的问题,而是"灯在哪儿、什么状态、还能撑多久"这些运维层面的痛点。适合正在做应急照明、消防联动、工业设备状态回传的硬件工程师和嵌入式开发者参考,也适合对 LoRa 通信和低功耗设计感兴趣的朋友。
1. 为什么应急灯要上无线通信
1.1 传统应急灯的三个死穴
先说清楚为什么要折腾无线。传统应急灯的逻辑很简单:市电断了,继电器切换,电池供电点亮。这套逻辑用了几十年,可靠性没问题,但放到现代楼宇和工业场景里,三个问题绕不过去。
第一个是状态不可知。灯装在天花板、走廊尽头、设备间里,平时没人管。电池老化、灯珠衰减、充电电路故障,这些都不会主动告诉你。等到真断电了,灯不亮,才发现问题。我见过一个项目,验收时全部正常,两年后消防检查,三成灯具电池已经失效,全部要拆下来换,人工成本比灯本身还贵。
第二个是测试成本高。按规范,应急灯要定期做放电测试,传统做法是人工逐个按测试按钮,记录放电时间。一栋楼几百盏灯,两个人干一整天。而且人工记录容易漏、容易错,数据也没法归档分析。
第三个是联动能力弱。火灾发生时,应急灯应该按疏散路径引导,但传统灯只能"全亮"或"全灭",没法根据火警位置动态调整指示方向。这在大型综合体里是个硬伤。
1.2 LoRa 在这个场景里的位置
无线方案有好几种选择,Wi-Fi、蓝牙、ZigBee、LoRa,为什么选 LoRa?核心就一个字:穿。
应急灯装在什么环境?混凝土墙体、金属吊顶、设备机房、地下车库。Wi-Fi 在 2.4 GHz,穿两堵墙就衰减得厉害;ZigBee 同样在 2.4 GHz,组网复杂,节点多了还容易掉线。LoRa 工作在 Sub-GHz 频段,915 MHz 在国内属于免许可频段,波长更长,绕射能力强,穿透损耗低。实测下来,同样一堵承重墙,2.4 GHz 信号衰减 20 dB 以上,915 MHz 大概 8 到 10 dB,差距很明显。
另一个原因是功耗。应急灯本身靠电池供电,通信模块不能太费电。LoRa 的调制方式决定了它在低速率下灵敏度极高,可以用很小的发射功率实现远距离通信。SX1276 在睡眠模式下电流只有零点几微安,接收模式下大概 10 mA 出头,发射时根据功率不同在 20 到 120 mA 之间。这个功耗水平,配合合理的占空比设计,用电池撑几年是有可能的。
还有一点是网络容量。LoRa 网关可以同时管理几百上千个节点,应急灯这种"数量多、数据量小、上报频率低"的场景,正好匹配。
1.3 LoRa1276-C1-915 模组的基本盘
LoRa1276-C1-915 是一个基于 SX1276 的模组,把射频前端、匹配网络、晶振都集成好了,对外只留 SPI 接口和几个控制引脚。对硬件工程师来说,省掉了射频匹配的调试,直接当数字器件用就行。
关键参数先列一下:
| 参数项 | 典型值 | 说明 |
|---|---|---|
| 工作频段 | 902-928 MHz | 国内 915 MHz 免许可频段 |
| 调制方式 | LoRa / FSK / OOK | 主要用 LoRa 模式 |
| 接口 | SPI | 最高 10 MHz |
| 供电电压 | 1.8-3.7 V | 典型 3.3 V |
| 睡眠电流 | < 1 μA | 寄存器配置后 |
| 接收电流 | ~10 mA | 持续接收模式 |
| 发射电流 | 20-120 mA | 取决于输出功率 |
| 最大发射功率 | +20 dBm | 可编程调节 |
| 灵敏度 | -148 dBm | SF12, BW125kHz |
这个模组的核心是 SX1276,所有配置都通过 SPI 读写寄存器完成。SPI 接口是它跟主控之间唯一的数字通道,理解 SPI 时序和寄存器映射,是用好这个模组的前提。
2. SPI 接口的实操细节与常见翻车点
2.1 SX1276 的 SPI 时序到底特殊在哪
SX1276 的 SPI 接口,标准模式是 Mode 0,也就是 CPOL=0、CPHA=0。时钟空闲时低电平,数据在上升沿采样。这个跟大多数 SPI 从设备一致,看起来没什么特别。但实际调试时,有几个细节容易翻车。
第一个是片选时序。SX1276 要求片选拉低之后,要等至少一个时钟周期才能开始发数据。很多 MCU 的硬件 SPI 在片选拉低后立刻就开始输出时钟,如果从设备还没准备好,第一个字节就可能丢。解决办法是在片选拉低后加一个微秒级的延时,或者用软件片选手动控制。
第二个是地址字节的格式。SX1276 的寄存器地址是 7 位,第 8 位是读写标志。写操作时地址字节的最高位是 0,读操作时最高位是 1。比如要写寄存器 0x01,发送的地址字节是 0x01;要读寄存器 0x01,发送的地址字节是 0x81。这个跟很多 SPI 器件不一样,第一次用容易搞混。
第三个是连续读写。SX1276 支持地址自增,一次片选可以连续读写多个寄存器。FIFO 操作就靠这个特性,一次把几十字节的数据推出去。但要注意,地址自增到 0x7F 之后会回绕到 0x00,如果数据长度没算对,会覆盖掉前面的配置寄存器。
2.2 硬件片选还是软件片选
这个问题在论坛上吵了很多年。我的经验是:看你的 SPI 总线上挂了多少设备,以及时序要求有多严。
如果 SPI 总线上只有 SX1276 一个设备,硬件片选完全够用,配置好 SPI 外设,片选自动拉低拉高,代码简单。但如果有多个从设备,比如同时挂了 Flash、传感器、射频模组,硬件片选的数量可能不够,或者片选之间的切换时序不好控制,这时候软件片选更灵活。
软件片选的做法是:把片选引脚配置成普通 GPIO,在每次 SPI 传输前后手动拉低拉高。这样你可以精确控制片选和时钟之间的时序关系,也能在片选拉低后插入必要的延时。
// 软件片选示例 void sx1276_write_reg(uint8_t addr, uint8_t data) { SX1276_CS_LOW(); delay_us(1); // 片选建立时间 spi_transfer(addr & 0x7F); // 写操作,最高位为0 spi_transfer(data); SX1276_CS_HIGH(); delay_us(1); // 片选保持时间 }注意:软件片选时,SPI 外设的 NSS 引脚要配置成软件模式,否则硬件会自动控制片选,跟你的 GPIO 操作冲突。
2.3 SPI 时钟频率能跑多快
SX1276 手册标称 SPI 最高 10 MHz,但实际能跑多快,取决于你的 PCB 走线、MCU 的 SPI 外设能力、以及电源质量。
我实测过几块板子,STM32F103 的硬件 SPI 跑到 9 MHz 没问题,但换到某款国产 MCU,超过 4 MHz 就开始丢数据。后来查出来是 PCB 走线太长,SPI 时钟线上有振铃,从设备采样时数据不稳定。
几个经验值:
- 板子小、走线短(< 5 cm),电源干净,可以跑到 8-10 MHz。
- 走线较长或经过排线,建议降到 4 MHz 以下。
- 如果用了电平转换芯片,要看转换芯片的速率上限,很多便宜的电平转换只能到 24 MHz,但实际用起来 10 MHz 以上就明显失真。
调试时如果发现读回来的寄存器值不对,先别怀疑代码,把 SPI 时钟降一半试试。这个办法救过我很多次。
2.4 用 Python 通过 USB 模拟 SPI 调试
项目前期,硬件还没定型,或者手头没有逻辑分析仪,可以用 Python 配合 USB 转 SPI 模块来调试 SX1276。市面上有一些 USB 转 SPI 的小板子,上位机用 Python 调用,可以手动发寄存器读写命令,验证模组能不能正常通信。
import spidev import time spi = spidev.SpiDev() spi.open(0, 0) # bus 0, device 0 spi.max_speed_hz = 1000000 # 1 MHz spi.mode = 0 def write_reg(addr, value): spi.xfer2([addr & 0x7F, value]) def read_reg(addr): resp = spi.xfer2([addr | 0x80, 0x00]) return resp[1] # 读版本号寄存器,SX1276 应该是 0x12 version = read_reg(0x42) print(f"Version: 0x{version:02X}")这个办法的好处是快。不用编译下载,改一行跑一行,特别适合验证寄存器配置。缺点是速度慢,不适合跑完整的通信协议,但用来确认硬件通路没问题,足够了。
3. 低功耗设计的几个关键决策
3.1 先算清楚功耗预算
低功耗设计不是上来就调寄存器,而是先算账。应急灯的电池容量是固定的,比如 3.7 V / 2000 mAh 的锂电池,或者 4 节镍氢电池。你要保证在待机状态下能撑多久,在应急状态下能亮多久。
假设电池容量 2000 mAh,要求待机 3 年,应急点亮 90 分钟。应急状态功耗好算,LED 驱动电流乘以电压,比如 3 W 的灯,90 分钟耗电 4.5 Wh,折合 3.7 V 下约 1216 mAh。这部分占了大头,但它是"一次性"的,用完就完了。
待机功耗才是细水长流的部分。3 年约 26280 小时,2000 mAh 分摊下来,平均电流不能超过 76 μA。这个预算里,MCU、射频模组、传感器、电源电路都要分。
| 模块 | 待机电流目标 | 说明 |
|---|---|---|
| MCU | < 10 μA | 深度睡眠模式 |
| SX1276 | < 1 μA | Sleep 模式 |
| 电池监测 | < 5 μA | 分压电阻要大 |
| 充电管理 | < 20 μA | 选低静态电流芯片 |
| LDO | < 10 μA | 选低 Iq 型号 |
| 其他 | < 30 μA | 余量 |
算下来,76 μA 的预算其实很紧张。如果 LDO 选了个静态电流 50 μA 的,光这一项就吃掉大半。所以低功耗设计的第一步,是选对器件,而不是后面靠软件省。
3.2 SX1276 的睡眠与唤醒策略
SX1276 有几个功耗模式,从高到低:
- TX 模式:发射时,电流取决于输出功率,+20 dBm 时约 120 mA。
- RX 模式:持续接收,约 10 mA。
- Standby 模式:晶振运行,约 1.5 mA。
- Sleep 模式:寄存器配置保留,约 0.2 μA。
应急灯的场景,通信是间歇性的。平时不需要一直接收,只需要定期上报状态,或者被网关唤醒。所以策略是:大部分时间让 SX1276 处于 Sleep 模式,需要通信时唤醒到 Standby,配置好参数后进入 TX 或 RX,完成后立刻回 Sleep。
这里有个坑:从 Sleep 唤醒到能正常收发,需要重新配置一些寄存器,因为 Sleep 模式下部分寄存器会复位。具体来说,FIFO 指针、IRQ 标志、模式设置都要重新写。我一开始没注意,唤醒后直接发数据,结果发出去的是乱码。后来老老实实加了一个初始化序列,每次唤醒都跑一遍。
void sx1276_wakeup(void) { sx1276_write_reg(REG_OP_MODE, MODE_STDBY); delay_ms(1); // 重新配置必要寄存器 sx1276_write_reg(REG_FIFO_TX_BASE_ADDR, 0x00); sx1276_write_reg(REG_FIFO_RX_BASE_ADDR, 0x00); sx1276_write_reg(REG_IRQ_FLAGS, 0xFF); // 清中断 // 其他配置... }3.3 MCU 的睡眠与射频唤醒配合
MCU 这边,我用的是一款低功耗 ARM Cortex-M0+,深度睡眠电流能做到 2 μA 以下。关键是睡眠和射频唤醒的配合。
有两种模式:
第一种是 MCU 定时唤醒,主动上报。MCU 内部 RTC 定时,比如每 5 分钟醒一次,唤醒 SX1276,发一包状态数据,然后继续睡。这种模式简单可靠,但上报频率固定,网关不知道灯什么时候会发。
第二种是 SX1276 接收唤醒,MCU 被动响应。SX1276 配置成 RX 模式,收到前导码后产生中断,唤醒 MCU。这种模式响应快,但 SX1276 要一直处于 RX 或周期性 RX 状态,功耗高。
实际项目里,我用了混合模式:平时 MCU 定时唤醒上报,同时 SX1276 配置成周期性 RX(比如每 2 秒开一次接收窗口,持续 100 ms),网关如果有紧急指令,可以在这个窗口内下发。这样平均功耗可控,又能保证一定的实时性。
计算一下:SX1276 RX 电流 10 mA,每 2 秒开 100 ms,占空比 5%,平均电流 0.5 mA。这个数字对 76 μA 的预算来说太大了。所以实际参数要调,比如改成每 10 秒开 50 ms,平均电流 50 μA,勉强能接受。或者干脆取消周期性 RX,完全靠定时上报,紧急指令通过下次上报的响应带回。
3.4 电源电路的静态功耗陷阱
电源电路是低功耗设计里最容易翻车的地方。我踩过的坑包括:
LDO 选型只看输出电流,不看静态电流。有些 LDO 标称 500 mA 输出,静态电流 80 μA,用在待机场景就是灾难。后来换成静态电流 1 μA 的型号,整个系统待机电流直接降了一个数量级。
分压电阻取值太小。电池电压监测用两个电阻分压,如果取 10 kΩ 和 10 kΩ,电池 4 V 时,分压电路一直消耗 200 μA。改成 1 MΩ 和 1 MΩ,消耗降到 2 μA,但 ADC 输入阻抗要匹配,否则采样不准。解决办法是在分压电阻和 ADC 之间加一个电压跟随器,或者用 MCU 内部的可编程上拉,采样时才接通。
充电芯片的静态电流被忽略。锂电池充电管理芯片,充电时电流几百 mA,但充电完成后,如果芯片没有进入低功耗模式,静态电流可能几十 μA。选型时要看"充电完成后的静态电流"这个参数,很多手册不直接标,要发邮件问 FAE。
LED 驱动电路的漏电流。应急灯的 LED 驱动,如果用的是简单的 MOS 管加限流电阻,MOS 管关断时可能有漏电流。选低漏电流的 MOS 管,或者加一级使能控制,不用时彻底断电。
4. 状态监测与数据上报的设计
4.1 应急灯到底要监测什么
状态监测不是越多越好,要跟运维需求匹配。我梳理下来,核心监测项有这么几个:
| 监测项 | 采集方式 | 上报频率 | 用途 |
|---|---|---|---|
| 电池电压 | ADC 分压 | 每次上报 | 判断电池健康度 |
| 充电状态 | GPIO 电平 | 状态变化时 | 判断充电电路是否正常 |
| 灯珠状态 | 电流检测 | 测试时 | 判断灯珠是否失效 |
| 环境温度 | 内部温度传感器 | 每次上报 | 电池温度补偿 |
| 工作时长 | RTC 累计 | 每次上报 | 寿命评估 |
| 故障标志 | 软件标志位 | 状态变化时 | 快速定位问题 |
电池电压是最重要的。锂电池的放电曲线比较平,3.7 V 到 3.4 V 之间容量变化很大,所以光看电压不够,要结合放电测试。我的做法是:每次应急放电测试时,记录电压下降曲线,跟出厂标定的曲线对比,偏差超过阈值就报"电池老化"。
4.2 数据帧格式怎么设计
LoRa 的速率低,单包数据不能太大。SX1276 的 FIFO 是 256 字节,但实际用的时候,考虑到空中传输时间和功耗,单包控制在 20 字节以内比较合适。
我设计的数据帧格式:
字节 0: 帧头 0xAA 字节 1: 设备类型 0x01(应急灯) 字节 2: 设备 ID 高字节 字节 3: 设备 ID 低字节 字节 4: 电池电压高字节(mV) 字节 5: 电池电压低字节 字节 6: 温度(偏移 40,单位 ℃) 字节 7: 状态标志位 字节 8: 故障码 字节 9: 累计工作时长(小时,高字节) 字节 10: 累计工作时长(低字节) 字节 11: 保留 字节 12: CRC16 高字节 字节 13: CRC16 低字节14 个字节,加上前导码和 CRC,空中时间在 SF7、BW125 下大概 50 ms。这个时间对功耗影响不大。
状态标志位按位定义:
- Bit 0: 市电正常
- Bit 1: 正在充电
- Bit 2: 电池已充满
- Bit 3: 应急模式激活
- Bit 4: 自检通过
- Bit 5: 通信正常
- Bit 6-7: 保留
故障码单独一个字节,0x00 表示无故障,其他值对应不同故障类型。这样设计的好处是,网关收到数据后,不用解析复杂结构,直接按位判断就行。
4.3 上报策略与冲突避免
几百盏灯同时上报,LoRa 网络会冲突。解决办法有几个:
随机退避。每盏灯的上报时间加一个随机偏移,比如基准 5 分钟,偏移 ±30 秒。这样即使同时上电,上报时间也会散开。
分时上报。网关给每盏灯分配一个时间片,灯在自己的时间片内上报。这需要网关和灯之间有时间同步机制,复杂度高一些,但冲突最少。
事件触发 + 定时上报结合。正常状态定时上报,故障状态立即上报。故障上报优先级高,但也要加随机退避,避免多盏灯同时故障时冲突。
我实际用的是随机退避加定时上报。基准周期 5 分钟,随机偏移 ±30 秒,实测下来,100 盏灯的网络,丢包率在 1% 以下。如果灯的数量更多,比如 500 盏,就要考虑分时上报了。
4.4 接收窗口与下行指令
网关有时候需要给灯下发指令,比如"立即自检"、"调整上报周期"、"固件升级"。下行指令的接收,靠的是灯在每次上报后,打开一个短暂的接收窗口。
流程是这样的:
- 灯唤醒,上报数据。
- 上报完成后,SX1276 切换到 RX 模式,打开接收窗口,比如 500 ms。
- 网关收到上报后,如果有下行指令,在这个窗口内下发。
- 灯收到指令,执行,然后回 Sleep。
- 如果窗口内没收到,灯直接回 Sleep,等下次上报。
这个机制的关键是窗口时间要匹配。窗口太短,网关来不及响应;窗口太长,功耗高。500 ms 是个经验值,网关的处理延迟一般在 100 ms 以内,留 400 ms 余量。
注意:接收窗口期间,SX1276 处于 RX 模式,电流 10 mA。如果每 5 分钟开 500 ms,平均电流约 17 μA。这个要算进功耗预算。
5. 实测中的问题与排查过程
5.1 通信距离不达标的排查
第一版样机做出来,标称通信距离 1 km,实测只有 200 m 就丢包。排查过程记录一下,供参考。
第一步,确认发射功率。读 SX1276 的 REG_PA_CONFIG 寄存器,确认输出功率配置正确。发现配置的是 +17 dBm,不是预期的 +20 dBm。改过来,距离提升到 300 m,但还是不够。
第二步,检查天线。用矢量网络分析仪测天线的驻波比,发现在 915 MHz 处驻波比 2.5,匹配不好。换了一根调试好的天线,驻波比降到 1.3,距离提升到 500 m。
第三步,检查电源。发射时用示波器看电源纹波,发现发射瞬间电源跌落 0.5 V。原因是 LDO 的动态响应不够,发射电流突变时电压不稳。在电源脚加了一个 100 μF 的钽电容,距离提升到 700 m。
第四步,调整扩频因子。原来用的是 SF7,改成 SF9,灵敏度提升,距离到 900 m。但速率下降,单包时间变长,功耗增加。最后折中用了 SF8。
第五步,检查环境。测试场地周围有金属结构,对 915 MHz 有反射和吸收。换到开阔场地,距离达到 1.2 km,达标。
这个排查过程说明,通信距离不达标,往往是多个因素叠加。要按"发射功率→天线→电源→参数→环境"的顺序逐个排除。
5.2 低功耗实测与理论差距大
理论算下来待机电流 50 μA,实测 200 μA。差了 4 倍,肯定有问题。
用高精度电流表逐项断开测量:
- 断开 SX1276,电流降到 180 μA,说明 SX1276 不是主因。
- 断开 MCU,电流降到 150 μA,MCU 睡眠电流比预期高。
- 断开 LDO 输出,只留 LDO 输入,电流 80 μA,LDO 静态电流超标。
- 断开电池监测分压,电流降到 20 μA。
最后定位到三个问题:LDO 静态电流 80 μA(手册标 1 μA,但那是无负载条件,实际带载后偏高);MCU 睡眠时没关外设时钟,多耗了 30 μA;电池监测分压电阻取值太小,耗了 60 μA。
逐个解决:换 LDO,MCU 睡眠前关外设时钟,分压电阻从 10 kΩ 改成 1 MΩ 并在 ADC 前加跟随器。最终待机电流降到 45 μA,达标。
5.3 SPI 通信偶发失败的定位
调试时发现,SPI 读写寄存器,大部分时候正常,但偶尔读回来的值不对。概率大概百分之一。
这种偶发问题最难查。我用了几个手段:
逻辑分析仪抓波形。抓了几百次 SPI 传输,发现失败的那次,时钟线上有一个额外的窄脉冲。原因是 MCU 的 SPI 外设在某些条件下会多输出一个时钟。
检查片选时序。发现片选拉高和时钟停止之间的时间太短,从设备还没完成内部处理,片选就变了。在片选拉高前加了一个微秒级延时,问题消失。
电源噪声。SPI 传输时,如果电源上有噪声,从设备可能采样错误。在 SX1276 的电源脚加了 0.1 μF 和 10 μF 电容,进一步降低噪声。
这个问题给我的教训是:SPI 通信看起来简单,但时序余量要留够。片选建立时间、保持时间、时钟边沿,都要按最坏情况设计。
5.4 电池电压采集不准的修正
ADC 采集电池电压,发现跟万用表测的差 100 mV 以上。排查下来有几个原因:
分压电阻精度。用了 5% 精度的电阻,分压比不准。换成 1% 精度,误差降到 30 mV。
ADC 参考电压。MCU 的 ADC 参考电压用的是内部参考,标称 1.2 V,实际有偏差。用外部基准芯片,误差降到 10 mV。
采样时机。电池在充电和放电时,端电压不同。充电时测的电压偏高,放电时偏低。统一在待机状态下采样,避免充电电流影响。
软件滤波。单次采样有噪声,用 10 次采样取平均,再配合一阶低通滤波,电压波动控制在 5 mV 以内。
最后电池电压采集精度做到 ±20 mV,对判断电池状态足够了。
6. 一些零散但重要的经验
6.1 固件升级通道要提前留
应急灯装上去之后,再拆下来升级固件成本很高。所以设计时就要考虑空中升级(OTA)。LoRa 的速率低,传一个几十 KB 的固件要很久,但可以分片传输,每次上报带一小段固件数据,慢慢升级。
关键是Bootloader 要提前设计。Flash 要分区,Bootloader 区、应用区、备份区。升级时先写到备份区,校验通过后再切换。这样即使升级失败,也能回滚到旧版本。
我第一版没考虑这个,后来想加 OTA,发现 Flash 空间不够,只能换 MCU。这个教训很深刻。
6.2 天线布局的注意事项
LoRa1276-C1-915 模组的天线接口,布局时要注意:
- 天线走线要短,尽量靠近模组。
- 天线下方和周围要净空,不要铺地,不要走其他信号线。
- 如果用地线天线,要留足够的净空区,具体尺寸参考模组手册。
- 天线附近不要放金属件,电池、螺丝、外壳都会影响。
我见过一个设计,天线旁边放了一块锂电池,通信距离直接减半。后来把电池移到另一侧,恢复正常。
6.3 温度对电池和射频的影响
应急灯可能装在室外或非空调环境,温度范围要考虑。锂电池在低温下容量下降,0 ℃ 时容量可能只有常温的 70%。射频芯片的晶振频率也会随温度漂移,虽然 SX1276 内部有温度补偿,但极端温度下还是要留余量。
我的做法是:电池选低温性能好的型号,或者加加热片(功耗允许的话);射频参数在高温和低温下分别测试,确认通信距离达标。
6.4 测试工装的设计
批量生产时,每盏灯都要测试。如果靠人工逐个配置和验证,效率太低。做一个测试工装,用 LoRa 网关加自动化脚本,灯上电后自动上报,工装判断数据是否正确,自动记录。
工装的核心是一个 Python 脚本,调用串口跟网关通信,解析上报数据,跟预期值对比。测试结果自动存数据库,方便追溯。
import serial import json ser = serial.Serial('COM3', 115200, timeout=1) def test_light(device_id): # 等待灯上报 while True: line = ser.readline().decode().strip() if not line: continue data = json.loads(line) if data['id'] == device_id: # 校验数据 assert data['voltage'] > 3000, "电压异常" assert data['status'] & 0x01, "市电状态异常" return True这个工装把测试时间从每盏灯 5 分钟降到 30 秒,而且数据可追溯。
6.5 关于 SPI 硬件测试用例的补充
如果项目要求严格的硬件测试,SPI 接口的测试用例要覆盖:
- 寄存器读写一致性:写一个值,读回来对比。
- 边界地址:读写 0x00 和 0x7F 地址。
- 连续读写:一次读写多个寄存器,验证地址自增。
- 异常时序:片选提前拉高、时钟频率超限,验证从设备的容错。
- 电源波动:在电源电压上下限时测试 SPI 通信。
这些用例用 Python 脚本加 USB 转 SPI 模块就能跑,不需要完整的 MCU 固件。前期验证硬件设计时很有用。
6.6 最后说一个关于低功耗设计的心得
低功耗设计是个系统工程,不是某个模块的事。我见过很多项目,MCU 选了低功耗的,射频也配了睡眠模式,但整体功耗还是下不来。问题往往出在"看不见"的地方:上拉电阻、指示灯、电平转换、甚至 PCB 上的漏电流。
我的习惯是,每版硬件回来,先不跑功能,用高精度电流表测各个状态的电流。待机、接收、发射、充电,每个状态都测,跟理论值对比。差距大的地方,就是优化的点。
还有一点,低功耗设计要从原理图阶段就开始,不要等硬件做完了再想办法。选器件时多看静态电流参数,电阻取值时算一下功耗,这些前期工作做到位,后面能省很多事。
这套 LoRa 应急灯方案,前后迭代了三个版本,踩过的坑基本都写在这里了。射频和低功耗这两个领域,理论很重要,但经验更重要。很多问题,手册上不会写,只有实际做过才知道。希望这些记录对正在做类似项目的朋友有帮助。