被设计模式坑了三个项目后,我学会了在嵌入式里怎么用 说实话刚做嵌入式那会儿我看什么状态机回调函数观察者模式这些东西就觉得这群写Java的又来污染C语言了。一个while(1)循环加一堆if-else什么功能搞不定但当你一个项目代码写到两三万行连自己都找不到某个flag是在哪里被set的就知道我当年有多天真了。状态机不是用来装逼的先说我改观最大的——有限状态机FSM。之前做的一个物联网网关项目需要处理设备注册、心跳维持、OTA升级、数据上报、远程配置。我图省事全写在一个大循环里while(1) { if(check_uart()) handle_cmd(); if(check_sensor()) report_data(); if(check_ota()) start_update(); if(check_heartbeat()) send_pong(); // ...... 再来十几个if }这玩意儿刚跑起来没问题。上线三天后运维说网关每隔几个小时就会自己重启。查了两天才发现OTA下载过程中进了数据上报的分支上报的buffer和OTA buffer共用了同一块DMA内存——互相覆盖了。改状态机之后每个状态独占一个文件状态之间只有明确的transitiontypedef enum { GW_INIT, GW_IDLE, GW_SENSING, GW_REPORTING, GW_OTA, GW_ERROR } gw_state_t; gw_state_t state GW_INIT; void gw_tick(void) { switch(state) { case GW_INIT: if(hw_init_ok()) state GW_IDLE; break; case GW_IDLE: if(sensor_ready()) state GW_SENSING; if(ota_available()) state GW_OTA; break; case GW_SENSING: // 只做采集 state GW_REPORTING; break; case GW_REPORTING: // 只做上报 state GW_IDLE; break; case GW_OTA: // 只做升级不做别的 if(ota_done()) state GW_IDLE; break; default: state GW_ERROR; } }代码量没少多少但每个状态的逻辑边界清晰到不可能互相踩内存。自那以后我所有超过10个文件的嵌入式项目顶层一定是一个状态机。回调函数别滥用回调函数在嵌入式里太常见了——定时器回调、中断回调、协议栈回调。但有个坑回调里不要做费时操作。之前做一个CAN总线项目CAN接收中断里直接调了报文解析存储转发结果CAN总线负载率一高中断响应不过来丢帧丢到怀疑人生。正确的做法是回调里只做一件事把数据塞进队列然后return。void can_rx_callback(can_msg_t *msg) { // 不要在这里做任何处理 xQueueSendFromISR(rx_queue, msg, NULL); } // 在任务里统一处理 void can_processing_task(void *arg) { can_msg_t msg; while(1) { if(xQueueReceive(rx_queue, msg, portMAX_DELAY)) { parse_and_forward(msg); // 这里是任务上下文随便干 } } }这个模式说白了就是生产者-消费者模式。但当时不懂这算个设计模式只觉得把处理逻辑搬出中断好使。后来才知道人家有名字。最实用的分层抽象硬件抽象层HAL这个词挺唬人的但说白了就是把怎么操作硬件和用硬件做什么分开。我之前写的一个温控项目一开始直接对着寄存器操作// 直接在逻辑里操作寄存器 if(temperature threshold) { TIM2-CCR1 800; // 设置PWM占空比 GPIOB-BSRR GPIO_PIN_5; // 开启加热 }换了个MCU型号之后所有代码都得重写。后来学乖了加一层抽象// hal_heater.h — 硬件抽象层接口 void heater_set_power(uint8_t percent); void heater_on(void); void heater_off(void); // 逻辑代码完全不知道底层是什么 void temp_control_task(void) { if(temperature threshold) { heater_set_power(80); heater_on(); } }换MCU只用重写hal_heater.c逻辑代码一行不动。这个分层就是策略模式的嵌入式版——虽然我当时没听过这个名词但实践下来换芯片的痛苦从改几百行变成改几十行。核心区别桌面软件的设计模式侧重对象之间的关系嵌入式的设计模式更多是为了解决这三件事1. 资源有限— 没有malloc/new不能随便new对象所有的内存分配在编译期就得确定2. 实时性— 一个函数执行超过10ms可能就出问题了3. 可移植性— 换芯片是家常便饭不抽象等于重写最搞笑的是我之前一直觉得自己在写野路子代码后来翻到一本《嵌入式系统设计模式》的书发现里面一堆模式我都用过就是不知道名字。比如环形缓冲区生产者消费者中断任务观察者模式分层架构适配器模式。所以设计模式这东西不是让你学一堆名词去面试用的。是在你踩了足够多的坑之后帮你把好像应该这么写变成就是这么写的东西。没踩够坑之前硬套设计模式反而写出来的代码又丑又慢。踩够了坑再回头看——哦原来我写的就是这个模式。