第二章:嵌入式软件架构设计
好,咱们进入第二章。这一章我打算聊聊软件架构。说实话,很多做安全气囊的工程师,一开始都容易忽略架构设计。觉得先把功能跑通再说。嗯,我年轻时也这么干过,结果后来改 bug 改到怀疑人生。
安全气囊系统,说白了就是「人命关天」的软件。它必须快,必须稳,必须不出错。那怎么保证?靠的就是一套好的架构。
2.1 分层架构:把复杂问题拆开
我个人习惯把安全气囊软件分成三层:
- 硬件抽象层(HAL):直接跟传感器、点火芯片打交道。
- 中间层(Middleware):做数据处理、故障诊断、算法运算。
- 应用层(Application):负责决策逻辑,比如「要不要点火」。
为什么要分层?你想想看,如果所有代码都揉在一起,换个传感器型号,整个软件都得重写。分层之后,你只需要改 HAL 层,上面两层动都不用动。
我遇到过的一个案例:某项目换了加速度传感器,从模拟接口换成数字 SPI 接口。因为架构分得好,只改了 HAL 层的驱动代码,应用层完全没动。整个适配工作只花了三天。
2.2 模块划分:各司其职
分层是纵向的,模块划分是横向的。我建议把安全气囊软件拆成这几个模块:
| 模块名称 | 职责 | 关键接口 |
|---|---|---|
| 传感器管理 | 采集加速度、压力等信号 | Sensor_Read() |
| 故障诊断 | 检测传感器、线路、点火回路是否正常 | Diag_Run() |
| 碰撞算法 | 判断是否发生碰撞,决定点火时机 | Algo_Process() |
| 点火控制 | 执行点火指令,监控点火电流 | Fire_Actuate() |
| 通信管理 | 与车身 CAN 总线交互 | CAN_Send() / CAN_Recv() |
每个模块只做一件事。比如碰撞算法模块,它只管算,不管点火。点火控制模块只管执行,不管判断。这样出了问题,你一眼就能定位到是哪个模块的锅。
一个小技巧:模块之间的依赖关系要画成单向的。比如传感器管理可以调用故障诊断,但故障诊断不能反过来调用传感器管理。否则就成了循环依赖,维护起来特别头疼。
2.3 接口设计:约定好「握手」方式
接口设计,说白了就是模块之间怎么打招呼。我一般用 C 语言的结构体指针来传递数据。举个例子:
/* 传感器数据接口 */
typedef struct {
uint16_t accel_x; /* X轴加速度,单位:mG */
uint16_t accel_y; /* Y轴加速度,单位:mG */
uint16_t pressure; /* 压力传感器值,单位:kPa */
uint8_t status; /* 传感器状态:0=正常,1=故障 */
} SensorData_t;
/* 碰撞算法输入接口 */
typedef struct {
SensorData_t *sensor; /* 指向传感器数据的指针 */
uint32_t timestamp; /* 时间戳,单位:us */
} AlgoInput_t;
/* 碰撞算法输出接口 */
typedef struct {
uint8_t fire_decision; /* 0=不点火,1=点火 */
uint8_t fire_stage; /* 点火阶段:1/2/3级 */
} AlgoOutput_t;
接口设计有个原则:参数越少越好。如果一个函数需要传七八个参数,八成是设计有问题。我习惯把相关参数打包成结构体,只传一个指针。这样既清晰,又方便扩展。
我曾经踩过的坑:接口里用了全局变量来传数据。结果两个模块同时修改同一个变量,导致数据错乱。后来我强制规定:模块之间只能通过函数参数传递数据,禁止使用全局变量。从那以后,这类 bug 再也没出现过。
2.4 任务调度策略:谁先跑,谁后跑
安全气囊系统里,任务调度是个大学问。为什么?因为有些任务必须准时跑,晚一毫秒都不行。比如碰撞检测任务,必须每 1ms 执行一次。而有些任务可以慢一点,比如故障诊断,每 10ms 跑一次就够了。
我常用的调度策略是 固定优先级抢占式调度。优先级分配如下:
- 最高优先级:碰撞检测任务(1ms 周期)
- 次高优先级:点火控制任务(事件触发)
- 中等优先级:传感器采集任务(2ms 周期)
- 较低优先级:故障诊断任务(10ms 周期)
- 最低优先级:通信管理任务(20ms 周期)
为什么会这样安排?你想想看,碰撞检测如果被耽误了,车都撞了气囊还没点,那后果不堪设想。所以它必须跑在最前面。
我个人的经验:任务调度最怕的是「优先级反转」。比如低优先级的任务占着某个资源,高优先级的任务等着用,结果中间优先级的任务插进来,把高优先级任务堵死了。解决方法是使用优先级继承协议,或者干脆把临界区做短一点。
另外,我建议给每个任务加一个「看门狗」监控。如果某个任务超时没跑完,系统就进入安全状态。嗯,这个在安全气囊里特别重要——你不能让一个任务死循环把整个系统拖垮。
2.5 小结
这一章我们聊了分层架构、模块划分、接口设计和任务调度。说白了,架构设计就是「分而治之」——把大问题拆成小问题,然后让每个小问题各司其职。好的架构,能让你的代码跑得又快又稳,出了问题也好修。
下一章,我会讲讲安全气囊软件里最核心的部分——碰撞算法。咱们到时候见。