QP active object framework for mbed
History: Last updated for QP 4.5.02 on 05-Sep-2012
Description
QP is a lightweight, open source, active object framework for microcontrollers such as ARM Cortex-M3/M0. QP enables you to build well-structured event-driven programs as systems of concurrently executing, encapsulated state machines (active objects a.k.a. actors). You can think of QP as a modern real-time operating system (RTOS) specifically designed for executing state machines. The QP framework does everything you can expect from a traditional RTOS, such as preemptive multitasking, plus many things an RTOS can't do at all.
QP is also an excellent target for automatic code generation. To this end, QP is now supported by the the free graphical QM modeling tool, which can automatically generate complete mbed applications from UML state diagrams. Thus QP is your entry into graphical programming for the mbed platform.
QP improves productivity, because you no longer need to struggle with convoluted if-else
"spaghetti" code and you don't need to worry about semaphores and other such low-level RTOS mechanisms. Instead, you can work at a higher level of abstraction of events and state machines.
Background
Traditionally, mbed programs are written in a sequential manner, which means that whenever a mbed program needs to synchronize with some external event, such as a button press, arrival of a character through the serial port, or a time delay, it explicitly waits in-line for the occurrence of the event. Waiting ''in-line'' means that the mbed processor spends all of its cycles constantly checking for some condition in a tight loop (called the polling loop).
Although this approach is functional in many situations, it doesn't work very well when there are multiple possible sources of events whose arrival times and order you cannot predict and where it is important to handle the events in a timely manner. The fundamental problem is that while a sequential program is waiting for one kind of event (e.g., a button press), it is not doing any other work and is ''not responsive'' to other events (e.g., bytes from the serial port).
Another big problem with the sequential program structure is wastefulness in terms of power dissipation. Regardless of how much or how little actual work is being done, the mbed processor is always running at top speed, which drains the battery quickly and prevents you from making truly long-lasting battery-powered devices.
For these and other reasons experienced programmers turn to the long-know design strategy called event-driven programming, which requires a distinctly different way of thinking than conventional sequential programs. All event-driven programs are naturally divided into the application, which actually handles the events, and the supervisory event-driven infrastructure (framework), which waits for events and dispatches them to the application. The control resides in the event-driven framework, so from the application standpoint, the control is inverted compared to a traditional sequential program.
It turns out that the event-driven QP active object framework beautifully complements the mbed platform and provides everything you need to build responsive, robust, and power-efficient mbed programs based on modern hierarchical state machines. Enjoy!
Example Programs
The qp_dpp (Dining Philosophers Problem) program provides a simple example of using the QP framework. Please refer to the QL Notebook page "Building Applications with QP (qp_dpp Example)" for more information. Among others, the qp_dpp
program illustrates the use of the preemptive QK kernel.
The qp_pelican (PEdestrian LIght CONtrolled crossing) program provides a more interesting hierarchical state machine example. Please refer to the QL Notebook page "Hierarchical State Machines (qp_pelican Example)" for more information.
About the QP Port for mbed
The QP framework has been designed from the ground up to be highly portable and to date has been ported to more than a dozen different CPU architectures and many different compilers. The QP library offered for the mbed platform is actually the QP/C++ version of QP (the other QP versions are QP/C and QP-nano). The QP port to mbed is based on the QP/C++ port to ARM Cortex, which has been available for a number of years at state-machine.com/arm.
NOTE
The standard QP/C++ distribution (available for download from SourceForge.org) is intended to be used as a pre-compiled, fine-granularity library, which allows elimination of any unused code at link-time. This baseline QP/C++ version will work "as-is" on the mbed platform. However, it turns out that the mbed compiler recompiles all files of any library provided in source code almost every time any change is made to the including program. To minimize the overhead of this annoying re-compilation, the QP/C++ source code has been consolidated for the mbed platform in just three source files (two .CPP files and one assembly file).
The files comprising the QP/C++ framework for mbed are as follows:
+-qp/ - qp library for mbed +-qp_port.h - QP/C++ port for mbed header file +-qp.h - QP/C++ platform-independent header file +-qp.cpp - QP/C++ platform-independent source code +-qk_port.s - QK port to ARM Cortex-M0/M3, mbed ARM assembler code
Typically you should not need to edit any of the QP source code. Compile-time configuration of the QP library is achieved by a separate header file qp_config.h
, which is defined at the application level and included in the qp_port.h
header file and is described below.
Configuring QP via the qp_config.h
header file
The QP library for mbed is configured through the qp_config.h
header file, which must be provided by the application that uses QP. You can use the following version of the qp_config.h
header file to copy-and-paste into your applications:
qp_config.h
#ifndef qp_config_h #define qp_config_h // enable the Q-SPY software tracing instrumentation #define Q_SPY // enable preemptive QK kernel (cooperative kernel is used when not defined) #define QK_PREEMPTIVE // The maximum number of active objects in the application (could be up to 63) #define QF_MAX_ACTIVE 16 // Uncomment the following macros only if you want to change the given default //#define Q_EVT_CTOR 1 //#define Q_SIGNAL_SIZE 2 //#define QF_MAX_EPOOL 3 //#define QF_EVENT_SIZ_SIZE 2 //#define QF_EQUEUE_CTR_SIZE 1 //#define QF_MPOOL_SIZ_SIZE 2 //#define QF_MPOOL_CTR_SIZE 2 //#define QF_TIMEEVT_CTR_SIZE 2 #endif // qp_config_h
The most important options are:
Q_SPY
: if defined, activates the software tracing instrumentation in the QP framework. This means that virtually any activity inside a framework is output to a circular buffer in memory and can be sent to the host for inspection (please see Section "Software Tracing" below). If the macroQ_SPY
is not defined, the software tracing instrumentation becomes inactive both in the framework and in the application. "Inactive" means that the software tracing instrumentation the software tracing instrumentation remains in the code, but stops generating any code, so no overhead of any kind is incurred.
QK_PREEMPTIVE
: if defined, configures QP to use the preemptive QK kernel. If the macroQK_PREEMPTIVE
is not defined, the simpler cooperative "vanilla" kernel is used. NOTE: using the preemptive kernel requires adding special macros (QK_ISR_ENTRY
andQK_ISR_EXIT
) to every interrupt service routine (ISR). Also, the two kernels use different idle callbacks (QK_onIdle()
andQF_onIdle()
, respectively).
QF_MAX_ACTIVE
: determines the maximum number of active objects in the application. The term "active object" comes from the UML and denotes an autonomous object running in its own thread of execution. In QP active objects are state machines with a private event queue and unique priority level. The number of active objects in QP must not exceed 63, inclusive, but setting a lower limit than this maximum saves some memory (RAM).
Free Modeling and Code Generation Tool
All QP examples for the mbed microcontroller have been automatically generated by the QM (QP Modeler) tool. QM is a free, cross-platform, graphical UML modeling tool for designing and implementing real-time embedded applications based on the QP state machine frameworks. QM provides best-in-class, intuitive diagramming environment and generates very compact C or C++ code that is 100% traceable from your design. QM is available for Windows, Linux, and Mac OS X. The steps of obtaining the free QM tool and generating the code are described in the QL Notebook page "Graphical Modeling and Automatic Code Generation ".
The following screen shot shows the Dining Philosopher Problem (DPP) model open in QM (click on the picture to enlarge it to 100%):
NOTE
The QM models for the mbed projects are available for a separate download from the QP Notebook pages describing the examples, because the mbed project archives accept only source code (.h and .cpp files).
Preemptive Multitasking
The QP framework contains a fully preemptive real-time, priority-based kernel called QK. QK is a special run-to-completion kernel type particularly suitable for executing state machines. QK achieves deterministic execution of high-priority tasks, which is as good as any traditional preemptive kernel. The details of implementing the QK kernel on ARM Cortex are described in the Quantum Leaps Application Note: QP and ARM-Cortex (see also the "Documentation and Resources" at the end of this page).
Software Tracing Instrumentation
One of the biggest problems with developing embedded software is very low visibility into the running code inside the target system. You simply don't know what's going on in there. For generations, embedded developers have been using printf()
statements coupled with serial port output to get some feedback from the embedded systems to their host workstations. This technique, known as software tracing is particularly popular with the mbed platform, because the online development system offers no traditional single-step debugger.
However, clogging any time-sensitive code with printf()
statements is often too intrusive and simply not workable in many systems. When you think about it, printf()
, which performs very time-consuming formatting of binary data to ASCII, is exactly not what you should be doing in the resource-constrained target. Instead, you should perform the absolute minimum of work in the target and let the powerful host computer do the heavy lifting of formatting and pretty-printing of the data. Additional benefit of sending unformatted, binary tracing data from the target is natural data compression. It simply takes much less bytes to send the same information in binary than in ASCII.
The QP framework offers a software tracing system called QP-Spy based on binary data transmission, sophisticated data filtering, flexible data output, decoupling of data collecting from data transmission, use of the idle task for data transmission to minimize the impact on the target, thread-safety, and many other features expected from a professional software tracing system.
As any software tracing system, QP-Spy consists of a target-resident component, called QS, and the application running on the hots workstation, called QSPY.
Target-resident QS Component
On the mbed platform, the QS component sends the tracing data through the UART0, connected to the USB virtual COM port, so the tracing data arrives on the host through the same USB cable that powers mbed. The qp_dpp example program contains fully functional tracing implementation for mbed.
Host-resident QSPY Application
The binary QS output needs to be decompressed and formatted by the QSPY host application, which is available for download from SourceForge.net. The ZIP file contains pre-compiled QSPY for Windows and Linux hosts. The source code is also provided, so you can customize it and compile QSPY on any other platform.
QSPY is a console-style application that you invoke from a command line. It outputs human-readable tracing data to the console and also to a file, if you choose so. The reference manual of QSPY is available online at state-machine.com/doxygen/qspy.
Documentation and Resources
Obviously this Notebook page can only highlight some aspects of a mature software framework like QP. If you are interested to learn more about event-driven programming for embedded systems and modern hierarchical state machines, the following resources can get you started.
Information
The Quantum Leaps Application Note: QP and ARM-Cortex with GNU describes the details of the QP port to the ARM-Cortex architecture, including the preemptive QK kernel as well as a cooperative kernel available in QP. This Application Note uses the GNU compiler and the LCPXpresso-1114 (Cortex-M0) and LPCXpresso-1343 (Cortex-M3) boards, but the discussion is applicable to almost any ARM Cortex device, such as the LPC1768 used in mbed.
Information
The Embedded Systems Design cover story article "Build a Super Simple Tasker" describes the principles of an run-to-completion, preemptive, priority-based real-time kernel. The preemptive QK kernel included in QP works exactly like the "Super Simple Tasker", but does not replicate the event queuing mechanism already available in QP. This ESD article is a good reading for everyone interested in using preemptive multitasking kernel in event-driven systems and with state machines in particular.
Information
The book "Practical UML Statecharts in C/C++, Second Edition: Event-Driven Programming for Embedded Systems" (Newnes 2008) is the most popular book about UML statecharts, event-driven programming, and active object computing model for embedded systems. This ultimate resource describes all the related concepts and provides a very detailed design study of the QP frameworks.
This book is presented in two parts. In Part I, you get a practical description of the relevant state machine concepts starting from traditional finite state automata to modern UML state machines followed by state machine coding techniques and state-machine design patterns, all illustrated with executable examples.
In Part II, you find a detailed design study of a generic real-time, active object framework indispensable for combining concurrent, event-driven state machines into robust applications. Part II begins with a clear explanation of the key event-driven programming concepts such as inversion of control ("Hollywood Principle"), blocking versus non-blocking code, thread-safe encapsulation, run-to-completion (RTC) execution semantics, the importance of event queues, dealing with time, and the role of state machines to maintain the context from one event to the next. This background is designed to help software developers in making the transition from the traditional sequential to the modern event-driven programming, which can be one of the trickiest paradigm shifts.
The book is very hands-on and contains many working examples for ARM Cortex-M3, 80x86, MSP430, as well as Linux and traditional RTOS (uC/OS-II).
11 comments on QP active object framework for mbed:
Please log in to post comments.
Hello, I downloaded qp_dpp a few hours ago and couldn't get the terminal to behave correctly. In going through the code, I found the baud rate of 115200 which I changed in my terminal emulator. However, it is not operating as intended. I've tried Windows' Hyperterm and Tera Term with various terminal emulator options, encoding and serial control options. Do you have some terminal configuration hints?
thanks...kevin