当前位置: 首页 > 工具软件 > 理解机 > 使用案例 >

状态机理解

闻人博
2023-12-01

        最近一直在看状态机相关的书籍和文章,总感觉有些东西需要写下来,算是总结吧。

想到哪里写到哪里,看客勿怪,有不对的地方,清批评指正。


       对于状态机的理解,最初可能是if/else和switch/case,它们直观易懂,根据条件或者

状态值去执行相应的动作,这个是我理解的状态机的最初形式。由于初学,程序规模很小,

甚至就是单任务的,那么这个形式是可以应付的,绝对是物超所值。我们乐此不彼的去套用

,随着程序规模和业务逻辑的增长,某天我们突然发现自己的代码变成了意大利面条,于是

我们不得不摒弃这种直观的形式,去寻求其它的形式。比如消息地图,根据不同的消息去执

行相应的处理函数。再升级,比如消息驱动型编程,根据消息去执行相应的函数。这些是不

是都是状态机?


        状态机不仅仅只在函数内部可用,函数与函数之间的相互对应,也可以看做是状态机。

基于OS编程,我们只把自己的TASK看做是单任务,独占MCU即可,因为OS承担了任务调度、

资源分配、信号量同步等等各种服务。假如我们打开思维,把每个函数都看做一个state,每个

state执行完了之后,主动放弃MCU的执行权,并且指定自己的下一个state,那么一个大的TASK

就可以由很多个state协作完成。如果每个任务都是按着这个思维逻辑去写,那么我们的系统也是

多任务的。只不过,任务的调度和分解,由我们自己决定,每个state都是“RTC”的,保证每个state

的“粒度”在合适的大小,那么我们的系统实时性依然很高。这种编程思维是不是和基于OS的编程思维

很像?只不过OS帮助我们做了下层建筑,这就相当于我们得到了“付费服务”(ROM、RAM、版权费)。

而全状态机编程需要我们的大脑去组织和实现这些东西,这也是我们嵌入式工程师的价值所在。


        我再大胆的设想下,如果有一个MCU在跑OS,我建立几个平级的线程(按时间调度),每个线程

都跑各自的状态机,是不是就可以认为这个MCU是个“多核”MCU?

        凑不够字数,就这么多吧。


2017.09.06 20:11

今天坐地铁想了一路,什么是“全状态机”编程,这个问题。首先从软件编程中常见的“阻塞”说起,软件中常见的阻塞主要分为三种:

1、时间延时;

2、外部交互等待,比如:操作某个外设的忙等待;

3、复杂的“指令集合”,比如:复杂算法,排序,滤波,FFT等;


    时间延时的消除,一般用软件定时器解决,这个解决思路,没什么复杂的,不再过多介绍。

    “IO”口操作的忙等待,也是一直阻塞,而且会严重影响实时响应,比如:flash,LCD,等。我一直在思考这类问题如何解决,通过看以前代码,我才知道,原来驱动写的都是“阻塞”式,本身就没有办法用状态机解决,必须把驱动改写为状态机形式。

    复杂算法就需要分解了,每次执行一个片段退出,再进来再执行另外一个片段,这样需要一个static struct来充当临时栈,以牺牲RAM来换取程序的分片执行。或者把复杂算法放到main()任务线程,其他任务放到中断线程,达到抢占的目的。


有句话这么说的:让MCU等待某件事情的完成,是件多么傻的事情。目前总算有了些体会。。。。。。



2017.09.07 

昨天说了下“全状态机”编程,没有介绍怎么去实现。今天接着这个话题展开,主要用flash的例子来说说。

首先来看看flash阻塞的代码,手册里面有这样一句话:

//The Write In Progress (WIP) bit indicates whether the memory 
//is busy in program/erase/write status register progress.

由于flash内部操作比起MCU执行指令要慢的多,因此MCU每次执行完一个操作需要等待,

一般操作是这样的:

/*****************************************************************************
* Function:         SPI_FLASH_WaitForWriteEnd
* PreCondition:	    None
* Input:	    void
* Output:	    void
* Side Effects:	    None
* Overview:	    等待操作完成
* Note:		    None
*****************************************************************************/
static  void SPI_FLASH_WaitForWriteEnd(void)
{
	unsigned char FLASH_Status = 0;
	Flash_Csn_PORT	= L_PIN_PORT;
	Flash_Simulation_SendByte(ReadStatusRegister);
	do
	{	
	    Flash_Simulation_SendByte(Dummy_Byte);//为什么要发送A5
	    FLASH_Status = Flash_Simulation_ReceiveByte();
	} 
//The Write In Progress (WIP) bit indicates whether the memory 
//is busy in program/erase/write status register progress.
	while((FLASH_Status & WriteStatusRegister) == 1);
	Flash_Csn_PORT	= H_PIN_PORT;
}

