4. 软件安全检测:看门狗、CRC校验、冗余校验、超时检测
电机解锁,说白了就是给系统上最后一道保险。硬件锁死了,软件还得盯住。万一程序跑飞、数据被干扰、通信卡死,电机突然转起来,那可不是闹着玩的。
我做了这么多年嵌入式,见过最惨的一次——同事的无人机在空中因为看门狗没喂好,直接复位,螺旋桨停转,从二十米高掉下来。嗯,从那以后,我对软件安全检测再也不敢马虎。
这一章,我们重点聊四个东西:看门狗、CRC校验、冗余校验、超时检测。它们各自管一摊,合起来就是一张安全网。
4.1 看门狗——系统的最后一道防线
看门狗(Watchdog)是什么?说白了就是一个硬件定时器,它不停地在倒计时。如果倒计时归零,它就认为系统死机了,直接给你来个硬件复位。
你想想看,电机解锁过程中,如果主控芯片因为电磁干扰、堆栈溢出或者死循环卡住了,看门狗就是那个把你从坑里拉出来的东西。
核心原则:看门狗必须在主循环或关键任务中定期“喂狗”(清零计数器)。喂狗的位置不能太密,也不能太疏。
我个人习惯把喂狗放在主循环的末尾,或者放在最高优先级的空闲任务里。但要注意——绝对不能放在中断服务函数里喂狗。为什么?因为如果主循环已经死了,但中断还在跑,看门狗永远不会被触发,那就失去意义了。
我曾经在一个项目中踩过这个坑。电机控制的主循环因为一个指针越界卡死了,但定时器中断还在正常喂狗。结果电机一直输出一个固定占空比,堵转烧了。嗯,从那以后,我规定团队:喂狗只能放在主循环,中断里只做标志位。
看门狗的超时时间怎么设?我一般给个经验值:
| 应用场景 | 推荐超时时间 | 说明 |
|---|---|---|
| 电机解锁主循环 | 100ms - 500ms | 保证主循环完整跑完一次 |
| 通信等待 | 1s - 3s | 给通信重试留出余量 |
| 紧急停机状态 | 50ms - 100ms | 快速响应,防止失控 |
注意:看门狗复位后,系统必须进入安全状态。我建议在复位向量里先检查复位原因,如果是看门狗复位,直接进入电机锁定状态,不要尝试自动解锁。
4.2 CRC校验——数据完整性守护者
CRC(循环冗余校验)这东西,说白了就是给数据算一个“指纹”。发送方算一遍,接收方算一遍,两个指纹对上,数据就没问题。
在电机解锁流程里,CRC主要用在两个地方:
- 通信数据校验:上位机发给下位机的解锁指令,必须带CRC。防止通信线路上的干扰导致误触发。
- 参数存储校验:电机参数、解锁密钥存在EEPROM或Flash里,每次上电读出来先算CRC,不对就恢复默认值。
我个人最常用的是CRC-16-IBM,多项式0x8005。为什么?因为计算量小,8位单片机也能跑得动,而且检错能力足够覆盖电机控制场景。
下面是一个标准的CRC-16计算函数,我直接贴出来:
uint16_t crc16_ibm(uint8_t *data, uint16_t len)
{
uint16_t crc = 0x0000;
uint16_t i, j;
for (i = 0; i < len; i++)
{
crc ^= (uint16_t)data[i] << 8;
for (j = 0; j < 8; j++)
{
if (crc & 0x8000)
crc = (crc << 1) ^ 0x8005;
else
crc <<= 1;
}
}
return crc;
}
小技巧:如果MCU有硬件CRC模块,一定要用硬件。我测过,软件CRC算256字节大概要几百微秒,硬件CRC只要几个时钟周期。电机解锁这种实时性要求高的场景,能省则省。
4.3 冗余校验——别把鸡蛋放一个篮子里
冗余校验,听起来高大上,其实核心思想很简单:同一个关键信息,用两种或多种方式验证。
举个例子,电机解锁指令里有一个“解锁使能位”。如果只用一个bit,万一这个bit被干扰翻转了,电机就误解锁了。怎么办?
- 位冗余:同一个使能位,在数据帧里出现两次。接收端比较两个位,必须一致才执行。
- 取反冗余:使能位0x55,再发一个0xAA(取反)。接收端检查两者是否互为取反。
- 多帧确认:连续收到两帧相同的有效解锁指令,才执行解锁动作。
我记得有一次做电动工具项目,客户要求解锁指令必须连续三帧一致才能解锁。当时觉得有点过度设计,后来发现——工厂产线上的电磁干扰特别大,单帧误码率很高。三帧确认反而成了刚需。
我的建议:电机解锁这种安全关键操作,至少做两层冗余。比如“位冗余 + 多帧确认”,或者“CRC + 取反冗余”。别嫌麻烦,安全无小事。
4.4 超时检测——别让系统死等
超时检测,说白了就是给每个操作设一个“最后期限”。到了时间没完成,就按失败处理。
电机解锁流程里,哪些地方需要超时?
| 检测点 | 超时时间 | 超时后的动作 |
|---|---|---|
| 等待解锁指令 | 5秒 | 退出解锁流程,进入锁定状态 |
| 等待电机响应 | 100ms | 重试3次,失败则报错 |
| 等待传感器稳定 | 500ms | 标记传感器异常,禁止解锁 |
| 通信总线空闲 | 1秒 | 复位通信模块 |
实现超时检测,我一般用系统滴答定时器(SysTick)。每1ms中断一次,维护一个全局的毫秒计数器。每个任务需要超时检测时,记录开始时间,然后在循环里不断检查当前时间减去开始时间是否超过阈值。
这里有个坑——不要在中断里做超时判断。中断里只做计数,超时逻辑放在主循环里处理。否则中断嵌套一多,时间就不准了。
避坑指南:我曾经在一个项目里把超时检测放在定时器中断里,结果中断优先级被一个更高优先级的中断抢占了,超时时间被拉长了一倍。从那以后,我所有超时检测都放在任务级,只用中断提供时间基准。
4.5 四者如何协同工作
这四个东西不是孤立的。在实际的电机解锁代码里,它们经常配合使用:
- 上电初始化:从Flash读取解锁参数,做CRC校验。校验失败则使用默认安全参数。
- 等待解锁指令:开启超时检测,5秒内收不到有效指令则进入休眠。
- 指令校验:收到指令后,先做CRC校验,再做冗余校验(比如位冗余+取反)。
- 执行解锁:解锁过程中,主循环末尾喂狗。如果程序卡死,看门狗复位,系统回到锁定状态。
- 运行监控:电机运行期间,持续进行超时检测和CRC校验,异常立即停机。
总结一句话:看门狗管系统死机,CRC管数据完整,冗余管单点故障,超时管无限等待。四管齐下,电机解锁才能做到真正的安全可靠。
嗯,这一章的内容就到这里。代码和思路都给了,关键是要在项目里真正用起来。别等到电机飞了、板子烧了,才想起来加这些保护。