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;
}

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


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

·

这里写图片描述
被掏空了,没东西写了

  • 4
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值