状态机在单片机程序设计中的应用

发布者:yanfeng00最新更新时间:2011-11-02 关键字:状态机  单片机程序 手机看文章 扫描二维码
随时随地手机看文章

      状态机是软件编程中的一个重要概念。比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。

      比如说一个按键命令解析程序,就可以被看做状态机:本来在A状态下,触发一个按键后切换到了B状态;再触发另一个键后切换到C状态,或者返回到A状态。这就是最简单的按键状态机例子。实际的按键解析程序会比这更复杂些,但这不影响我们对状态机的认识。

      进一步看,击键动作本身也可以看做一个状态机。一个细小的击键动作包含了:释放、抖动、闭合、抖动和重新释放等状态。

      同样,一个串行通信的时序(不管它是遵循何种协议,标准串口也好、I2C也好;也不管它是有线的、还是红外的、无线的)也都可以看做由一系列有限的状态构成。

      显示扫描程序也是状态机;通信命令解析程序也是状态机;甚至连继电器的吸合/释放控制、发光管(LED)的亮/灭控制又何尝不是个状态机。

      当我们打开思路,把状态机作为一种思想导入到程序中去时,就会找到解决问题的一条有效的捷径。有时候用状态机的思维去思考程序该干什么,比用控制流程的思维去思考,可能会更有效。这样一来状态机便有了更实际的功用。

程序其实就是状态机。

      也许你还不理解上面这句话。请想想看,计算机的大厦不就是建立在“0”和“1”两个基本状态的地基之上么?

状态机的要素

      状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:

      ①现态:是指当前所处的状态。

      ②条件:又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

      ③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

      ④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

      如果我们进一步归纳,把“现态”和“次态”统一起来,而把“动作”忽略(降格处理),则只剩下两个最关键的要素,即:状态、迁移条件。

      状态机的表示方法有许多种,我们可以用文字、图形或表格的形式来表示一个状态机。

      纯粹用文字描述是很低效的,所以就不介绍了。接下来先介绍图形的方式。

      状态迁移图(STD)

      状态迁移图(STD),是一种描述系统的状态、以及相互转化关系的图形方式。状态迁移图的画法有许多种,不过一般都大同小异。我们结合一个例子来说明一下它的画法,如图1所示。

图1 状态迁移图

      ①状态框:用方框表示状态,包括所谓的“现态”和“次态”。

      ②条件及迁移箭头:用箭头表示状态迁移的方向,并在该箭头上标注触发条件。

      ③节点圆圈:当多个箭头指向一个状态时,可以用节点符号(小圆圈)连接汇总。

      ④动作框:用椭圆框表示。

      ⑤附加条件判断框:用六角菱形框表示。

      状态迁移图和我们常见的流程图相比有着本质的区别,具体体现为:在流程图中,箭头代表了程序PC指针的跳转;而在状态迁移图中,箭头代表的是状态的改变。

      我们会发现,这种状态迁移图比普通程序流程图更简练、直观、易懂。这正是我们需要达到的目的。

      状态迁移表

      除了状态迁移图,我们还可以用表格的形式来表示状态之间的关系。这种表一般称为状态迁移表。

      表1就是前面介绍的那张状态迁移图的另一种描述形式。

表1 状态迁移表

      ①采用表格方式来描述状态机,优点是可容纳更多的文字信息。例如,我们不但可以在状态迁移表中描述状态的迁移关系,还可以把每个状态的特征描述也包含在内。

      ②如果表格内容较多,过于臃肿不利于阅读,我们也可以将状态迁移表进行拆分。经过拆分后的表格根据其具体内容,表格名称也有所变化。

      ③比如,我们可以把状态特征和迁移关系分开列表。被单独拆分出来的描述状态特征的表格,也可以称为“状态真值表”。这其中比较常见的就是把每个状态的显示内容单独列表。这种描述每个状态显示内容的表称之为“显示真值表”。同样,我们把单独表述基于按键的状态迁移表称为“按键功能真值表”。另外,如果每一个状态包含的信息量过多,我们也可以把每个状态单独列表。

      ④由此可见,状态迁移表作为状态迁移图的有益补充,它的表现形式是灵活的。

      ⑤状态迁移表优点是信息涵盖面大,缺点是视觉上不够直观,因此它并不能取代状态迁移图。比较理想的是将图形和表格结合应用。用图形展现宏观,用表格说明细节。二者互为参照,相得益彰。

用状态机思路实现一个时钟程序

      接下来,我将就状态机的应用,结合流程图、状态迁移图和状态迁移,举一个实际例子。下面这张图是一个时钟程序的状态迁移图,如图2所示。

图2 时钟程序状态迁移图

      把这张图稍做归纳,就可以得到它的另一种表现形式——状态迁移表,如表2所示。

表2 时钟程序状态迁移表

状态机应用的注意事项

      基于状态机的程序调度机制,其应用的难点并不在于对状态机概念的理解,而在于对系统工作状态的合理划分。

      初学者往往会把某个“程序动作”当作是一种“状态”来处理。我称之为“伪态”。那么如何区分“动作”和“状态”。本匠人的心得是看二者的本质:“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。

      初学者的另一种比较致命的错误,就是在状态划分时漏掉一些状态。我称之为“漏态”。

      “伪态”和“漏态”这两种错误的存在,将会导致程序结构的涣散。因此要特别小心避免。

