参考: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的状态图)。

·

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的用户代码与这些类之间的关系。

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应用中,只有一个名为Blinky的active object,这个active object被设计得很小巧,并只应用了最基本的QP功能。

定义一个简单的Blinky active object (AO) class;

为Blinky AO手工编码状态机;

使用了一个周期time event;

初始化QP框架;

启动一个AO;

State Machine

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

State Machine of the Blinky AO

在这个状态机最顶端的initial迁移 设定了一个QP event(QTimeEvt_armX())用来在每隔半秒钟一次投递TIMEOUT signal。

initial迁移导致到状态“off”,并在entry action中执行关闭LED(BSP_ledOff())。

当TIMEOUT event抵达“off”状态的时候,“off”状态将会迁移到“on”状态。

“on”状态里的entry action将会关闭LED(BSP_ledOn()).

当“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

·

被掏空了,没东西写了

c语言随机函数1 qp,QP/C初步入门相关推荐

  1. C语言初步入门学习大略

    C语言的入门学 C语言初步入门 2022/11/29 01. 看前需知 02. 内容构成 03. 学习时间 04. 预定计划 05. 网站推荐 2022/12/01 01. 驱动程序是什么?和其他电脑 ...

  2. Shel脚本-初步入门之《03》

    Shel脚本-初步入门-Shell 脚本在 Linux 运维工作中的地位 3.Shell 脚本在 Linux 运维工作中的地位 Shell 脚本语言很适合用于处理纯文本类型的数据,而 Linux 系统 ...

  3. 自然语言处理初步入门

    自然语言处理(NLP)如今发展得火热,但其实它应用领域还存在着很多不成熟的技术.作为一块刚刚挖掘不久的宝矿,自然语言处理还有着无限的前景等着我们去开发.本文碍于篇幅的限制,就不带大家深入钻研自然语言处 ...

  4. Shel脚本-初步入门之《02》

    Shel脚本-初步入门-什么是 Shell 脚本 2.什么是 Shell脚本 当命令或程序语句不在命令行下执行,而是通过一个程序文件来执行时,改程序就被称为 Shell 脚本.如果在 Shell 脚本 ...

  5. 爬取淘宝女郎的照片-写给初步入门爬虫的读者

    爬取淘宝女郎照片-写给初步入门爬虫的读者 要爬取的照片示例: python2.7爬虫代码如下: #coding=utf-8 import urllib2mmurl = "https://mm ...

  6. 西门子 PLCSim Advanced 初步入门

    西门子 PLCSim Advanced 初步入门 概述 安装 通讯方式 配置通讯方式 本地虚拟总线通讯 本地TCPIP通讯 分布式TCPIP通讯 开始仿真 仿真通讯 V5.0 更新 概述 PLCSim ...

  7. 【Kubernetes】菜鸟学K8S之初步入门

    菜鸟学K8S之初步入门 Kubernetes初步入门 什么是Kubernetes 为什么用Kubernetes Kubernetes重要概念 Cluster(集群) Master(主控) 1. Kub ...

  8. c语言随机函数1 qp,QP 发布步骤

    QP 发布步骤 1.概览 所有的 QP 资源包的发布都包括这几个步骤:BETA 发布.灰度发布.线上发布. BETA 发布:将静态资源打成 QP 资源包,并发布到 BETA 服务器上,此时就可以通过 ...

  9. c语言编程Turbo C 程序假想初步

    本节关键讲授Turbo C程序假想的全然步调及似果何编译.调试和运行源程序. 同时给出Turbo C的常用编纂号令.末了讲授Turbo C编译.毗连和运行时的常见错 误. 一.Turbo C程序假想全 ...

最新文章

  1. 为什么我的开发方式如此愚蠢?
  2. SQL语句中各个部分的执行顺序(转)
  3. 软件黑盒测试的意思,请问黑盒测试是什么意思?
  4. linux内核杂记(15)-系统调用(2)
  5. google+stackoverflow_哪些开发问题最让程序员“头秃”?我们分析了Stack Overflow的11000个问题...
  6. 任务管理器启动资源管理器
  7. 使用 k8s 搭建 confluence 6.10.x 版本
  8. Spring Security的HTTP基本验证示例
  9. hdu 4288 线段树 暴力 **
  10. Dubbo不向zookeeper/nacos注册
  11. Matlab 2016a 安装教程【转】
  12. Modelsim SE 的下载安装与注册
  13. 手机热点总是正在连接服务器,电脑连接手机热点无法上网的三种解决方法
  14. CF374C Inna and Dima 题解
  15. calibre如何使用邮件推送电子书
  16. 【电脑全部浏览器显示您与网站连接不是私密连接】
  17. java自我介绍_口语化java自我介绍
  18. dp 最佳加法表达式
  19. Python修改私有变量、统计访问次数、华摄氏度转换
  20. 仿大众点评——秒杀系统部分01

热门文章

  1. unitTest使用
  2. Point-NeRF:基于点的神经辐射场(CVPR 2022)
  3. 找不到该项目无法删除
  4. markdown文字居中以及尺寸颜色设置(二)
  5. 相控阵天线(四):阵列天线波束赋形(遗传算法、粒子群算法、进化差分算法、含python代码)
  6. linux服务器jpegoptim和optipng批量无损压缩图片
  7. 软件度量不是猫抓老鼠的游戏
  8. ekman螺线matlab,METT艾克曼
  9. linux建立分区,介绍Linux硬盘系统建立分区步骤
  10. 实现NRF24L01自动对频功能