做嵌入式软件开发这些年,遇到过不少问题。有些是代码写错了,有些是逻辑想简单了,还有些是根本没想到。把这些经历攒一攒,慢慢发现软件可靠性不是技术多高深,而是把该想到的都想一遍,该做的都做到位。
一、编译器有时候不太听话
写代码的人都知道代码是怎么写的,但不一定知道编译器是怎么编译的。
比如延时。有时候需要等硬件稳定,就写个空循环。优化一开,编译器觉得这循环啥也没干,直接删掉。硬件还没准备好,代码已经跑过去了。后来养成习惯,关键延时用示波器量一下,或者用专门的延时函数,加个volatile,不让编译器替我做主。
还有变量。一个变量在中断里改,主循环里判断。编译器看主循环里没改,觉得它不会变,就优化成常量。加个volatile,告诉编译器这东西可能意外变化,别动它。
二、接口不能只信手册
硬件手册给的时序,是理想情况下的数据。自己的板子电源干不干净、电容选得对不对、走线长不长,都会影响实际表现。
有一次调SPI,波形都对,数据就是偶尔错。查了好久发现,CS拉低后等了1微秒,手册说500纳秒就行。但板子上有个电容充得慢,得等更久。后来加了软启动,先发几个空时钟唤醒一下,再发数据,就好了。
做产品不能卡着边界设计,得留余量。这是吃过亏才明白的。
三、重要数据得备份
有个产品偶尔丢配置,用户返修回来,EEPROM读出来全是FF。查到最后,是写EEPROM的时候断电了。写一半,数据坏了。
后来改了方案:先写备份区,校验通过了再写主区。万一写失败,能回滚恢复。关键数据存三份,读出后三取二,或者加校验码。
这些方法不复杂,但能挡住很多意外。
四、用户不一定按你想的操作
做界面的同事遇到过一件事:按钮按下发消息,消息处理里做复杂操作。用户等不及,又按了一下,又发一个消息。消息队列积压,系统卡了。
后来加了忙标志,操作中忽略后续输入。再后来把按钮和操作解耦,按钮只管发请求,有专门的模块管理状态。
界面设计不只是好不好看,用户怎么误操作都搞不坏,才叫可靠。
五、报警不能只亮个灯
报警灯亮了,现场人员没注意,设备一直带病运行,最后真出事了。
后来改方案:严重故障必须响蜂鸣器,必须点击确认才能消音。报警分级,不同级别用不同的声光频率。代码里还做了报警自检,每秒检查一次报警输出,发现异常直接停机。
六、从设计阶段就想想哪里可能出错
这些经验攒多了,慢慢明白一件事:软件可靠性的问题,很多在设计阶段就能发现。
比如考虑编译器会怎么优化,考虑存储介质什么特性,考虑接口时序有没有余量,考虑用户会怎么误操作,考虑报警会不会被忽视。把这些都想一遍,系统就扎实多了。
工程师高培觉得这些东西,教科书里不太讲,项目紧的时候也没空想。但遇到一次坑,补一个知识点,慢慢就积累起来了。
可靠的软件不是写出来的,是一点点补出来的。