QP-nano技术详解:从代码结构到关键机制
1. QP-nano基础概述
QP-nano是一个轻量级的实时框架,在嵌入式系统中有着广泛的应用。在使用QP-nano时,
QF_active[]
数组的长度必须与
qpn_port.h
头文件中的
QF_MAX_ACTIVE
宏精确匹配,并且该数组中活动对象控制块的顺序决定了活动对象的优先级。
2. QP-nano代码结构
QP-nano的代码组织清晰,主要包含以下几个部分:
-
根目录
:
<qp>\qpn\
是QP-nano的根目录。
-
包含目录
:
<qp>\qpn\include\
存放与平台无关的QP头文件,如
qassert.h
(用于QP-nano断言)、
qepn.h
(QEP-nano与平台无关的接口)、
qfn.h
(QF-nano与平台无关的接口)和
qkn.h
(QK-nano与平台无关的接口)。
-
源文件目录
:
<qp>\qpn\source\
包含QP-nano与平台无关的源文件(
.C
文件),例如
qepn.c
(QEP-nano与平台无关的源文件)、
qfn.c
(QF-nano与平台无关的源文件)和
qkn.c
(QK-nano与平台无关的源文件)。
-
示例目录
:
<qp>\qpn\examples\
提供了针对不同嵌入式目标的QP-nano可执行示例,涵盖了众多设计模式和应用,如 “Orthogonal Component” 模式、“Deferred Event” 模式、DPP应用等。
-
文档目录
:
<qp>\qpn\doxygen\
包含使用Doxygen工具从源代码生成的 “QP-nano参考手册”,有HTML和CHM帮助两种格式。
以下是QP-nano代码组织的具体结构:
<qp>\qpn\
- QP-nano root directory
|
+-include\
- Platform independent QP header files
| +-qassert.h
- QP-nano assertions
| +-qepn.h
- QEP-nano platform-independent interface
| +-qfn.h
- QF-nano platform-independent interface
| +-qkn.h
- QK-nano platform-independent interface
|
+-source\
- QP-nano platform-independent source code (*.C files)
| +-qepn.c
- QEP-nano platform-independent source code
| +-qfn.c
- QF-nano platform-independent source code
| +-qkn.c
- QK-nano platform-independent source code
|
+-examples\
- Platform-specific QP examples
| +-80x86\
- Examples for the 80x86 processor
| | +-tcpp101\
- Examples with the Turbo C++ 1.01 compiler
| |
+-comp\
- "Orthogonal Component" pattern
| |
+-defer\
- "Deferred Event" pattern
| |
+-dpp\
- DPP application
| |
+-dpp-qk\
- DPP application with QK-nano
| |
+-game\
- "Fly ‘n’ Shoot" game example
| |
| +-dbg\
- Debug build
| |
| | +-GAME.EXE - Debug executable
| |
| +-GAME.PRJ
- Turbo C++ project to build the Debug version
| |
| +-qpn_port.h - QP-nano port
| |
| +-game.h
- The application header file
| |
| +-bsp.c
- BSP for the application
| |
| +-main.c
-
| |
+-. . .
| |
+-game-qk\
- "Fly ‘n’ Shoot" game example with QK-nano
| |
+-history\
- "Transition to History" pattern
| |
+-hook\
- "Ultimate Hook" pattern
| |
+-pelican\
- PELICAN crossing example
| |
+-pelican-qk\
- PELICAN crossing example with QK-nano
| |
+-qhsmtst\
- QHsmTst example
| |
+-reminder\
- "Reminder" pattern
| |
| +-cortex-m3\
- Examples for the Cortex-M3 processor
| | +-iar\
- Examples with the IAR compiler
| |
+-game-ev-lm3s811
- "Fly ‘n’ Shoot" game example
| |
+-game-qk-ev-lm3s811
- "Fly ‘n’ Shoot" game example with QK-nano
| |
+-pelican-ev-lm3s811
- PELICAN crossing example
| |
+-pelican-qk-ev-lm3s811 - PELICAN crossing example with QK-nano
| |
| +-msp430\
- Examples for the MSP430 processor
| | +-iar\
- Examples with the IAR compiler
| |
+-bomb-eZ430
- Time bomb example
| |
+-bomb-qk-eZ430
- Time bomb with QK-nano
| |
+-dpp-eZ430
- Simplified DPP application
| |
+-pelican-eZ430
- PELICAN crossing example
| |
+-pelican-qk-eZ430 - PELICAN crossing example with QK-nano
| |
+-qhsmtst-eZ430
- QHsmTst example
|
+-doxygen\
- QP-nano documentation generated with Doxygen
| +-html\
- "QP-nano Reference Manual" in HTML format
| | +-index.html
- The starting HTML page for "QP-nano Reference Manual"
| | +- . . .
| +-Doxyfile
- Doxygen configuration file to generate the Manual
| +-qpn.chm
- QP-nano Reference Manual" in CHM Help format
| +-qpn_rev.h
- QP-nano revision history
3. QP-nano中的临界区
QP-nano通过短暂锁定和解锁中断来实现临界区的原子执行。与完整版QP不同的是,QP-nano针对任务级代码和ISR采用了不同的中断锁定策略。
3.1 任务级中断锁定
对于任务级(从活动对象调用的代码),QP-nano采用简单的无条件中断锁定和解锁策略。在这种策略下,无论进入临界区之前中断是锁定还是解锁的,退出临界区时都会无条件解锁中断。QP-nano的宏
QF_INT_LOCK()
和
QF_INT_UNLOCK()
封装了编译器提供的从C语言锁定和解锁中断的实际机制。以下是任务级中断锁定和解锁宏的示例:
#define QF_INT_LOCK() disable()
#define QF_INT_UNLOCK() enable()
这种简单的任务级策略非常适合低端MCU的架构,临界区执行速度快且直接,但不允许临界区嵌套。需要注意的是,由于大多数QP-nano函数内部会锁定中断,因此不应在已经建立的临界区内调用QP-nano函数。
3.2 ISR级中断锁定
对于ISR级,QP-nano提供了三种临界区实现选项:
1.
默认选项
:不做任何操作,即ISR内部既不锁定也不解锁中断。这种策略适用于中断不能嵌套的情况,因为此时整个ISR代表一个临界区代码。但在ISR内部解锁中断通常不可取,因为大多数低端微控制器不适合处理中断嵌套。
2.
使用任务级中断策略
:通过定义宏
QF_ISR_NEST
来选择此策略。在这种情况下,
QActive_postISR()
和
QF_tick()
函数会无条件锁定和解锁中断。为避免临界区嵌套,在从ISR调用这些函数之前,必须确保中断已解锁。示例代码如下:
#define QF_ISR_NEST
/* QF_ISR_KEY_TYPE not defined */
-
保存和恢复中断状态
:这是一种更高级的策略,通过定义
QF_ISR_NEST、QF_ISR_KEY_TYPE、QF_ISR_LOCK()和QF_ISR_UNLOCK()宏来实现。该策略虽然成本较高,但最为健壮,因为只有在进入临界区时中断是解锁的情况下,退出时才会解锁中断。示例代码如下:
#define QF_ISR_NEST
#define QF_ISR_KEY_TYPE int
#define QF_ISR_LOCK(key_) ((key_) = int_lock())
#define QF_ISR_UNLOCK(key_) int_unlock(key_)
这种策略在基于ARM7或ARM9内核的MCU中非常有用,因为这些架构支持FIQ和IRQ两种中断类型,不同类型的中断处理方式不同。
4. QP-nano中的状态机
QP-nano包含一个名为QEP-nano的分层事件处理器,支持分层和非分层状态机。与完整版QEP的唯一区别在于,QEP-nano中的当前事件直接嵌入在状态机中,可通过 “me” 指针访问,无需作为参数传递给状态处理函数。
以下是QHsm基本结构(类)的声明:
typedef uint8_t QState; /* status returned from a state-handler function */
typedef QState (*QStateHandler)(struct QHsmTag *me);
typedef struct QHsmTag {
QStateHandler state; /* current active state of the HSM (private) */
QEvent evt; /* currently processed event in the HSM (protected) */
} QHsm;
#define QHsm_ctor(me_, initial_) ((me_)->state = (initial_))
void QHsm_init(QHsm *me);
#ifndef QK_PREEMPTIVE
void QHsm_dispatch(QHsm *me);
#else
void QHsm_dispatch(QHsm *me) Q_REENTRANT;
#endif
QState QHsm_top(QHsm *me);
#define Q_SIG(me_) (((QFsm *)(me_))->evt.sig)
#if (Q_PARAM_SIZE != 0)
#define Q_PAR(me_) (((FHsm *)(me_))->evt.par)
#endif
#define Q_RET_HANDLED ((QState)0)
#define Q_RET_IGNORED ((QState)1)
#define Q_RET_TRAN ((QState)2)
#define Q_RET_SUPER ((QState)3)
#define Q_HANDLED() (Q_RET_HANDLED)
#define Q_IGNORED() (Q_RET_IGNORED)
#define Q_TRAN(target_) (((QFsm *)me)->state = (QStateHandler)(target_), Q_RET_TRAN)
#define Q_SUPER(super_) (((QFsm *)me)->state = (QStateHandler)(super_), Q_RET_SUPER)
5. QP-nano中的活动对象
QF-nano实时框架提供了基结构
QActive
用于派生特定于应用的活动对象。
QActive
结合了以下三个基本元素:
1.
状态机
:派生自
QHsm
或
QFsm
。
2.
事件队列
:用于存储待处理的事件。
3.
执行线程
:具有唯一的优先级。
以下是
QActive
基结构和相关函数的声明:
typedef struct QActiveTag {
#ifndef QF_FSM_ACTIVE
QHsm super; /* derives from the QHsm base structure */
#else
QFsm super; /* derives from the QFsm base structure */
#endif
uint8_t prio; /* active object priority 1..QF_MAX_ACTIVE */
uint8_t head; /* index to the event queue head */
uint8_t tail; /* index to the event queue tail */
uint8_t nUsed; /* number of events currently present in the queue */
#if (QF_TIMEEVT_CTR_SIZE != 0)
QTimeEvtCtr tickCtr; /* time event down-counter */
#endif
} QActive;
#ifndef QF_FSM_ACTIVE
#define QActive_ctor(me_, initial_) QHsm_ctor(me_, initial_)
#else
#define QActive_ctor(me_, initial_) QFsm_ctor(me_, initial_)
#endif
#if (Q_PARAM_SIZE != 0)
void QActive_post(QActive *me, QSignal sig, QParam par);
void QActive_postISR(QActive *me, QSignal sig, QParam par);
#else
void QActive_post(QActive *me, QSignal sig);
void QActive_postISR(QActive *me, QSignal sig);
#endif
#if (QF_TIMEEVT_CTR_SIZE != 0)
void QF_tick(void);
#if (QF_TIMEEVT_CTR_SIZE == 1)
#define QActive_arm(me_, tout_) ((me_)->tickCtr = (QTimeEvtCtr)(tout_))
#define QActive_disarm(me_) ((me_)->tickCtr = (QTimeEvtCtr)0)
#else
void QActive_arm(QActive *me, QTimeEvtCtr tout);
void QActive_disarm(QActive *me);
#endif
#endif
6. QP-nano中的系统时钟滴答
为了管理时间事件,QP-nano要求从一个称为系统时钟滴答的周期性时间源调用
QF_tick()
函数。系统时钟滴答的速率通常在10Hz到100Hz之间。
以下是
QF_tick()
函数的实现:
void QF_tick(void) {
static uint8_t p; /* declared static to save stack space */
p = (uint8_t)QF_MAX_ACTIVE;
do {
static QActive *a; /* declared static to save stack space */
a = (QActive *)Q_ROM_PTR(QF_active[p].act);
if (a->tickCtr != (QTimeEvtCtr)0) {
if ((--a->tickCtr) == (QTimeEvtCtr)0) {
#if (Q_PARAM_SIZE != 0)
QActive_postISR(a, (QSignal)Q_TIMEOUT_SIG, (QParam)0);
#else
QActive_postISR(a, (QSignal)Q_TIMEOUT_SIG);
#endif
}
}
} while ((--p) != (uint8_t)0);
}
在QP-nano中,
QF_tick()
函数只能从时钟滴答ISR中调用,必须始终运行到完成,且不能自我抢占。特别要注意的是,调用
QF_tick()
的时钟滴答ISR不能自我抢占,也不应从两个可能相互抢占的不同ISR中调用该函数。
综上所述,QP-nano在嵌入式系统中具有重要的应用价值,通过合理运用其代码结构、临界区策略、状态机和活动对象等特性,可以高效地开发出满足需求的嵌入式应用。
QP-nano技术详解:从代码结构到关键机制
7. 关键技术点分析
7.1 数组与优先级的关系
QF_active[]
数组在QP-nano中起着关键作用,其长度与
QF_MAX_ACTIVE
宏精确匹配,这确保了系统对活动对象数量的准确管理。数组中活动对象控制块的顺序决定了活动对象的优先级,优先级编号从1开始,数值越大表示优先级越高,最高优先级由
QF_MAX_ACTIVE
决定,且在QP-nano中不能超过8,优先级0则保留给空闲循环。这种设计使得系统能够清晰地对不同优先级的活动对象进行调度和管理。
7.2 临界区策略的选择
QP-nano针对任务级和ISR级采用不同的中断锁定策略,这是为了适应不同场景的需求。任务级采用简单的无条件中断锁定和解锁策略,适用于低端MCU架构,保证了临界区执行的快速和直接,但要注意避免临界区嵌套。而ISR级提供了三种选项,用户可以根据具体的硬件和应用场景进行选择。默认选项适用于中断不能嵌套的情况;使用任务级中断策略时,要确保在调用相关函数前中断已解锁;保存和恢复中断状态的高级策略虽然成本高,但在处理不同类型中断的MCU中非常有用,如基于ARM7或ARM9内核的MCU。
7.3 状态机的特性
QEP-nano作为分层事件处理器,支持分层和非分层状态机。与完整版QEP的区别在于当前事件直接嵌入在状态机中,通过 “me” 指针访问,无需作为参数传递给状态处理函数。这种设计简化了状态处理函数的参数传递,提高了代码的可读性和可维护性。同时,QEP-nano支持完整的分层状态机特性,包括入口和出口动作以及嵌套初始转换。
7.4 活动对象的构成
QActive
基结构是QP-nano中活动对象的基础,它结合了状态机、事件队列和具有唯一优先级的执行线程。状态机可以派生自
QHsm
或
QFsm
,用户可以根据需求选择分层或非分层状态机。事件队列用于存储待处理的事件,通过
head
、
tail
和
nUsed
来管理队列的状态。执行线程的优先级决定了活动对象在系统中的调度顺序。
8. 操作步骤总结
8.1 配置QP-nano环境
-
确定
QF_MAX_ACTIVE的值,确保QF_active[]数组的长度与之匹配。 -
根据目标硬件和编译器,配置
QF_INT_LOCK()和QF_INT_UNLOCK()宏,实现任务级中断锁定和解锁。 -
根据中断嵌套情况,选择合适的ISR级中断锁定策略,并在
qpn_port.h中进行相应的宏定义。
8.2 创建状态机
-
定义状态处理函数,返回
QState类型的状态。 -
使用
QHsm_ctor()初始化状态机的初始状态。 -
调用
QHsm_init()触发状态机的初始转换。 -
使用
QHsm_dispatch()函数将事件分发给状态机进行处理。
8.3 创建活动对象
-
根据需求选择
QHsm或QFsm作为基类,定义活动对象的状态机。 -
使用
QActive_ctor()初始化活动对象的状态机。 -
根据事件参数的配置,选择合适的
QActive_post()和QActive_postISR()函数来发布事件。 -
如果需要使用时间事件,配置
QF_TIMEEVT_CTR_SIZE,并使用QActive_arm()和QActive_disarm()函数来管理时间事件。
8.4 配置系统时钟滴答
- 选择合适的系统时钟滴答速率,通常在10Hz到100Hz之间。
-
在时钟滴答ISR中调用
QF_tick()函数,确保该函数能够正常运行且不被自我抢占。
9. 总结与展望
QP-nano是一个轻量级且功能强大的实时框架,适用于嵌入式系统开发。通过合理运用其代码结构、临界区策略、状态机和活动对象等特性,可以高效地开发出满足需求的嵌入式应用。在未来的开发中,开发者可以进一步探索QP-nano的高级特性,如在不同硬件平台上的优化、更复杂的状态机设计和多任务调度等。同时,随着嵌入式系统的不断发展,QP-nano也有望在更多领域得到应用,为嵌入式开发带来更多的便利和创新。
以下是QP-nano关键机制的流程图:
graph TD;
A[系统启动] --> B[配置环境];
B --> C[创建状态机];
B --> D[创建活动对象];
B --> E[配置系统时钟滴答];
C --> F[状态机处理事件];
D --> G[活动对象发布事件];
E --> H[时钟滴答触发QF_tick];
H --> I[处理时间事件];
F --> J[状态转换];
G --> F;
I --> G;
通过以上的分析和总结,我们对QP-nano有了更深入的了解,希望这些内容能够帮助开发者更好地使用QP-nano进行嵌入式系统开发。
QP-nano技术:代码结构与关键机制详解
超级会员免费看
1万+

被折叠的 条评论
为什么被折叠?



