谈谈单片机编程思想——状态机

    玩单片机还可以,各个外设也都会驱动,但是如果让你完整的写一套代码时,却无逻辑与框架可言。这说明编程还处于比较低的水平,你需要学会一种好的编程框架或者一种编程思想!比如模块化编程、状态机编程、分层思想等。

    本文来说一下状态机编程。

什么是状态机?

    状态机(state machine)有5个要素:

  • 状态(state)

  • 迁移(transition)

  • 事件(event)

  • 动作(action)

  • 条件(guard)

    状态:一个系统在某一时刻所存在的稳定的工作情况,系统在整个工作周期中可能有多个状态。例如一部电动机共有正转、反转、停转这 3 种状态。

    一个状态机需要在状态集合中选取一个状态作为初始状态。

    迁移:系统从一个状态转移到另一个状态的过程称作迁移,迁移不是自动发生的,需要外界对系统施加影响。停转的电动机自己不会转起来,让它转起来必须上电。

    事件:某一时刻发生的对系统有意义的事情,状态机之所以发生状态迁移,就是因为出现了事件。对电动机来讲,加正电压、加负电压、断电就是事件。

    动作:在状态机的迁移过程中,状态机会做出一些其它的行为,这些行为就是动作,动作是状态机对事件的响应。给停转的电动机加正电压,电动机由停转状态迁移到正转状态,同时会启动电机,这个启动过程可以看做是动作,也就是对上电事件的响应。

    条件:状态机对事件并不是有求必应的,有了事件,状态机还要满足一定的条件才能发生状态迁移。还是以停转状态的电动机为例,虽然合闸上电了,但是如果供电线路有问题的话,电动机还是不能转起来。

举个例子

要解决的问题

    电路如下图:

    器件包括单片机MCU、一按键K0、LED灯L1和L2。

    实现功能描述:

  • L1L2状态转换顺序OFF/OFF--->ON/OFF--->ON/ON--->OFF/ON--->OFF/OFF

  • 通过按键控制L1L2的状态,每次状态转换需连续按键5次

  • L1L2的初始状态OFF/OFF

状态转换图

    在状态机编程中,正确的顺序应该是先有状态转换图,后有程序,程序应该是根据设计好的状态图写出来的。

    下面这张按键控制流水灯状态转换图,是用UML(统一建模语言)的语法元素画出来的,语法不是很标准,但拿来解释问题足够了。

    上图中,圆角矩形代表状态机的各个状态,里面标注着状态的名称。

    带箭头的直线或弧线代表状态迁移,起于初态,止于次态。

    图中的文字内容是对迁移的说明,格式是:事件[条件]/动作列表(后两项可选)。

    “事件[条件]/动作列表”要说明的意思是:如果在某个状态下发生了“事件”,并且状态机

    满足“[条件]”,那么就要执行此次状态转移,同时要产生一系列“动作”,以响应事件。在这个例子里,我用“KEY”表示击键事件。

    图中有一个黑色实心圆点,表示状态机在工作之前所处的一种不可知的状态,在运行之前状态机必须强制地由这个状态迁移到初始状态,这个迁移可以有动作列表(如图1所示),但不需要事件触发。

    图中还有一个包含黑色实心圆点的圆圈,表示状态机生命周期的结束,这个例子中的状态机生生不息,所以没有状态指向该圆圈。

程序代码

    下面是根据上述状态转换图写成的代码:

void main(void)
{
 sys_init(); 
  led_off(LED1); 
  led_off(LED2); 
  g_stFSM.u8LedStat = LS_OFFOFF; 
  g_stFSM.u8KeyCnt = 0; 
  while(1) 
  { 
   if(test_key()==TRUE)  
   {  
    fsm_active();  
   }  
   else  
   {  
    ; /*idle code*/  
   } 
  }
}
void fsm_active(void)
{
 if(g_stFSM.u8KeyCnt > 3) /*击键是否满 5 次*/ 
  { 
   switch(g_stFSM.u8LedStat)  
   {  
    case LS_OFFOFF:   
      led_on(LED1); /*输出动作*/    
     g_stFSM.u8KeyCnt = 0;    
     g_stFSM.u8LedStat = LS_ONOFF; /*状态迁移*/    
     break;   
   case LS_ONOFF:    
     led_on(LED2); /*输出动作*/    
     g_stFSM.u8KeyCnt = 0;    
     g_stFSM.u8LedStat = LS_ONON; /*状态迁移*/    
     break;   
   case LS_ONON:    
     led_off(LED1); /*输出动作*/    
     g_stFSM.u8KeyCnt = 0;    
     g_stFSM.u8LedStat = LS_OFFON; /*状态迁移*/    
     break;   
   case LS_OFFON:    
     led_off(LED2); /*输出动作*/    
     g_stFSM.u8KeyCnt = 0;    
     g_stFSM.u8LedStat = LS_OFFOFF; /*状态迁移*/    
     break;   
   default: /*非法状态*/    
     led_off(LED1);    
     led_off(LED2);    
     g_stFSM.u8KeyCnt = 0;    
     g_stFSM.u8LedStat = LS_OFFOFF; /*恢复初始状态*/    
     break;  
    } 
  } 
  else 
  {  
    g_stFSM.u8KeyCnt  ; /*状态不迁移,仅记录击键次数*/ 
  }
}

    先看一下fsm_active()这个函数,g_stFSM.u8KeyCnt = 0;这个语句在switch—case里共出现了 5 次,前 4 次是作为各个状态迁移的动作出现的。从代码简化提高效率的角度来看,我们完全可以把这 5 次合并为 1 次放在 switch—case 语句之前,两者的效果是完全一样的,代码里之所以这样啰嗦,是为了清晰地表明每次状态迁移中所有的动作细节,这种方式和上面状态转换图所要表达的意图是完全一致的。

    再看一下g_stFSM这个状态机结构体变量,它有两个成员:u8LedStat和 u8KeyCnt。用这个结构体来做状态机好像有点儿啰嗦,我们能不能只用一个像 u8LedStat 这样的整型变量来做状态机呢?

    当然可以!我们把上图中的这 4 个状态各自拆分成 5 个小状态,这样用 20 个状态同样能实现这个状态机,而且只需要一个 unsigned char 型的变量就足够了,每次击键都会引发状态迁移, 每迁移 5 次就能改变一次 LED 灯的状态,从外面看两种方法的效果完全一样。

    假设我把功能要求改一下,把连续击键5次改变L1L2的状态改为连续击键100次才能改变L1L2的状态。这样的话第二种方法需要4X100=400个状态!而且函数fsm_active()中的switch—case语句里要有400个case,这样的程序还有法儿写么?!

    同样的功能改动,如果用g_stFSM这个结构体来实现状态机的话,函数fsm_active()只需要将if(g_stFSM.u8KeyCnt>3)改为if(g_stFSM.u8KeyCnt > 98)就可以了!

    g_stFSM结构体的两个成员中,u8LedStat可以看作是质变因子,相当于主变量;u8KeyCnt可以看作是量变因子,相当于辅助变量。量变因子的逐步积累会引发质变因子的变化。

    像g_stFSM这样的状态机被称作Extended State Machine。

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单片机状态机编程思想是一种常用的软件设计方法,用于处理复杂的系统控制逻辑。它将系统的行为分解为一系列离散的状态,并定义了状态之间的转换条件和动作。以下是单片机状态机编程思想的基本概念和实现方式: 1. 状态(State):表示系统在某一时刻的工作状态,可以是一个具体的功能或操作。例如,一个电梯系统可以有"停止"、"上升"和"下降"等状态。 2. 事件(Event):触发状态转换的外部或内部事件,可以是传感器输入、定时器溢出等。例如,电梯系统中的事件可以是"按下上升按钮"、"到达某一楼层"等。 3. 状态转换(Transition):定义了从一个状态到另一个状态的条件和动作。条件可以是事件的发生、某些输入信号的状态等。动作可以是改变输出、执行某些操作等。例如,当电梯处于"停止"状态时,如果接收到"按下上升按钮"事件,则转换到"上升"状态,并执行相应的动作。 4. 状态机(StateMachine):由一组状态、事件和状态转换组成的模型,描述了系统的行为。通过不断地检测事件和执行状态转换,状态机可以实现系统的控制逻辑。 在单片机中实现状态机编程思想可以采用以下步骤: 1. 定义状态:根据系统需求,确定系统的各个状态,并为每个状态分配一个唯一的标识符。 2. 定义事件:根据系统需求,确定可能触发状态转换的事件,并为每个事件分配一个唯一的标识符。 3. 定义状态转换:为每个状态定义可能的状态转换条件和动作。根据事件和当前状态,确定下一个状态和执行的动作。 4. 实现状态机:在单片机程序中,使用条件语句或查表法等方式实现状态机。通过不断地检测事件和执行状态转换,实现系统的控制逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值