0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式状态机编程-QP状态机框架基本知识介绍

lilihe92 来源:CSDN 2023-10-18 09:05 次阅读

状态机基本术语

现态:是指当前所处的状态。条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。

动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

56af7e5e-6d01-11ee-939d-92fbcf53809c.png

传统有限状态机Fsm实现方法

56bbbc6e-6d01-11ee-939d-92fbcf53809c.png

如图,是一个定时计数器,计数器存在两种状态,一种为设置状态,一种为计时状态

设置状态

“+” “-” 按键对初始倒计时进行设置当计数值设置完成,点击确认键启动计时 ,即切换到计时状态

计时状态

按下“+” “-” 会进行密码的输入。“+”表示1 ,“-”表示输入0 ,密码共有4位

确认键:只有输入的密码等于默认密码,按确认键才能停止计时,否则计时直接到零,并执行相关操作

嵌套switch

/***************************************
1.列出所有的状态
***************************************/
typedefenum{
SETTING,
TIMING
}STATE_TYPE;
/***************************************
2.列出所有的事件
***************************************/
typedefenum{
UP_EVT,
DOWN_EVT,
ARM_EVT,
TICK_EVT
}EVENT_TYPE;
/***************************************
3.定义和状态机相关结构
***************************************/
structbomb
{
uint8_tstate;
uint8_ttimeout;
uint8_tcode;
uint8_tdefuse_code;
}bomb1;
/***************************************
4.初始化状态机
***************************************/
voidbomb1_init(void)
{
bomb1.state=SETTING;
bomb1.defuse_code=6;//0110
}
/***************************************
5.状态机事件派发
***************************************/
voidbomb1_fsm_dispatch(EVENT_TYPEevt,void*param)
{
switch(bomb1.state)
{
caseSETTING:
{
switch(evt)
{
caseUP_EVT://"+"按键按下事件
if(bomb1.timeout< 60)  ++bomb1.timeout;
                          bsp_display(bomb1.timeout);
                      break;
                      case DOWN_EVT:  // "-"   按键按下事件
                          if(bomb1.timeout >0)--bomb1.timeout;
bsp_display(bomb1.timeout);
break;
caseARM_EVT://"确认"按键按下事件
bomb1.state=TIMING;
bomb1.code=0;
break;
}
}break;
caseTIMING:
{
switch(evt)
{
caseUP_EVT://"+"按键按下事件
bomb1.code=(bomb1.code<<1) |0x01;
                      break;
                      case DOWN_EVT:  // "-"   按键按下事件
                          bomb1.code = (bomb1.code <<1); 
                      break;
                      case ARM_EVT:   // "确认" 按键按下事件
                          if(bomb1.code == bomb1.defuse_code){
                              bomb1.state = SETTING;
                          }
                          else{
                           bsp_display("bomb!")
                          }
                      break;
                      case TICK_EVT:
                          if(bomb1.timeout)
                          {
                              --bomb1.timeout;
                              bsp_display(bomb1.timeout);
                          }
                          if(bomb1.timeout == 0)
                          {
                              bsp_display("bomb!")
                          }
                      break;
                  }   
              }break;
          }
      }
56bf49d8-6d01-11ee-939d-92fbcf53809c.png

优点:

简单,代码阅读连贯,容易理解

缺点

当状态或事件增多时,代码状态函数需要经常改动,状态事件处理函数会代码量会不断增加

状态机没有进行封装,移植性差。

没有实现状态的进入和退出的操作。进入和退出在状态机中尤为重要

进入事件:只会在刚进入时触发一次,主要作用是对状态进行必要的初始化

退出事件:只会在状态切换时触发一次 ,主要的作用是清除状态产生的中间参数,为下次进入提供干净环境

状态表

二维状态转换表

状态机可以分为状态和事件 ,状态的跃迁都是受事件驱动的,因此可以通过一个二维表格来表示状态的跃迁。

56cfd348-6d01-11ee-939d-92fbcf53809c.png

(*) 仅当( code == defuse_code) 时才发生到setting 的转换。

/*1.列出所有的状态*/
enum
{
SETTING,
TIMING,
MAX_STATE
};
/*2.列出所有的事件*/
enum
{
UP_EVT,
DOWN_EVT,
ARM_EVT,
TICK_EVT,
MAX_EVT
};

