AN: Capstone Dive Computer Example - Quantum Leaps
AN: Capstone Dive Computer Example - Quantum Leaps
AN: Capstone Dive Computer Example - Quantum Leaps
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
QP state machine framework example<br />
Application Note<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong><br />
<strong>Example</strong><br />
Document Revision B<br />
September 2008<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC<br />
www.quantum-leaps.com<br />
www.state-machine.com
Table of Contents<br />
1 Introduction.................................................................................................... 1<br />
1.1 Scalability........................................................................................................ 2<br />
1.2 Source Code .................................................................................................... 2<br />
1.3 Portability ........................................................................................................ 3<br />
1.4 Support for Modern State Machines ..................................................................... 3<br />
1.5 Direct Event Posting and Publish-Subscribe Event Delivery...................................... 3<br />
1.6 Zero-Copy Event Memory Management................................................................ 4<br />
1.7 Open-Ended Number of Time Events ................................................................... 4<br />
1.8 Native Event Queues ......................................................................................... 4<br />
1.9 Native Memory Pool .......................................................................................... 4<br />
1.10 Built-in “Vanilla” Scheduler................................................................................. 4<br />
1.11 Tight Integration with the QK preemptive Kernel ................................................... 4<br />
1.12 Low-Power Architecture ..................................................................................... 5<br />
1.13 Assertion-Based Error Handling........................................................................... 5<br />
1.14 Built-in Software Tracing Instrumentation ............................................................ 5<br />
2 The <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong> ........................................................................... 6<br />
2.1 Step 1: Requirements........................................................................................ 7<br />
2.2 Step 2: Sequence Diagrams ............................................................................... 8<br />
2.3 Step 3: Signals, Events, and Active Objects.......................................................... 9<br />
2.4 Step 4: Designing the State Machines................................................................ 13<br />
2.4.1 <strong>Capstone</strong> State Machine............................................................................. 13<br />
2.4.2 AlarmMgr State Machine ............................................................................ 15<br />
2.5 Step 5: Coding Active Objects .......................................................................... 16<br />
2.6 Step 6: Coding State Machines of Active Objects ................................................. 17<br />
2.7 Step 7: Initializing and Starting the Application ................................................... 19<br />
2.8 Step 8: Choosing the Real-Time Execution Model ................................................ 21<br />
2.8.1 Simple Non-Preemptive “Vanilla” Kernel ....................................................... 21<br />
2.8.2 The QK Preemptive Kernel ......................................................................... 22<br />
2.8.3 Traditional OS/RTOS ................................................................................. 22<br />
3 The <strong>Quantum</strong> Spy (QS) Instrumentation....................................................... 23<br />
3.1 QS Time Stamp Callback QS_onGetTime().......................................................... 25<br />
3.2 Invoking the QSpy Host Application ................................................................... 26<br />
4 References .................................................................................................... 28<br />
5 Contact Information...................................................................................... 29<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
i
1 Introduction<br />
This Application Note describes an example application for the QP event-driven platform. QP is<br />
a family of very lightweight, open source, state machine-based frameworks for developing eventdriven<br />
applications. QP enables building well-structured embedded applications as a set of concurrently<br />
executing hierarchical state machines (UML statecharts) directly in C or C++ without big<br />
tools. QP is described in great detail in the book “Practical UML Statecharts in C/C++, Second Edition”<br />
[PSiCC2] (Newnes, 2008).<br />
As shown in Figure 1, QP consists of a universal UML-compliant event processor (QEP), a portable<br />
real-time framework (QF), a tiny run-to-completion kernel (QK), and software tracing instrumentation<br />
(QS). Current versions of QP include: QP/C and QP/C++, which require about 4KB of code<br />
and a few hundred bytes of RAM, and the ultra-lightweight QP-nano, which requires only 1-2KB of<br />
code and just several bytes of RAM.<br />
Figure 1 QP Components (in grey) and their relationship with the target hardware,<br />
board support package (BSP), and the application.<br />
QP can work with or without a traditional RTOS or OS. In the simplest configuration, QP can completely<br />
replace a traditional RTOS. QP includes a simple non-preemptive scheduler and a fully preemptive<br />
kernel (QK). QK is smaller and faster than most traditional preemptive kernels or RTOS,<br />
yet offers fully deterministic, preemptive execution of embedded applications. QP can manage up<br />
to 63 concurrently executing tasks structured as state machines (active objects).<br />
QP/C and QP/C++ can also work with a traditional OS/RTOS to take advantage of existing device<br />
drivers, communication stacks, and other middleware. QP has been ported to Linux/BSD, Windows,<br />
VxWorks, uC/OS-II, and other popular OS/RTOS.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
1 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
1.1 Scalability<br />
All components of the QP event-driven platform are designed for scalability, so that your final application<br />
image contains only the services that you actually use. QP is designed for deployment as<br />
a fine-granularity object library, which you statically link with your applications. This strategy puts<br />
the onus on the linker to eliminate any unused code automatically at link-time, instead of burdening<br />
the application programmer with configuring the QF code for each application at compile time.<br />
As shown in Figure 2, a minimal QP/C or QP/C++ system requires some 8KB of code space (ROM)<br />
and abut 1KB of data space (RAM), to leave enough room for a meaningful application code and<br />
data. This code size corresponds to the footprint of a typical, small, bare-bones RTOS application,<br />
except that the RTOS approach requires typically more RAM for the stacks.<br />
Figure 2 RAM/ROM footprints of QP/C, QP/C++, QP-nano, and other RTOS/OS. The<br />
chart shows approximate total system size, as opposed to just the RTOS/OS footprints.<br />
Please note logarithmic axes.<br />
NOTE: A typical, standalone QP configuration with QEP, QF, and the “vanilla” scheduler or the<br />
QK preemptive kernel, with all major features enabled, requires around 4KB of code. Obviously,<br />
you need to budget additional ROM and RAM for your own application code and data. Figure 7.2<br />
shows the application footprint.<br />
1.2 Source Code<br />
The <strong>Quantum</strong> <strong>Leaps</strong> website at www.state-machine.com/downloads/ contains the complete source<br />
code for all QP components. The source code is very clean and consistent. The code has been written<br />
in strict adherence to the coding standard documented at www.state-machine.com/doc/-<br />
<strong>AN</strong>_QL_Coding_Standard.pdf.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
2 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
All QP source code is “lint-free”. The compliance was checked with PC-lint/FlexLint static analysis<br />
tool from Gimpel Software (www.gimpel.com). The QP distribution includes the \ports\lint\<br />
subdirectory, which contains the batch script make.bat for compiling all the QP components with<br />
PC-lint.<br />
The QP source code is also 98% compliant with the Motor Industry Software Reliability Association<br />
(MISRA) Guidelines for the Use of the C Language in Vehicle Based Software [MISRA 98]. These<br />
standards were created by MISRA to improve the reliability and predictability of C programs in critical<br />
automotive systems. Full details of this standard can be obtained directly from the MISRA web<br />
site at www.misra.org.uk. The PC-lint configuration used to analyze QP code includes the MISRA<br />
rule checker.<br />
Finally and most importantly, simply giving you the source code is not enough. To gain real confidence<br />
in event-driven programming, you need to understand how a real-time framework is ultimately<br />
implemented and how the different pieces fit together. The [PSiCC2] book, and numerous<br />
Application Notes and QDKs, provide this kind of information.<br />
1.3 Portability<br />
All QP source code is written in portable <strong>AN</strong>SI-C, or in the Embedded C++ subset in case of<br />
QP/C++, with all processor-specific, compiler-specific, or operating system-specific code abstracted<br />
into a clearly defined platform abstraction layer (PAL).<br />
In the simplest standalone configurations, QP runs on “bare-metal” target CPU completely replacing<br />
the traditional RTOS. As shown in Figure 1, the QP event-driven platform includes the simple<br />
non-preemptive “vanilla” scheduler as well as the fully preemptive kernel QK. To date, the standalone<br />
QF configurations have been ported to over 10 different CPU architectures, ranging from 8-<br />
bit (e.g., 8051, PIC, AVR, 68H(S)08), through 16-bit (e.g., MSP430, M16C, x86-real mode), to 32-<br />
bit architectures (e.g., traditional ARM, ARM Cortex-M3, ColdFire, Altera Nios II, x86).<br />
The QF framework can also work with a traditional OS/RTOS to take advantage of the existing device<br />
drivers, communication stacks, middleware, or any legacy code that requires a conventional<br />
“blocking” kernel. To date, QF has been ported to six major operating systems and RTOSs, including<br />
Linux (POSIX) and Win32.<br />
1.4 Support for Modern State Machines<br />
As shown in Figure 1, the QF real-time framework is designed to work closely with the QEP hierarchical<br />
event processor. The two components complement each other in that QEP provides the UMLcompliant<br />
state machine implementation, while QF provides the infrastructure of executing such<br />
state machines concurrently.<br />
The design of QF leaves a lot of flexibility, however. You can configure the base class for derivation<br />
of active objects to be either the hierarchical state machine (HSM), the simpler non-hierarchical<br />
state machine (FSM), or even your own base class not defined in the QEP event processor. The latter<br />
option allows you to use QF with your own event processor.<br />
1.5 Direct Event Posting and Publish-Subscribe Event Delivery<br />
The QF real-time framework supports direct event posting to specific active objects with first-infirst-out<br />
(FIFO) and last-in-first-out (LIFO) policies. QF supports also the more advanced publishsubscribe<br />
event deliver mechanism, as described in Chapter 6 of [PSiCC2]. Both mechanisms can<br />
coexist in a single application.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
3 of 29
1.6 Zero-Copy Event Memory Management<br />
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
Perhaps that most valuable feature provided by the QF real-time framework is the efficient “zerocopy”<br />
event memory management, as described in Chapter 6 of [PSiCC2]. QF supports event multicasting<br />
based on the reference-counting algorithm, automatic garbage collection for events, efficient<br />
static events, “zero-copy” event deferral, and up to three event pools with different block<br />
sizes for optimal memory utilization.<br />
1.7 Open-Ended Number of Time Events<br />
QP can manage open ended number of time events (timers). QF time events are extensible via<br />
structure derivation (inheritance in C++). Each time event can be armed as a one-shot or a periodic<br />
timeout generator. Only armed (active) time events consume CPU cycles.<br />
1.8 Native Event Queues<br />
QP provides two versions of native event queues. The first version is optimized for active objects<br />
and contains a portability layer to adapt it for either the traditional blocking kernels, the simple cooperative<br />
“vanilla” kernel, or the QK preemptive kernel (Chapter 10 in [PSiCC2]). The second native<br />
queue version is a simple “thread-safe” queue not capable of blocking and designed for sending<br />
events to interrupts as well as storing deferred events. Both native QF event queue types are<br />
lightweight, efficient, deterministic, and thread-safe. They are optimized for passing just the pointers<br />
to events and are probably smaller and faster than full-blown message queues available in a<br />
typical RTOS.<br />
1.9 Native Memory Pool<br />
QF provides a fast, deterministic, and thread-safe memory pool. Internally, QF uses memory pools<br />
as event pools for managing dynamic events, but you can also use memory pools for allocating any<br />
other objects in you application.<br />
1.10 Built-in “Vanilla” Scheduler<br />
The QF real-time framework contains a portable, cooperative “vanilla” kernel, as described in Section<br />
6.3.7 of Chapter 6. The QF port to the “vanilla” kernel is described in Chapter 7 of [PSiCC2].<br />
1.11 Tight Integration with the QK preemptive Kernel<br />
The QF real-time framework can also work with a deterministic, preemptive, non-blocking QK kernel.<br />
As described in Section 6.3.8 in Chapter 6, run-to-completion kernels, like QK, provide preemptive<br />
multitasking to event-driven systems at a fraction of the cost in CPU and stack usage<br />
compared to traditional blocking kernels/RTOSs. The QK implementation is presented in Chapter 10<br />
of [PSiCC2].<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
4 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
1.12 Low-Power Architecture<br />
Most modern embedded microcontrollers (MCUs) provide an assortment of low-power sleep modes<br />
designed to conserve power by gating the clock to the CPU and various peripherals. The sleepmodes<br />
are entered under the software control and are exited upon an external interrupt.<br />
The event-driven paradigm is particularly suitable for taking advantage of these power-savings features,<br />
because every event-driven system can easily detect situation when the system has no more<br />
events to process, which is called the idle condition (Chapter 6 in [PSiCC2]). In both standalone QF<br />
configurations, either with the cooperative “vanilla” kernel, or with the QK preemptive kernel, the<br />
QF framework provides callback functions for handling the idle condition. These callbacks are carefully<br />
designed to place the MCU into a low-power sleep mode safely and without creating race conditions<br />
with active interrupts.<br />
1.13 Assertion-Based Error Handling<br />
The QF real-time framework consistently uses the Design by Contract (DbC) philosophy described<br />
in Chapter 6 of [PSiCC2]. QF constantly monitors the application by means of assertions built into<br />
the framework. Among others, QF uses assertions to enforce the event delivery guarantee, which<br />
immensely simplifies event-driven application design.<br />
1.14 Built-in Software Tracing Instrumentation<br />
The QP code contains the software tracing instrumentation, so it can provide unprecedented visibility<br />
into the system. Nominally, the instrumentation is inactive, meaning that it does not add any<br />
code size or runtime overhead. But by defining the macro Q_SPY, you can activate the instrumentation.<br />
Chapter 9 in [PSiCC2] is devoted to software tracing.<br />
NOTE: The QF code is instrumented with QS (Q-spy) macros to generate software trace output<br />
from active object execution. However, the instrumentation is disabled by default and will not<br />
be shown in the listings discussed in this chapter for better clarity. Please refer to Chapter 9 for<br />
more information about the QS software tracing implementation.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
5 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
2 The <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
The <strong>Dive</strong> <strong>Computer</strong> example demonstrates programming with modern UML state machines and the<br />
QP event-driven framework. The example is based on the capstone exercise used at the Embedded<br />
Software Boot Camp training program run by Netrino Institute (www.netrino.com) [Netrino 08]. It<br />
is intended to be educational and to reflect real-world behavior associated with safety critical<br />
SCUBA diving equipment. The project makes use of the following hardware resources on the ARM9-<br />
based STR912-SK development kit (see Figure 3).<br />
Idle task<br />
LED16<br />
ADC conv<br />
LED15<br />
Alarm<br />
LED14<br />
Diving<br />
LED11<br />
Surfaced<br />
LED10<br />
Surfaced<br />
LED9<br />
STR912<br />
ARM9 MCU<br />
Speaker<br />
for alarms<br />
Ascent rate<br />
bar-graph<br />
Depth<br />
in meters/feet<br />
2x16<br />
character LCD<br />
Amount of gas<br />
bar-graph<br />
Time to Surface<br />
/ <strong>Dive</strong> Time<br />
Button B1<br />
add gas<br />
Figure 3 The <strong>Dive</strong> <strong>Computer</strong> User Interface.<br />
Button B2<br />
depth units<br />
Ascent rate<br />
potentiometer<br />
Inputs<br />
• Button B1<br />
• Button B2<br />
• Potentiometer via A/D Converter<br />
Outputs<br />
• LEDs<br />
• LCD Display<br />
• Speaker<br />
The hardware inputs are used to provide stimuli to a simulated <strong>Dive</strong> <strong>Computer</strong>. The hardware outputs<br />
provide a user interface and feedback to the operator of the system, as he or she performs<br />
simulated dives.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
6 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
2.1 Step 1: Requirements<br />
1. Upon reset:<br />
a. The diver is at the surface (0 m) and prefers to see depth in metric units<br />
b. The diver has not been under water yet (hence elapsed dive time is 0:00 seconds)<br />
c. The air tank is “empty,” at standard temperature and pressure (STP)<br />
2. At all times:<br />
a. LCD displays<br />
i. Top Left (line 0, characters 0-8): Current ascent rate, in m/min<br />
ii. Top Right (line 0, characters 9-15): Current depth, in user selectable units of<br />
meters (‘m’) or feet (‘f’)<br />
iii. Bottom Left (line 1, characters 0-8): The amount of compressed air in liters<br />
iv. Bottom Right (line 1, characters 9-15): Alternates (every 2 s) between<br />
elapsed dive time and safe minimum time-to-surface (“TTS”), in minutes:seconds<br />
b. Each “press” of button B2 toggles the units of the displayed depth<br />
c. LED9 blinks a continuous 1 Hz heartbeat<br />
d. LED10 is on at the surface and is off while diving<br />
e. LED11 is on while diving and is off at the surface<br />
f. LED14 is on while any alarm is active<br />
g. LED15 is on while the ADC is converting the ascent rate<br />
h. LED16 is glowing at the intensity proportional to the CPU’s idle time<br />
3. At the surface:<br />
a. Attempts to ascend are ignored (including no display of ascent rate)<br />
b. Pressing or holding button B1 causes air to be forced into the tank in 50 l increments;<br />
the maximum capacity is 2,000 liters<br />
c. Elapsed dive time does not increment (but is not reset until next dive)<br />
4. While diving:<br />
a. The rate of descent/ascent (-40 to +40 m/min) is measured<br />
i. Potentiometer readings (ADC) from 0 to 510 indicate descent<br />
ii. A potentiometer reading of 511 indicate diver neutrality (i.e., 0 m/min)<br />
iii. Potentiometer readings 512 to 1,023 indicate ascent<br />
b. Depth, in millimeters of precision, should increase or decrease as appropriate<br />
c. The rate of air consumption varies with depth, and this reduces the amount of air in<br />
the tank by some number of centiliters every half second<br />
d. Elapsed dive time increases<br />
e. The minimum safe TTS is calculated based on an ascent rate of 15 m/min<br />
f. The following alarm conditions are raised by audible tone patterns:<br />
i. Most Dangerous: gas_to_surface_in_cl() exceeds available air supply!<br />
ii. Dangerous: ascent_rate_in_mm_per_min() is faster than 15000 mm/min!<br />
iii. Least Dangerous: depth_in_mm exceeds() 40000 mm (40 m)!<br />
5. To reduce the complexity of the exercise as well as ensure consistent functionality across<br />
implementations the following code is provided in the baseline project:<br />
• The ST Microelectronics library provides functions to display strings (and bar graphs)<br />
on the HD44780-type text-based LCD<br />
• Module scuba.c handles several diving-specific algorithms, all of which expect to be<br />
called every 500 ms as they operate on a base unit of ½ second of elapsed time:<br />
o depth_change_in_mm(ascent_rate_in_mm_per_min) can be used to increase/decrease<br />
depth<br />
o gas_rate_in_cl(depth_in_mm) returns the volume of air, in centiliters, consumed<br />
each half second at a given depth<br />
o gas_to_surface_in_cl(depth_in_mm) performs the numerical integration necessary<br />
to determine the amount of air, in centiliters, necessary to surface safely<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
7 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
2.2 Step 2: Sequence Diagrams<br />
A good starting point in designing any event-driven system is to draw sequence diagrams for the<br />
main scenarios (main use cases) identified from the problem specification. To draw such diagrams,<br />
you need to break up your problem into active objects with the main goal of minimizing the coupling<br />
among active objects. You seek a partitioning of the problem that avoids resource sharing<br />
and requires minimal communication in terms of number and size of exchanged events.<br />
Another important consideration at this stage is deciding the ownership of resources, such as<br />
the LCD screen, the Speaker, or the LEDs. Generally, you should avoid any sharing of resources.<br />
Instead, every resource should be encapsulated inside a dedicated active object which becomes the<br />
“manager” of that resource and has exclusive access to the managed resource. All other components<br />
of the application must make requests (via events) to the respective “manager” of that resource.<br />
In case of the <strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> the LCD and ADC resources are assigned to the<br />
<strong>Capstone</strong> active object and the Speaker resource to the Alarm active object, respectively.<br />
The sequence diagram in Figure 4 shows the most representative event exchanges in the <strong>Capstone</strong><br />
<strong>Dive</strong> <strong>Computer</strong>.<br />
Figure 4 The sequence diagram of the <strong>Capstone</strong> application.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
8 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
(1) The <strong>Capstone</strong> active object starts in the “surfaced” state.<br />
(2) The Alarm active object starts in the “silent” state.<br />
(3) The system clock tick ISR posts the HEARTBEAT event to the <strong>Capstone</strong> state machine every ½<br />
second.<br />
(4) Upon receiving the HEARTBEAT event, <strong>Capstone</strong> triggers ADC conversion to find out the ascent<br />
rate.<br />
(5) When the ADC finishes the conversion, the ADC ISR posts the ASCENT_RATE_ADC(raw) event<br />
to <strong>Capstone</strong>. The event parameter raw contains the raw, 10-bit ADC conversion value.<br />
(6) The system clock tick ISR also performs debouncing of the buttons B1 and B2 (see Figure 3).<br />
This ISR publishes event B1_DOWN when button B1 is depressed and B2_DOWN when button<br />
B2 is depressed.<br />
(7) Similarly, the system clock publishes event B1_UP when button B1 is released and B2_UP when<br />
button B2 is released.<br />
(8) <strong>Capstone</strong> ignores positive ascent rates in the “surfaced” state. However, a negative ascent rate<br />
(diving) triggers the transition to the “diving” state.<br />
(9) In the “diving” state <strong>Capstone</strong> tests for alarm conditions. If any of the alarm condition arises,<br />
the <strong>Capstone</strong> active object posts an ALARM_REQUEST(alarm_type) event to the Alarm state<br />
machine. The event parameter alarm_type conatins the enumerated type of the alarm.<br />
(10) The ALARM_REQUEST(alarm_type) event triggers transition to “playing” in the Alarm active<br />
object.<br />
(11) The Alarm active object changes the pitch of the speaker based on the TIMEOUT event produced<br />
by the system clock tick ISR.<br />
(12) When the <strong>Capstone</strong> active object determines that the alarm should be silenced, it sends the<br />
ALARM_SILENCE(alarm_type) to the Alarm active object. It is up to the Alarm active object to<br />
handle the priorities of the requested and silence alarms, as the alarms can overlap.<br />
2.3 Step 3: Signals, Events, and Active Objects<br />
Sequence diagrams, like Figure 4, help you discover events exchanged among active objects. The<br />
choice of signals and event parameters is perhaps the most important design decision in any<br />
event-driven system. The events affect the other main application components: events and state<br />
machines of the active objects.<br />
In QP, signals are typically enumerated constants and events with parameters are structures derived<br />
from the QEvent base structure. Listing 1 shows signals and events used in the <strong>Capstone</strong> application.<br />
The sample code for the non-preemptive kernel is located in the Solutions\<strong>Dive</strong><strong>Computer</strong><br />
directory.<br />
NOTE: This section describes the platform-independent code of the application. This code is actually<br />
identical in both versions.<br />
#ifndef capstone_h<br />
#define capstone_h<br />
(1) enum <strong>Capstone</strong>Signals {<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
9 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
(2) B1_DOWN_SIG = Q_USER_SIG, /**< button B1 depressed */<br />
B1_UP_SIG,<br />
B2_DOWN_SIG,<br />
/**< button B1 released */<br />
/**< button B2 depressed */<br />
B2_UP_SIG, /**< button B2 released */<br />
(3) MAX_PUB_SIG, /**< the last published signal */<br />
ASCENT_RATE_ADC_SIG, /**< raw ascent rate ADC reading */<br />
HEARTBEAT_SIG, /**< heartbeat of the <strong>Capstone</strong> scuba diving computer */<br />
DT_TTS_SIG, /**< signal to alternate dive time/time to surface display */<br />
ALARM_REQUEST_SIG,<br />
ALARM_SILENCE_SIG,<br />
/**< alarm request to AlarmMgr */<br />
/**< alarm silence to AlarmMgr */<br />
TIMEOUT_SIG, /**< timeout for playing a note in AlarmMgr */<br />
(4) MAX_SIG /**< the last signal */<br />
};<br />
(5) typedef struct ADCEvtTag {<br />
(6) QEvent super; /* derives from QEvent */<br />
(7) uint16_t raw; /* raw ADC reading */<br />
} ADCEvt;<br />
(8) typedef struct AlarmEvtTag {<br />
QEvent super; /* derives from QEvent */<br />
(9) uint8_t alarm_type; /* alarm type */<br />
} AlarmEvt;<br />
(10) enum AlarmTypes { /* arranged in ascending order of alarm priority */<br />
ALL_ALARMS,<br />
DEPTH_ALARM,<br />
ASCENT_RATE_ALARM,<br />
OUT_OF_AIR_ALARM,<br />
MAX_ALARM /* keep always last */<br />
};<br />
/*..........................................................................*/<br />
(11) void <strong>Capstone</strong>_ctor(void);<br />
(12) void AlarmMgr_ctor(void);<br />
(13) extern QActive * const AO_<strong>Capstone</strong>; /* "opaque" pointer to <strong>Capstone</strong> AO */<br />
(14) extern QActive * const AO_AlarmMgr; /* "opaque" pointer to AlarmMgr AO */<br />
#endif /* capstone_h */<br />
Listing 1 Signals and events used in the <strong>Dive</strong> <strong>Computer</strong> (file capstone.h)<br />
(1) For smaller applications, such as the <strong>Dive</strong> <strong>Computer</strong>, all signals can be defined in one enumeration<br />
(rather than in separate enumerations or, worse, as preprocessor #define macros). An<br />
enumeration automatically guarantees the uniqueness of signals.<br />
(2) Note that the user signals must start with the offset Q_USER_SIG to avoid overlapping the reserved<br />
QEP signals.<br />
(3) The globally published signals are grouped at top of the enumeration. The MAX_PUB_SIG enumeration<br />
automatically keeps track of the maximum published signals in the application.<br />
(4) The MAX_SIG enumeration automatically keeps track of the total number of signals used in the<br />
application.<br />
(5-7) Every event with parameters, such as the ADCEvt derives from the QEvent base structure.<br />
Any number of event parameters can be added after the first member super (see also the sidebar<br />
below).<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
10 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
Single Inheritance in C<br />
Inheritance is the ability to derive new structures based on existing structures in order to reuse<br />
and organize code. You can implement single inheritance in C very simply by literally embedding<br />
the base structure as the first member of the derived structure. For example, the following<br />
diagram (a) shows the structure ADCEvt derived from the base structure QEvent by embedding<br />
the QEvent instance as the first member of ADCEvt. To make this idiom better stand out, QP always<br />
names the base structure member super.<br />
Derivation of structures in C (a), memory alignment (b), the UML class diagram (c).<br />
As shown in panel (b) of the figure above, such nesting of structures always aligns the first data<br />
member super at the beginning of every instance of the derived structure. This alignment is<br />
guaranteed by the C standard, such as WG14/N1124, Section 6.7.2.1.13, which says: “… A<br />
pointer to a structure object, suitably converted, points to its initial member. There may be unnamed<br />
padding within a structure object, but not at its beginning” [ISO/IEC 9899:TC2]. In particular,<br />
this alignment lets you treat a pointer to the derived ADCEvt structure as a pointer to the<br />
QEvent base structure.<br />
Consequently, you can always safely pass a pointer to ADCEvt to any C function that expects a<br />
pointer to QEvent. (To be strictly correct in C, you should explicitly cast this pointer. In OOP<br />
such casting is called upcasting and is always safe.) Therefore, all functions designed for the<br />
QEvent structure are automatically available to the ScoreEvt structure as well as other structures<br />
derived from QEvent. Panel (c) in the figure above shows the UML class diagram depicting<br />
the inheritance relationship between ADCEvt and QEvent structures.<br />
QP uses single inheritance quite extensively not just for derivation of events with parameters,<br />
but also for derivation of state machines and active objects. Of course, the C++ version of QP<br />
uses the native C++ support for class inheritance rather than “derivation of structures”.<br />
(8-9) The AlarmEvt structure describes the ALARM(alarm_type) event.<br />
(10) The enumeration AlarmTypes enumerates all alarm types in the order of priority.<br />
The capstone.h header file shows how to keep the code and data structure of every active object<br />
strictly encapsulated within its own C-file. For example, all code and data for the active object <strong>Capstone</strong><br />
are encapsulated in the file capstone.c, with the external interface consisting of the function<br />
<strong>Capstone</strong>_ctor() and the pointer AO_<strong>Capstone</strong>.<br />
(11-12) These functions perform an early initialization of the active objects in the system.<br />
They play the role of static “constructors”, which in C you need to call explicitly, typically at the<br />
beginning of main().<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
11 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
(13-14) These global pointers represent active objects in the application and are used for<br />
posting events directly to active objects. Because the pointers can be initialized at compile<br />
time, they are declared const, sot that they can be placed in ROM. The active object pointers<br />
are “opaque”, because they cannot access the whole active object, but only the part inherited<br />
from the QActive structure.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
12 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
2.4 Step 4: Designing the State Machines<br />
At the application level, you can mostly ignore such aspects of active objects as the separate task<br />
contexts, or event queues, and view them predominantly as state machines. In fact, your main job<br />
in developing QP application consists of elaborating the state machines of your active objects.<br />
2.4.1 <strong>Capstone</strong> State Machine<br />
Figure 5 shows the state machine associated with <strong>Capstone</strong> active object. The explanation section<br />
immediately following the state diagram explains the main interesting points.<br />
Figure 5 <strong>Capstone</strong> state machine.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
13 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
The state machine in Figure 5 reflects the structure of the requirements, which divide the behavior<br />
of the <strong>Dive</strong> <strong>Computer</strong> into “always”, “at the surface”, and “while diving” categories. The state “always”<br />
contains the actions of the <strong>Dive</strong> <strong>Computer</strong> in the “always” category. The substate “surfaced”<br />
performs all actions from the “at the surface” category. Finally, the substate “diving” performs all<br />
the actions in the “while diving” category.<br />
NOTE: This design makes use of the state nesting feature of the modern UML state machines.<br />
State nesting allows the “always” state to have nested substates “surfaced” and “diving”, which<br />
inherit the behavior of their superstate.<br />
(1) The state transition originating at the black ball is called the initial transition. Such transition<br />
designates the first active state after the state machine object is created. An initial transition<br />
can have associated actions, which in the UML notation are enlisted after the forward slash “/”.<br />
In this particular case, the <strong>Capstone</strong> state machine starts in the “surfaced” state and the actions<br />
executed upon the initialization consist of subscribing to button events. Subscribing to an<br />
event means that the QP framework will deliver the specified event to the <strong>Capstone</strong> active object<br />
every time the event is published to the framework.<br />
NOTE: The UML intentionally does not specify the notation for actions. In practice, the actions<br />
are often written in the programming language used for coding the particular state machine,<br />
which is C in this case. However, to avoid clutter in the diagram, the action code must necessarily<br />
be abbreviated, so it conveys the meaning of the action, but might not necessarily be<br />
directly executable.<br />
The convention used in the code presented in this Application Note is that the C expressions refer<br />
to the data members associated with the state machine object through the “me->” prefix and<br />
to the event parameters through the “e->” prefix. For example, the action “me->ascent_rate =<br />
e->raw;” means that the internal data member ascent_rate of the active object is assigned the<br />
value of the event parameter raw.<br />
(2) The label entry below the state name denotes entry actions executed unconditionally whenever<br />
this state is entered. These actions consist in this case of arming the time events (timers) to<br />
post themselves periodically to the <strong>Capstone</strong> active objects. The me->heartbeat timer posts itself<br />
every ½ seconds while the me->dt_tts timer posts itself every 2 seconds.<br />
(3) The event name HEARTBEAT enlisted in the compartment below the state name denotes an internal<br />
transition. Internal transitions are simple reactions to events performed without a change<br />
of state. The HEARTBEAT event is generated by the me->heartbeat timer every ½ seconds, as<br />
described at step (2). This event triggers ADC conversion and toggles the heartbeat LED.<br />
(4) An internal transition, as well as a regular transition, can have a guard condition, enclosed in<br />
square brackets. Guard condition is a Boolean expression evaluated at runtime. If the guard<br />
evaluates to TRUE, the transition is taken. Otherwise, the transition is not taken and no actions<br />
enlisted after the forward slash “/” are executed. In the particular case of the B2_DOWN event,<br />
the guard condition checks whether the depth is displayed in meters (‘m’). If so, units of depth<br />
are changed to feet ‘f’ and the depth display is updated.<br />
(5) The guard [else] denotes a complementary guard to the guard already present in the state for<br />
the same event. Here, the B2_DOWN event triggers change of units to meters ‘m’ and the<br />
depth display is updated.<br />
(6-7) The event DT_TTS is generated from the timer me->dt_tts every 2 seconds. This event toggles<br />
between <strong>Dive</strong>-Time and Time to Surface (TTS).<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
14 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
Per the semantics of state nesting, the substate “surfaced” inherits all the behavior from its superstate<br />
“always”. The substate can override the inherited behavior and it can also define new behavior.<br />
2.4.2 AlarmMgr State Machine<br />
The main responsibility of the Alarm Manager (AlarmMgr) state machine is to handle all alarm conditions<br />
autonomously. As shown in the sequence diagram in Figure 4, the <strong>Capstone</strong> active object<br />
sends only alarm requests and silences alarms based on the diving conditions. However, <strong>Capstone</strong><br />
might end up requesting several alarms simultaneously, and it is up to the AlarmMgr to always play<br />
the highest-priority alarm at any one time. This includes the situation when an alarm is silenced,<br />
but others still remain active, in which case the AlarmMgr must keep playing the highest-priority<br />
alarm at that time.<br />
The AlarmMgr is designed generally, to handle any number of alarms (it is not hard-coded to handle<br />
only three alarms specified in the requirements). The design of the AlarmMgr state machine<br />
hinges on the data representation for alarms.<br />
The central data structure is the priority-set, which is provided in the QP framework. The priority<br />
set allows inserting elements numbered 1..n and removing elements. The set also allows discovering<br />
the highest-number event currently present in the priority set quickly and very efficiently.<br />
Figure 6 shows the state machine associated with Alarm active object.<br />
Figure 6 Alarm state machine.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
15 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
2.5 Step 5: Coding Active Objects<br />
As mentioned before, each active object is strictly encapsulated inside a dedicated source file (.C<br />
file). Listing 3 shows the declaration (active object structure) of the <strong>Capstone</strong> active object found in<br />
the file capstone.c. The explanation section immediately following this listing describes the techniques<br />
of encapsulating active objects and using QP services.<br />
#include "qp_port.h"<br />
#include "capstone.h"<br />
#include "bsp.h"<br />
#include "scuba.h" /* generic scuba diving algorithms */<br />
#include "drv_hd44780.h" /* LCD display driver */<br />
/* Active object class -----------------------------------------------------*/<br />
(1) typedef struct <strong>Capstone</strong>Tag {<br />
(2) QActive super; /* derive from QActive */<br />
(3) QTimeEvt heartbeat;<br />
QTimeEvt dt_tts;/* timer to alternate dive time/time to surface display */<br />
int32_t depth_in_mm;<br />
int8_t depth_units[2];<br />
uint8_t heartbeat_led_sel;<br />
uint8_t dt_tts_sel;<br />
int32_t ascent_rate_in_mm_per_sec;<br />
uint32_t start_dive_time_in_ticks;<br />
uint32_t dive_time_in_ticks;<br />
uint32_t tts_in_ticks; /* time to surface */<br />
uint32_t gas_in_cylinder_in_cl; /* amount of gas in centliters at STP */<br />
uint32_t consumed_gas_in_cl; /* consumed gas in centiliters */<br />
} <strong>Capstone</strong>;<br />
(4) static QState <strong>Capstone</strong>_initial (<strong>Capstone</strong> *me, QEvent const *e);<br />
(5) static QState <strong>Capstone</strong>_always (<strong>Capstone</strong> *me, QEvent const *e);<br />
static QState <strong>Capstone</strong>_surfaced(<strong>Capstone</strong> *me, QEvent const *e);<br />
static QState <strong>Capstone</strong>_diving (<strong>Capstone</strong> *me, QEvent const *e);<br />
(6) static void <strong>Capstone</strong>_display_assent (<strong>Capstone</strong> *me);<br />
static void <strong>Capstone</strong>_display_depth (<strong>Capstone</strong> *me);<br />
static void <strong>Capstone</strong>_display_pressure(<strong>Capstone</strong> *me);<br />
/* Local objects -----------------------------------------------------------*/<br />
(7) static <strong>Capstone</strong> l_capstone; /* the single instance of the <strong>Capstone</strong> AO */<br />
/* Global-scope objects ----------------------------------------------------*/<br />
(8) QActive * const AO_<strong>Capstone</strong> = (QActive *)&l_capstone;/* "opaque" AO pointer */<br />
/* ctor ....................................................................*/<br />
(9) void <strong>Capstone</strong>_ctor(void) {<br />
<strong>Capstone</strong> *me = &l_capstone;<br />
(10) QActive_ctor(&me->super, (QStateHandler)&<strong>Capstone</strong>_initial);<br />
(11) QTimeEvt_ctor(&me->heartbeat, HEARTBEAT_SIG);<br />
QTimeEvt_ctor(&me->dt_tts, DT_TTS_SIG);<br />
}<br />
Listing 2 Defining <strong>Capstone</strong> active object (file capstone.c)<br />
(1) To achieve true encapsulation, the declaration of the active object structure is placed in the<br />
source file (.C file).<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
16 of 29
(2) Each active object in the application derives from the QActive base structure.<br />
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
(3) The <strong>Capstone</strong> active object uses two independent time events (timers) for generation of the<br />
HEARTBEAT event and the TD_TTS event, respectively.<br />
(4) The <strong>Capstone</strong>_initial() function defines the actions executed by the top-most initial transition.<br />
(5) The all other functions with the signature QState <strong>Capstone</strong>_xxx(<strong>Capstone</strong> *me, QEvent const *e)<br />
are state-handler functions. Each state in the diagram (Figure 5) is represented by exactly<br />
one such state-handler function.<br />
(6) The other <strong>Capstone</strong>_xxx() functions are helper functions executed in the actions by the state<br />
machine.<br />
(7) The actual instance of the <strong>Capstone</strong> active object is defined only locally within the capstone.c<br />
module (static declaration). This means that the l_capsone object is strictly encapsulated.<br />
(8) Externally, the <strong>Capstone</strong> active object is known only through the “opaque” pointer AO_<strong>Capstone</strong>.<br />
The pointer is declared ‘const’ (with the const after the ‘*’), which means that the pointer itself<br />
cannot change. This ensures that the active object pointer cannot change accidentally and also<br />
allows the compiler to allocate the active object pointer in ROM.<br />
(9) The function <strong>Capstone</strong>_ctor() performs the instantiation of the <strong>Capstone</strong> active object. It plays<br />
the role of the static “constructor”, which in C you need to call explicitly, typically at the beginning<br />
of main().<br />
NOTE: In C++, static constructors are invoked automatically before main(). This means that in<br />
C++, you provide a regular constructor for the class and don’t bother with calling it explicitly.<br />
However, you must make sure that the startup code for your particular embedded target includes<br />
the additional steps required by the C++ initialization.<br />
(10) The constructor must first instantiate the QActive superclass.<br />
(11) The constructor must also instantiate all objects it contains, such as the time events (timers).<br />
2.6 Step 6: Coding State Machines of Active Objects<br />
Listing 3 shows the definition of the hierarchical state of the <strong>Capstone</strong> active object found in the file<br />
capstone.c. The explanation section immediately following this listing describes the techniques of<br />
encapsulating active objects and using QP services. The recipes for coding state machine elements<br />
are not repeated here, because they are already described in the “QP Tutorials” available from the<br />
[PSiCC2] book and online from www.state-machine.com.<br />
/* HSM =====================================================================*/<br />
(1) QState <strong>Capstone</strong>_initial(<strong>Capstone</strong> *me, QEvent const *e) {<br />
(void)e; /* suppress the compiler warning about unused parameter */<br />
(2) HD44780_PowerUpInit(); /* LCD initialization */<br />
HD44780_DispHorBarInit();<br />
HD44780_StrShow(LCD_DEPTH_X, LCD_DEPTH_Y, "Dpt");<br />
(3) me->depth_units[0] = 'm'; /* meters */<br />
me->depth_units[1] = '\0'; /* zero terminate */<br />
me->gas_in_cylinder_in_cl = 0;<br />
me->heartbeat_led_sel = 0;<br />
me->dt_tts_sel = 0;<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
17 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
(4) QActive_subscribe((QActive *)me, B1_DOWN_SIG);<br />
QActive_subscribe((QActive *)me, B1_UP_SIG);<br />
QActive_subscribe((QActive *)me, B2_DOWN_SIG);<br />
QActive_subscribe((QActive *)me, B2_UP_SIG);<br />
. . .<br />
(5) return Q_TR<strong>AN</strong>(&<strong>Capstone</strong>_always);<br />
}<br />
/*..........................................................................*/<br />
(6) QState <strong>Capstone</strong>_always(<strong>Capstone</strong> *me, QEvent const *e) {<br />
(7) switch (e->sig) {<br />
(8) case Q_ENTRY_SIG: {<br />
QTimeEvt_postEvery(&me->heartbeat, (QActive *)me,<br />
BSP_TICKS_PER_SEC/2); /* every 1/2 second */<br />
QTimeEvt_postEvery(&me->dt_tts, (QActive *)me,<br />
BSP_TICKS_PER_SEC*2); /* every 2 seconds */<br />
(9) return Q_H<strong>AN</strong>DLED();<br />
}<br />
case Q_EXIT_SIG: {<br />
QTimeEvt_disarm(&me->heartbeat);<br />
QTimeEvt_disarm(&me->dt_tts);<br />
return Q_H<strong>AN</strong>DLED();<br />
}<br />
case Q_INIT_SIG: {<br />
(10) return Q_TR<strong>AN</strong>(&<strong>Capstone</strong>_surfaced);<br />
}<br />
case B2_DOWN_SIG: { /* depth unit change request */<br />
if (me->depth_units[0] == 'm') {<br />
me->depth_units[0] = 'f';<br />
}<br />
else {<br />
me->depth_units[0] = 'm';<br />
}<br />
<strong>Capstone</strong>_display_depth(me);<br />
return Q_H<strong>AN</strong>DLED();<br />
}<br />
case HEARTBEAT_SIG: { /* heartbeat arrives every 1/2 sec */<br />
ADC_StandbyModeCmd(DISABLE); /* exit ADC standby mode */<br />
ADC_ConversionCmd(ADC_Conversion_Start);/* start the conversion */<br />
BSP_LED_on(15); /* trun on the ADC conversion LED */<br />
if (me->heartbeat_led_sel) {<br />
BSP_LED_on(9);<br />
}<br />
else {<br />
BSP_LED_off(9);<br />
}<br />
me->heartbeat_led_sel = !me->heartbeat_led_sel;<br />
return Q_H<strong>AN</strong>DLED();<br />
}<br />
case DT_TTS_SIG: { /* alternate between dive-time/tts */<br />
if (me->dt_tts_sel) {<br />
HD44780_StrShow(LCD_TTS_X, LCD_TTS_Y, "TTS");<br />
HD44780_StrShow(LCD_TTS_X + 3, LCD_TTS_Y,<br />
ticks2min_sec(me->tts_in_ticks));<br />
}<br />
else {<br />
HD44780_StrShow(LCD_TTS_X, LCD_TTS_Y, "Div");<br />
HD44780_StrShow(LCD_TTS_X + 3, LCD_TTS_Y,<br />
ticks2min_sec(me->dive_time_in_ticks));<br />
}<br />
me->dt_tts_sel = !me->dt_tts_sel;<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
18 of 29
eturn Q_H<strong>AN</strong>DLED();<br />
}<br />
}<br />
(11) return Q_SUPER(&QHsm_top);<br />
}<br />
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
Listing 3 <strong>Capstone</strong> state machine (file capstone.c). Boldface indicates the QP services<br />
(1) The <strong>Capstone</strong>_initial() function defines the actions executed by the top-most initial transition.<br />
(2) The top-most initial transition is the right place to initialize the hardware owned by the active<br />
object. <strong>Capstone</strong> object owns the LCD, so here it initializes the HD44780 device driver.<br />
(3) The top-most initial transition initializes the extended-state variables of the active object.<br />
(4) The active object subscribes to all interesting to it signals in the top-most initial transition.<br />
NOTE: New QP users often forget to subscribe to events and then the application appears unresponsive<br />
to certain events when you run it.<br />
(5) The initial state-handler must always return the Q_TR<strong>AN</strong>() macro, in which it must provide the<br />
pointer to the default initial state (&<strong>Capstone</strong>_surfaced in this case).<br />
(6) The state handler function with the signature QState <strong>Capstone</strong>_always(<strong>Capstone</strong> *me, QEvent<br />
const *e) corresponds to the state “always” in the <strong>Capstone</strong> state diagram (Figure 5).<br />
(7) Generally, every state-handler function is structured as a switch statement discriminating<br />
based on the event signal e->sig .<br />
(8) Case Q_ENTRY_SIG specifies the entry actions.<br />
(9) The Q_ENTRY_SIG case must return through the Q_H<strong>AN</strong>LDED() macro provided by the QEP event<br />
processor.<br />
(10) The Q_INIT_SIG case represents the local initial transition in a composite state. The Q_INIT_SIG<br />
case must return through the Q_TR<strong>AN</strong>() macro, in which is specifies the default substate.<br />
(11) The final return Q_SUPER() statement specifies the nesting of the given state. If a state does<br />
not explicitly nest in any state, the QEP event processor provides the state handler QHsm_top,<br />
which is the ultimate root of state hierarchy.<br />
2.7 Step 7: Initializing and Starting the Application<br />
Most of the system initialization and application startup can be written in a platform-independent<br />
way. In other words, you can use essentially the same main() function for the application with<br />
many QP ports.<br />
Typically, you start all your active objects from main(). The signature of the QActive_start() function<br />
forces you to make several important decisions about each active object upon startup. First,<br />
you need to decide the relative priorities of the active objects. Second, you need to decide the size<br />
of the event queues you pre-allocate for each active object. The correct size of the queue is actually<br />
related to the priority, as described in Chapter 9 of PSiCC2. Third, in some QF ports, you need<br />
to give each active object a separate stack, which also needs to be pre-allocated adequately. And<br />
finally, you need to decide the order in which you start your active objects.<br />
The order of starting active objects becomes important when you use an OS or RTOS in conjunction<br />
with the QP framework, and only when a spawned thread starts to run immediately, possibly<br />
preempting the main() thread from which you launch your application. This could cause problems,<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
19 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
if for example the newly created active object attempts to post an event directly to another active<br />
object that has not been yet created. Such situation does not occur in the example, but if it is an<br />
issue for you, you can try to lock the scheduler until all active objects are started. You can then<br />
unlock the scheduler in the QF_onStartup() callback, which is invoked right before QF takes over<br />
control. Some RTOSs (e.g., µC/OS-II) allow you to defer starting multitasking until after you start<br />
active objects. Another alternative is to start active objects from within other active objects, but<br />
this design increases coupling because the active object that serves as the launch pad must know<br />
the priorities, queue sizes, and stack sizes for all active objects to be started.<br />
#include "qp_port.h"<br />
#include "capstone.h"<br />
#include "bsp.h"<br />
/* Local-scope objects -----------------------------------------------------*/<br />
(1) static QEvent const *l_capstoneQueueSto[5];<br />
(2) static QEvent const *l_alarmmgrQueueSto[5];<br />
(3) static QSubscrList l_subscrSto[MAX_PUB_SIG];<br />
(4) static union SmallEvent {<br />
(5) void *min_size;<br />
ADCEvt adce;<br />
(6) /* other event types to go into this pool */<br />
(7) } l_smlPoolSto[10]; /* storage for the small event pool */<br />
/*..........................................................................*/<br />
int main(void) {<br />
(8) <strong>Capstone</strong>_ctor(); /* instantiate all <strong>Capstone</strong> active objects */<br />
(9) AlarmMgr_ctor(); /* instantiate all AlarmMgr active objects */<br />
(10) BSP_init(); /* initialize the Board Support Package */<br />
(11) QF_init(); /* initialize the framework and the underlying RT kernel */<br />
(12) QF_psInit(l_subscrSto, Q_DIM(l_subscrSto)); /* init publish-subscribe */<br />
/* initialize event pools... */<br />
(13) QF_poolInit(l_smlPoolSto, sizeof(l_smlPoolSto), sizeof(l_smlPoolSto[0]));<br />
(14) QActive_start(AO_<strong>Capstone</strong>, 1,<br />
l_capstoneQueueSto, Q_DIM(l_capstoneQueueSto),<br />
(void *)0, 0, (QEvent *)0);<br />
(15) QActive_start(AO_AlarmMgr, 2,<br />
l_alarmmgrQueueSto, Q_DIM(l_alarmmgrQueueSto),<br />
(void *)0, 0, (QEvent *)0);<br />
(16) QF_run(); /* run the QF application */<br />
}<br />
return 0;<br />
Listing 4 Initializing and Starting the Application (file main.c).<br />
(1-2) The memory buffers for all event queues are statically allocated.<br />
(3) The memory space for subscriber lists is also statically allocated. The MAX_PUB_SIG enumeration<br />
comes in handy here.<br />
(4) The union SmallEvent contains all events that are served by the “small” event pool.<br />
(5) The union contains a pointer-size member to make sure that the union size will be at least that<br />
big.<br />
(6) You add all events that you want to be served from this event pool.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
20 of 29
(7) The memory buffer for the “small” event pool is statically allocated.<br />
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
(8-9) main() starts with calling all static “constructors”. This step is not necessary in C++.<br />
(10) The target board is initialized.<br />
(11) QF is initialized together with the underlying OS/RTOS.<br />
(12) The publish-subscribe mechanism is initialized. You don’t need to call QF_psInit() if your<br />
application does not use publish-subscribe.<br />
(13) Up to three event pools can be initialized by calling QF_poolInit() up to three times. The<br />
subsequent calls must be made in the order of increasing block-sizes of the event pools. You<br />
don’t need to call QF_poolInit() if your application does not use dynamic events.<br />
(14-15) All active objects are started using the “opaque” active object pointers. In this particular<br />
example, the active objects are started without private stacks. However, some RTOSs,<br />
such as µC/OS-II, require pre-allocating stacks for all active objects.<br />
(16) The control is transferred to QF to run the application. QF_run() might never return.<br />
2.8 Step 8: Choosing the Real-Time Execution Model<br />
Listing 4(16) shows that the main() function eventually gives control to the event-driven framework<br />
by calling QF_run() to execute the application. This function can have different implementations,<br />
depending on which real-time execution model you choose. QP allows you to choose between a<br />
simple non-preemptive “Vanilla” kernel, the fully preemptive QK kernel, or a third-party RTOS, if<br />
you want to use one. This section explains the options.<br />
2.8.1 Simple Non-Preemptive “Vanilla” Kernel<br />
The <strong>Example</strong>1a accompanying this Application Note uses the simplest QP configuration, in which QP<br />
runs on bare-metal target processor without any underlying operating system or kernel . Such a QP<br />
configuration is called “plain vanilla” or just “vanilla”.<br />
QP includes a simple non-preemptive “vanilla” kernel which executes one active object at a time in<br />
the infinite loop (a.k.a., the background loop). The “vanilla” scheduler is engaged after each event<br />
is processed in the run-to-completion (RTC) fashion to choose the next highest-priority active object<br />
ready to process the next event. The “vanilla” kernel is cooperative, which means that all active<br />
objects cooperate to share a single CPU and implicitly yield to each other after every RTC step.<br />
The scheduler is non-preemptive, meaning that every active object must completely process an<br />
event before any other active object can start processing another event.<br />
The interrupt service routines (ISRs) can preempt the execution of active objects at any time, but<br />
due to the simplistic nature of the “vanilla” scheduler, every ISR returns to exactly the preemption<br />
point. If the ISR posts or publishes an event to any active object, the processing of this event won’t<br />
start until the current RTC step completes. The maximum time an event for the highest-priority active<br />
object can be delayed this way is called the task-level response. With the non-preemptive “vanilla”<br />
scheduler, the task-level response is equal to the longest RTC step of all active objects in the<br />
system. Please note that the task-level response of the “vanilla” scheduler is still a lot better than<br />
the traditional “superloop” (a.k.a., main+ISRs) architecture. In fact, the task-level response of the<br />
simple “vanilla” scheduler turns out to be adequate for surprisingly many applications, because<br />
state machines by nature handle events quickly without a need to busy-wait for events. (A state<br />
machine simply runs-to-completion and becomes dormant until another event arrives). Please also<br />
note that often you can make the task-level response as fast as you need by breaking up longer<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
21 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
RTC steps into shorter ones (e.g., by using the “Reminder” state pattern described in Chapter 5 of<br />
PSiCC2).<br />
2.8.2 The QK Preemptive Kernel<br />
In some cases, breaking up long RTC steps into short enough pieces might be very difficult, and<br />
consequently the task-level response of the non-preemptive “vanilla” scheduler might be too long.<br />
An example system could be a GPS receiver. Such a receiver performs a lot of floating point number<br />
crunching on a fixed-point CPU to calculate the GPS position. At the same time, the GPS receiver<br />
must track the GPS satellite signals, which involves closing control loops in sub-millisecond<br />
intervals. It turns out that it’s not easy to break up the position-fix computation into short enough<br />
RTC steps to allow reliable signal tracking.<br />
But the RTC semantics of state machine execution does not mean that a state machine has to monopolize<br />
the CPU for the duration of the RTC step. A preemptive kernel can perform a context<br />
switch in the middle of the long RTC step to allow a higher-priority active object to run. As long as<br />
the active objects don’t share resources they can run concurrently and complete their RTC steps<br />
independently.<br />
QP includes a tiny, fully preemptive, priority-based real-time kernel called QK, which is specifically<br />
designed for processing events in the RTC fashion. Configuring QP to use the preemptive QK kernel<br />
is very easy, but you must be very careful with any resources shared among active objects. The<br />
<strong>Capstone</strong> example has been purposely designed to avoid any resource sharing among active objects,<br />
so the application code does not need to change at all to run on top of the QK, or any other<br />
preemptive kernel or RTOS for that matter. The <strong>Example</strong>1b accompanying this Application Note<br />
demonstrates the use of the preemptive QK kernel.<br />
2.8.3 Traditional OS/RTOS<br />
QP can also work with a traditional operating system (OS), such as Windows or Linux, or virtually<br />
any real-time operating system (RTOS) such as µC/OS-II to take advantage of the existing device<br />
drivers, communication stacks, and other middleware.<br />
QP contains a portability layer, which makes adapting QP to virtually any operating system easy.<br />
The carefully designed QP portability layer allows tight integration with the underlying OS/RTOS by<br />
reusing any provided facilities for interrupt management, message queues, and memory partitions<br />
as QP critical section, event queues, or event pools, respectively. Porting QP is described in Chapter<br />
8 of PSiCC2.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
22 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
3 The <strong>Quantum</strong> Spy (QS) Instrumentation<br />
The <strong>Capstone</strong> examples (<strong>Example</strong>1a and <strong>Example</strong>2b) demonstrate how to use the <strong>Quantum</strong> Spy<br />
(QS) to generate real-time trace of a running QP application. Normally, the QS instrumentation is<br />
inactive and does not add any overhead to your application, but you can turn the instrumentation<br />
on by defining the Q_SPY macro and recompiling the code.<br />
<strong>Quantum</strong> Spy (QS) is a software tracing facility built into all QP components and also available to<br />
the Application code. QS allows you to gain unprecedented visibility into your application by selectively<br />
logging almost all interesting events occurring within state machines, the framework, the<br />
kernel, and your application code. QS software tracing is minimally intrusive, offers precise timestamping,<br />
sophisticated runtime filtering of events, and good data compression (see Chapter 11 in<br />
PSiCC2 [PSiCC2]).<br />
QS can be configured to send the real-time data out of the serial port of the target device. On the<br />
STR912F MCU, QS uses the built-in USART0 to send the trace data out (see Figure 3), so the QSPY<br />
host application can conveniently receive the trace data on the host PC. The complete implementation<br />
of the QS software-tracing output consists of several steps, which are all summarized in Listing<br />
5 (file bsp.c).<br />
(1) #ifdef Q_SPY<br />
(2) static uint32_t l_currTime32;<br />
(3) static uint16_t l_prevTime16;<br />
(4) enum AppRecords {<br />
PHILO_STAT = QS_USER<br />
/* application-specific trace records */<br />
};<br />
#endif<br />
/*--------------------------------------------------------------------------*/<br />
#ifdef Q_SPY<br />
(5) uint8_t QS_onStartup(void const *arg) {<br />
(6) static uint8_t qsBuf[BSP_QS_BUF_SIZE]; /* buffer for <strong>Quantum</strong> Spy */<br />
GPIO_InitTypeDef GPIO_InitStruct;<br />
UART_InitTypeDef UART_InitStruct;<br />
(7) QS_initBuf(qsBuf, sizeof(qsBuf));<br />
/* configure the UART0 for QSPY output ... */<br />
(8) SCU_APBPeriphClockConfig(__UART0, ENABLE); /* enable clock for UART0 */<br />
(9) SCU_APBPeriphClockConfig(__GPIO3, ENABLE); /* enable clock for GPIO3 */<br />
(10) SCU_APBPeriphReset(__UART0, DISABLE); /* remove UART0 from reset */<br />
(11) SCU_APBPeriphReset(__GPIO3, DISABLE); /* remove GPIO3 from reset */<br />
/* configure UART0_TX pin GPIO3.4 ... */<br />
(12) GPIO_DeInit(GPIO3);<br />
GPIO_InitStruct.GPIO_Pin<br />
= GPIO_Pin_4;<br />
GPIO_InitStruct.GPIO_Direction = GPIO_PinOutput;<br />
GPIO_InitStruct.GPIO_Type = GPIO_Type_PushPull;<br />
GPIO_InitStruct.GPIO_IPConnected = GPIO_IPConnected_Disable;<br />
GPIO_InitStruct.GPIO_Alternate = GPIO_OutputAlt3;<br />
GPIO_Init(GPIO3, &GPIO_InitStruct);<br />
/* configure UART0... */<br />
(13) UART_DeInit(UART0); /* force UART0 registers to reset values */<br />
UART_InitStruct.UART_WordLength = UART_WordLength_8D;<br />
UART_InitStruct.UART_StopBits<br />
UART_InitStruct.UART_Parity<br />
= UART_StopBits_1;<br />
= UART_Parity_No;<br />
UART_InitStruct.UART_BaudRate = BSP_QS_BAUD_RATE;<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
23 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
UART_InitStruct.UART_HardwareFlowControl = UART_HardwareFlowControl_None;<br />
UART_InitStruct.UART_Mode = UART_Mode_Tx;<br />
UART_InitStruct.UART_FIFO = UART_FIFO_Enable;<br />
UART_InitStruct.UART_TxFIFOLevel = UART_FIFOLevel_1_8;<br />
UART_InitStruct.UART_RxFIFOLevel = UART_FIFOLevel_1_8;<br />
UART_Init(UART0, &UART_InitStruct); /* initialize UART0 */<br />
UART_Cmd(UART0, ENABLE); /* enable UART0 */<br />
(14) QS_FILTER_ON(QS_ALL_RECORDS);<br />
QS_FILTER_OFF(...);<br />
...<br />
/* setup the QS filters... */<br />
(15)<br />
}<br />
return (uint8_t)1; /* indicate successfull QS initialization */<br />
/*..........................................................................*/<br />
(16) void QS_onCleanup(void) {<br />
}<br />
/*..........................................................................*/<br />
/* NOTE: getTime is invoked within a critical section (inetrrupts disabled) */<br />
(17) uint32_t QS_onGetTime(void) {<br />
(18) uint16_t currTime16 = (uint16_t)TIM3->CNTR;<br />
(19)<br />
(20)<br />
l_currTime32 += (currTime16 - l_prevTime16) & 0xFFFF;<br />
l_prevTime16 = currTime16;<br />
(21) return l_currTime32;<br />
}<br />
/*..........................................................................*/<br />
(22) void QS_onFlush(void) {<br />
uint16_t nBytes = BSP_UART_TX_FIFO; /* the capacity of the UART TX FIFO */<br />
uint8_t const *block;<br />
(23) while ((block = QS_getBlock(&nBytes)) != (uint8_t *)0) {<br />
(24) while ((UART0->FR & 0x80) == 0) {<br />
}<br />
/* TX FIFO not empty? */<br />
/* keep waiting... */<br />
(25) while (nBytes-- != 0) {<br />
}<br />
UART0->DR = *block++; /* stick the byte to the TX FIFO */<br />
nBytes = BSP_UART_TX_FIFO; /* for the next time around */<br />
}<br />
}<br />
#endif /* Q_SPY */<br />
/*--------------------------------------------------------------------------*/<br />
Listing 5 QS implementation to send data out of the UART0 of the STR912 device.<br />
(1) The QS instrumentation is enabled only when the macro Q_SPY is defined<br />
(2) The static l_currTime32 variable is used to hold the 32-bit-wide timestamp.<br />
(3) The static l_prevTime16 variable is used to hold the last 16-bit-wide reading of the free-running<br />
16-bit Timer 3 (the same used to generate the system clock tick interrupt).<br />
(4) This enumeration defines application-specific QS trace record(s), to demonstrate how to use<br />
them.<br />
(5) You need to define the QS_onStartup() callback to initialize the QS software tracing.<br />
(6) You should adjust the QS buffer size (in bytes) to your particular application<br />
(7) You always need to call QS_initBuf() from QS_onStartup() to initialize the trace buffer.<br />
(8-9) The clock to the UART0 peripheral is enabled. Also, the clock to the GPIO3 peripheral is enabled.<br />
GPIO3 controls the UART0 transmit and receive pins.<br />
(10-11) The UART0 and GPIO3 peripherals are removed from reset.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
24 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
(12) The transmit pin GPIO3.4 is configured as output, alternative function 3 (UART0 Tx) using the<br />
ST driver library interface.<br />
(13) The UART0 is not properly configured using the ST driver library interface.<br />
(14) The QS filters are setup (see Chapter 11 in [PSiCC2] as well as “QP Reference Manual”<br />
online).<br />
(15) The QS_onStartup() callback returns 1, meaning that the QS initialization was successful.<br />
(16) The QS_onCleanup() callback is empty for MSP430 (the application never exits).<br />
(17-21) The QS_onGetTime() callback provides a fine-granularity timestamp. The timestamp is discussed<br />
in the next section.<br />
(22) The QS_onFlush() callback flushes the QS trace buffer to the host. Typically, the function busywaits<br />
for the transfer to complete. It is only used in the initialization phase for sending the QS<br />
dictionary records to the host.<br />
(23) The implementation of QS for STR912 uses the block-oriented QS-buffer interface<br />
QS_getBlock(), which provides up to 16 bytes to fill the FIFO of the UART (see Chapter 11 in<br />
[PSiCC2]).<br />
(24) The QS_onFlush() callback busy-waits in-line until the transmit buffer is empty.<br />
(25) The data byte is inserted into the UART0 data register.<br />
3.1 QS Time Stamp Callback QS_onGetTime()<br />
The platform-specific QS port must provide function QS_onGetTime() (Listing 5(17-21)) that returns<br />
the current time stamp in 32-bit resolution. To provide such a fine-granularity time stamp, the BSP<br />
uses the free running Timer 3, which is the same timer already used for generation of the system<br />
clock-tick interrupt.<br />
NOTE: The QS_onGetTime() callback is always called with interrupts locked.<br />
Figure 7 shows how the Timer 3 Count Register (TIM3->CNTR) reading is extended to 32 bits.<br />
The drawing below shows a free running Timer 3 Count Register (TIM3->CNTR) that counts up from<br />
0 to 0xFFFF and rolls over to 0. If the system tick rate is faster than the rollover rate then you<br />
could ‘oversample’ this free-running timer by reading it in the clock tick ISR.<br />
The 32-bit variable l_currTime32 contains the sum of the 16-bit deltas from each readout of the<br />
free running Timer 3 Count Register. Because of unsigned 16-bit arithmetic used in Listing 5(19),<br />
even a ‘small’ current value minus a ‘large’ previous value still results in the proper delta. Note that<br />
QS_onGetTime() can actually be called at just about any time and thus, also needs to update<br />
l_currTime32 before it returns the current value.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
25 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
count<br />
32-bit time stamp<br />
returned from<br />
QS_onGetTime()<br />
System<br />
clock tick<br />
period<br />
TIM3->CNTR<br />
Register<br />
0xFFFF<br />
0x0000<br />
System<br />
clock-tick<br />
time<br />
Figure 7 Using the Timer 3 Count Register to provide 32-bit QS time stamp.<br />
3.2 Invoking the QSpy Host Application<br />
The QSPY host application receives the QS trace data, parses it and displays on the host workstation<br />
(currently Windows or Linux). For the configuration options chosen in this port, you invoke<br />
the QSPY host application as follows (please refer to the QSPY section in the “QP Reference Manual”):<br />
qspy –c COM1 –b 115200<br />
The following Figure 8 shows the human-readable output from the QSPY host application connected<br />
to the SR912-SK board running the <strong>Capstone</strong> code.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
26 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
Figure 8 QSPY software trace output from the <strong>Capstone</strong> example<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
27 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
4 References<br />
Document<br />
[PSiCC2] “Practical UML Statecharts in<br />
C/C++, Second Edition”, Miro Samek, Newnes,<br />
2008, ISBN 0750687061<br />
[Netrino 08] Embedded Software Boot Camp<br />
training program, Netrino 08<br />
[QP/C 08] “QP/C Reference Manual”, <strong>Quantum</strong><br />
<strong>Leaps</strong>, LLC, 2008<br />
[QP/C++ 08] “QP/C++ Reference Manual”,<br />
<strong>Quantum</strong> <strong>Leaps</strong>, LLC, 2008<br />
[QP-nano 08] “QP-nano Reference Manual”,<br />
<strong>Quantum</strong> <strong>Leaps</strong>, LLC, 2008<br />
[QL <strong>AN</strong>-Directory 07] “Application Note: QP<br />
Directory Structure”, <strong>Quantum</strong> <strong>Leaps</strong>, LLC,<br />
2007<br />
Location<br />
Available from most online book retailers, such as<br />
amazon.com. See also: http://www.statemachine.com/psicc2.htm<br />
http://www.netrino.com/Embedded-<br />
Systems/Training-Courses/Boot-Camp<br />
http://www.state-machine.com/doxygen/qpc/<br />
http://www.state-machine.com/doxygen/qpcpp/<br />
http://www.state-machine.com/doxygen/qpn/<br />
http://www.state-machine.com/doc/-<br />
<strong>AN</strong>_QP_Directory_Structure.pdf<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
28 of 29
Application Note:<br />
<strong>Capstone</strong> <strong>Dive</strong> <strong>Computer</strong> <strong>Example</strong><br />
www.state-machine.com<br />
5 Contact Information<br />
<strong>Quantum</strong> <strong>Leaps</strong>, LLC<br />
103 Cobble Ridge Drive<br />
Chapel Hill, NC 27516<br />
USA<br />
+1 866 450 LEAP (toll free, USA only)<br />
+1 919 869-2998 (FAX)<br />
e-mail: info@quantum-leaps.com<br />
WEB : http://www.quantum-leaps.com<br />
http://www.state-machine.com<br />
“Practical UML Statecharts<br />
in C/C++,<br />
Second Edition”<br />
(PSiCC2),<br />
by Miro Samek,<br />
Newnes, 2008,<br />
ISBN 0750687061<br />
Netrino, LLC<br />
9250 Bendix Road, Suite 505<br />
Columbia, Maryland 21045<br />
Toll-free: (866) 78-EMBED<br />
Main: +1 (410) 997-3401<br />
Fax: +1 (410) 997-3402<br />
WEB: http://www.netrino.com<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
29 of 29