还可以这样写状态机?QP嵌入式实时框架

李肖遥 2025-03-20 22:12
    关注、星标公众号,直达精彩内容
来源:网络素材
整理:李肖遥

1 QP嵌入式实时框架

事件驱动型编程

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

QP官网:http://www.state-machine.com/

面向对象

类和单一继承:

图片

工具

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

图片

QS软件追踪工具:

图片
图片

2 QEP实现有限状态机Fsm

图片
/* qevent.h ----------------------------------------------------------------*/
  typedef struct QEventTag 
  {
  
    QSignal sig;     
    uint8_t dynamic_;  
  } QEvent;
  /* qep.h -------------------------------------------------------------------*/
  typedef uint8_t QState; /* status returned from a state-handler function */
  typedef QState (*QStateHandler) (void *me, QEvent const *e)/* argument list */
  typedef struct QFsmTag   /* Finite State Machine */
  {
 
    QStateHandler state;     /* current active state */
  }QFsm;
  
  #define QFsm_ctor(me_, initial_) ((me_)->state = (initial_))
  void QFsm_init (QFsm *me, QEvent const *e);
  void QFsm_dispatch(QFsm *me, QEvent const *e);
  
  #define Q_RET_HANDLED ((QState)0)
  #define Q_RET_IGNORED ((QState)1)
  #define Q_RET_TRAN ((QState)2)
  #define Q_HANDLED() (Q_RET_HANDLED)
  #define Q_IGNORED() (Q_RET_IGNORED)
  
   #define Q_TRAN(target_) (((QFsm *)me)->state = (QStateHandler)   (target_),Q_RET_TRAN)
  
  enum QReservedSignals
  {
      Q_ENTRY_SIG = 1
    Q_EXIT_SIG, 
    Q_INIT_SIG, 
    Q_USER_SIG 
  };
  
  /* file qfsm_ini.c ---------------------------------------------------------*/
  #include "qep_port.h" /* the port of the QEP event processor */
  #include "qassert.h" /* embedded systems-friendly assertions */
  void QFsm_init(QFsm *me, QEvent const *e) 
  
{
      (*me->state)(me, e); /* execute the top-most initial transition */
    /* enter the target */
    (void)(*me->state)(me , &QEP_reservedEvt_[Q_ENTRY_SIG]);
  }
  /* file qfsm_dis.c ---------------------------------------------------------*/
  void QFsm_dispatch(QFsm *me, QEvent const *e)
  
{
      QStateHandler s = me->state; /* save the current state */
    QState r = (*s)(me, e); /* call the event handler */
    if (r == Q_RET_TRAN)  /* transition taken? */
      {
            (void)(*s)(me, &QEP_reservedEvt_[Q_EXIT_SIG]); /* exit the source */
            (void)(*me->state)(me, &QEP_reservedEvt_[Q_ENTRY_SIG]);/*enter target*/
    }
  }
