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

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

3天内不再提示

嵌入式中状态机的设置

strongerHuang 来源:嵌入式大杂烩 作者:嵌入式大杂烩 2022-11-02 09:04 次阅读

状态机在嵌入式软件中随处可见,可能你会说状态机有什么难的,不就是switch 吗?

switch仅仅是最基础的一个点,关于状态机的更多操作,或许你都没有见过,下面分享几种实现方法。

状态机基本术语

现态:是指当前所处的状态。

条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

be7e6e58-5a49-11ed-a3b6-dac502259ad0.png

传统有限状态机FSM

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

设置状态

“+” “-” 按键对初始倒计时进行设置

当计数值设置完成,点击确认键启动计时 ,即切换到计时状态

计时状态

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

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

be883bc2-5a49-11ed-a3b6-dac502259ad0.png

嵌套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;
  }
}
be924d88-5a49-11ed-a3b6-dac502259ad0.png

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

缺点

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

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

没有实现状态的进入和退出的操作。进入和退出在状态机中尤为重要。进入事件:只会在刚进入时触发一次,主要作用是对状态进行必要的初始化。退出事件:只会在状态切换时触发一次 ,主要的作用是清除状态产生的中间参数,为下次进入提供干净环境

状态表

二维状态转换表

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

beafb7b0-5a49-11ed-a3b6-dac502259ad0.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);
}

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

一维状态转换表

becaa9da-5a49-11ed-a3b6-dac502259ad0.png

实现原理:beef2878-5a49-11ed-a3b6-dac502259ad0.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.)

面向对象

类和单一继承

bf24666e-5a49-11ed-a3b6-dac502259ad0.png

工具

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

bf2e598a-5a49-11ed-a3b6-dac502259ad0.png

QS软件追踪工具

bfaac024-5a49-11ed-a3b6-dac502259ad0.pngc0970826-5a49-11ed-a3b6-dac502259ad0.png

QEP实现有限状态机Fsm

c0a765fe-5a49-11ed-a3b6-dac502259ad0.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 实现层次状态机

c0b963e4-5a49-11ed-a3b6-dac502259ad0.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;
}

状态切换

c0c03746-5a49-11ed-a3b6-dac502259ad0.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*/
}
c0cd54c6-5a49-11ed-a3b6-dac502259ad0.png
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实时框架的组成

c0dde1ba-5a49-11ed-a3b6-dac502259ad0.pngc0f23a16-5a49-11ed-a3b6-dac502259ad0.png

内存管理

使用内存池,对于低性能mcu,内存极为有限,引入内存管理主要是整个架构中,是以事件作为主要的任务通信手段,且事件是带参数的,可能相同类型的事件会多次触发,而事件处理完成后,需要清除事件,无法使用静态的事件,因此是有必要为不同事件创建内存池的。对于不同块大小的内存池,需要考虑的是每个块的起始地址对齐问题。在进行内存池初始化时,我们是根据blocksize+header大小来进行划分内存池的。假设一个2字节的结构,如果以2来进行划分,假设mcu 4字节对齐,那么将有一半的结构起始地址无法对齐,这时需要为每个块预留空间,保证每个块的对齐。

c0fc094c-5a49-11ed-a3b6-dac502259ad0.png

事件队列

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

c1066086-5a49-11ed-a3b6-dac502259ad0.png

事件派发

直接事件发送QActive_postLIFO()

发行订阅事件发送竖轴表示信号(为事件的基类)活动对象支持64个优先级,每一个活动对象要求拥有唯一优先级通过优先级的bit位来表示某个事件被哪些活动对象订阅,并在事件触发后根据优先级为活动对象派发事件。

c11c0c6a-5a49-11ed-a3b6-dac502259ad0.png

代码风格

c1368676-5a49-11ed-a3b6-dac502259ad0.png

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

    关注

    5068

    文章

    19008

    浏览量

    302940
  • 软件
    +关注

    关注

    69

    文章

    4762

    浏览量

    87144
  • 计数器
    +关注

    关注

    32

    文章

    2253

    浏览量

    94339

原文标题:嵌入式中状态机的几种骚操作

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

收藏 人收藏

    评论

    相关推荐

    嵌入式状态机的几种大牛才懂的操作

    状态机嵌入式软件随处可见,可能你会说状态机有什么难的,不就是 switch 吗? switch仅仅是最基础的一个点,关于状态机的更多操作
    发表于 11-17 10:41 1440次阅读
    <b class='flag-5'>嵌入式</b><b class='flag-5'>状态机</b>的几种大牛才懂的操作

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

    嵌入式软件开发状态机编程是一个十分重要的编程思想,它也是嵌入式开发中一个常用的编程框架。掌握了状态机编程思想,可以更加逻辑清晰的实现复
    发表于 09-06 10:25 1921次阅读

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

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

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

    干货 | 嵌入式状态机编程干货篇文章描述了基本的状态机编程概念,感觉还可以。如果在搭上事件驱动框架,就可以写一个简单的RTOS了,这个OS可以作为一种不可剥夺型内核。...
    发表于 12-22 06:25

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

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

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

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

    状态机嵌入式系统的应用

    为了便于研究和描述状态机嵌入式前后台软件系统的应用,本文将以移动2G光纤直放站近端的监控软件案例来阐述和说明。
    发表于 05-23 10:48 2133次阅读
    <b class='flag-5'>状态机</b>在<b class='flag-5'>嵌入式</b>系统<b class='flag-5'>中</b>的应用

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

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

    有限状态机嵌入式系统的实现及应用

    如何使嵌入式软件代码更加可靠 增强程序的可维护性 一直以来都是嵌入式程序员追 求的目标。论述了有限状态机的原理和其实现方法;采用状态机方法编写了一个按键扫描程序介绍了
    发表于 03-22 15:40 1次下载

    有限状态机嵌入式软件的应用

    有限状态机嵌入式软件的应用,感兴趣的小伙伴们可以看看。
    发表于 07-26 10:43 27次下载

    基于事件驱动的有限状态机介绍

    EFSM(event finite state machine,事件驱动型有限状态机),是一个基于事件驱动的有限状态机,主要应用于嵌入式设备的软件系统
    的头像 发表于 02-11 10:17 1022次阅读

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

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

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

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

    C语言实现嵌入式状态机简单描述与应用

    嵌入式状态机是一种常用的软件设计模式,它能够提高代码的可读性和可维护性。
    发表于 05-20 14:52 1647次阅读

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

    电子发烧友网站提供《LSM6DSOX嵌入式有限状态机的使用和配置.pdf》资料免费下载
    发表于 07-31 10:55 4次下载
    LSM6DSOX<b class='flag-5'>嵌入式</b>有限<b class='flag-5'>状态机</b>的使用和配置