看到do{}while()循环没有,这就是阻塞式的编程思路,它严重影响了系统的实时响应。


解决思路:

1、OS思路

基于OS的思路,每个任务都可以是阻塞式的,因为OS调度器能够实现“抢占”,保证最高优先级的任务响应。

2、状态机

把驱动改写为状态机形式,查询现在是否空闲,如果空闲则执行下一个操作,否则退出,让出执行权。

现在说说状态机方案:

1、驱动改写为状态机方案,返回当前操作状态,上层APP根据返回状态决定下一步动作;

2、利用管道技术,拆分为入队和出队线程,出队放到main()循环里面,入队由USER控制;

3、利用协作式调度器,USER一单启动操作,那么就有立即返回,同时操作在后台执行,并且执行完后自动注销;


目前正在重构以前代码,主要利用后面两种思路去解决,待有结果,再发上来,或者您有更好的思路,请留言。


2017.09.18


最近做东西要用到delay,按着以前的架构,需要task返回false(告诉内核不要自动添加到任务就绪队列),同时用

UESR_NEW_SOFTWARE_TIMER_SERVICE()把任务添加到延时队列,怎么看都怎么感觉别扭,于是想模仿OS

的sys_delay()功能。

思路很简单,如下:

1、获得当前执行任务的this指针;

2、OS_delay()利用this指针,把当前任务添加到延时队列;

3、OS_delay()告诉任务调度器忽略task的返回值,不再自动添加到任务就绪队列;

以一个非阻塞的led闪烁灯为例:


/*****************************************************************************
* Function:         led_frist
* PreCondition:	    None
* Input:			void
* Output:			void
* Side Effects:	    None
* Overview:			指示灯上电闪烁
* Note:				None
*****************************************************************************/
static  bool    led_frist(void* pRam)
{
    static  enum{
        LED_FRIST_FSM_START=0,
        LED_FRIST_FSM_ON_1,
        LED_FRIST_FSM_OFF_1,
        LED_FRIST_FSM_ON_2,
        LED_FRIST_FSM_OFF_2,
        LED_FRIST_FSM_ON_3,
        LED_FRIST_FSM_OFF_3,
    }s_tState = LED_FRIST_FSM_START;

    #define RESET_LED_FRIST_FSM()   {s_tState = LED_FRIST_FSM_START;}
    
    switch(s_tState){
    case LED_FRIST_FSM_START:
        s_tState = LED_FRIST_FSM_ON_1;
        //break;
    case LED_FRIST_FSM_ON_1:
        LED_RED_ON();
        LED_GREEN_ON();
        USER_DELAY_TASK(500);
        s_tState = LED_FRIST_FSM_OFF_1;
        break;
    case LED_FRIST_FSM_OFF_1:
        LED_RED_OFF();
        LED_GREEN_OFF();
        USER_DELAY_TASK(500);
        s_tState = LED_FRIST_FSM_ON_2;
        break;
    case LED_FRIST_FSM_ON_2:
        LED_RED_ON();
        LED_GREEN_ON();
        USER_DELAY_TASK(500);
        s_tState = LED_FRIST_FSM_OFF_2;
        break;
    case LED_FRIST_FSM_OFF_2:
        LED_RED_OFF();
        LED_GREEN_OFF();
        USER_DELAY_TASK(500);
        s_tState = LED_FRIST_FSM_ON_3;
        break;
    case LED_FRIST_FSM_ON_3:
        LED_RED_ON();
        LED_GREEN_ON();
        USER_DELAY_TASK(500);
        s_tState = LED_FRIST_FSM_OFF_3;
        break;
    case LED_FRIST_FSM_OFF_3:
        LED_RED_OFF();
        LED_GREEN_OFF();
        //USER_DELAY_TASK(500);
        //break;
    default:
        RESET_LED_FRIST_FSM();
        return false;
    }
    return  true;
}


怎么样,有没有类似OS编程的感觉。。。




 类似资料: