QP/C 初步入门-程序员宅基地

技术标签: quantum  Quantum LeaPs  

原文链接:QP/C初步入门



参考:http://www.state-machine.com/qpc/index.html

转载请标明出处:http://blog.csdn.net/chenbb8/article/details/52334895
本文介绍QP的代码结构,在对QP的实现有个初步了解后,通过一个例子学习如何根据状态图,手工编码QP的状态机。本文介绍QP所用到的状态模式,需要的读者可以到官网下载《UML状态图的实用C/C++设计》详细了解,或者自行搜索相关的UML状态模式文章。


前言
常用集中式状态机的实现技术有三种实现技术:switch…case状态机,函数状态机型,表格状态机,本文介绍的QP属于函数状态机型。
之所以要使用QP框架,而不是自行编写状态机。是因为没必要重复造轮子,并且QP也可以提供很多必要的组间,比如事件queue。而自己从头开始写状态机,一般情况下只会实现到switch…case状态机的地步,这种状态机在状态比较多的时候,可阅读性偏低。另外选择QM,还可以使用UML状态模式代码自动生成工具QM,这样就不需要手工编码状态机了。

Note:相对集中式的状态机,就是分散式的状态机。假设有一个变量state存储状态,然后在所有产生event的地方根据state的取值来执行相应的动作,这就是分散式状态机。而集中式的状态机,则是在event产生的地方,将event投递到状态机实现的地方,进行集中式的处理,这种实现方式可阅读性更高。


QP的结构

如下图所示,QP是具有层状结构,最底层的是目标硬件。板级支持包(BSP)用于访问特定的硬件功能,比如外设。包括自带的QV QK QXK和别家普通的RTOS作为实时内核,提供多任务的基础,包括任务调度,上下文切换,任务间通讯。基于这些服务,事件驱动框架QF为执行active objects(活跃对象)提供了事件驱动(event-driven),并确保他们之间的通讯线程安全。最后事件处理器(QEP)提供了层次状态机的语意(基于UML的状态图)。
·

sss
Components of the QP Framework

QEP Hierarchical Event Processor
QEP是一个通用的兼容UML的事件处理器,它实现了基于ANSI-C的较高可读性的层次状态机(UML状态图)。QEP拥有可追溯性的特点,这意味着状态图的每一个元素都能精确清晰的追溯到代码上。QEP支持状态的层次嵌套,这样就能避免在很多状态上重复着同样的动作和状态迁移,替代的是复用动作 (See QEP for detailed documentation)。

QF Active-Object Framework
QF是一个专为嵌入式系统设计的轻量级的,事件驱动,active objects框架。这个框架的主要任务是保证每个active objects的线程安全,运行-到-完成(run-to-completion )的事件处理。它包含了直接的事件传送,发布-订阅(publish-subscribe)的事件转发,事件队列,时间事件(延时传送时间事件)。(See QF for detailed documentation).

QV Cooperative Kernel
协作内核(以前被称为香草Vanilla内核),它只在time to completion的时候处理event,并在处理所有event后,对active object执行基于priority-based的调度器。它是隐式合作(implicitly-cooperative),因为活跃定时器不需要明确的放弃CPU。代替的是在完成事件处理后,简单的return到QV调度器中。由于状态机event处理的自然持续时间短,简单的QV内核在很多real-time系统中通常是足够的。(See QV for detailed documentation).

QK Preemptive Non-Blocking Kernel
QK是一个超快速的抢占式,基于优先级的,单stack,实时内核专门为执行active objects而设计的。它总是会处理event queued中的高优先级的active objects,但它将event当作一次性的函数来调用(而不是像传统内核那样的endless循环)。尽管如此,如果新的事件优先级比当前处理的事件优先级高,QK内核依然提供了抢占式的一次性的event处理功能(很像抢占式中断处理器允许中断彼此抢占)。这意味着,QK可以使用单stack来保存所有active objects的context。QK满足速率单调调度(RMS Rate Monotonic Scheduling 又名Rate Monotonic Analysis — RMA)的所有的要求,可在硬实时系统中使用。(See QK for detailed documentation).