更复杂的状态机

      前面介绍的是一种简单的状态结构。它只有一级,并且只有一维,如图3所示。

图3  线性状态机结构

      如果有必要,我们可以建立更复杂的状态机模型。

      1 多级状态结构

      状态机可以是多级的。在分层的多级状态机系统里面,一个“父状态”下可以划分多个“子状态”,这些子状态共同拥有上级父状态的某些共性,同时又各自拥有自己的一些个性。

      在某些状态下,还可以进一步划分子状态。比如,我们可以把前面的时钟例子修改如下:
把所有和时钟功能有关的状态,合并成1个一级状态。在这个状态下,又可以划分出3个二级子状态,分别为显示时间、设置小时、设置分钟;

      同样,我们也可以把所有和闹钟功能有关的状态,合并成1个一级状态。在这个状态下,再划分出4个二级子状态,分别为显示闹钟、设置“时”、设置“分”、设置鸣叫时间。

      我们需要用另一个状态变量(寄存器)来表示这些子状态。

      子状态下面当然还可以有更低一级的孙状态(子子孙孙无穷尽也),从而将整个状态体系变成了树状多级状态结构,如图4所示。

图4 树状多级状态结构

      2 多维状态结构

      状态结构也可以是多维的。从不同的角度对系统进行状态的划分,这些状态的某些特性是交叉的。比如,在按照按键和显示划分状态的同时,又按照系统的工作进程做出另一种状态划分。这两种状态划分同时存在,相互交叉,从而构成了二维的状态结构空间。

      举一个这方面的例子,如:空调遥控器,如图5所示。

图5 多维状态机结构

      同样,我们也可以构建三维、四维甚至更多维的状态结构。每一维的状态都需要用一个状态变量(寄存器)来表示。

      无论多级状态结构和多维状态结构看上去多么迷人,匠人的忠告是:我们依然要尽可能地简化状态结构,能用单级、单维的结构,就不要给自己找事,去玩那噩梦般的复杂结构。

      简单的才是最有效的。

结束语

      对状态机的理解需要一个由浅入深的过程。这个过程应该是与实践应用和具体案例思考相结合的。当一种良好的思路成为设计的习惯,它就能给设计者带来回报。愿这篇手记里介绍的基于状态机的编程思路能给新手们带来一些启迪,帮助大家找到“程序设计”的感觉。

关键字:状态机  单片机程序 引用地址:状态机在单片机程序设计中的应用

上一篇:单片机的ISP在线编程设计
下一篇:51系列单片机不同初始化

推荐阅读最新更新时间:2024-03-16 12:45

基于单片机的LCD1602显示秒表程序
#include reg52.h //包含单片机寄存器的头文件 #include stdlib.h //包含随机函数rand()的定义文件 #include intrins.h //包含_nop_()函数定义的头文件 sbit RS=P2^2; //寄存器选择位,将RS位定义为P2.0引脚 sbit RW=P2^1; //读写选择位,将RW位定义为P2.1引脚 sbit E=P2^0; //使能信号位,将E位定义为P2.2引脚 sbit BF=P0^7; //忙碌标志位,,将BF位定义为P0.7引脚 unsigned char code digit ={ 0123456789 }; //
[单片机]
基于AT89C51单片机直流电机PWM调速程序分享
这是一款AT89C51单片机直流电机PWM调速程序,程序可以直接用于AT89C52、AT89S51、AT89S51,STC89C51、STC89C52单片机中,单片机晶振采用11.0592M,直流电机由L298集成电路控制,产生的PWM的频率约为91Hz。L298各引脚已在程序中标明,原理图大家可以自己画一下,这里就不具给出。下面是源程序。 #include[reg52.h》//注意请把‘ [ ’换成 “《”,下同。否则编译时会出错。 #include [intrins.h》// #define uchar unsigned char #define uint unsigned int sbit en1=P2^
[单片机]
基于AT89C51<font color='red'>单片机</font>直流电机PWM调速<font color='red'>程序</font>分享
PIC单片机C语言程序设计(13)
  十四、城市交通路口管理的红、绿灯   在城市大街的十字交通路口,都安置了各种由红、绿色高亮度LED组成的指示图案,用于管理机动车辆、非机动车辆和行人,使之能够安全、顺利地通过各自的路口道。这些红绿灯图案,多种多样。笔者以其中一种十字路口的红绿灯图案的控制为例,来说明PIC单片机C语言在城市交通灯管理中的应用。   图55a是一种由红色高亮LED组成的掌形指示图案,路口显示该图案。表示此时该方向禁止行人通过。图55b、图55c分别是用绿色高亮LED组成的仿人形和仿自行车形的指示图案,在路口显示该图案,表示此时允许人们通行。图56是由红、绿色两种颜色的高亮LED组成的圆形指示图案(红、绿色分时显示), 以提示是否允许机动车辆通行
