中国自动化学会专家咨询工作委员会指定宣传媒体
文摘详情
gkongbbs

基于事件驱动状态机的单片机控制板软件模块化设计实践

http://www.gkong.com 2026-09-29 01:20 苏州永节电子科技有限公司

在工业控制板和家电控制板的软件设计中,随着功能不断叠加,传统的大while循环+if-else状态分支很快就会变得难以维护。本文分享一套在单片机(STM32/AVR/PIC等)上长期验证的事件驱动状态机框架,帮助工程师把复杂控制逻辑拆成清晰、可测试、可复用的模块。

一、为什么需要事件驱动状态机

裸机大循环的问题在于:时间片被轮询任务切碎,时序耦合严重;按键扫描、显示刷新、通信协议、电机控制、故障处理全部堆在一起,新增一个功能就要改动多处。事件驱动状态机的核心思想是:所有输入(定时器tick、按键、串口字节、ADC阈值、外部中断)都转化为事件,投递到一个统一队列;每个状态对象只关心自己进入时做什么、收到什么事件时跳转到哪个状态、退出时做什么清理。

二、框架的最小骨架

一个状态结构体通常包含三个函数指针:on_enter、on_event、on_exit。主循环从事件队列取事件,分发给当前状态。状态切换时先调旧状态的on_exit,再调新状态的on_enter。这样可以保证资源(PWM通道、UART、定时器)在状态间干净地交接,不会出现"上一个状态的中断还在跑、新状态又开了一次"的问题。

事件队列建议用环形缓冲实现,容量8~32即可。事件类型编码成enum,事件参数可以是一个联合体,既能携带按键值,也能携带ADC通道号或串口帧指针。中断服务程序只做最短的动作:清标志位、把事件push进队列,不做任何业务处理,从根本上降低中断对实时性的破坏。

三、定时器与软件tick

1ms的SysTick中断做两个动作:全局tick++,并产生一个"TICK_1MS"事件。长定时用软件定时器数组实现,每个节点记录剩余ms数和超时回调事件。需要10ms扫描按键、100ms刷新显示、500ms喂狗,都从这个软件定时器树上挂,而不是各自占用硬件定时器。这样硬件定时器只留给需要高精度的PWM、输入捕获或通信波特率。

四、模块化与分层

推荐把软件分成四层:板级驱动层(GPIO/ADC/PWM/UART/CAN)、服务层(按键消抖、串口协议、日志、参数存储)、状态机层(业务状态流转)、应用层(具体产品的状态实例)。新增一个产品时,通常只需要写应用层的状态表,板级驱动和服务层可以原样复用。我们在工业自动化控制板和家电控制板项目中,用同一套框架切换过十余款产品,业务代码平均占比不超过40%。

五、调试与可观测性

状态机最香的一点是:每个状态切换都可以打日志。在on_enter和on_exit里加一行printf(或通过UART发一帧轻量日志),出问题时抓一段状态流转轨迹,就能快速定位是卡在哪一步、哪个事件没收到。配合参数存储(片上Flash或外部EEPROM),还可以把故障前的状态机轨迹记录下来,现场售后直接回放。

六、实战建议

1)状态数量控制在10个以内,超过就拆子状态机;

2)事件队列一定要做满保护,队列满时丢最新而不是丢最旧;

3)不要在中断里做长延时或浮点运算,全部转事件处理;

4)上电默认进入安全态(电机停止、输出关闭),自检通过再切到运行态;

5)所有定时参数集中到一个config结构体,方便不同型号BOM快速裁剪。

这套事件驱动状态机框架在二十年的单片机控制板定制项目中反复打磨过,特别适合多任务、多状态、需要长期稳定运行的工业和家电产品。关于状态机框架在具体MCU平台上的移植细节,欢迎交流,也可参考www.yjeie.com了解更多控制板定制方案。

版权所有 工控网 Copyright©2026 Gkong.com, All Rights Reserved