QXK Preemptive Blocking Kernel
QXK是一个简单的抢占,基于优先级的,阻塞,实时内核专门为传统的阻塞代码的主动对象,如商用中间件(TCP / IP协议栈,UDP协议栈,嵌入式文件系统等)或遗留代码混合设计。
QXK有你所期望的一个传统的RTOS阻塞内核的大部分功能,建议作为首选RTOS内核需要混合使用传统的阻塞码active objects QP / C应用程序。(See QXK for detailed documentation).
Note:QXK有你所期待的传统的阻塞式RTOS内核,建议在混合传统阻塞代码和active object的应用中使用。因为QXK和QP的其它部分紧密结合,所以使用QXK相对其余RTOS能获得更好的性能和内存占用。

QS Software Tracing System
QS是软件追踪系统,使开发人员能够监控以最少的目标系统资源,并没有停止或显著放缓代码直播事件驱动QP的应用程序。QS是用于测试,故障排除和优化QP应用的理想工具。QS甚至可以用于支持产品制造验收测试。


Classes in QP/C
在下图描绘出QP/C架构中包含的主要类,并举例说明在例子中“Fly ‘n’ Shoot” Game的用户代码与这些类之间的关系。

sss
Main Classes in the QP Framework

0、QEvt类提供了没有参数的event,并且派生出time event和其他所有的带有参数的event。比如,应用级事件ObjectPosEvt和ObjectImageEvt都是继承于QEvt,并且增加了参数(see [8])。
1、抽象类QMsm提供了更为有效率,更快速的策略用来编码嵌套状态机,但它不是手工可维护代码,需要使用QM modeling tool。在”Fly ‘n’ Shoot” Game中有在用户级的类直接派生自QMsm(see [7]).
2、抽象类QHsm派生自QMsm并且实现了合适的状态机编码策略用来用户维护和编码这个代码。QHsm同样被QM modeling tool支持,但这没有QMsm快和有效率。
3、抽象类QMActive提供了使用QMsm风格的状态机策略的活跃对象。它需要使用QM modeling tool来自动生成状态机代码,它只需要最少的run-time支持(更小的event-processor)。
4、抽象类QActive提供了使用QHsm风格的状态机策略的活跃对象,这个策略是为手工编码定制的,但它同样被QM modeling tool支持,只是生产的代码比QMsm风格实现策略的要慢。
5、QTimeEvt提供了QP中使用的time events。QTimeEvt是一种有事件片概念的特殊QP event。使用QTimeEvt的基本用法如下。一个active object可以allocates一个或者多个QTimeEvt对象(并存储它们)。当这个active object需要timeout,他可以单次或者周期性的派发一个time events。每个timers都是独立的,所以QP的应用可以使用多路独立的timeout请求(从同样或者不同的active objects)。当QP察觉到合适的时间片刻抵达,他就会将time event直接插入到请求者的event队列中。接着请求者就像别的event那样的处理time event。
6、用户的active object继承自QActive或者QMActive。
7、用户同样可以将类直接继承自QHsm或者QMsm,用来表现出原生的(raw)状态机,而非active object,因为他们没有event队列和执行线程。这样的原生状态机一般用作正交构件(Orthogonal Components)。
8、带有参数的用户级event,派生自QEvt类。


例子1:Simple Blinky Application
Blinky是一个很简单的例子,在嵌入式系统中的地位相当于”Hello World!”,这同时也是使用QP做的一个最简单的“something”。在这个blinky中,以1HZ的速率闪烁LED灯,0.5s开灯,05s关灯。

·

Blinky on EK-TM4C123GLX (TivaC LaunchPad)
Blinky on EK-TM4C123GLX (TivaC LaunchPad)

在这个超简单的blinky应用中,只有一个名为Blinky的active object,这个active object被设计得很小巧,并只应用了最基本的QP功能。

  1. 定义一个简单的Blinky active object (AO) class;
  2. 为Blinky AO手工编码状态机;
  3. 使用了一个周期time event;
  4. 初始化QP框架;
  5. 启动一个AO;

State Machine
这个简单的Blinky AO的状态机如下图所示:

State Machine of the Blinky AO
State Machine of the Blinky AO

  1. 在这个状态机最顶端的initial迁移 设定了一个QP event(QTimeEvt_armX())用来在每隔半秒钟一次投递TIMEOUT signal。
  2. initial迁移导致到状态“off”,并在entry action中执行关闭LED(BSP_ledOff())。
  3. 当TIMEOUT event抵达“off”状态的时候,“off”状态将会迁移到“on”状态。
  4. “on”状态里的entry action将会关闭LED(BSP_ledOn()).
  5. 当“on”状态接收到TIMEOUT event,“on”状态会返回到“off”状态,“off”状态的entry action将会被执行关闭LED(BSP_ledOff())。从这点来说,以上的循环将会一直重复,因为在预先确定的速率下TIMEOUT events持续产生。

State Machine Code
以上所示的的Blinky状态机的blinky.c源文件中实现的,如下面的代码列表所示。下面的代码被专门设计成不需要直接访问目标资源,而是访问调用封装好的BSP。例如,将LED关闭或者打开不是写入特定的嵌入式主板的GPIO资源,而是调用BSP的函数BSP_ledOn() 和 BSP_ledOff()。这些代码可以为每个不同的目标主板(甚至台式工作站),而不需要改变它的状态机代码。
注意
该的Blinky源代码(blinky.c)实际上在所有的平台,包括Windows和嵌入式主板是相同的。唯一的区别是 在板级支持包(bsp.c),这是具体的目标。

#include "qpc.h"
#include "blinky.h"
#include "bsp.h"

//Q_DEFINE_THIS_FILE

/*..........................................................................*/
typedef struct {     /* the Blinky active object */
    QActive super;   /* [继承自QActive ] inherit QActive */

    QTimeEvt timeEvt; /* [私有的time event发生器] private time event generator */
} Blinky;

static Blinky l_blinky; /* the Blinky active object */

QActive * const AO_Blinky = &l_blinky.super;

/* [声明层次状态机] hierarchical state machine ... */
static QState Blinky_initial(Blinky * const me, QEvt const * const e);
static QState Blinky_off    (Blinky * const me, QEvt const * const e);
static QState Blinky_on     (Blinky * const me, QEvt const * const e);

/*..........................................................................*/
void Blinky_ctor(void) {
    Blinky * const me = &l_blinky;
    QActive_ctor(&me->super, Q_STATE_CAST(&Blinky_initial));/* 用户定义的Blinky_initial动作 */
    QTimeEvt_ctorX(&me->timeEvt, &me->super, TIMEOUT_SIG, 0U);/* [TIMEOUT_SIG是用户定义的信号值,可以同时定义多个time event信号值。] */
}

/* HSM definition ----------------------------------------------------------*/
QState Blinky_initial(Blinky * const me, QEvt const * const e) {
    (void)e; /* avoid compiler warning about unused parameter */

    /* arm the time event to expire in half a second and every half second */
    QTimeEvt_armX(&me->timeEvt, BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U);/* 第一个参数是启动间隔,第二个是后面每次的间隔 */
    return Q_TRAN(&Blinky_off);/* [Q_TRAN()宏用于迁移状态,其中的参数目标状态的对应函数名] */
}
/*..........................................................................*/
QState Blinky_off(Blinky * const me, QEvt const * const e) {
    QState status;
    switch (e->sig) {
        case Q_ENTRY_SIG: {
            BSP_ledOff();
            status = Q_HANDLED();/* 告知框架已经处理事件,没有别的什么动作~毕竟这个函数的返回值是QState,需要不需要迁移状态都要填入返回值*/
            break;
        }
        case TIMEOUT_SIG: {
            status = Q_TRAN(&Blinky_on);
            break;
        }
        default: {
            status = Q_SUPER(&QHsm_top);/* 返回到上级 */
            break;
        }
    }
    return status;
}
/*..........................................................................*/
QState Blinky_on(Blinky * const me, QEvt const * const e) {
    QState status;
    switch (e->sig) {
        case Q_ENTRY_SIG: {
            BSP_ledOn();
            status = Q_HANDLED();
            break;
        }
        case TIMEOUT_SIG: {
            status = Q_TRAN(&Blinky_off);
            break;
        }
        default: {
            status = Q_SUPER(&QHsm_top);
            break;
        }
    }
    return status;
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

例子2:Dining Philosophers Problem (DPP)
现在QP官网的链接目前例子2 3都没写好, 自己看书去吧,少年


例子3:“Fly ‘n’ Shoot” Game

·

这里写图片描述
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jin787730090/article/details/119643766

智能推荐

Sandboxie v5.45.2正式版 系统安全工具_sandboxie系统安全工具-程序员宅基地

文章浏览阅读141次。简介:菜鸟高手裸奔工具沙盘Sandboxie是一款国外著名的系统安全工具,它可以让选定程序在安全的隔离环境下运行,只要在此环境中运行的软件,浏览器或注册表信息等都可以完整的进行清空,不留一点痕迹。同时可以防御些带有木马或者病毒的恶意网站,对于经常测试软件或者不放心的软件,可放心在沙盘里面运行!下载地址:http://www.bytepan.com/J7BwpqQdKzR..._sandboxie系统安全工具

Mac技巧|如何在 MacBook上设置一位数登录密码-程序员宅基地

文章浏览阅读230次,点赞4次,收藏5次。Mac老用户都知道之前的老版本系统是可以设置一位数登陆密码的,但是更新到10.14以后就不可以了,今天就教大家怎么在新版本下设置Mac一位数登陆密码。

chatgpt中的强化学习 PPO_chatgpt使用的强化学习-程序员宅基地

文章浏览阅读3.4k次。本该到此结束,但是上述实现的时候其实是把生成的每一步的奖励都使用统一的句子级reward,但该代码其实也额外按照每个token来计算奖励值的,为了获取每个token的奖励,我们在生成模型的隐层表示上,多加一个线性层,映射到一维,作为每个状态的预测奖励值。类似的,在文本生成中我们也可以用蒙特卡洛方法来估计一个模型的状态价值。假如我们只采样到了s1和s2,没有采样到s3,由于7和3都是正向奖励,s1和s2的训练后生成的概率都会变大,且s1的概率变的更大,这看似合理,但是s3是未参与训练的,它的概率反而减小了。_chatgpt使用的强化学习

获取不规则多边形中心点_truf计算重心-程序员宅基地

文章浏览阅读433次,点赞10次,收藏8次。尝试了3种方法,都失败了!_truf计算重心

HDU 1950最长上升子序列 学习nlogn_poj 1631 hdu 1950为啥是最长上升子序列-程序员宅基地

文章浏览阅读406次。学习LIS_poj 1631 hdu 1950为啥是最长上升子序列

kubernetes===》二进制安装_sed -ie 's#image.*#image: ${ epic_image_fullname }-程序员宅基地

文章浏览阅读550次。一、节点规划主机名称IP域名解析k8s-m-01192.168.12.51m1k8s-m-02192.168.12.52m2k8s-m-03192.168.12.53m3k8s-n-01192.168.12.54n1k8s-n-02192.168.12.55n2k8s-m-vip192.168.12.56vip二、插件规划#1.master节点规划kube-apiserverkube-controller-manage_sed -ie 's#image.*#image: ${ epic_image_fullname }#g

随便推点

UAC绕过提权_uac白名单 提权-程序员宅基地

文章浏览阅读106次。UAC绕过提权_uac白名单 提权

Linux一键部署OpenVPN脚本-程序员宅基地

文章浏览阅读664次,点赞7次,收藏12次。每次架设OpenVPN Server就很痛苦,步骤太多,会出错的地方也多,基本很少一次性成功的。

头文件的相互包含问题_多个头文件相互包含-程序员宅基地

文章浏览阅读397次。 今天看了继承以及派生类,并且运行了教程中的一个实例,但是仍然有好多坑。主要如下:建立了一个基类bClass以及由基类bClass派生的一个dClass,并且建立两个头文件.h分别申明这两个类,在cpp程序中进行运行来检验。具体程序如下:#ifndef ITEM_BASE//为避免类重复定义,需要在头文件的开头和结尾加上如这个所示 #define ITEM_BASEclass bClass..._多个头文件相互包含

python -- PyQt5(designer)安装详细教程-程序员宅基地

文章浏览阅读1.3w次,点赞19次,收藏88次。PyQt5安装详细教程,安装步骤很详细

微信小程序scroll-view去除滚动条-程序员宅基地

文章浏览阅读154次。官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html。_scroll-view去除滚动条

POJ-3233 Matrix Power Series 矩阵A^1+A^2+A^3...求和转化-程序员宅基地

文章浏览阅读146次。S(k)=A^1+A^2...+A^k.保利求解就超时了,我们考虑一下当k为偶数的情况,A^1+A^2+A^3+A^4...+A^k,取其中前一半A^1+A^2...A^k/2,后一半提取公共矩阵A^k/2后可以发现也是前一半A^1+A^2...A^k/2。因此我们可以考虑只算其中一半,然后A^k/2用矩阵快速幂处理。对于k为奇数,只要转化为k-1+A^k即可。n为矩阵数量,m为矩阵..._a^1 a^2 ... a^k