[单片机]
PIC<font color='red'>单片机</font>C语言<font color='red'>程序</font>设计(13)
A7105(2.4G)与STC89C52单片机无线收发程序(发射程序)
A7105是台湾笙科的一款2.4G收发芯片,一般网上都可以找得到程序的,但是下面的程序都是我自己亲自测试过的。 以下A7105的发射程序: #include reg52.h #include intrins.h typedef unsigned int uint; typedef unsigned char uchar; #define MODE_REG 0x00//寄存器设置 #define MODE_CONTROL_REG 0x01// #define CALIBRATION_REG 0x02 #define FIFO_REGISTER1_REG 0x03 #define FIFO_REG
[单片机]
51单片机PWM调速程序 同样可以对 led 灯进行亮度调节
/*/ /* 程序名:PWM直流电机调速 */ /* 晶振:11.00592 MHz CPU型号:AT89C51 */ /* 直流电机的PWM波控制,可以直接的调速从0到20级的调速 */ /*/ #include reg51.h #define TH0_TL0 (65536-1000)//设定中断的间隔时长 unsigned char count0 = 50;//高电平的占空比 unsigned char count1 = 0;//比较用的临时变量 bit Flag = 1;//电机正反转标志位,1正转,0反转 sbit Key_add=P2 ^ 0; //电机减速 sbit Key_dec=P2 ^ 1; //电
[单片机]
PlC单片机C语言程序设计(10)
4.C程序pic07.c的SlM软件仿真调试 在《PIC单片机C语言程序设计(8)》和《PIC单片机C语言程序设计(9)》中,我们已对C程序pic07 C进行了编辑和编译,现在需要查看该程序能否达到预期的设计目标,即能否完成pic07.C的0~99秒增量计时LED数码显示功能(脉冲发生器),因此需要调试程序。 调试程序,可以使用MPLABICD2在线调试器、MPLABICE2000硬件仿真器等开发工具。对于初学C语言程序,又没有上述硬件仿真调试器的读者,最好选用MPLAB SIM软件模拟仿真器进行程序的调试。 模拟仿真调试是检查程序是否正确,能否实现预期功能的有效手段。有了它,可以实现程序的“单步运行”、“单步越过”、
[单片机]
PlC<font color='red'>单片机</font>C语言<font color='red'>程序</font>设计(10)
51单片机驱动16乘16点阵显示汉字程序
代码: #include reg52.h typedef unsigned char uint8; typedef unsigned int uint16; sbit acon=P1^0; //上8位地址线 sbit bcon=P1^1; sbit ccon=P1^2; sbit acon1=P1^3; //下8位地址线 sbit bcon1=P1^4; sbit ccon1=P1^5; sbit en=P1^6; //38译码器使能 sbit en1=P1^7; uint8 j=0,k=0; code uint8 ledcode ={0xDC,0x07,0xDD,0xF7,0xDD,0xF7,0xDC,0x07,0x0
[单片机]
51<font color='red'>单片机</font>驱动16乘16点阵显示汉字<font color='red'>程序</font>
基于VHDL状态机设计的智能交通控制灯
1 设计方案 十字路口设计两组交通灯分别控制东西和南北两个方向的交通。如图1所示,当东西方向的红灯亮时,南北方向对应绿灯亮,过渡阶段黄灯亮,即东西方向红灯亮的时间等于南北方向绿灯和黄灯亮的时间之和。交通灯维持变亮的时间取决于键盘输入的控制键值。同理,当南北方向的红灯变亮时,东西方向的交通灯也遵循此逻辑。总体上由状态机实现控制,本设计中使用两个状态机分别控制东西和南北两个方向的交通。每个状态机中都设有4个状态,分别对应红灯亮、绿灯亮、黄灯亮和出现紧急状况时两个方向上的红灯同时变亮,停止倒计时的同时数码管上出现闪烁。路口的繁忙程度是不一样的,白天时的交通比较繁忙,因此,红绿灯要变化快一些以便提高通过效率,减少拥堵时间;相反,夜晚交通
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • ARM裸机篇--按键中断
    先看看GPOI的输入实验:按键电路图:GPF1管教的功能:EINT1要使用GPF1作为EINT1的功能时,只要将GPFCON的3:2位配置成10就可以了!GPF1先配 ...
  • 网上下的--ARM入门笔记
    简单的介绍打今天起菜鸟的ARM笔记算是开张了,也算给我的这些笔记找个存的地方。为什么要发布出来?也许是大家感兴趣的,其实这些笔记之所 ...
  • 学习ARM开发(23)
    三个任务准备与运行结果下来看看创建任务和任运的栈空间怎么样的,以及运行输出。Made in china by UCSDN(caijunsheng)Lichee 1 0 0 ...
  • 学习ARM开发(22)
    关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个 ...
  • 学习ARM开发(21)
    先要声明任务指针,因为后面需要使用。 任务指针 volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • 学习ARM开发(20)
  • 学习ARM开发(19)
  • 学习ARM开发(14)
  • 学习ARM开发(15)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved