QM  4.5.1
QM™ Tutorial

This tutorial describes how to use QM™ to model and implement a simple "Blinky" application, which can blink an LED on an embedded board or just print the state changes of the LED to the screen when executed on the desktop.

Note
This tutorial assumes QP/C Framework (version 6). To work with other QP framework types (QP/C++ or QP-nano) it is recommended to create your Blinky model from a model template for the specific framework type, as described below.

QM Tutorial Video

If you prefer to watch the video version of this tutorial, it is available on the Quantum Leaps YouTube Channel:


Creating New Model from Scratch

To 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 :

New Model dialog
  • 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:" field). 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:" field). 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 <your-directory>\blinky, where <your-directory> is a directory of your choice. 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.
  • If you wish to build the model from scratch, skip the next section and proceed to Adding Model Items.

Creating Blinky Model from Template

Alternatively, you can create the Blinky model from the Provided Template. As shown in the screen shot below, you select the blinky.qm Template in the New Model dialog:

the Blinky Template in New Model dialog

When you close the dialog with the OK button in this case, the blinky model will be copied from the provided template and will be ready. You can inspect the model, show the Blinky state diagram, generate code from it, build the application and run it on your desktop PC.

Attention
The blinky model templates are available for all QP framework types (QP/C, QP/C++ and QP-nano), not just for QP/C. These model templates are a bit more advanced than the model you will create from scratch in the rest of this Tutorial. The models created from these templates are annotated with comments and documentation and it is highly recommended that you get familiar with the structure of these models.

Adding Model Items

Now you can to start adding items to the new model.

Add a Package

The first item you add is a Package. A package in UML is a grouping construct that allows you to combine other model items into a higher-level unit—the package. The most common use of a package is to group together classes, but a package can hold also free attributes, free operations, and even other packages.

  • In the Model 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.
Add Package
  • In the Property Editor change the package name to AOs (Active Objects) and the stereotype to components.
Package Properties

Add a Class

Next you need to add a class to the new package, because only classes can have behavior (i.e., State Machines).

  • In the Model Explorer view right-click on the AOs package and select Add Class from the popup menu.
Add Class
  • In the Property Editor change the class name to Blinky and the superclass to qpc::QActive.
Setting Superclass
Note
In QM™ a State Machine can be associated only with a class that inherits one of the QP state machine classes. The choice of the state machine superclass determines the state machine implementation strategy.

Add a Time Event

Next, you need to add a Time Event attribute that will deliver the periodic stimulus to trigger blinking in your Blinky state machine.

  • In the Model Explorer right-click on the Blinky class and select Add Attribute from the popup menu.
Adding Attribute
  • In the Property Editor change the attribute name to timeEvt, the type to qpc::QTimeEvt, and visibility to private.
Attribute Properties

Add State Machine

  • In the Model Explorer right-click on the Blinky class and select Add State Machine from the popup menu.
Adding State Machine

Drawing State Machine Diagram

  • In the Model Explorer right-click on the SM (State Machine) item and select Show Diagram from the popup menu. Alternatively you can just double-click on the SM item to execute its default action, which is to show the diagram.
Show State Machine Diagram

Add States

  • In the Diagram 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-right corner of the state shape. Release the mouse.
Adding the off State
  • 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();.

Add Initial Transition

  • In the Diagram 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.
Adding the Initial Transition
  • In the Property Editor add action code to this initial transition QTimeEvt_armX(&me->timeEvt, BSP_TICKS_PER_SEC/2, BSP_TICKS_PER_SEC/2);
Note
The action code of the initial transition arms the time event to expire in BSP_TICKS_PER_SEC/2 number of clock ticks (i.e., in 1/2 of a second) and also every BSP_TICKS_PER_SEC/2 number of clock ticks (i.e., every 1/2 of a second).

Add Transitions

  • In the Diagram 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 (). 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.
Adding a Transition
  • In the Property Editor change the trigger of this transition to TIMEOUT.
  • In the similar way as before add second transition and change its trigger also to TIMEOUT.
Complete State Machine

Generating Code

Compared 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 (see Physical Design). 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 .

Add Directory

First, you need to create a directory, which will determine the location of the files generated on disk relative to the QM Model File.

Adding a Directory
  • In the Model Explorer right-click on the blinky model item and select Add Directory in the popup menu. This will be the directory, where your code will be generated. The path of the directory relative to the QM Model File can be edited in the Property Editor. Type . (dot) for the name of the directory. The dot means the same directory as the QM Model File, meaning that your code will be generated in the same directory as your model.

