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

最新更新时间:2023-02-02来源: zhihu关键字:单片机  编程思想  状态机 手机看文章 扫描二维码
随时随地手机看文章

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


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


什么是状态机?

状态机(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)


先看一下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。


关键字:单片机  编程思想  状态机 编辑:什么鱼 引用地址:谈谈单片机编程思想——状态机

上一篇:电流检测电路
下一篇:单片机工作电压5V的来历

推荐阅读

基于51单片机的计时器设计
一.系统概述系统使用的模块有AT89C51单片机+74HC573锁存器+8位共阴数码管+按键+小灯+蜂鸣器。本设计采用51单片机为核心控制器,系统内设置了24秒的定时,通过按键可以控制定时的开始和关闭,当定时时间到达后就会点亮小灯并通过蜂鸣器进行报警。二.仿真概述1.采用数码管显示定时时间,并且定时过程也会在数码管上显示。2.烟雾浓度的报警值可以通过按键设定,按键可以对阈值进行加减。3.定时时间到达后小灯就会亮起,并且蜂鸣器就会进行报警。4.定时时间结束后2秒后定时器就会被重置。三.程序设计使用Keil51进行程序设计,打开Proteus时程序是默认烧录的状态,如果没有烧录点击AT89C51单片机并将程序导入就能运行系统(程序文件是
发表于 2023-03-27
基于51<font color='red'>单片机</font>的计时器设计
51单片机读写AT29C040的C51程序
给大家介绍一个51单片机读写AT29C040的c51程序#include 《reg51.h》#include 《intrins.h》unsigned chartime;void int_t0(void) interrupt 1 {time++;}unsigned char d; //rec databit rec(void) {TH0=0;time=0;while (!RI) {if (time》1) return(1);}RI=0;d=SBUF;return(0);}void sen(unsigned char dd) {SBUF=dd;while (!TI) {}TI=0;}void wait(void) {time=0;whi
发表于 2023-03-27
基于AT89C4051数字时钟的设计
这是一个用AT89C405l单片机做的一个实时数字时钟。由于使用AT89C405l,所以这个数字时钟成本很低、体积很小,可以做成一个小模块,使用非常方便。一、硬件设计下图是数字时钟的电路原理图。AT89C4051单片机的P1口用来连接LCD的数据线(从LCD的7脚到14脚)。使用20×4的LCD字符显示屏。二、LCD自定义字符方法右图上可以看到数字比正常的字体尺寸大很多,为此使用了带有CGRAM(CGRAM为字型、字符产生器的RAM,全称CharacterGeneratorRAM,简称CGRAM。)的LCD,用户可以灵活自定义字符。定义一个字符,首先要得到该字符的数值然后写入CGRAM区。该CGRAM区域从O×40开始,每个字符有8
发表于 2023-03-27
基于AT89C4051数字时钟的设计
基于AT89C2051单片机的门禁系统设计
电路描述:安全是我们日常的生活中最关心的问题。 每个人都觉得安全问题是非常至关重要的,在家里的门和安全,可以尽可能多的安全。 为了对于门访问安全因此,我们打算通过引进一个电子密码锁系统,该系统包括一个人得到一个访问某些项目之前要输入密码的安全性,以及在家里,一个房间密码锁系统,不只是普通的单用户密码锁系统,需要用户插入已编程的代码来访问一个房间; 它是一个密码锁系统,有密码而且可以启用多个用户访问。在基于51单片机的门储物柜,只允许授权人员进入限制区域的门禁系统。 该系统由8位微控制器AT89C2051具有ROM的2K字节的程序存储器完全控制。 该系统具有通过该密码,可以通过它来输入键盘。 当输入的密码与存储在存储器中的口令相等则门
发表于 2023-03-27
基于AT89C2051<font color='red'>单片机</font>的门禁系统设计
AD7751的典型应用
由AD7751和89C51单片机构成电能计量系统的简化电路如图所示.C1~C4为+5V电源的退耦电容.R端经上拉电阻R1接UDD使R=1,令复位端失效.由3.579545MHZ的石英晶体与电容器C3、C6构成晶振电路,向AD7751提供时钟。C7为UREF端的消噪声电容。89C51中P1口的P1.0~P1.5分别用来设定G0、G1、S0、S1和SCF端的逻辑电平。P2口接键盘。由AD7751输出的FC信号,同时送给89C51的TO定时器、T1定时器的输入端。现利用TO定时器测量频率FC+T1定时器则对FC脉冲进行累加计数,再通过程序计算出有功功率和在一定时间内所消耗的电能。P、W的串行数据通过TXD端分别发送给显示卡、接口卡。AD7
发表于 2023-03-27
AD7751的典型应用
89c51单片机动态扫描接口设计
什么叫动态扫描显示在单片机系统中动态扫描显示 接口是单片机中应用最为广泛的一种显示方式之一。其接口 电路 是把所有显示器的8个笔划段a-h同名端连在一起,而每一个显示器的公共极COM是各自独立地受I/O线控制。CPU向字段输出口送出字形码时,所有显示器接收到相同的字形码,但究竟是那个显示器亮,则取决于COM端,而这一端是由I/O控制的,所以我们就能自行决定何时显示哪一位了。而所谓动态扫描就是指我们采用分时的办法,轮流控制各个显示器的COM端,使各个显示器轮流点亮。在轮流点亮扫描过程中,每位显示器的点亮时间是极为短暂的(约1ms),但由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位显示器并非同时点亮,但只要扫描的速度足够快,
发表于 2023-03-27
小广播
设计资源 培训 开发板 精华推荐

何立民专栏 单片机及嵌入式宝典

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

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