网站关键词指数查询,为什么收不到自己网站,美食推广平台有哪些,大学生求职创业补贴玩单片机还可以#xff0c;各个外设也都会驱动#xff0c;但是如果让你完整的写一套代码时#xff0c;却无逻辑与框架可言。这说明编程还处于比较低的水平#xff0c;你需要学会一种好的编程框架或者一种编程思想#xff01;比如模块化编程、状态机编程、分层思想等。本文… 玩单片机还可以各个外设也都会驱动但是如果让你完整的写一套代码时却无逻辑与框架可言。这说明编程还处于比较低的水平你需要学会一种好的编程框架或者一种编程思想比如模块化编程、状态机编程、分层思想等。 本文来说一下状态机编程。什么是状态机 状态机(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的状态。这样的话第二种方法需要4X100400个状态而且函数fsm_active()中的switch—case语句里要有400个case这样的程序还有法儿写么 同样的功能改动如果用g_stFSM这个结构体来实现状态机的话函数fsm_active()只需要将if(g_stFSM.u8KeyCnt3)改为if(g_stFSM.u8KeyCnt 98)就可以了 g_stFSM结构体的两个成员中u8LedStat可以看作是质变因子相当于主变量u8KeyCnt可以看作是量变因子相当于辅助变量。量变因子的逐步积累会引发质变因子的变化。 像g_stFSM这样的状态机被称作Extended State Machine。声明本文于网络整理版权归原作者所有如来源信息有误或侵犯权益请联系我们删除或授权事宜。