当单片机遇到状态机——入门QP
扫描二维码
随时随地手机看文章
前言
前些日子在微信上看到李肖遥的公众号,里面系统讲述了QP框架,我很有感触。我用QP框架很多年了,一开始是使用QM和QPC ,到后来抛弃了QM,直接使用QPC裸写程序,到后来自己写状态机框架。
- 入门QP
- 从switch-case到框架的进化
- QP的高阶使用和QM的使用
- QP的哲学
- 其他
入门QP
我们学习一个语言,或者一项技术,第一件要做的事情,就是实现一个类似于Hello world的最小程序。在单片机上,当然就是LED灯的闪烁。不说废话了,先上代码。
代码结构
代码结构,可以在Keil工程中看到,是一个QP的运行最小系统。QP版本使用的是最新的V6.9.3版本。
-
main.c 包含了硬件的初始化、QP框架的初始化、各状态机模块(暂定称呼,严谨应叫AO模块)的构建,框架的启动等一系列流程。
-
bsp.c 硬件初始化,此处仅包含SysTick的初始化和SysTick中断函数。
-
ao_led.c LED状态机的源码。
-
hook.c QP框架的回调函数的实现,此处都为空函数,暂时不予实现。
-
evt_def.h 事件的定义。QP框架的事件定义,使用枚举实现。个人觉得,事件的定义,如果用字符串实现,更加有利于模块的解耦和对分布式的支持(这个问题可参考后续的博客《将软总线进行到底》)。QP使用枚举来定义事件,个人认为是为了降低RAM和CPU的开销。
-
其他
- QP源码
- QP接口代码
- QP框架对硬件平台或者RTOS的接口源码。
- MCU相关代码,包含Startup文件、CMSIS相关、固件库相关代码
QP的启动流程
以下代码就是QP框架的启动过程。
#include "evt_def.h" // 事件定义头文件
#include "bsp.h" // 硬件初始化
#include "ao_led.h" // LED状态机
Q_DEFINE_THIS_MODULE("Main") // 定义当前的模块名称,此名称在QS和断言中会使用。
ao_led_t led; // 状态机LED对象
int main(void)
{
static QSubscrList sub_sto[MAX_PUB_SIG]; // 定义订阅缓冲区
static QF_MPOOL_EL(m_evt_t) sml_pool_sto[128]; // 定义事件池
QF_init(); // 状态机框架初始化
QF_psInit(sub_sto, Q_DIM(sub_sto)); // 发布-订阅缓冲区的初始化
QF_poolInit(sml_pool_sto, // 事件池的初始化
sizeof(sml_pool_sto),
sizeof(sml_pool_sto[0]));
ao_led_ctor(