实现上面定时器例子
  #include "qep_port.h" /* the port of the QEP event processor */
  #include "bsp.h" /* board support package */
  
  enum BombSignals /* all signals for the Bomb FSM */
  { 
      UP_SIG = Q_USER_SIG,
      DOWN_SIG,
      ARM_SIG,
      TICK_SIG
  };
  typedef struct TickEvtTag 
  {

    QEvent super;      /* derive from the QEvent structure */
    uint8_t fine_time; /* the fine 1/10 s counter */
  }TickEvt;
  
  typedef struct Bomb4Tag 
  {

    QFsm super;   /* derive from QFsm */
    uint8_t timeout; /* number of seconds till explosion */
        uint8_t code;    /* currently entered code to disarm the bomb */
        uint8_t defuse;  /* secret defuse code to disarm the bomb */
  } Bomb4;
  
  void Bomb4_ctor (Bomb4 *me, uint8_t defuse);
  QState Bomb4_initial(Bomb4 *me, QEvent const *e);
  QState Bomb4_setting(Bomb4 *me, QEvent const *e);
  QState Bomb4_timing (Bomb4 *me, QEvent const *e);
  /*--------------------------------------------------------------------------*/
  /* the initial value of the timeout */
  #define INIT_TIMEOUT 10
  /*..........................................................................*/
  void Bomb4_ctor(Bomb4 *me, uint8_t defuse) {
    QFsm_ctor_(&me->super, (QStateHandler)&Bomb4_initial);
    me->defuse = defuse; /* the defuse code is assigned at instantiation */
  }
  /*..........................................................................*/
  QState Bomb4_initial(Bomb4 *me, QEvent const *e) {
    (void)e;
    me->timeout = INIT_TIMEOUT;
    return Q_TRAN(&Bomb4_setting);
  }
  /*..........................................................................*/
  QState Bomb4_setting(Bomb4 *me, QEvent const *e) {
    switch (e->sig){
        case UP_SIG:{
            if (me->timeout < 60) {
                ++me->timeout;
                BSP_display(me->timeout);
            }
              return Q_HANDLED();
        }
        case DOWN_SIG: {
            if (me->timeout > 1) {
                --me->timeout;
                BSP_display(me->timeout);
            }
            return Q_HANDLED();
        }
        case ARM_SIG: {
            return Q_TRAN(&Bomb4_timing); /* transition to "timing" */
        }
    }
    return Q_IGNORED();
  }
  /*..........................................................................*/
  void Bomb4_timing(Bomb4 *me, QEvent const *e) {
    switch (e->sig) {
        case Q_ENTRY_SIG: {
            me->code = 0/* clear the defuse code */
            return Q_HANDLED();
          }
        case UP_SIG: {
            me->code <<= 1;
            me->code |= 1;
            return Q_HANDLED();
          }
        case DOWN_SIG: {
            me->code <<= 1;
            return Q_HANDLED();
        }
        case ARM_SIG: {
            if (me->code == me->defuse) {
                return Q_TRAN(&Bomb4_setting);
            }
            return Q_HANDLED();
        }
        case TICK_SIG: {
            if (((TickEvt const *)e)->fine_time == 0) {
                --me->timeout;
                BSP_display(me->timeout);
                if (me->timeout == 0) {
                BSP_boom(); /* destroy the bomb */
                }
            }
            return Q_HANDLED();
        }
    }
    return Q_IGNORED();
  }

优点:

  • 采用面向对象的设计方法,很好的移植性
  • 实现了进入退出动作
  • 合适的粒度,且事件的粒度可控
  • 状态切换时通过改变指针,效率高
  • 可扩展成为层次状态机

缺点:

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

3 QP实现层次状态机

图片

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

void QHsm_init(QHsm *me, QEvent const *e) 
    
{
     Q_ALLEGE((*me->state)(me, e) == Q_RET_TRAN);
        t = (QStateHandler)&QHsm_top; /* HSM starts in the top state */
      do { /* drill into the target... */
      QStateHandler path[QEP_MAX_NEST_DEPTH_];
       int8_t ip = (int8_t)0/* transition entry path index */
       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]; /* restore the target of the initial tran. */
      /* 钻到最底层的状态,执行路径中的所有进入事件 */
        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]; /* current state becomes the new source */
       } while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN);
      me->state = t;
    }

状态切换:

图片
/*.................................................................*/
QState result(Calc *me, QEvent const *e) 
{
    switch (e->sig) 
    {you
        case ENTER_SIG:{
            break;
        }
        case EXIT_SIG:{
            break;
        }
        case C_SIG: 
        {
            printf("clear");    
            return Q_HANDLED();
        }
        case B_SIG:
        {  
            return Q_TRAN(&begin);
        }
    }
    return Q_SUPER(&reday);
}
/*.ready为result和begin的超状态................................................*/
QState ready(Calc *me, QEvent const *e) 
{
    switch (e->sig) 
    {
        case ENTER_SIG:{
            break;
        }
        case EXIT_SIG:{
            break;
        }
        case OPER_SIG:
        {  
            return Q_TRAN(&opEntered);
        }
    }
    return Q_SUPER(&on);
}



