您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 交通运输 > 有限状态机在单片机编程中的应用
学习笔记有限状态机在单片机编程中的应用在单片机编程中,如果在不使用操作系统的情况下同时执行多个任务,可能会遇到下面这些情况:一个任务的执行时间过长,导致其他任务无法及时执行在一些任务中大量使用delay()等函数进行软件延时,这些延时函数占用过多时间,影响其他任务的执行一些复杂任务的程序逻辑不清晰,不便于以后对程序进行维护,或添加新功能本文介绍的有限状态机,可以做到将一个耗时较长的复杂任务分解为多个简单任务,同时使代码逻辑更加清晰,从而解决上述问题。目录:1.什么是有限状态机2.有限状态机的作用2.1分解耗时过长的任务2.2避免软件延时对CPU资源造成浪费2.3使程序逻辑更加清晰3.有限状态机的实现3.1通过switch-case语句实现3.2通过Arduino库实现3.3其他方式4.示例一:按键去抖动程序的优化4.1传统的按键去抖动程序4.2优化后的按键去抖动程序5.示例二:通过有限状态机实现的闹钟程序6.后记1.什么是有限状态机根据维基百科上的定义,有限状态机(finite-statemachine,FSM,简称状态机)是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。1为了理解这句话,假设自己还有三天就要考试,这时候就要进入紧张的备考状态,将空闲时间用在复习上。但是,为了保证足够的精力,小睡一会儿也是十分有必要的。那么,什么时候复习,什么时候睡觉呢?可以这样描述:在复习的时候:如果感到瞌睡,则睡觉如果没有感觉到瞌睡,则继续复习在小睡的时候:如果感觉不再瞌睡,则开始复习如果感觉依旧瞌睡,则继续睡觉也可通过一幅简单的示意图(也叫「状态转移图」)表示出来:这个例子其实就是一个简单的有限状态机,其中,复习和小睡是两个状态,感觉瞌睡和感觉清醒这两个条件可以使状态发生转换。2另外,ProgrammingBasics3网站上也提供了状态机相关的教程,用形象化的图片解释了什么是有限状态机,可通过此链接访问。在嵌入式程序设计中,如果一个系统需要处理一系列连续发生的任务,或在不同的模式下对输入进行不同的处理,常常使用有限状态机实现。例如测量、监测、控制等控制逻辑型应用。42.有限状态机的作用2.1分解耗时过长的任务大家应该都知道,CPU没有并行执行任务的能力。计算机「同时」运行多个程序,其实是多个程序依次交替执行,给人以程序同时运行的错觉。各个程序在什么时候开始执行,执行多长时间后切换到下一个程序,由操作系统决定。单片机执行多任务也是类似的过程,但由于其资源有限,为了节省对CPU和存储空间的占用,在很多情况下没有使用操作系统。这时,单片机中运行的各个任务必须在一定时间内主动执行完毕,才能保证下一个任务能够及时执行。对于一些需要长时间执行的任务,例如按键去除抖动、读取和播放MP3文件等,采用有限状态机的方式,将任务划分为多个小的步骤(状态),每次只执行其中的一步。这样,其他任务就有机会「插入」到这个任务之中,确保了各个任务都能按时执行。2.2避免软件延时对CPU资源造成浪费对于一些简单的程序,可通过delay(),delay_ms()之类的函数进行软件延时。这些延时函数,一般是通过将某个变量循环递加或递加,到达一定值后跳出循环,从而通过消耗CPU时间实现了延时。这种方式虽然简单,但在延时函数执行的过程中,其他程序无法运行,消耗了大量CPU资源。而通过状态机,有助于减少软件延时的使用,提高CPU利用率。请参考下文中的示例一:按键去抖动程序的优化,这一例子展示了如何通过软件延时分解耗时较长的任务,同时减少软件延时的使用。2.3使程序逻辑更加清晰通过状态机,将一个复杂任务划分为多个状态,可以使程序清晰易懂,便于维护。以后想要添加、删除程序中的功能,都会变得非常容易。下文中的示例二:通过状态机实现的闹钟展示了如何通过状态机优化程序逻辑。3.有限状态机的实现3.1通过switch-case语句实现如果使用C语言,switch-case语句,即可简单地实现有限状态机。/*定义各个状态所对应的数值*/#defineSTATUS_A0#defineSTATUS_B1#defineSTATUS_C2/*该变量的值即为当前状态机所处的状态*/uint8_tcurrentStatus=STATUS_A;/*通过状态机实现的某个任务,*需要放入while(1)等地方循环执行*/voidfsm_app(void){switch(currentStatus)/*根据现在的状态执行相应的程序*/{caseSTATUS_A:/*状态A*/doThingsForStatusA();/*执行状态A中需要执行的任务*//*若满足状态转换的条件,则转换到另一个状态*/if(condition_1){currentStatus=STATUE_B;}break;caseSTATUS_B:/*状态B*/doThingsForStatusB();/*执行状态B中需要执行的任务*//*若满足状态转换的条件,则转换到另一个状态*/if(condition_2){currentStatus=STATUE_C;}if(condition_3){currentStatus=STATUE_A;}break;caseSTATUS_C:/*状态C*/doThingsForStatusB();/*执行状态B中需要执行的任务*//*若满足状态转换的条件,则转换到另一个状态*/if(condition_4){currentStatus=STATUE_A;}break;default:currentStatus=STATUE_A;}}通过这段程序,即可实现一个具有三个状态的状态机。状态转移图如下图所示:3.2通过Arduino库实现对于Arduino用户,还可以使用FSMLibrary实现。这一库将有限状态机进行了封装,可以以更简洁的方式实现状态机。下载地址及使用说明:其他方式对于一些更复杂的任务,使用switch-case语句,代码可能会太简洁。这时候,使用其他方式实现状态机,可能会更好。具体请查阅相关资料。4.示例一:按键去抖动程序的优化4.1传统的按键去抖动程序初学单片机时,我们接触的按键去抖动程序一般是这样的5:voidkeyscan(){if(key1==0)//如果按键1按下{delayms(10);//延时10ms,消除因干扰产生的抖动if(key1==0)//再次检测按键1,如果依旧按下{doSomething();//此时说明按键1已按下,执行按键1需要执行的任务while(!key1);//等待按键释放}}}对应的流程图如下:从流程图中可知,delayms()延时函数和最后的等待按键释放的程序,会占用过多时间。4.2优化后的按键去抖动程序如果使用有限状态机的思路,可以按照下图方式实现:该状态机有三个状态,分别是按键未按下,等待,按键按下。当按键按下时,则会进入等待状态,若在等待状态中按键一直保持按下,说明按键已经稳定地按下,进入按键按下的状态,等待按键释放。程序代码如下:/*按键去抖动状态机中的三个状态*/#defineKEY_STATE_RELEASE//按键未按下#defineKEY_STATE_WAITING//等待(消抖)#defineKEY_STATE_PRESSED//按键按下(等待释放)/*等待状态持续时间*需要根据单片机速度和按键消抖程序被调用的速度来进行调整*/#defineDURIATION_TIME40/*按键检测函数的返回值,按下为1,未按下为0*/#definePRESSED1#defineNOT_PRESSED0/*按键扫描程序所处的状态*初始状态为:按键按下(KEY_STATE_RELEASE)*/uint8_tkeyState=KEY_STATE_RELEASE;/*按键检测函数,通过有限状态机实现*函数在从等待状态转换到按键按下状态时返回PRESSED,代表按键已被触发*其他情况返回NOT_PRESSED*/uint8_tkeyDetect(void){staticuint8_tduriation;//用于在等待状态中计数switch(keyState){caseKEY_STATE_RELEASE:if(readKey()==1)//如果按键按下{keyState=KEY_STATE_WAITING;//转换至下一个状态}returnNOT_PRESSED;//返回:按键未按下break;caseKEY_STATE_WAITING:if(readKey()==1)//如果按键按下{duriation++;if(duriation=DURIATION_TIME)//如果经过多次检测,按键仍然按下{//说明没有抖动了,可以确定按键已按下duriation=0;keyState=KEY_STATE_PRESSED;//转换至下一个状态returnPRESSED;}}else//如果此时按键松开{//可能存在抖动或干扰duriation=0;//清零的目的是便于下次重新计数keyState=KEY_STATE_RELEASE;//重新返回按键松开的状态returnNOT_PRESSED;}break;caseKEY_STATE_PRESSED:if(readKey()==0)//如果按键松开{keyState=KEY_STATE_RELEASE;//回到按键松开的状态}returnNOT_PRESSED;break;default:keyState=KEY_STATE_RELEASE;returnNOT_PRESSED;}}该程序也可经过扩展,实现判断按键双击、长按等功能。只需增加相应的状态和转移条件即可。5.示例二:通过有限状态机实现的闹钟程序最近正在制作一个闹钟。这个闹钟支持播放MP3格式的闹钟声6,支持贪睡模式,同时还有一些功能打算以后再添加上。为了使程序逻辑更加清晰,也为了更方便地添加新功能,我打算采用有限状态机实现。相关程序如下:#includeApp_Alarm.h#includeUSART1.h#includestdio.h#includediag/Trace.h/*相关常量定义*/#defineALARM_MUSIC_END0//闹钟音乐播放完毕#defineFORMAT_OK0//格式正确#defineFORMAT_ERROR(-1)//格式错误/*输入信息定义*作为函数的返回值供函数getInput()使用*getInput()将获取并返回键盘或触摸屏等设备中输入的控制命令或闹钟时间值*/#defineINPUT_ERROR(-1)//输入格式错误#defineINPUT_CANCEL(-2)//输入了「取消」命令#defineINPUT_SNOOZE(-3)//输入了「小睡」命令#defineINPUT_ALARM_ON(-4)//输入了「打开闹钟」命令#defineNO_INPUT(-10)//没有输入/*输出信息定义*作为为函数的参数供函数displayMessege()使用*displayMessege()用于在显示屏上显示相关的提示信息*/#defineMESSEGE_SET_ALARM_TIME(0)//提示:设置闹钟时间#defineMESSEGE_CLEAR(1)//提示:已取消#defineMESSEGE_ALARM_IS_ON(2)//提示:闹钟已打开#defineMESSEGE_WAITING(3)//提示:等待闹钟响起#defineMESSEGE_SET_SNOOZE_TIME(4)//提示:设置小睡时间#defineMESSEGE_GET_UP(5)//提示:该起床了/*闹钟的状态*/enumalarmStates{ALARM_OFF,//闹钟关闭SET_ALARM_TIME,//设置闹钟时间WATING_FOR_ALARM,//等待闹钟响起P
本文标题:有限状态机在单片机编程中的应用
链接地址:https://www.777doc.com/doc-6891711 .html