嵌入式系统的状态机

时间:2012-05-29 10:19:50

标签: switch-statement state-machine

我使用C的switch语句为嵌入式系统实现了一个简单的状态机。我知道如果我使用具有查找表的函数指针会更好,但我将其保存为下一步。

我的状态机具有以下状态:

  1. 启动(初始状态)
  2. 启动错误。
  3. 空闲(系统只检查此状态下的输入。它不会更新显示或其他任何内容。它只是'空闲')。
  4. 检查(这是实际应用程序)
  5. 程序
  6. 复制到内存
  7. 当系统启动时,它进入启动状态,配置端口,初始化显示,并与SPI总线上连接的IC进行握手,以确保一切正常。如果是,则系统进入空闲状态。如果没有,它进入启动错误状态,在LCD上显示错误,标记变量然后进入空闲状态。

    在空闲状态下,程序轮询微控制器上的3个引脚,检查是否按下了3个按钮(Check,Program,Copy to Mem。)中的一个。根据按下的按钮,它进入适当的状态,执行一些代码,更新LCD然后返回到空闲状态。注意:如果系统中出现硬件故障,系统不关心是否按下按钮。启动错误状态标记一个名为hardware_fault的变量,如果设置该变量,则确保空闲状态不会打扰轮询任何输入按钮。

    这是我第一次实现状态机,我只是不确定这是不是一个好的设计。我还没有看到任何FSM的例子,他们在空闲状态下轮询输入。相反,似乎大多数例子本质上都是顺序的(例如计数器)。所以,我的问题是,我的设计合理吗?它可以工作,但正如大家都知道的那样,设计不好,然后有好的设计。

1 个答案:

答案 0 :(得分:7)

您应该从switch语句重构代码,并在提及时使用转换表。交换机周围的代码不会扩展,很乱,很快就会变得难以阅读和更新。

要回答你的问题,我会说大多数状态机实际上和你的一样:他们对事件做出反应(在你的情况下,民意调查)。如果它们是纯粹的顺序,没有任何事件,它们是专门的用法,比如实现一个正则表达式...

注意,Idle状态等同于状态机库的运行时核心:它拦截事件并将它们发布到状态机。使用状态机库,它将从客户端代码中“隐藏”,而像你这样的简单实现必须明确地进行事件轮询。

现在一个设计批评:一般应该避免全局变量,特别是在状态机中。状态Idle不应该知道hardware_fault全局变量。对于状态机,人们应该通过添加状态(当它有意义时)努力将全局变量“嵌入”状态机本身。 (层级)状态机的非常好的介绍和基本原理的解释是here

使用UML表示法(请参阅here获取教程),您的状态机是: state machine v1

一起轻松重构以消除hardware_fault依赖关系: state machine v2

也就是说,我们永远处于StartupError状态,而不会转换为Idle

ε(epsilon)表示空转换,即,与源节点关联的活动结束后立即执行的转换,没有任何事件。这就是“顺序”状态机的含义。

我还将第一个图表的源代码包含在内(第二个图表非常相似),使用非常简单且功能强大的PlantUML生成。

@startuml

skinparam shadowing false
title Embedded v1

state Startup
state StartupError
state Idle
state Program
state Check
state Copy

Startup : entry/ init HW
StartupError: entry/\n  display error\n  hw_fault = true
Idle : do/ if not hw_fault: poll
Program : do/ program
Check : do/ check
Copy : do/ copy

[*] -> Startup

Startup -> StartupError : ε [error]
Startup --> Idle : ε [not error]

StartupError --> Idle : ε

Idle --> Program : program_btn
Idle --> Check : check_btn
Idle --> Copy : copy_btn

Program --> Idle : ε

Check --> Idle : ε

Copy --> Idle : ε

@enduml