void QHsm_dispatch(QHsm *me, QEvent const *e) 
{
    QStateHandler path[QEP_MAX_NEST_DEPTH_];
    QStateHandler s;
    QStateHandler t;
    QState r;
    t = me->state;     /* save the current state */
    do {       /* process the event hierarchically... */
        s = me->state;
        r = (*s)(me, e);   /* invoke state handler s */
    } while (r == Q_RET_SUPER); //当前状态不能处理事件 ,直到找到能处理事件的状态
    
    if (r == Q_RET_TRAN) {     /* transition taken? */
        int8_t ip = (int8_t)(-1);   /* transition entry path index */
        int8_t iq;       /* helper transition entry path index */
        path[0] = me->state;    /* save the target of the transition */
        path[1] = t;
        while (t != s) {   /* exit current state to transition source s... */
            if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) {/*exit handled? */
                (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* find superstate of t */
            }
            t = me->state;   /* me->state holds the superstate */
        }
     . . .
    }
    me->state = t;     /* set new state or restore the current state */
}
图片
t = path[0]; /* target of the transition */
    if (s == t) { /* (a) check source==target (transition to self) */
         QEP_EXIT_(s) /* exit the source */
         ip = (int8_t)0/* enter the target */
     }
     else {
         (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* superstate of target */
         t = me->state;
         if (s == t) { /* (b) check source==target->super */
              ip = (int8_t)0/* enter the target */
          }
         else {
             (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); /* superstate of src */
             /* (c) check source->super==target->super */
             if(me->state == t) {
                 QEP_EXIT_(s) /* exit the source */
                 ip = (int8_t)0/* enter the target */
              }
              else {
                   /* (d) check source->super==target */
                   if (me->state == path[0]) {
                      QEP_EXIT_(s) /* exit the source */
                   }
                   else { /* (e) check rest of source==target->super->super..
                       * and store the entry path along the way */

                    ....

4 QP实时框架的组成

图片
图片

内存管理

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

图片

事件队列

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

图片

事件派发

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

代码风格

图片


一键三连,公众号后台回复【QP资料】可以获取资料。

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

关注我的微信公众号,回复“星球”加入知识星球,有问必答。



点击“阅读原文”查看知识星球详情,欢迎点分享、收藏、点赞、在看。

李肖遥 公众号“技术让梦想更伟大”,作者:李肖遥,专注嵌入式,只推荐适合你的博文,干货,技术心得,与君共勉。
评论 (0)
  • 家电“以旧换新”政策的覆盖范围已从传统的八大类家电(冰箱、洗衣机、电视、空调、电脑、热水器、家用灶具、吸油烟机)扩展至各地根据本地特色和需求定制的“8+N”新品类。这一政策的补贴再叠加各大电商平台的优惠,家电销售规模显著增长,消费潜力得到进一步释放。晶尊微方案为升级换代的智能家电提供了高效且稳定的触摸感应和水位检测功能,使得操作更加便捷和可靠!主要体现在:水位检测1健康家电:养生壶、温奶器、加湿器的缺水保护安全2清洁电器:洗地机、扫地机器人的低液位和溢液提醒3宠物家电:宠物饮水机的缺水提醒/满水
    ICMAN 2025-03-20 15:23 106浏览
  • 全球领先的光学解决方案供应商艾迈斯欧司朗(SIX:AMS)近日宣布,凭借AS1163独立智能驱动器(SAID)成为中国领先的智能集成系统产品汽车制造商宁波福尔达智能科技股份有限公司(“福尔达”)环境动态照明应用的关键供应商。此次合作标志着汽车技术发展的一个重要时刻,充分展现了AS1163在优化动态照明应用系统成本方面的多功能性和先进性能。该产品支持传感器集成,拥有专为车顶照明设计的超薄外形,并能提升车内照明系统的性能。AS1163是一款先进的智能LED驱动器,能够与开放系统协议(OSP)网络无缝
    艾迈斯欧司朗 2025-03-20 14:26 75浏览
  • 贞光科技代理的品牌-光颉科技高精密薄膜电阻凭借0.01%的超高精度,在AI服务器电源模块中实现了精确电压分配、优化功率因数和减少热损耗,显著提升系统能效和可靠性。在当今的数字时代,人工智能(AI)服务器已成为数据中心的核心。随着AI应用的激增,服务器的性能和能效需求也在不断提高。电源模块作为服务器的关键组件,其性能直接影响整个系统的效率和可靠性。本文将探讨光颉科技高精密薄膜电阻,特别是其0.01%的精度,如何在AI服务器电源模块中提升能效。电源模块在AI服务器中的重要性电源模块负责将输入电源转换
    贞光科技 2025-03-20 16:55 112浏览
  • 如同任何对我们工作方式的改变,新的工作方式必然会遇到许多必须面对的挑战。如果不解决组织在实施精益六西格玛过程中面临的障碍以及如何克服它们的问题,那么关于精益六西格玛的讨论就不算完整。以下列举了组织在成功实施精益六西格玛时常见的几个障碍,以及克服它们的方法:1)对精益六西格玛方法论缺乏理解。抵触情绪通常源于对精益六西格玛方法论的不了解,以及不相信它能真正发挥作用。这种情况在所有层级的人员中都会出现,包括管理层。虽然教育培训可以帮助改善这一问题,但成功的项目往往是打消疑虑的最佳方式。归根结底,这是一
    优思学院 2025-03-20 12:35 78浏览
  •         在当今电子设备高度集成的时代,电路保护显得尤为重要。TVS管(瞬态电压抑制二极管)和压敏电阻作为一种高效的电路保护器件,被广泛应用于各种电子设备中,用以吸收突波,抑制瞬态过电压,从而保护后续电路免受损坏。而箝位电压,作为TVS管和压敏电阻的核心参数之一,直接关系到其保护性能的优劣。箝位电压的定义        箝位电压指瞬态保护器件(如TVS二极管、压敏电阻)在遭遇过压时,将电路电压限制在安全范围内的
    广电计量 2025-03-20 14:05 74浏览
  • 为有效降低人为疏失导致交通事故发生的发生率,各大汽车制造厂及系统厂近年来持续开发「先进驾驶辅助系统」ADAS, Advanced Driver Assistance Systems。在众多车辆安全辅助系统之中,「紧急刹车辅助系统」功能(AEB, Autonomous Emergency Braking)对于行车安全性的提升便有着相当大的帮助。AEB透过镜头影像模块与毫米波雷达感测前方目标,可在发生碰撞前警示或自动刹车以降低车辆损伤以及乘员伤害。面临的挑战以本次分享的客户个案为例,该车厂客户预计在
    百佳泰测试实验室 2025-03-20 15:07 72浏览
  • 流感季急诊室外彻夜排起的长队,手机屏幕里不断闪烁的重症数据,深夜此起彼伏的剧烈咳嗽声——当病毒以更狡猾的姿态席卷全球,守护健康的战争早已从医院前移到每个人的身上。在医学界公认的「72小时黄金预警期」里,可穿戴设备闪烁的光芒正穿透皮肤组织,持续捕捉血氧浓度、心率变异性和体温波动数据。这不是科幻电影的末日警报,而是光电传感器发出的生命预警,当体温监测精度精确到±0.0℃,当动态血氧检测突破运动伪影干扰……科技正在重新定义健康监护的时空边界。从智能手表到耳机,再到智能戒指和智能衣物,这些小巧的设备通过
    艾迈斯欧司朗 2025-03-20 15:45 129浏览
  • 本文内容来自微信公众号【工程师进阶笔记】,以工程师的第一视角分析了飞凌嵌入式OK3506J-S开发板的产品优势,感谢原作者温老师的专业分享。前两周,有一位老朋友联系我,他想找人开发一款数据采集器,用来采集工业现场的设备数据,并且可以根据不同的业务场景,通过不同的接口把这些数据分发出去。我把他提的需求总结了一下,这款产品方案大概有以下功能接口,妥妥地一款工业网关,在网上也能找到很多类似的产品方案,为啥他不直接买来用?再跟朋友深入地聊了一下,他之所以联系我,是因为看到我在公众号介绍过一款由飞凌嵌入式
    飞凌嵌入式 2025-03-20 11:51 104浏览
  • PCIe 5.0应用环境逐步成形,潜在风险却蠢蠢欲动?随着人工智能、云端运算蓬勃发展,系统对于高速数据传输的需求不断上升,PCI Express(PCIe)成为服务器应用最广的传输技术,尤其在高效能运算HPC(High Performance Computing)及AI服务器几乎皆导入了最新的PCIe 5.0规格,使得数据传输的双向吞吐量达到了128GB/s,让这两类的服务器能够发挥最大的效能。不过随着PCIe 5.0的频率达到16GHz,PCB板因为高频而导致讯号衰减加剧的特性,使得厂商面临很
    百佳泰测试实验室 2025-03-20 13:47 72浏览
  • 近日,保定飞凌嵌入式技术有限公司(以下简称“飞凌嵌入式”)携手瑞芯微电子股份有限公司(以下简称“瑞芯微”)正式加入2025年全国大学生嵌入式芯片与系统设计竞赛(以下简称“嵌入式大赛”),并在应用赛道中设立专属赛题。本次嵌入式大赛,双方选用基于瑞芯微RK3588芯片设计的ELF 2开发板作为参赛平台,旨在通过此次合作,促进产教融合,共同推动嵌入式系统创新人才的培养。全国大学生嵌入式芯片与系统设计竞赛是一项A类电子设计竞赛,同时也是被教育部列入白名单的赛事,由中国电子学会主办,是学生保研、求职的公认
    飞凌嵌入式 2025-03-20 11:53 62浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