• 嵌入式软件有限状态机的 C 语言实现


    状态机模式是一种行为模式,通过多态实现不同状态的调转行为的确是一种很好的方法,只可惜在嵌入式环境下,有时只能写纯C代码,并且还需要考虑代码的重入和多任务请求跳转等情形,因此实现起来着实需要一番考虑。

    近日在看到了一个状态机的实现,也学着写了一个,与大家分享。

    首先,分析一下一个普通的状态机究竟要实现哪些内容。

    状态机存储从开始时刻到现在的变化,并根据当前输入,决定下一个状态。这意味着,状态机要存储状态、获得输入(我们把它叫做跳转条件)、做出响应。

    如上图所示,{s1, s2, s3}均为状态,箭头c1/a1表示在s1状态、输入为c1时,跳转到s2,并进行a1操作。

    最下方为一组输入,状态机应做出如下反应:

    图片

    当某个状态遇到不能识别的输入时,就默认进入陷阱状态,在陷阱状态中,不论遇到怎样的输入都不能跳出。

    为了表达上面这个自动机,我们定义它们的状态和输入类型:

    1. typedef int State;
    2. typedef int Condition;
    3.  
    4. #define STATES 3 + 1
    5. #define STATE_1 0
    6. #define STATE_2 1
    7. #define STATE_3 2
    8. #define STATE_TRAP 3
    9.  
    10. #define CONDITIONS 2
    11. #define CONDITION_1 0
    12. #define CONDITION_2 1

    嵌入式环境中,由于存储空间比较小,因此把它们全部定义成宏。此外,为了降低执行时间的不确定性,我们使用O(1)的跳转表来模拟状态的跳转。

    首先定义跳转类型:

    1. typedef void (*ActionType)(State state, Condition condition);
    2.  
    3. typedef struct
    4. {
    5.     State next;
    6.     ActionType action;
    7. } Trasition, * pTrasition;

    然后按照上图中的跳转关系,把三个跳转加一个陷阱跳转先定义出来:

    1. // (s1, c1, s2, a1)
    2. Trasition t1 = {
    3.     STATE_2,
    4.     action_1
    5. };
    6.  
    7. // (s2, c2, s3, a2)
    8. Trasition t2 = {
    9.     STATE_3,
    10.     action_2
    11. };
    12.  
    13. // (s3, c1, s2, a3)
    14. Trasition t3 = {
    15.     STATE_2,
    16.     action_3
    17. };
    18.  
    19. // (s, c, trap, a1)
    20. Trasition tt = {
    21.     STATE_TRAP,
    22.     action_trap
    23. };

    其中的动作,由用户自己完成,在这里仅定义一条输出语句。

    1. void action_1(State state, Condition condition)
    2. {
    3.     printf("Action 1 triggered.\n");
    4. }

    最后定义跳转表:

    1. pTrasition transition_table[STATES][CONDITIONS] = {
    2. /*      c1,  c2*/
    3. /* s1 */&t1&tt,
    4. /* s2 */&tt, &t2,
    5. /* s3 */&t3&tt,
    6. /* st */&tt, &tt,
    7. };

    即可表达上文中的跳转关系。

    最后定义状态机,如果不考虑多任务请求,那么状态机仅需要存储当前状态便行了。例如:

    1. typedef struct
    2. {
    3.     State current;
    4. } StateMachine, * pStateMachine;
    5.  
    6. State step(pStateMachine machine, Condition condition)
    7. {
    8.     pTrasition t = transition_table[machine->current][condition];
    9.     (*(t->action))(machine->current, condition);
    10.     machine->current = t->next;
    11.     return machine->current;
    12. }

    但是考虑到当一个跳转正在进行的时候,同时又有其他任务请求跳转,则会出现数据不一致的问题。

    举个例子:task1(s1, c1/a1 –> s2)和task2(s2, c2/a2 –> s3)先后执行,是可以顺利到达s3状态的,但若操作a1运行的时候,执行权限被task2抢占,则task2此时看到的当前状态还是s1,s1遇到c2就进入陷阱状态,而不会到达s3了,也就是说,状态的跳转发生了不确定,这是不能容忍的。

    因此要重新设计状态机,增加一个“事务中”条件和一个用于存储输入的条件队列。修改后的代码如下:

    1. #define E_OK        0
    2. #define E_NO_DATA   1
    3. #define E_OVERFLOW  2
    4.  
    5. typedef struct
    6. {
    7.     Condition queue[QMAX];
    8.     int head;
    9.     int tail;
    10.     bool overflow;
    11. } ConditionQueue, * pConditionQueue;
    12.  
    13.  
    14. int push(ConditionQueue * queue, Condition c)
    15. {   
    16.     unsigned int flags;
    17.     Irq_Save(flags);
    18.     if ((queue->head == queue->tail + 1) || ((queue->head == 0&& (queue->tail == 0)))
    19.     {
    20.         queue->overflow = true;
    21.         Irq_Restore(flags);
    22.         return E_OVERFLOW;
    23.     }
    24.     else
    25.     {
    26.         queue->queue[queue->tail] = c;
    27.         queue->tail = (queue->tail + 1) % QMAX;
    28.         Irq_Restore(flags);
    29.     }
    30.     return E_OK;
    31. }
    32.  
    33. int poll(ConditionQueue * queue, Condition * c)
    34. {
    35.     unsigned int flags;
    36.     Irq_Save(flags);
    37.     if (queue->head == queue->tail)
    38.     {
    39.         Irq_Restore(flags);
    40.         return E_NO_DATA;
    41.     }
    42.     else
    43.     {
    44.         *= queue->queue[queue->head];
    45.         queue->overflow = false;
    46.         queue->head = (queue->head + 1) % QMAX;
    47.         Irq_Restore(flags);
    48.     }
    49.     return E_OK;
    50. }
    51.  
    52. typedef struct
    53. {
    54.     State current;
    55.     bool inTransaction;
    56.     ConditionQueue queue;
    57. } StateMachine, * pStateMachine;
    58.  
    59. static State __step(pStateMachine machine, Condition condition)
    60. {
    61.     State current = machine -> current;
    62.     pTrasition t = transition_table[current][condition];
    63.     (*(t->action))(current, condition);
    64.     current = t->next;
    65.     machine->current = current;
    66.     return current;
    67. }
    68.  
    69. State step(pStateMachine machine, Condition condition)
    70. {
    71.     Condition next_condition;
    72.     int status;
    73.     State current;
    74.     if (machine->inTransaction)
    75.     {
    76.         push(&(machine->queue), condition);
    77.         return STATE_INTRANSACTION;
    78.     }
    79.     else
    80.     {
    81.         machine->inTransaction = true;
    82.         current = __step(machine, condition);
    83.         status = poll(&(machine->queue), &next_condition);
    84.         while(status == E_OK)
    85.         {
    86.             __step(machine, next_condition);
    87.             status = poll(&(machine->queue), &next_condition);
    88.         }
    89.         machine->inTransaction = false;
    90.         return current;
    91.     }
    92. }
    93.  
    94. void initialize(pStateMachine machine, State s)
    95. {
    96.     machine->current = s;
    97.     machine->inTransaction = false;
    98.     machine->queue.head = 0;
    99.     machine->queue.tail = 0;
    100.     machine->queue.overflow = false;
    101. }
  • 相关阅读:
    C++ continue 语句
    2022中国大健康展,山东大健康展,济南健康展,健康产业展
    大厂 Java 面经!阿里高工不小心把今年秋招面试题泄露了出来。
    14-GuliMall ElasticSearch安装与入门
    已超1000+测试员分享!Python自动化测试案例实战
    【Git】Git 的基本操作 -- 详解
    程序员的不知道的代码
    薅羊毛-微信阅读
    G1D27-deberta&右键创建md文档
    明星为何会偷税?我国的交税政策是?
  • 原文地址:https://blog.csdn.net/m0_61687959/article/details/132715356