单片机状态机编程详解(状态转换图)
不知道大家有没有这样的感觉,就是玩MCU还可以,所有的功能模块都可以驱动,但是如果写一套完整的代码。却无逻辑与框架可言,上来就是开始写!东抄抄西抄抄。
说明编程还处于比较低的水平,那么如何才能提高自己的编程水平呢?学会一种好的编程框架或者一种编程思想,可能会受用终生!比如模块化编程,框架式编程,状态机编程等等,都是一种好的框架。
今天说的就是状态机编程,由于篇幅较长,大家慢慢欣赏。那么状态机是一个这样的东东?状态机(state machine)有5个要素,分别是状态(state)、迁移(transition)、事件(event)、动作(action)、条件(guard)。
什么是状态机?
状态机是一个这样的东东:状态机(state machine)有 5 个要素,分别是状态(state)、迁移(transition)、事件(event)、动作(action)、条件(guard)。
状态 :一个系统在某一时刻所存在的稳定的工作情况,系统在整个工作周期中可能有多个状态。例如一部电动机共有正转、反转、停转这 3 种状态。
一个状态机需要在状态集合中选取一个状态作为初始状态。
迁移 :系统从一个状态转移到另一个状态的过程称作迁移,迁移不是自动发生的 ,需要外界对系统施加影响。停转的电动机自己不会转起来,让它转起来必须上电。
事件 :某一时刻发生的对系统有意义的事情,状态机之所以发生状态迁移,就是因为出现了事件。对电动机来讲,加正电压、加负电压、断电就是事件 。
动作 :在状态机的迁移过程中,状态机会做出一些其它的行为,这些行为就是动作 ,动作是状态机对事件的响应。给停转的电动机加正电压,电动机由停转状态迁移到正转状态,同时会启动电机,这个启动过程可以看做是动作,也就是对上电事件的响应。
条件 :状态机对事件并不是有求必应的,有了事件,状态机还要满足一定的条件才能发生状态迁移 。还是以停转状态的电动机为例,虽然合闸上电了,但是如果供电线路有问题的话,电动机还是不能转起来。
只谈概念太空洞了,上一个小例子:一单片机、一按键、俩 LED 灯(记为L1和L2)、一人, 足矣!
规则描述:
1、L1L2状态转换顺序OFF/OFF--->ON/OFF--->ON/ON--->OFF/ON--->OFF/OFF
2、通过按键控制L1L2的状态,每次状态转换需连续按键5次
3、L1L2的初始状态OFF/OFF
实际上在状态机编程中,正确的顺序应该是先有状态转换图,后有程序,程序应该是根据设计好的状态图写出来的 。不过考虑到有些童鞋会觉得代码要比转换图来得亲切,我就先把程序放在前头了。
这张状态转换图是用UML(统一建模语言)的语法元素画出来的,语法不是很标准,但拿来解释问题足够了。
图2按键控制流水灯状态转换图
程序清单代码需移步 ---戳进-->单片机状态机编程详解
单片机编程中的状态机浅析
说到单片机编程,不得不说到状态机,状态机做为软件编程的主要架构已经在各种语言中应用,当然包括C语言,在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。灵活的应用状态机不仅是程序更高效,而且可读性和扩展性也很好。状态无处不在,状态中有状态,只要掌握了这种思维,让它成为您编程中的一种习惯,相信您会受益匪浅。
状态机可归纳为4个要素,即现态、条件、动作、次态。 这样的归纳,主要是出于对状态机的内在因果联系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:
①现态: 是指当前所处的状态。
②条件: 又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
③动作: 条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
④次态: 条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。
如果我们进一步归纳,把“现态”和“次态”统一起来,而把“动作”忽略(降格处理),则只剩下两个最关键的要素,即:状态、迁移条件。
状态机的表示要领有许多种,我们可以用文字、图形或表格的形式来表示一个状态机。
举个简单的例子:就按键处理来说,击键动作本身也可以看做一个状态机。一个细小的击键动作包含了:释放、抖动、闭合、抖动和重新释放等状态。 当我们打开思路,把状态机作为一种思想导入到程序中去时,就会找到处理疑问的一条有效的捷径。有时候用状态机的思维去思考程序该干什么,比用控制流程的思维去思考,可能会更有效。这样一来状态机便有了更实际的功用。废话不多说,实践才是检验真理的唯一标准。
也许有人觉得状态机把问题复杂化了,其实做过软件设计的人无形之中已经在用状态机,下面就总结介绍几种状态机。
第一种:switch case结构状态机
switch()。
case1:。
if(not反复执行状态1)。
进入1状态前要做的准备。
进入1状态的过程。
if(not反复执行状态1)。
离开状态1的过程。
case2:。
...。
但这种方式不能很有效预定义所有的状态,也不能把这些状态之间的切换过程合理的定义出来,“状态”本身没有一个合理的定义,几乎是一种面向过程的方式,只过这种方式足够简单,也最容易让人接受,缺点就没有“状态”的定义和指派功能,导致状态的混乱,出现状态处理重复代码,甚至处理不一致的问题,按照OO的观念,状态描述本来就应该是一种实体。
第二种状态机:ifelse语句结构状态机
这种状态机相对灵活一点,但对于一些大的项目,系统软件设计会相对复杂。
以上2种状态机是是大家接触最多的,也是经常用到的,这里不多说了。下面重点谈谈第三种状态机。
第三种状态机:消息触发状态机
该类型的状态机实现方式也是很多的,形态多样,但万变不离其宗的就是状态机的4要素及现态、条件、动作、次态。
下面介绍一种消息触发类型的状态机。
基于消息驱动的状态机机制
原理:一旦有消息触发,系统服务函数立即寻找所在状态的消息与消息处理函数对,找到后执行消息处理函数
步骤:
1.添加消息与消息映射
…
BEGIN_MESSAGE_ MAP(Name,Count) :状态机名,消息数
ADD_NEW_MSG_ITEM (Msg,OnMsg) :消息与消息处理函数
END_MESSAGE_MAP:结束
…
2.在这里注册
BEGIN_Register_Task:头
...
ADD_Register_Task(Name,Count):状态机名,消息数
...
END_Register_Task:尾
1.划分电子秤状态,完成以上步骤后,完成OnMsg消息处理函数
Void OnMsg(void)
{
…
}
说明:以上用宏完成,具体宏是如下定义:
#defineBEGIN_MESSAGE_MAP(Name,Count) constMSG_NODE_TYP MSG_node_Array_##Name[(Count)]={
#define ADD_NEW_MSG_ITEM(Msg,OnMsg) {Msg,OnMsg},
#define END_MESSAGE_MAP };
#define BEGIN_Register_Task const MSG_MAP TaskMap[TotalTask]={
#define ADD_Register_Task(Name,Count) {(MSG_NODE_TYP*)MSG_node_Array_##Name,Count},
#define END_Register_Task };
从以上代码可知:
1. 添加消息与消息映射实际上是定义消息与消息处理函数对的数组,以形成一个表
2. 注册状态机实际上是把所有消息对数组的入口定义成一个数组,以形成一个表
消息是如何被执行的?
分发消息
void Default_DisposeMessage(unsigned char *pMsg)
{
unsigned chari;
unsigned charcount=TaskMap[g_Status].cItemCount;//定位到状态表
for(i=0;i<count;i++)< span="">
{
if(*pMsg==TaskMap[g_Status].pMsgItems.msg)//看能否匹配消息
{
TaskMap[g_Status].pMsgItems.pMsgFunc();//找到就执行消息处理函数
return;
}
}
}
void DispatchMessage(unsigned char*pMsg)
{
if(*pMsg)
{
Default_DisposeMessage(pMsg);
}
}
核心函数:消息处理中心
void Message_Dispose_Central(void)
{
BYTE Msg;
while(GetMessage(&Msg)) //获取消息
{
TranslateMessage(&Msg); //解释消息
DispatchMessage(&Msg); //分发消息
}
}
大家在使用单片机的时候,怎么灵活使用状态机思想的呢,欢迎大家留言评论。
相关问答
状态机 编程思路及方法?二用自定义的数据类型顶一个指针数组:最后一项指针函数为NULL.三用单片机的一个定时器作为系统的协调中心:心跳=基本定时,延时值都为心跳的整数倍,在定时...
stc 单片机 断电再上电,正在执行中的程序怎么在上电后继续从断点执行?做不到绝对从关机时的断点重新开始运行,但可以即时保存程序运行的功能步骤、运行状态数据和计算结果,重新运行时让MCU以相同的运行状态数据、计算结果和功能步...
向 单片机 高手求助,怎样用C语言编写歌曲程序和歌曲代码?需要用那些工具怎么样编写的要求详细解答。谢谢?{key_state=7;key_return=3;}elsekey_state=5; //抖动消除,退回5继续等待输入或等待输入有效时间过去case7: //按钮...
不用plc怎么用按钮控制气缸?可以使用一个简单的控制器,如单片机,来控制气缸。通过编写程序,将按钮的状态检测到单片机中,然后通过单片机的输出来控制气缸的操作。具体实现步骤如下:1...
在嵌入式开发中,怎么开发SPI IIC的驱动程序?SPI和IIC都是比较常用的短距离通讯方式,主要用在PCB板间或者芯片之间实现近距离通信。比如AT24C02就是通过IIC和单片机实现数据通信的,BMP280即可以通过IIC又可...
诸位老师咨询一下,设计时序控制器生产厂家,时序控制器买那...[回答]如果可以用单片机就简单多了。如果不能就只能用时序逻辑电路了。华中科技大学计算机组成原理MIPSCPU设计educoder,logisim。变长指令周期3级时序单...
帮个忙诸位朋友!求教:上海靠谱月牙机设计,月牙机外观怎么样??[回答]址的识别和分析是本地搜索必不可少的技术,尽管有许多识别和分析地址的方法,最有效的是有限状态机。brbr一个有限状态机是一个特殊的有向图(参见有关...
一个机械动作,需要很多个条件,怎么做程序更简便一些?在编写PLC程序时,确实可能会遇到一个机械动作需要满足多个条件的情况。以下是一些可以简化程序的方法:1.使用状态机(StateMachine):将机械动作的控制转换为...
童鞋们,急需帮忙!交通灯控制逻辑电路设计?工业控制电路设...[回答]1.设计一个十字路口的交通灯控制电路,要求甲车道和乙车道两条交叉道路上的车辆交替运行,每次通行时间都设为25秒;2.要求黄灯先亮5秒,才能变换运行车...
各位好基友!有人知道么,永新港机系列叉车参数及原理,港机...[回答]江西赣力叉车致力于为客户提供优质的产品和全方位的服务有些驾驶员在起动发动机的时候,因为蓄电池存电不足,就并联上一个充满电的蓄电池共同使用。...