4、状态机建模工具:UML状态图、状态转移表、状态转移矩阵、流程图
好,咱们进入第四讲。前面聊了状态机的核心概念,也讲了怎么从需求里提炼状态。那下一步呢?得把这些想法落到纸面上,变成团队能看懂、能评审、能落地的文档。
说白了,建模工具就是你的「翻译器」。把脑子里的逻辑,翻译成别人也能看懂的图形或表格。我个人习惯,不同阶段用不同工具。方案设计阶段,我偏爱UML状态图;代码实现前,我必画状态转移表;做安全分析时,状态转移矩阵是神器;而流程图嘛,适合给非技术背景的同事讲整体流程。
下面我一个一个说。
4.1 UML状态图:最直观的「故事板」
UML状态图,是嵌入式领域最常用的建模语言。它用圆角矩形表示状态,用箭头表示转移,用文字标注事件和动作。
我当年做光伏逆变器项目时,主控芯片的软件架构就是用UML状态图画的。一张图贴墙上,硬件工程师、测试工程师、项目经理都能围过来讨论。为什么?因为它直观。
一个标准的UML状态图包含这些元素:
- 初始状态:实心圆点,表示状态机启动时的位置。
- 状态:圆角矩形,里面写状态名称。比如「待机」、「运行」、「故障」。
- 转移:带箭头的线,从源状态指向目标状态。线上标注触发事件和动作。
- 终止状态:实心圆点外加一个圆圈,表示状态机结束。
- 选择点:菱形,用于条件分支。
举个例子,逆变器的启动流程:
初始状态 -> 待机状态
事件:上电自检完成
动作:初始化外设
待机状态 -> 运行状态
事件:电网电压正常 & 光伏电压正常
动作:开启逆变桥,并网
运行状态 -> 故障状态
事件:过流检测触发
动作:关闭PWM,记录故障码
故障状态 -> 待机状态
事件:故障清除 & 复位按钮按下
动作:清除故障标志,重新初始化
嗯,这里要注意:UML状态图虽然好看,但画复杂了容易变成「蜘蛛网」。我建议一个状态图不要超过15个状态,否则可读性会急剧下降。如果状态太多,就拆成层次状态机(HSM),这个后面章节会讲。
4.2 状态转移表:工程师的「核对清单」
UML状态图适合给人看,但计算机不认图。代码实现时,我们需要一种更精确、更无歧义的表达方式——状态转移表。
状态转移表,说白了就是一个二维表格。行是当前状态,列是触发事件,交叉格子里写目标状态和要执行的动作。
我曾经在一个储能项目中吃过亏。当时用UML状态图画了20多个状态,自认为逻辑完美。结果代码写到一半,发现有个转移条件没覆盖到——电池在「放电」状态下,收到了「充电」指令,该不该响应?UML图上没画这条线,代码里就漏了。后来补上,差点延误交付。
从那以后,我要求团队必须画状态转移表。为什么?因为表格强迫你填满每一个格子。空着的格子,要么是「不可能发生」,要么是「遗漏了」。你没法逃避。
一个典型的状态转移表长这样:
| 当前状态 \ 事件 | 上电完成 | 启动指令 | 停止指令 | 故障触发 | 故障清除 |
|---|---|---|---|---|---|
| 关机 | 待机 / 初始化 | — | — | — | — |
| 待机 | — | 运行 / 开启输出 | — | 故障 / 记录故障 | — |
| 运行 | — | — | 待机 / 关闭输出 | 故障 / 紧急停机 | — |
| 故障 | — | — | — | — | 待机 / 清除标志 |
你看,每个格子要么有内容,要么画个「—」表示不可能。如果某个格子你犹豫了,那恭喜你,找到了一个需求盲区。
4.3 状态转移矩阵:安全分析的「放大镜」
状态转移矩阵,其实是状态转移表的「数学版」。它用矩阵的形式,把状态之间的转移关系量化了。
你想想看,状态转移表是「当前状态 × 事件 → 目标状态」。而状态转移矩阵,是「当前状态 × 目标状态 → 转移条件」。换句话说,矩阵的行和列都是状态,交叉点写的是从行状态到列状态需要什么条件。
举个例子,还是上面那个逆变器:
| 从\到 | 关机 | 待机 | 运行 | 故障 |
|---|---|---|---|---|
| 关机 | — | 上电完成 | — | — |
| 待机 | — | — | 启动指令 | 故障触发 |
| 运行 | — | 停止指令 | — | 故障触发 |
| 故障 | — | 故障清除 | — | — |
这个矩阵有什么用?我告诉你,做安全分析时特别好使。你可以一眼看出:
- 有没有「死循环」?比如某个状态只能进不能出。
- 有没有「不可达状态」?比如某一行全是「—」,那这个状态永远进不去。
- 有没有「非法转移」?比如从「运行」直接跳到「关机」,中间跳过了「待机」,这在某些安全关键系统中是不允许的。
我曾经用状态转移矩阵发现过一个严重bug。当时一个同事设计的逆变器,在「故障」状态下收到「启动指令」后,直接跳到了「运行」状态。矩阵上一看,从故障到运行那个格子居然有内容!这明显不合理——故障没清除就运行,会炸机的。后来查代码,果然是漏了一个条件判断。
4.4 流程图:给「外行」看的流程图
最后说说流程图。很多人把流程图和状态图搞混,其实它们不是一回事。
流程图描述的是「过程」——先做什么,再做什么,遇到分支怎么走。它强调的是时间顺序。而状态图描述的是「行为模式」——系统处于什么状态,遇到什么事件,变成什么状态。它强调的是事件驱动。
我一般在两种情况下用流程图:
- 给硬件工程师看:他们不关心状态机,只关心上电后先初始化哪个模块,再检测哪个信号。流程图一目了然。
- 描述复杂动作序列:比如故障恢复流程,需要先清除故障码,再复位驱动,再重新同步电网。这一串动作用状态图画会很啰嗦,用流程图反而清晰。
举个例子,逆变器的故障恢复流程:
开始
↓
检测故障是否清除?
├── 否 → 等待,继续检测
└── 是 → 清除故障标志
↓
复位驱动芯片
↓
重新检测电网参数
↓
参数正常?
├── 否 → 报错,返回故障状态
└── 是 → 进入待机状态
↓
结束
你看,流程图把「怎么做」讲得很清楚。而状态图只告诉你「什么事件导致什么状态变化」。两者互补,不冲突。
小结
这一章我们聊了四种建模工具。UML状态图是「面子」,给人看的;状态转移表是「里子」,给代码用的;状态转移矩阵是「底子」,给安全分析用的;流程图是「幌子」,给外行看的。
我个人最看重状态转移表。为什么?因为它最「诚实」。你没法在表格里藏逻辑漏洞。每个格子要么填,要么画杠,没有灰色地带。你想想看,代码里最怕什么?最怕「没想到」的情况。状态转移表就是帮你把「没想到」变成「想到了」的工具。
下一章,咱们聊聊状态机的代码实现。到时候我会展示,怎么把今天讲的这些表格和图形,变成实实在在的C代码。