05.11.2014 Views

AN: Capstone Dive Computer Example - Quantum Leaps

AN: Capstone Dive Computer Example - Quantum Leaps

AN: Capstone Dive Computer Example - Quantum Leaps

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!