作者 | Alicedodo
上一篇推文:咱们是时候改变一下嵌入式软件开发思维方式了!提到了状态机。在之前的推文中:干货 | 嵌入式之状态机编程。有简单介绍了状态机的示例。这次我们一起来学习C语言实现状态机的三种方法解析。
状态机的实现无非就是 3 个要素:状态、事件、响应。转换成具体的行为就 3 句话。
用 C 语言实现状态机主要有 3 种方法:switch—case 法、表格驱动法、函数指针法。
状态用 switch—case 组织起来, 将事件也用switch—case 组织起来, 然后让其中一个 switch—case 整体插入到另一个 switch—case 的每一个 case 项中 。
「程序清单 List4 :」
switch(StateVal)
{
case S0:
switch(EvntID)
{
case E1:
action_S0_E1(); /*S0 状态下 E1 事件的响应*/
StateVal = new state value;/*状态迁移,不迁移则没有此行*/
break;
case E2:
action_S0_E2(); /*S0 状态下 E2 事件的响应*/
StateVal = new state value;
break;
......
case Em:
action_S0_Em(); /*S0 状态下 Em 事件的响应*/
StateVal = new state value;
break;
default:
break;
}
break;
case S1:
......
break;
......
case Sn:
......
break;
default:
break;
}
上面的伪代码示例只是通用的情况,实际应用远没有这么复杂。虽然一个系统中事件可能有很多种,但在实际应用中,许多事件可能对某个状态是没有意义的。
例如在程序清单 List4中,如果 E2、······ Em 对处在 S0 状态下的系统没有意义,那么在 S0 的 case 下有关事件E2、······ Em 的代码根本没有必要写,状态 S0 只需要考虑事件 E1 的处理就行了。
既然是两个 switch—case 之间的嵌套, 那么就有一个谁嵌套谁的问题, 所以说 switch—case法有两种写法:状态嵌套事件和事件嵌套状态。这两种写法都可以, 各有利弊, 至于到底选用哪种方式就留给设计人员根据具体情况自行决断吧。
关于 switch—case 法还有最后一点要说明, 因为 switch—case 的原理是从上到下挨个比较,越靠后,查找耗费的时间就越长,所以要注意状态和事件在各自的 switch 语句中的安排顺序,不推荐程序清单 List4 那样按顺序号排布的方式。出现频率高或者实时性要求高的状态和事件的位置应该尽量靠前。
如果说 switch—case 法是线性的,那么表格驱动法则是平面的。表格驱动法的实质就是将状态和事件之间的关系固化到一张二维表格里, 把事件当做纵轴,把状态当做横轴,交点[Sn , Em]则是系统在 Sn 状态下对事件 Em 的响应 。