/*3.定义状态表*/
typedefvoid(*fp_state)(EVT_TYPEevt,void*param);
staticconstfp_statebomb2_table[MAX_STATE][MAX_EVENT]=
{
{setting_UP,setting_DOWN,setting_ARM,null},
{setting_UP,setting_DOWN,setting_ARM,timing_TICK}
};

structbomb_t
{
constfp_stateconst*state_table;/*theState-Table*/
uint8_tstate;/*thecurrentactivestate*/

uint8_ttimeout;
uint8_tcode;
uint8_tdefuse_code;
};
structbombbomb2=
{
.state_table=bomb2_table;
}
voidbomb2_init(void)
{
bomb2.defuse_code=6;//0110
bomb2.state=SETTING;
}

voidbomb2_dispatch(EVT_TYPEevt,void*param)
{
fp_states=NULL;
if(evt>MAX_EVT)
{
LOG("EVTtypeerror!");
return;
}
s=bomb2.state_table[bomb2.state*MAX_EVT+evt];
if(s!=NULL)
{
s(evt,param);
}
}
/*列出所有的状态对应的事件处理函数*/
voidsetting_UP(EVT_TYPEevt,void*param)
{
if(bomb1.timeout< 60)  ++bomb1.timeout;
          bsp_display(bomb1.timeout);
      }

优点

各个状态面向用户相对独立,增加事件和状态不需要去修改先前已存在的状态事件函数。

可将状态机进行封装,有较好的移植性函数指针的安全转换 , 利用下面的特性,用户可以扩展带有私有属性的状态机和事件而使用统一的基础状态机接口

typedefvoid(*Tran)(structStateTableTag*me,Eventconst*e);
voidBomb2_setting_ARM(Bomb2*me,Eventconst*e);
typedefstructBomb
{
structStateTableTag*me;//必须为第一个成员
uint8_tprivate;
}

缺点

函数粒度太小是最明显的一个缺点,一个状态和一个事件就会产生一个函数,当状态和事件较多时,处理函数将增加很快,在阅读代码时,逻辑分散。

没有实现进入退出动作。

一维状态转换表

56e89856-6d01-11ee-939d-92fbcf53809c.png

实现原理:

56f63c40-6d01-11ee-939d-92fbcf53809c.png

typedefvoid(*fp_action)(EVT_TYPEevt,void*param);

/*转换表基础结构*/
structtran_evt_t
{
EVT_TYPEevt;
uint8_tnext_state;
};
/*状态的描述*/
structfsm_state_t
{
fp_actionenter_action;//进入动作
fp_actionexit_action;//退出动作
fp_actionaction;

tran_evt_t*tran;//转换表
uint8_ttran_nb;//转换表的大小
constchar*name;
}
/*状态表本体*/
#defineARRAY(x)x,sizeof(x)/sizeof(x[0])
conststructfsm_state_tstate_table[]=
{
{setting_enter,setting_exit,setting_action,ARRAY(set_tran_evt),"setting"},
{timing_enter,timing_exit,timing_action,ARRAY(time_tran_evt),"timing"}
};

/*构建一个状态机*/
structfsm
{
conststructstate_t*state_table;/*theState-Table*/
uint8_tcur_state;/*thecurrentactivestate*/

uint8_ttimeout;
uint8_tcode;
uint8_tdefuse_code;
}bomb3;

/*初始化状态机*/
voidbomb3_init(void)
{
bomb3.state_table=state_table;//指向状态表
bomb3.cur_state=setting;
bomb3.defuse_code=8;//1000
}
/*状态机事件派发*/
voidfsm_dispatch(EVT_TYPEevt,void*param)
{
tran_evt_t*p_tran=NULL;

/*获取当前状态的转换表*/
p_tran=bomb3.state_table[bomb3.cur_state]->tran;

/*判断所有可能的转换是否与当前触发的事件匹配*/
for(uint8_ti=0;ievt==evt)//事件会触发转换
{
if(NULL!=bomb3.state_table[bomb3.cur_state].exit_action){
bomb3.state_table[bomb3.cur_state].exit_action(NULL);//执行退出动作
}
if(bomb3.state_table[_tran[i]->next_state].enter_action){
bomb3.state_table[_tran[i]->next_state].enter_action(NULL);//执行进入动作
}
/*更新当前状态*/
bomb3.cur_state=p_tran[i]->next_state;
}
else
{
bomb3.state_table[bomb3.cur_state].action(evt,param);
}
}
}
/*************************************************************************
setting状态相关
************************************************************************/
voidsetting_enter(EVT_TYPEevt,void*param)
{

}
voidsetting_exit(EVT_TYPEevt,void*param)
{

}
voidsetting_action(EVT_TYPEevt,void*param)
{

}
tran_evt_tset_tran_evt[]=
{
{ARM,timing},
}
/*timing状态相关*/

优点

各个状态面向用户相对独立,增加事件和状态不需要去修改先前已存在的状态事件函数。

实现了状态的进入和退出

容易根据状态跃迁图来设计 (状态跃迁图列出了每个状态的跃迁可能,也就是这里的转换表)

实现灵活,可实现复杂逻辑,如上一次状态,增加监护条件来减少事件的数量。可实现非完全事件驱动

缺点

函数粒度较小(比二维小且增长慢),可以看到,每一个状态需要至少3个函数,还需要列出所有的转换关系。

QP嵌入式实时框架

特点

事件驱动型编程

好莱坞原则:和传统的顺序式编程方法例如“超级循环”,或传统的RTOS 的任务不同。绝大多数的现代事件驱动型系统根据好莱坞原则被构造,(Don’t call me; I’ll call you.)

面向对象

类和单一继承。

5706baca-6d01-11ee-939d-92fbcf53809c.png

工具

QM ,一个通过UML类图来描述状态机的软件,并且可以自动生成C代码:

5714aa04-6d01-11ee-939d-92fbcf53809c.png

QS软件追踪工具:

571f7970-6d01-11ee-939d-92fbcf53809c.png57254d3c-6d01-11ee-939d-92fbcf53809c.png

QEP实现有限状态机Fsm

573cb9ea-6d01-11ee-939d-92fbcf53809c.png

/*qevent.h----------------------------------------------------------------*/
typedefstructQEventTag
{
QSignalsig;
uint8_tdynamic_;
}QEvent;
/*qep.h-------------------------------------------------------------------*/
typedefuint8_tQState;/*statusreturnedfromastate-handlerfunction*/
typedefQState(*QStateHandler)(void*me,QEventconst*e);/*argumentlist*/
typedefstructQFsmTag/*FiniteStateMachine*/
{
QStateHandlerstate;/*currentactivestate*/
}QFsm;

#defineQFsm_ctor(me_,initial_)((me_)->state=(initial_))
voidQFsm_init(QFsm*me,QEventconst*e);
voidQFsm_dispatch(QFsm*me,QEventconst*e);

#defineQ_RET_HANDLED((QState)0)
#defineQ_RET_IGNORED((QState)1)
#defineQ_RET_TRAN((QState)2)
#defineQ_HANDLED()(Q_RET_HANDLED)
#defineQ_IGNORED()(Q_RET_IGNORED)

#defineQ_TRAN(target_)(((QFsm*)me)->state=(QStateHandler)(target_),Q_RET_TRAN)

enumQReservedSignals
{
Q_ENTRY_SIG=1,
Q_EXIT_SIG,
Q_INIT_SIG,
Q_USER_SIG
};

/*fileqfsm_ini.c---------------------------------------------------------*/
#include"qep_port.h"/*theportoftheQEPeventprocessor*/
#include"qassert.h"/*embeddedsystems-friendlyassertions*/
voidQFsm_init(QFsm*me,QEventconst*e)
{
(*me->state)(me,e);/*executethetop-mostinitialtransition*/
/*enterthetarget*/
(void)(*me->state)(me,&QEP_reservedEvt_[Q_ENTRY_SIG]);
}
/*fileqfsm_dis.c---------------------------------------------------------*/
voidQFsm_dispatch(QFsm*me,QEventconst*e)
{
QStateHandlers=me->state;/*savethecurrentstate*/
QStater=(*s)(me,e);/*calltheeventhandler*/
if(r==Q_RET_TRAN)/*transitiontaken?*/
{
(void)(*s)(me,&QEP_reservedEvt_[Q_EXIT_SIG]);/*exitthesource*/
(void)(*me->state)(me,&QEP_reservedEvt_[Q_ENTRY_SIG]);/*entertarget*/
}
}
实现上面定时器例子
#include"qep_port.h"/*theportoftheQEPeventprocessor*/
#include"bsp.h"/*boardsupportpackage*/

enumBombSignals/*allsignalsfortheBombFSM*/
{
UP_SIG=Q_USER_SIG,
DOWN_SIG,
ARM_SIG,
TICK_SIG
};
typedefstructTickEvtTag
{
QEventsuper;/*derivefromtheQEventstructure*/
uint8_tfine_time;/*thefine1/10scounter*/
}TickEvt;

typedefstructBomb4Tag
{
QFsmsuper;/*derivefromQFsm*/
uint8_ttimeout;/*numberofsecondstillexplosion*/
uint8_tcode;/*currentlyenteredcodetodisarmthebomb*/
uint8_tdefuse;/*secretdefusecodetodisarmthebomb*/
}Bomb4;

voidBomb4_ctor(Bomb4*me,uint8_tdefuse);
QStateBomb4_initial(Bomb4*me,QEventconst*e);
QStateBomb4_setting(Bomb4*me,QEventconst*e);
QStateBomb4_timing(Bomb4*me,QEventconst*e);
/*--------------------------------------------------------------------------*/
/*theinitialvalueofthetimeout*/
#defineINIT_TIMEOUT10
/*..........................................................................*/
voidBomb4_ctor(Bomb4*me,uint8_tdefuse){
QFsm_ctor_(&me->super,(QStateHandler)&Bomb4_initial);
me->defuse=defuse;/*thedefusecodeisassignedatinstantiation*/
}
/*..........................................................................*/
QStateBomb4_initial(Bomb4*me,QEventconst*e){
(void)e;
me->timeout=INIT_TIMEOUT;
returnQ_TRAN(&Bomb4_setting);
}
/*..........................................................................*/
QStateBomb4_setting(Bomb4*me,QEventconst*e){
switch(e->sig){
caseUP_SIG:{
if(me->timeout< 60) {
          ++me->timeout;
BSP_display(me->timeout);
}
returnQ_HANDLED();
}
caseDOWN_SIG:{
if(me->timeout>1){
--me->timeout;
BSP_display(me->timeout);
}
returnQ_HANDLED();
}
caseARM_SIG:{
returnQ_TRAN(&Bomb4_timing);/*transitionto"timing"*/
}
}
returnQ_IGNORED();
}
/*..........................................................................*/
voidBomb4_timing(Bomb4*me,QEventconst*e){
switch(e->sig){
caseQ_ENTRY_SIG:{
me->code=0;/*clearthedefusecode*/
returnQ_HANDLED();
}
caseUP_SIG:{
me->code<<= 1;
         me->code|=1;
returnQ_HANDLED();
}
caseDOWN_SIG:{
me->code<<= 1;
         return Q_HANDLED();
        }
        case ARM_SIG: {
         if (me->code==me->defuse){
returnQ_TRAN(&Bomb4_setting);
}
returnQ_HANDLED();
}
caseTICK_SIG:{
if(((TickEvtconst*)e)->fine_time==0){
--me->timeout;
BSP_display(me->timeout);
if(me->timeout==0){
BSP_boom();/*destroythebomb*/
}
}
returnQ_HANDLED();
}
}
returnQ_IGNORED();
}

优点

采用面向对象的设计方法,很好的移植性

实现了进入退出动作

合适的粒度,且事件的粒度可控

状态切换时通过改变指针,效率高

可扩展成为层次状态机

缺点

对事件的定义以及事件粒度的控制是设计的最大难点,如串口接收到一帧数据,这些变量的更新单独作为某个事件,还是串口收到数据作为一个事件。再或者显示屏,如果使用此种编程方式,如何设计事件。

QP 实现层次状态机 Hsm简介

57487564-6d01-11ee-939d-92fbcf53809c.png

初始化:

5751709c-6d01-11ee-939d-92fbcf53809c.png

初始化层次状态机的实现:在初始化时,用户所选取的状态永远是最底层的状态,如上图,我们在计算器开机后,应该进入的是开始状态,这就涉及到一个问题,由最初top(顶状态)到begin 是有一条状态切换路径的,当我们设置状态为begin如何搜索这条路径成为关键(知道了路径才能正确的进入begin,要执行路径中过渡状态的进入和退出事件)

voidQHsm_init(QHsm*me,QEventconst*e)
{
Q_ALLEGE((*me->state)(me,e)==Q_RET_TRAN);
t=(QStateHandler)&QHsm_top;/*HSMstartsinthetopstate*/
do{/*drillintothetarget...*/
QStateHandlerpath[QEP_MAX_NEST_DEPTH_];
int8_tip=(int8_t)0;/*transitionentrypathindex*/
path[0]=me->state;/*这里的状态为begin*/

/*通过执行空信号,从底层状态找到顶状态的路径*/
(void)QEP_TRIG_(me->state,QEP_EMPTY_SIG_);
while(me->state!=t){
path[++ip]=me->state;
(void)QEP_TRIG_(me->state,QEP_EMPTY_SIG_);
}
/*切换为begin*/
me->state=path[0];/*restorethetargetoftheinitialtran.*/
/*钻到最底层的状态,执行路径中的所有进入事件*/
Q_ASSERT(ip< (int8_t)QEP_MAX_NEST_DEPTH_);
      do { /* retrace the entry path in reverse (desired) order... */
          QEP_ENTER_(path[ip]); /* enter path[ip] */
       } while ((--ip) >=(int8_t)0);

t=path[0];/*currentstatebecomesthenewsource*/
}while(QEP_TRIG_(t,Q_INIT_SIG)==Q_RET_TRAN);
me->state=t;
}

状态切换:

575d232e-6d01-11ee-939d-92fbcf53809c.png

/*.................................................................*/
QStateresult(Calc*me,QEventconst*e)
{
switch(e->sig)
{you
caseENTER_SIG:{
break;
}
caseEXIT_SIG:{
break;
}
caseC_SIG:
{
printf("clear");
returnQ_HANDLED();
}
caseB_SIG:
{
returnQ_TRAN(&begin);
}
}
returnQ_SUPER(&reday);
}
/*.ready为result和begin的超状态................................................*/
QStateready(Calc*me,QEventconst*e)
{
switch(e->sig)
{
caseENTER_SIG:{
break;
}
caseEXIT_SIG:{
break;
}
caseOPER_SIG:
{
returnQ_TRAN(&opEntered);
}
}
returnQ_SUPER(&on);
}



voidQHsm_dispatch(QHsm*me,QEventconst*e)
{
QStateHandlerpath[QEP_MAX_NEST_DEPTH_];
QStateHandlers;
QStateHandlert;
QStater;
t=me->state;/*savethecurrentstate*/
do{/*processtheeventhierarchically...*/
s=me->state;
r=(*s)(me,e);/*invokestatehandlers*/
}while(r==Q_RET_SUPER);//当前状态不能处理事件,直到找到能处理事件的状态

if(r==Q_RET_TRAN){/*transitiontaken?*/
int8_tip=(int8_t)(-1);/*transitionentrypathindex*/
int8_tiq;/*helpertransitionentrypathindex*/
path[0]=me->state;/*savethetargetofthetransition*/
path[1]=t;
while(t!=s){/*exitcurrentstatetotransitionsources...*/
if(QEP_TRIG_(t,Q_EXIT_SIG)==Q_RET_HANDLED){/*exithandled?*/
(void)QEP_TRIG_(t,QEP_EMPTY_SIG_);/*findsuperstateoft*/
}
t=me->state;/*me->stateholdsthesuperstate*/
}
...
}
me->state=t;/*setnewstateorrestorethecurrentstate*/
}
576e9582-6d01-11ee-939d-92fbcf53809c.pngimg
t=path[0];/*targetofthetransition*/
if(s==t){/*(a)checksource==target(transitiontoself)*/
QEP_EXIT_(s)/*exitthesource*/
ip=(int8_t)0;/*enterthetarget*/
}
else{
(void)QEP_TRIG_(t,QEP_EMPTY_SIG_);/*superstateoftarget*/
t=me->state;
if(s==t){/*(b)checksource==target->super*/
ip=(int8_t)0;/*enterthetarget*/
}
else{
(void)QEP_TRIG_(s,QEP_EMPTY_SIG_);/*superstateofsrc*/
/*(c)checksource->super==target->super*/
if(me->state==t){
QEP_EXIT_(s)/*exitthesource*/
ip=(int8_t)0;/*enterthetarget*/
}
else{
/*(d)checksource->super==target*/
if(me->state==path[0]){
QEP_EXIT_(s)/*exitthesource*/
}
else{/*(e)checkrestofsource==target->super->super..
*andstoretheentrypathalongtheway*/
....

QP实时框架的组成

577802ca-6d01-11ee-939d-92fbcf53809c.png577c4326-6d01-11ee-939d-92fbcf53809c.png

内存管理

使用内存池,对于低性能mcu,内存极为有限,引入内存管理主要是整个架构中,是以事件作为主要的任务通信手段,且事件是带参数的,可能相同类型的事件会多次触发,而事件处理完成后,需要清除事件,无法使用静态的事件,因此是有必要为不同事件创建内存池的。

对于不同块大小的内存池,需要考虑的是每个块的起始地址对齐问题。在进行内存池初始化时,我们是根据blocksize+header大小来进行划分内存池的。假设一个2字节的结构,如果以2来进行划分,假设mcu 4字节对齐,那么将有一半的结构起始地址无法对齐,这时需要为每个块预留空间,保证每个块的对齐。

57909632-6d01-11ee-939d-92fbcf53809c.png

事件队列

每一个活动对象维护一个事件队列,事件都是由基础事件派生的,不同类型的事件只需要将其基础事件成员添加到活动对象的队列中即可,最终在取出的时候通过一个强制转换便能获得附加的参数。

57a947a4-6d01-11ee-939d-92fbcf53809c.png

事件派发

直接事件发送:

QActive_postLIFO()

发行订阅事件发送:

竖轴表示信号(为事件的基类)

活动对象支持64个优先级,每一个活动对象要求拥有唯一优先级

通过优先级的bit位来表示某个事件被哪些活动对象订阅,并在事件触发后根据优先级为活动对象派发事件。

57b3317e-6d01-11ee-939d-92fbcf53809c.png

定时事件

非有序链表:

57c13ca6-6d01-11ee-939d-92fbcf53809c.png

合作式调度器QV:

57d5d04e-6d01-11ee-939d-92fbcf53809c.png

QP nano的简介

完全支持层次式状态嵌套,包括在最多4 层状态嵌套情况下,对任何状态转换拓扑的可保证的进入/ 退出动作

支持高达8 个并发执行的,可确定的,线程安全的事件队列的活动对象57

支持一个字节宽( 255 个信号)的信号,和一个可伸缩的参数,它可被配置成0 (没有参数), 1 , 2 或4 字节

使用先进先出FIFO排队策略的直接事件派发机制

每个活动对象有一个一次性时间事件(定时器),它的可配置动态范围是0(没有时间事件) , 1 , 2 或4 字节

内建的合作式vanilla 内核

内建的名为QK-nano 的可抢占型RTC内核(见第六章6.3.8节)

带有空闲回调函数的低功耗架构,用来方便的实现节省功耗模式。

在代码里为流行的低端CPU架构的C编译器的非标准扩展进行了准备(例如,在代码空间分配常数对象,可重入函数,等等)

基于断言的错误处理策略

代码风格:

57de372a-6d01-11ee-939d-92fbcf53809c.png57e23e24-6d01-11ee-939d-92fbcf53809c.png57fbd0fa-6d01-11ee-939d-92fbcf53809c.png581d9000-6d01-11ee-939d-92fbcf53809c.png58294328-6d01-11ee-939d-92fbcf53809c.png








审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 驱动器
    +关注

    关注

    51

    文章

    7308

    浏览量

    142963
  • 嵌入式
    +关注

    关注

    4982

    文章

    18281

    浏览量

    288465
  • 计数器
    +关注

    关注

    32

    文章

    2121

    浏览量

    92983
  • RTOS
    +关注

    关注

    20

    文章

    776

    浏览量

    118796
  • 状态机
    +关注

    关注

    2

    文章

    486

    浏览量

    27168

原文标题:嵌入式状态机编程-QP状态机框架

文章出处:【微信号:最后一个bug,微信公众号:最后一个bug】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式软件开发中常用的状态机编程实现

    嵌入式软件开发中,状态机编程是一个十分重要的编程思想,它也是嵌入式开发中一个常用的编程
    发表于 09-06 10:25 1408次阅读

    raw os 之状态机编程

    事件的操作系统QP 具有异曲同工之妙。状态机编程的其中一个优势是所有的任务可以共享一个栈,这样可以避免传统的操作系统一个任务一个栈空间的局限。所以能普遍被资源短缺的单片系统采用,另外
    发表于 02-27 14:35

    基于状态机嵌入式系统开发

    给大家分享下,基于状态机嵌入式系统开发,慢慢看吧
    发表于 12-22 19:44

    什么是状态机状态机是如何编程的?

    什么是状态机状态机是如何编程的?
    发表于 10-20 07:43

    嵌入式状态机编程的概念是什么

    干货 | 嵌入式状态机编程干货篇文章描述了基本的状态机编程概念,感觉还可以。如果在搭上事件驱动框架
    发表于 12-22 06:25

    LSM6DSOX嵌入式有限状态机的使用和配置的信息

    本文档旨在提供有关 ST 的 LSM6DSOX 嵌入式有限状态机的使用和配置的信息。LSM6DSOX 可配置为由用户定义的运动模式激活中断信号生成。为此,最多可以为运动检测独立编程 16 组
    发表于 09-06 06:36

    ISM330DHCX嵌入式有限状态机的使用和配置信息

    本文档旨在提供有关 ST 的 ISM330DHCX嵌入式有限状态机的使用和配置的信息。ISM330DHCX 可配置为由用户定义的运动模式激活中断信号生成。为此,最多可以为运动检测独立编程 16 组
    发表于 09-08 08:00

    LSM6DSOX嵌入式有限状态机的使用和配置的信息

    本文档旨在提供有关 ST 的 LSM6DSOX 嵌入式有限状态机的使用和配置的信息。LSM6DSOX 可配置为由用户定义的运动模式激活中断信号生成。为此,最多可以为运动检测独立编程 16 组
    发表于 09-13 07:33

    嵌入式软件中状态机的抽象与实现

    文中提出了 在嵌入式软件中把状态机作为一个独立模块从控制模块中抽象出来的思想 , 描述了 抽象出来的状态机模块 。 并介绍了 如何将这种状态机
    发表于 03-22 15:47 1次下载

    什么是状态机状态机5要素

    玩单片机还可以,各个外设也都会驱动,但是如果让你完整的写一套代码时,却无逻辑与框架可言。这说明编程还处于比较低的水平,你需要学会一种好的编程框架或者一种
    的头像 发表于 07-27 11:23 1.9w次阅读
    什么是<b class='flag-5'>状态机</b>?<b class='flag-5'>状态机</b>5要素

    当单片机遇到状态机(一) QP框架的入门

    前言前些日子在微信上看到李肖遥的公众号,里面系统讲述了QP框架,我很有感触。我用QP框架很多年了,一开始是使用QM和QPC++,到后来抛弃了QM,直接使用QPC裸写程序,到后来自己写
    发表于 12-14 19:00 8次下载
    当单片机遇到<b class='flag-5'>状态机</b>(一) <b class='flag-5'>QP</b><b class='flag-5'>框架</b>的入门

    当单片机遇到状态机 入门QP

    当单片机遇到状态机前言前些日子在微信上看到李肖遥的公众号,里面系统讲述了QP框架,我很有感触。我用QP框架很多年了,一开始是使用QM和QPC
    发表于 01-17 12:03 9次下载
    当单片机遇到<b class='flag-5'>状态机</b> 入门<b class='flag-5'>QP</b>

    嵌入式状态机的设置

    状态机嵌入式软件中随处可见,可能你会说状态机有什么难的,不就是 switch 吗?
    的头像 发表于 11-02 09:04 852次阅读

    嵌入式状态机编程优点分析

    嵌入式状态机编程是真的好用,写出来的程序结构非常清晰!所以平时用的也比较多。
    的头像 发表于 02-25 16:21 642次阅读

    嵌入式状态机的设计与实现

    嵌入式状态机是一种常用的软件设计模式,它能够提高代码的可读性和可维护性。状态机是一个抽象的概念,它描述了一个系统或者组件的不同状态以及在不同状态
    的头像 发表于 04-14 11:55 1126次阅读