Add File

Once you have a directory, you can add Files to it. In a real-life project you would typically split the code into 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:

Adding a File
  • In the Model 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 in the Property Editor. Type blinky.c in the name box and press Enter. Note that the file icon changes to  .

Edit File

In QM™ you provide the body of every file-template, in which you can type your own code as well as Code-Generation Directives.

  • In the Model Explorer double-click on the blinky.c file to open the file in a window. Next copy the following code to the Clipboard and paste it into the file window.
#include "qpc.h"
#include <stdio.h>
#include <stdlib.h> /* for exit() */
Q_DEFINE_THIS_FILE
enum { 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 * const module, int loc) {
fprintf(stderr, "Assertion failed in %s:%d", module, loc);
exit(-1);
}
void QF_onStartup(void) {}
void QF_onCleanup(void) {}
void QF_onClockTick(void) {
QF_TICK_X(0U, (void *)0); /* perform the QF clock tick processing */
}
enum BlinkySignals {
TIMEOUT_SIG = Q_USER_SIG,
MAX_SIG
};
/*============== ask QM to declare the Blinky class ================*/
$declare${AOs::Blinky}
static Blinky l_blinky;
QActive * const AO_Blinky = &l_blinky.super;
static void Blinky_ctor(void) {
Blinky *me = (Blinky *)AO_Blinky;
QActive_ctor(&me->super, Q_STATE_CAST(&Blinky_initial));
QTimeEvt_ctorX(&me->timeEvt, &me->super, TIMEOUT_SIG, 0U);
}
int main() {
/* statically allocate event queue buffer for the Blinky AO */
static QEvt const *blinky_queueSto[10];
QF_init(); /* initialize the framework */
Blinky_ctor(); /* explicitly call the "constructor" */
QACTIVE_START(AO_Blinky,
1U, /* priority */
blinky_queueSto, Q_DIM(blinky_queueSto),
(void *)0, 0U, /* no stack */
(QEvt *)0); /* no initialization event */
return QF_run(); /* run the QF application */
}
/*================ ask QM to define the Blinky class ================*/
$define${AOs::Blinky}
Attention
The listing above shows two most important code generation directives: $declare${AOs::Blinky} for generating the Declaration of the Blinky class, and $define${AOs::Blinky} for generating the Definition of the of the Blinky class.


Opening blinky.c File and Pasting the Code

Generate Code

  • Generate code by pressing the Generate Code button in the Tools toolbar.
Code Generation

At this point QM™ has generated the blinky.c file in the same directory as the blinky.qm QM Model File. You can inspect the generated blinky.c file on the disk with your favorite code editor.


Building the Project

You build the generated code just as any other hand-crafted code. This simplistic tutorial generated the whole project in just one source file blinky.c.

Attention
The following sections show how to build the Blinky project from the command-prompt. However, it is also possible to build the project directly from QM, by defining an external tool in the Manage Tools Dialog Box.

Building Blinky on Windows

The 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() statements. Here is how to build the blinky.exe executable on Windows command prompt:

Blinky on Windows

First, you set the environment variable QPC to point to the location of the QP/C framework on your machine. Here, QCP=C:\qp\qpc, which is the default location, but if you installed QP/C in different location, you need to set QPC to the actual location on your machine.

set QPC=C:\qp\qpc

Next, you invoke the free gcc compiler to build your blinky.c file. Here, the MinGW gcc is taken from the QTools collection installed in the default C:\qp\qtools\bin.

gcc blinky.c -oblinky.exe -I%QPC%\include -I%QPC%\ports\win32 -L%QPC%\ports\win32\dbg -lqp
Note
On Windows the free gcc compiler is typically not installed, so you need to download and install it. The QTools collection for Windows contains the MinGW gcc compiler and other Unix-style utilities.

Finally, you run the produced blinky.exe file, which starts printing to your screen.

blinky

You exit the application by pressing Ctrl+C.

Building Blinky on Linux (POSIX)

Building and running Blinky on the POSIX platforms (such as Linux and MacOS) is a little bit more involved, because the QP framework is built from sources as opposed to being linked as a pre-built library. The recommended procedure is to create the Blinky model from the existing template, which also generates the Makefile for building the application. The following screen shot shows how to build and run the Blinky project on Linux:

Blinky on Linux

Building Blinky for an Embedded Board

To 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 "Getting Started" web-page for more information about getting started with the QP frameworks. This web-page contains also "Getting Started" manuals for QP/C, QP/C++, and QP-nano.

Blinky on TivaC LaunchPad

Next: Model Examples