本帖最后由 68336016 于 2014-2-5 11:36 编辑
只是算我个人学习QP™状态机框架的一个笔记吧,因为我学习东西没耐心一开始就看上几百页的书,看完之后满头雾水还不知道怎么入手。
所以我一般都是大概了解下概念,然后简单例子入手,以逐步加深认识,增强信心。
前提:使用MDK环境,STM32F10x芯片。
QP产品里几个名词区别:
暂时用得上的只是QP/C,QM,打开上面链接下载exe版本,一路点击NEXT安装完就行。
/*****************************************************************************************************************************************************/
这是代码,运行起来再说其它的。
我用的是MDK4.70,STM32F103R8T6,LED引脚是PB15,高电平点亮。
libqp_Cortex-M3.a路径为:..\QPC\ports\arm-cm\vanilla\arm_keil\rel\libqp_Cortex-M3.a
bsp.h- #ifndef bsp_h
- #define bsp_h
- void BSP_init(void);
- void BSP_led_Off(void);
- void BSP_led_On(void);
- /* bsp_h */
- #endif
复制代码
bsp.c- #include "qp_port.h"
- #include "bsp.h"
- #include "hw_config.h"
- #include "stm32f10x_lib.h"
- /*..........................................................................*/
- void BSP_init(void) {
- System_Init(); //系统初始化函数,定义在hw_config.c
- }
- /*..........................................................................*/
- void BSP_led_Off(void) {
- GPIOB->BRR = GPIO_Pin_15;
- }
- /*..........................................................................*/
- void BSP_led_On(void) {
- GPIOB->BSRR = GPIO_Pin_15;
- }
复制代码
blinky.c- /* @(/1/1/0) ...............................................................*/
- #include "qp_port.h"
- #include "bsp.h"
- #include "stm32f10x_lib.h"
- #define BSP_TICKS_PER_SEC 100
- void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line) {
- }
- void QF_onStartup(void) {
- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);/* Select AHB clock(HCLK) as SysTick clock source */
- NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick, 0, 0);/* Set SysTick Priority to 3 */
- SysTick_SetReload(480000);/* SysTick interrupt each 10ms with HCLK equal to 48MHz */
- SysTick_ITConfig(ENABLE);/* Enable the SysTick Interrupt */
- SysTick_CounterCmd(SysTick_Counter_Enable); //运行起来
- }
- void QF_onIdle(void) {
- #ifdef NDEBUG
- /* put the CPU and peripherals to the low-power mode, see NOTE02 */
- __WFI();
- #endif
- QF_INT_ENABLE(); /* always enable interrupts */
- }
- void QF_onCleanup(void) {}
- void QF_onClockTick(void) {
- QF_TICK((void *)0);
- }
- /*..........................................................................*/
- void SysTickHandler(void) {//把原先在stm32f10x_it.c中的代码注释掉,挪到这里
- //QF_onClockTick(); //SysTick_Configuration周期定义为10ms
- QF_TICK_X(0U, (void *)0); /* process all armed time events */
- }
- enum BlinkySignals {
- TIMEOUT_SIG = Q_USER_SIG,
- MAX_SIG
- };
- /* @(/1/0) .................................................................*/
- typedef struct BlinkyTag {
- /* protected: */
- QActive super;
- /* private: */
- QTimeEvt timeEvt;
- } Blinky;
- /* protected: */
- static QState Blinky_initial(Blinky * const me, QEvt const * const e);
- static QState Blinky_led_Off(Blinky * const me, QEvt const * const e);
- static QState Blinky_led_On(Blinky * const me, QEvt const * const e);
- /* @(/1/0) .................................................................*/
- /* @(/1/0/1) ...............................................................*/
- /* @(/1/0/1/0) */
- static QState Blinky_initial(Blinky * const me, QEvt const * const e) {
- QTimeEvt_postEvery(&me->timeEvt, (QActive *)me, BSP_TICKS_PER_SEC/2);
- return Q_TRAN(&Blinky_led_Off);
- }
- /* @(/1/0/1/1) .............................................................*/
- static QState Blinky_led_Off(Blinky * const me, QEvt const * const e) {
- QState status_;
- switch (e->sig) {
- /* @(/1/0/1/1) */
- case Q_ENTRY_SIG: {
- BSP_led_Off();
- status_ = Q_HANDLED();
- break;
- }
- /* @(/1/0/1/1/0) */
- case TIMEOUT_SIG: {
- status_ = Q_TRAN(&Blinky_led_On);
- break;
- }
- default: {
- status_ = Q_SUPER(&QHsm_top);
- break;
- }
- }
- return status_;
- }
- /* @(/1/0/1/2) .............................................................*/
- static QState Blinky_led_On(Blinky * const me, QEvt const * const e) {
- QState status_;
- switch (e->sig) {
- /* @(/1/0/1/2) */
- case Q_ENTRY_SIG: {
- BSP_led_On();
- status_ = Q_HANDLED();
- break;
- }
- /* @(/1/0/1/2/0) */
- case TIMEOUT_SIG: {
- status_ = Q_TRAN(&Blinky_led_Off);
- break;
- }
- default: {
- status_ = Q_SUPER(&QHsm_top);
- break;
- }
- }
- return status_;
- }
- static Blinky l_blinky;
- QActive *AO_Blinky = &l_blinky.super;
- static void Blinky_ctor(void) {
- Blinky *me = (Blinky *)AO_Blinky;
- QActive_ctor(&me->super, (QStateHandler)&Blinky_initial);
- QTimeEvt_ctor(&me->timeEvt, TIMEOUT_SIG);
- }
- int main() {
- static QEvt const *blinky_queueSto[10]; /* 这行挪到这里 */
- BSP_init(); /* 自己添加这行,定义在bsp.c */
- Blinky_ctor();
- QF_init();
- QActive_start(AO_Blinky, 1,
- blinky_queueSto, Q_DIM(blinky_queueSto),
- (void *)0, 1024, (QEvt *)0);
- return QF_run();
- }
复制代码
hw_config.h
- /* Define to prevent recursive inclusion -------------------------------------*/
- #ifndef __HW_CONFIG_H
- #define __HW_CONFIG_H
- /* Includes ------------------------------------------------------------------*/
- #include "stm32f10x_lib.h"
- /* Exported types ------------------------------------------------------------*/
- /* Exported constants --------------------------------------------------------*/
- /* Exported macro ------------------------------------------------------------*/
- /* Exported define -----------------------------------------------------------*/
- /* Exported functions ------------------------------------------------------- */
- void System_Init(void);
- /* External variables --------------------------------------------------------*/
- #endif /*__HW_CONFIG_H*/
- /******************* (C) COPYRIGHT 2008 STMicroelectronics *****END OF FILE****/
复制代码
hw_config.c
至于stm32f10x_it.c中断函数文件,将以下注释掉就可以
- //void SysTickHandler(void)
- //{
- //
- //}
复制代码
值得注意的是,导入libqp_Cortex-M3.a(在QPC安装目录下)要设置一下
将LED的IO口稍微修改下,估计在你的板上已经能闪灯了。
/*****************************************************************************************************************************************************/
现在再说blinky上的代码怎么来的,大部分是用QM生成的,小部分是得自己敲的。
QM怎么使用呢?请看官方的教程,官方代码是在PC上运行的,所以跟我这有很细微的差异,自己对比下Blinky.c一看就明白。
为了方便看贴,我把官方的例子贴在这,看图就行,英文几乎不用看
This tutorial describes how to use QM™ to model and implement a simple "Blinky" application, which can blink an LED on an ebmedded board or just print the state changes of the LED to the screen when executed on the desktop. NOTE: Before you start this tutorial, make sure that the QM™ tool is unlocked to allow editing the model. The tool is unlocked when the unlock button () is depressed.
Creating New ModelTo create a new model, go to File->New Model... menu or press the New Model button in the Edit toolbar. This will open the following "New Model" dialog box:
- Select the QP™ framework type you want to base the new model on (the "Frameworks" panel). The choices are: qpc for QP/C™, qpcpp for QP/C++™, and qpn for QP-nano™. For this tutorial, you leave the default qpc framework type.
- Choose the model template for your model ("Templates" panel). If you don't select the template and leave at "None", your model will be empty, except the selected framework. For this tutorial, you leave the default template None, so that you can start building your model from scratch.
- Name your model ("Name:"). NOTE: the .qm extension will be added automatically. For this tutorial, you rename the model to blinky.
- Choose the directory for your model file ("Location:"). You can either type in the path manually, or you can press the button to open the directory-search dialog box.
For this tutorial, you choose the model location to <any-directory>\blinky.
NOTE: the model directory provides also the reference point for the code generation. All generated directories and files are relative to the model file directory.- Press the OK button.
top Adding Model ItemsNow you can to start adding items to the new model. The first item you add is a Package. A package in UML is a grouping construct that allows you to combine any other items into a higher-level unit—the package. The most common use of a package is to group together classes, but a packgae can hold also free attributes, free operations, and even other packages. In the Explorer view right-click on the blinky model item to get a popup-menu specific to that item. Once the popup menu opens, select Add Package.
In the Property Editor change the package name to AOs (Active Objects) and the stereotype to components.
Next you need to add a class to the new package, because only classes can have behavior (i.e., state machines). In the Explorer view right-click on the AOs package and select Add Class from the popup menu.
In the Property Editor change the class name to Bliky and the superclass to qpc::QActive.
NOTE: QM™ allows only direct or indirect subclasses of QP base classes QHsm or QFsm to have a state machine (statechart). The class QActive is a subclass of QHsm, so all its subclasses, such as Blinky in this case, also inherit the statechart. The Model Explorer uses a special icon with a dot ( or ) for classes that can have a statechart. In the Explorer view right-click on the Blinky class and select Add Attribute from the popup menu.
In the Property Editor change the attribute name to timeEvt, the type to QTimeEvt, and visibility to private.
In the Explorer view right-click on the Blinky class and select Add Statechart from the popup menu.
top Drawing a StatechartIn the Explorer view right-click on the Statechart item and select Show Diagram from the popup menu. Alternatively you can just double-click on the Statechart item to execute its default action, which is to show the diagram.
In the Toolbox click on the state tool. Move your mouse (all mouse buttons released) to the diagram window where you want to position the upper-left corner of the state shape. Notice the new shape of the mouse cursor (shown on the right). Click the mouse and drag it (with mouse button depressed) to the location of the lower-rigth corner of the state shape. Release the mouse.Click on the picture below to watch a demo.
In the Property Editor change the state name to off and add the entry action to this state BSP_ledOff();.
In the similar way as before add second state. In the Property Editor change the state name to on and add the entry action to this state BSP_ledOn();.
In the Toolbox click on the initial transition tool. Move your mouse (all mouse buttons released) to the diagram window where you want to position the begin of the initial transition shape. Notice the new shape of the mouse cursor (shown on the right). Click the mouse and drag it (with mouse button depressed) to the edge of the state. Notice the cursor change when you reach the edge. Release the mouse. Click on the picture below to watch a demo.
In the Property Editor add action code to this initial transition QTimeEvt_postEvery(&me->timeEvt, (QActive *)me, BSP_TICKS_PER_SEC/2);.
NOTE: The action code of the initial transition arms a periodic time event me->timeEvt to trigger the blinking of the LED. You still need to add the timeEvt attribute to the Blinky class. In the Toolbox click on the transition tool. Move your mouse (all mouse buttons released) to the state edge where you want to position the begin of the transition connector. Notice the new shape of the mouse cursor (shown on the right). Click the mouse and drag it (with mouse button depressed) to the edge of the state. Notice the cursor change when you reach the edge. Release the mouse. Click on the picture below to watch a demo.
In the similar way as before add second transiton and change its trigger also to TIMEOUT.
top Generating CodeCompared to most other graphical tools based on state machines, QM™ turns the code generation upside down. QM™ lets you determine the generated code structure, directory names, file names, and elements that go into every file. You can mix your own code with the generated code and use QM to generate as much or as little of the overall code as you see fit. In a real-life project you would typically split the code into some header (.h) files, place each active object in its own source (.c) file, and use separate .c files for the Board Support Package (BSP) and main(). But for the sake of simplicity, this tutorial will put the whole implementation in just one file: blinky.c, which you create as follows. In the Explorer view right-click on the blinky model item and select Add Directory in the popup menu. The name of the directory can be edited right in the Explorer window (as well as in the Property Editor). Type . (dot) for the name of the directory. The dot means the same directory as the model. In the Explorer view right-click on the . directory item and select Add File in the popup menu. After the file is created, you can edit its name right in the Explorer (as well as in the Property Editor). Type blinky.c and press Enter. Note that the file icon changes to .
In the Explorer view double-click on the blinky.c file to open the file in a widnow. Next copy the following code into the file window (notice the highlighted code-generating directives $declare() and $define(). - #include "qp_port.h"
- #include <stdio.h>
- #include <stdlib.h>
- #define BSP_TICKS_PER_SEC 100
- void BSP_ledOff(void) {
- printf("LED OFF\n");
- }
- void BSP_ledOn(void) {
- printf("LED ON\n");
- }
- void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line) {
- fprintf(stderr, "Assertion failed in %s, line %d", file, line);
- exit(0);
- }
- void QF_onStartup(void) {}
- void QF_onCleanup(void) {}
- void QF_onClockTick(void) {
- QF_TICK((void *)0);
- }
- enum BlinkySignals {
- TIMEOUT_SIG = Q_USER_SIG,
- MAX_SIG
- };
- $declare(AOs::Blinky)
- $define(AOs::Blinky)
- static Blinky l_blinky;
- QActive *AO_Blinky = &l_blinky.super;
- static void Blinky_ctor(void) {
- Blinky *me = (Blinky *)AO_Blinky;
- QActive_ctor(&me->super, (QStateHandler)&Blinky_initial);
- QTimeEvt_ctor(&me->timeEvt, TIMEOUT_SIG);
- }
- int main() {
- Blinky_ctor();
- QF_init();
- static QEvt const *blinky_queueSto[10];
- QActive_start(AO_Blinky, 1,
- blinky_queueSto, Q_DIM(blinky_queueSto),
- (void *)0, 1024, (QEvt *)0);
- return QF_run();
复制代码
In QM™ you provide the body of every file and then you instruct QM™ to synthesize code for selected model items by means of code-generation directives. The listing above shows two most important such directives: $declare() for generating the declaration of the selected item, and $define() for generating the definition of the selected item. Please refer to the section Code Engineering for more information about code generation in QM™ Generate code by pressing the Generate Code button in the Tools toolbar.
At this point QM™ has generated the blinky.c file in the same directory as the blinky.qm model file. You can instpect the generated blinky.c file on the disk with your favorite code editor. top Building the ProjectYou build the generated code just as any other hand-crafted code. This simplistic tutorial generated the whole project in just one soruce file blinky.c. Building Blinky on Windows/LinuxThe Board Support Package (BSP) functions coded at the beginning if the blinky.c file are designed to run on the desktop OS, such as Windows or Linux, because of the printf() instructions. Here is how to build the blinky.exe executable on Windows using the MinGW compiler:
gcc blinky.c -oblinky.exe -I%QPC%\include -I%QPC%\ports\win32\mingw -L%QPC%\ports\win32\mingw\dbg -lqp And here is how to build the blinky executable on Linux using the standard GNU compiler: gcc blinky.c -oblinky -I$QPC/include -I$QPC/ports/posix/gnu -L$QPC/ports/posix/gnu/dbg -lqp -lpthread NOTE: this command assumes that you have installed QP/C on your machine and that you have defined the environment variable QPC to pont to the installation directory of QP/C. After you execute the build command, you can run the blinky.exe from the console, as shown in the following screen shot. You exit the application by pressing Ctrl+C.
Building Blinky for an Embedded BoardTo build the Blinky project for an embedded board, you need to modify the BSP (Board Support Package), to turn the LED on and off. You also need to use the specific cross-compiler. Please refer to the specific QP Development Kits (QDKs) for more information about building QP projects for specific embedded boards.
/*****************************************************************************************************************************************************/
这个例子很简单,事件也是QP自带的TIMEOUT事件,看到这里,可能有人也奇怪,要是我自己定义的事件呢?比如按键按下,怎么产生一个事件?建立个blinky.h头文件,方便其它文件调用。- #ifndef blinky_h
- #define blinky_h
- #include "qp_port.h" /* bsp_h *
- typedef struct KeyEvtTag { //定义一个按键事件结构,照葫芦画瓢就
- QEvt super; /* derives from QEvt *
- uint8_t key_code; /* code of the key *
- } KeyEvt;
- /*...............................................................................................................*
- enum BlinkySignals
- TIMEOUT_SIG = Q_USER_SIG
- KEY_SIG, //将原先blinky.c里面的枚举定义放到头文件中,增加了按键事件KEY_SIG
- MAX_SI
- }
- #endif
复制代码
那么按键动作怎么对应一个事件呢?在你要产生事件的地方,加入以下代码,在blinky.c里面的switch里加入对KEY_SIG事件处理。- if (Key_Read() == XXXXX) //按键读取函数根据电路实际情况编写
- {
- KeyEvt e; //KeyEvt定义看上面,e是一个按键事件
- ((QEvt*)&e)->sig = KEY_SIG; //这就将按键动作跟KEY_SIG事件对应起来了
- QMSM_DISPATCH(the_blinky, (QEvt *)&e); //将事件发送出去
- }
复制代码
the_blink在blinky.c定义
- QHsm * const the_blinky = (QHsm*)&l_blinky;
复制代码 照葫芦画瓢,先运行起来,再慢慢看代码。
|