最近一直在看状态机相关的书籍和文章,总感觉有些东西需要写下来,算是总结吧。
想到哪里写到哪里,看客勿怪,有不对的地方,清批评指正。
对于状态机的理解,最初可能是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;
}
解决思路:
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编程的感觉。。。