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

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

3天内不再提示

经典的QP框架原理

strongerHuang 来源:strongerHuang 2023-07-21 09:04 次阅读

状态机是嵌入式开发中常见的一种方法,但状态机的形式有很多,这里给大家分享一下经典的QP框架原理。

状态机基本术语

现态:是指当前所处的状态。条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。 动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。 fe8dd93a-275c-11ee-962d-dac502259ad0.png

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

feb150f4-275c-11ee-962d-dac502259ad0.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;
          }
      }
fec028e0-275c-11ee-962d-dac502259ad0.png

优点:

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

缺点

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

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

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

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

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

状态表

二维状态转换表

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

fec99736-275c-11ee-962d-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);
      }

优点

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

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

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

缺点

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

没有实现进入退出动作。

一维状态转换表

fed1d2ca-275c-11ee-962d-dac502259ad0.png

实现原理:

feda4496-275c-11ee-962d-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.)

面向对象

类和单一继承。

fee33c22-275c-11ee-962d-dac502259ad0.png

工具

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

fefb6022-275c-11ee-962d-dac502259ad0.png

QS软件追踪工具:

ff1f0e1e-275c-11ee-962d-dac502259ad0.pngff3bf04c-275c-11ee-962d-dac502259ad0.png

QEP实现有限状态机Fsm

ff53ea80-275c-11ee-962d-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 实现层次状态机 Hsm简介

ff653bdc-275c-11ee-962d-dac502259ad0.png

初始化:

ff7c0204-275c-11ee-962d-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;
}

状态切换:

ff952356-275c-11ee-962d-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*/
}
ffa980ee-275c-11ee-962d-dac502259ad0.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实时框架的组成

ffb87f04-275c-11ee-962d-dac502259ad0.pngffc7515a-275c-11ee-962d-dac502259ad0.png

内存管理

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

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

事件队列

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

fff12a5c-275c-11ee-962d-dac502259ad0.png

事件派发

直接事件发送:

QActive_postLIFO()

发行订阅事件发送:

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

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

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

000eaffa-275d-11ee-962d-dac502259ad0.png

定时事件

非有序链表:

001eef64-275d-11ee-962d-dac502259ad0.png

合作式调度器QV:

002f29ec-275d-11ee-962d-dac502259ad0.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编译器的非标准扩展进行了准备(例如,在代码空间分配常数对象,可重入函数,等等)

基于断言的错误处理策略

代码风格:

00455fdc-275d-11ee-962d-dac502259ad0.png006aec70-275d-11ee-962d-dac502259ad0.png008babb8-275d-11ee-962d-dac502259ad0.png00a311fe-275d-11ee-962d-dac502259ad0.png00be1102-275d-11ee-962d-dac502259ad0.png

责任编辑:彭菁

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

    关注

    32

    文章

    2253

    浏览量

    94343
  • 计时器
    +关注

    关注

    1

    文章

    418

    浏览量

    32630
  • 嵌入式开发
    +关注

    关注

    18

    文章

    1022

    浏览量

    47505
  • 函数
    +关注

    关注

    3

    文章

    4304

    浏览量

    62413
  • 状态机
    +关注

    关注

    2

    文章

    492

    浏览量

    27470

原文标题:经典的状态机QP框架与原理

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

收藏 人收藏

    评论

    相关推荐

    TC387QP启用HSm时,PFlash被擦除而锁定了怎么解决?

    你好, 我曾在 HSM 启用状态下进行过 Pflash 擦除。 因此,锁定微型 TC387QP。 请告诉我如何解锁微控制器 TC387QP
    发表于 03-04 06:05

    QP5000 移动电源 测评报告

    ,外观又小巧易携带的移动电源 www.q-qyg.com,那就再好不过了。今天就特别为大家介绍一下我们QYG最新推出的高容量移动电源QP5000。这款新产品最大的特点就是,高容量锂电池,达到5000
    发表于 02-13 17:11

    入门QP框架

    当单片机遇到状态机前言前些日子在微信上看到李肖遥的公众号,里面系统讲述了QP框架,我很有感触。我用QP框架很多年了,一开始是使用QM和QPC++,到后来抛弃了QM,直接使用QPC裸写程
    发表于 01-17 08:49

    如何入门QP

    如何入门QP
    发表于 02-14 06:20

    如何在StateMachines板上运用QP官网上Blinky闪灯呢

    介绍一个基于QP框架UML语言的状态机图形编程软件如何在StateMachines板上运用QP官网上Blinky闪灯呢?
    发表于 02-25 07:52

    ADP32F035QP64S 开发板

    *附件:ADP32F035QP64S 开发板.rar
    发表于 10-24 13:52

    10QP7,pdf 电子管资料数据手册

    10QP7,pdf 电子管资料数据手册
    发表于 07-23 14:48 7次下载

    350QP44.pdf 电子管资料数据手册

    350QP44.pdf 电子管资料数据手册
    发表于 08-13 11:19 6次下载

    基于I帧复杂度的初始量化参数(QP)选择算法

    针对不同的视频内容造成编码前几帧质量低下的问题,提出一种基于I帧复杂度的初始量化参数( QP)选择算法。在编码前利用解码码流中得到的I帧宏块模式信息,运用宏块中的3种不同帧内预测模式判断I帧的复杂度
    发表于 02-07 14:06 0次下载

    设置I帧的QP的示例代码

      介绍 在有些应用中,发现I帧不够大。MPSoC VCU CtrlSW可以设置每一帧的QP大小。因此,可以通过设置I帧的QP,提高I帧的大小,从而提高I帧的质量。      代码 下面是设置I帧
    的头像 发表于 10-20 11:35 1747次阅读

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

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

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

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

    如何设置I帧的QP提高I帧的质量

    在有些应用中,发现I帧不够大。MPSoC VCU CtrlSW可以设置每一帧的QP大小。因此,可以通过设置I帧的QP,提高I帧的大小,从而提高I帧的质量。
    的头像 发表于 08-02 08:03 1465次阅读

    QP系列微型 DC/DC 转换器5至500Vdc 

    PICO QP系列微型电源DC/DC转换器能够在W电压范围(4.5-9Vdc、9-18Vdc、18-36Vdc和36-72Vdc)内作业,同时保证严苛的输出调节和低谐波失真。 QP系列模块的安全性
    发表于 03-24 09:09 812次阅读

    EMI读数是以QP值还是PK值为准

    EMI读数是以QP值还是PK值为准 ?
    的头像 发表于 05-08 09:46 7114次阅读
    EMI读数是以<b class='flag-5'>QP</b>值还是PK值为准