qdk™ atmel avr with atmel studio -...

25
QP state machine frameworks for AVR QDK™ Atmel AVR with Atmel Studio Document Revision L March 2014 Copyright © Quantum Leaps, LLC www.quantum-leaps.com www.state-machine.com

Upload: others

Post on 20-Jun-2020

11 views

Category:

Documents


0 download

TRANSCRIPT

QP state machine frameworks for AVR

QDK™ Atmel AVR with Atmel Studio

Document Revision LMarch 2014

Copyright © Quantum Leaps, LLC

www.quantum-leaps.com www.state-machine.com

Table of Contents

1 Introduction ..................................................................................................................................................... 11.1 About QP™ .................................................................................................................................................... 21.2 About QM™ ................................................................................................................................................... 31.3 About this QDK .............................................................................................................................................. 31.4 Licensing QP™ .............................................................................................................................................. 41.5 Licensing QM™ .............................................................................................................................................. 4

2 Getting Started ................................................................................................................................................ 52.1 Installation ...................................................................................................................................................... 52.2 Building the QP Libraries ............................................................................................................................... 72.3 Building the Examples .................................................................................................................................... 82.4 Running the Examples ................................................................................................................................... 9

3 The Vanilla QP Port ......................................................................................................................................... 113.1 Compiler Options Used .................................................................................................................................. 113.2 The qep_port.h Header File ........................................................................................................................... 113.3 The qf_port.h Header File .............................................................................................................................. 123.4 BSP for AVR .................................................................................................................................................. 143.5 QP Idle Loop Customization in QF_onIdle() ................................................................................................... 153.6 Assertion Handling Policy in Q_onAssert() .................................................................................................... 16

4 The QK Port ..................................................................................................................................................... 174.1 Compiler and Linker Options Used ................................................................................................................ 174.2 The qk_port.h Header File ............................................................................................................................. 174.3 The QK-specific Interrupt Processing in the BSP ........................................................................................... 184.4 Idle Loop Customization in the QK_onIdle() ................................................................................................... 19

5 The QS Software Tracing Instrumentation ................................................................................................... 20

6 Related Documents and References ............................................................................................................. 22

7 Contact Information ........................................................................................................................................ 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

1 IntroductionThis QP™ Development Kit (QDK) describes how to use the QP/C™ state machine framework with the Atmel AVR and the Atmel Studio 6 and the GNU AVR compiler. The actual hardware/software used to test this QDK as shown in Figure 1 and described below:

Figure 1 Atmel STK600 board with the ATmega2560 device and the JTAGICE-mkII emulator.

1 of 23

UserLEDs

Atmega2560device

STK600-ATMEGA2560

card

Powerswitch

JTAGICEmkII

JTAGconnector

UART Rx/Txconnection

PORTDconnectionfor LEDs

Userswitches

RS-232to PCPower

from PCvia USB

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

The actual hardware/software used to test this QDK is described below:

1. STK600 board with STK600-ATMEGA2560 routing board

2. JTAGICE mkII debugger

3. Atmel Studio 6

4. AVR-GCC for Windows (bundled with Atmel Studio 6)

5. QP/C/C++ 5.2.1 or higher and QM v 2.2.03.

As shown in Figure 1, the Atmel’s STK600 is powered via a USB cable and is connected to the JTAG-mkII emulator via the JTAG ribbon cable. This QDK has been tested on the ATmega2560 device with 8KB of RAM and 256KB of on-board flash. However, the described port should be applicable to most AVRmega devices (such as ATtiny and ATmega families) big enough to accommodate QP.

NOTE: This QDK covers the classic AVR (ATtiny and ATmega device families), but does not apply tothe AVR xmega family.

1.1 About QP™

QP™ is a family of very lightweight, open source, state machine-basedframeworks for developing event-driven applications. QP enables buildingwell-structured embedded applications as a set of concurrently executinghierarchical state machines (UML statecharts) directly in C or C++ without big tools. QP is described in great detail in the book “PracticalUML Statecharts in C/C++, Second Edition: Event-Driven Programmingfor Embedded Systems” [PSiCC2] (Newnes, 2008).

As shown in Figure 2, QP consists of a universal UML-compliant eventprocessor (QEP), a portable real-time framework (QF), a tiny run-to-completion kernel (QK), and software tracing instrumentation (QS).Current versions of QP include: QP/C™ and QP/C++™, which require about 4KB of code and a few hundred bytes of RAM, and the ultra-lightweight QP-nano, which requires only 1-2KB of code and just several bytes of RAM. The Linux port described in this Application Note pertains to QP/C and QP/C++.

Figure 2 QP components and their relationship with the target hardware, board support package(BSP), and the application comprised of state machines

2 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

QP can work with or without a traditional RTOS or OS. In the simplest configuration, QP can completely replace a traditional RTOS. QP can manage up to 63 concurrently executing tasks structured as state machines (called active objects in UML).

1.2 About QM™

QM™ (QP™ Modeler) is a free, cross-platform, graphical UML modeling tool fordesigning and implementing real-time embedded applications based on theQP™ state machine frameworks. QM™ is available for Windows, Linux, andMac OS X. QM™ provides intuitive diagramming environment for creating goodlooking hierarchical state machine diagrams and hierarchical outline of yourentire application. QM™ eliminates coding errors by automatic generation ofcompact C or C++ code that is 100% traceable from your design. Please visit state-machine.com/qm for more information about QM™.

1.3 About this QDK

This QDK provides working examples of code running under both the cooperative Vanilla kernel and the preemptive QK kernel. The example code is based on the Dining Philosopher Problem (DPP) sample application described in Chapter 7 of [PSiCC2] as well as in the Application Note “Dining Philosopher Problem” [QL AN-DPP 08] (included in the example code distribution).

The entire source code included with this QDK can be edited manually in a traditional code editor. However, significant parts of the code have been generated automatically by the QM™ modeling tool from the dpp.qm model file included in the QDK. The preferred way of developing QP™ applications is to make all the changes in the model and generate the code automatically.

Figure 3: The example model opened in the QM™ modeling tool

3 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

The QDK-RX example code includes the following components:

• Board support package (BSP) which provides interrupt service routines (ISRs), all QP callbacks, and an interface to the board's LEDs, push buttons, system clock tick timer, and serial port.

• QP port to AVR for the Vanilla cooperative kernel described in Chapter 7 of [PSiCC2]

• QP port to AVR for the preemptive run-to-completion QK kernel described in Chapter 10 of [PSiCC2]

• The DPP example for both the cooperative Vanilla kernel and the preemptive QK kernel.

• The QM™ model of the Dinging Philosophers Problem (see Figure 3)

NOTE: The significant parts of the source code (files dpp.h, philo.c, and table.c) have been generated by the QM™ modeling tool from the dpp.qm model, which is the same for the Vanilla and QK versions of the DPP application. These files can be edited by hand (after unchecking the read-only property), but the changes made at the code level won't be incorporated back into the model.

1.4 Licensing QP™

The Generally Available (GA) distribution of QP™ available for download from the www.state-machine.com/ downloads website is offered with the following two licensing options:

• The GNU General Public License version 2 (GPL) as published by the FreeSoftware Foundation and appearing in the file GPL.TXT included in the packaging ofevery Quantum Leaps software distribution. The GPL open source license allowsyou to use the software at no charge under the condition that if you redistribute theoriginal software or applications derived from it, the complete source code for yourapplication must be also available under the conditions of the GPL (GPL Section2[b]).

• One of several Quantum Leaps commercial licenses, which are designed forcustomers who wish to retain the proprietary status of their code and therefore cannotuse the GNU General Public License. The customers who license Quantum Leapssoftware under the commercial licenses do not use the software under the GPL andtherefore are not subject to any of its terms.

For more information, please visit the licensing section of our website at: www.state-machine.com/licensing.

1.5 Licensing QM™

The QM™ graphical modeling tool available for download from the www.state-machine.com/ downloads website is free to use, but is not open source. During the installation you will need to accept a basic End-User License Agreement (EULA), which legally protects Quantum Leaps from any warranty claims, prohibits removing any copyright notices from QM, selling it, and creating similar competitive products.

4 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

2 Getting StartedThis section describes how to install, build, and use QDK-AVR-GNU based two examples. This information is intentionally included early in this document, so that you could start using the QDK as soon as possible. The main focus of this section is to walk you quickly through the main points without slowing you down with full-blown detail.

NOTE: This QDK assumes that the standard QP distribution consisting of QEP, QF, and QK has been installed, before installing this QDK. It is also strongly recommended that you read the QP tutorial before you start experimenting with this QDK.

2.1 Installation

The QDK code is distributed in a ZIP archive (qdkc_avr-gnu.zip. You can unzip the archive into the same directory into which you’ve installed all the standard QP components. The installation directory you choose will be referred henceforth as QP Root Directory <qp>. The following Listing 1 shows the directory structure and selected files included in the QDK-AVR-GNU distribution. (Please note that the QPdirectory structure is described in detail in a separate Application Note: “QP Directory Structure”):

Listing 1 Selected QP directories and files after installing QDK-AVR-GNU. The highlightedelements are included in the standard QDK-AVR-GNU distribution.

qpc\ - QP installation directory +-doc\ | +-AN_DPP.pdf - Application Note “Dining Philosopher Problem Example” | +-QDK_AVR-GNU-STK600.pdf – This QDK Manual “QDK AVR with GNU Compiler” | +-include/ - QP public include files | +-ports/ - QP ports | +-avr/ - AVR port | | +-qk/ - QK (Quantum Kernel) ports | | | +-gnu/ - GNU compiler | | | | +-dbg/ - QP library – Debug configuration | | | | | +-libqp_atmega2560.a - QP library | | | | +-rel/ - QP library – Release configuration | | | | +-spy/ - QP library – Spy configuration | | | | | | | | | +-make_atmega2560.bat - make script for building the QP libraries | | | | +-qep_port.h - QEP port | | | | +-qf_port.h - QF port | | | | +-qk_port.h - QK port | | | | +-qk_port.c - QK port implementation | | | | +-qs_port.h - QS port | | | | +-qp_port.h - QP port | | | | | +-vanilla/ - “vanilla” ports | | | +-gnu/ - GNU compiler | | | | +-dbg/ - QP library – Debug configuration | | | | +-dbg/ - QP library – Release configuration | | | | +-spy/ - QP library – Spy configuration | | | | | +-libqp_atmega2560.a - QP library | | | | | | | | | +-make_atmega2560.bat - make script for building the QP libraries

5 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

| | | | +-qep_port.h - QEP port | | | | +-qf_port.h - QF port | | | | +-qs_port.h - QS port | | | | +-qp_port.h - QP port | +-examples/ - subdirectory containing the examples | +-avr/ - AVR port | | | | | +-qk/ - QK examples | | | +-gnu/ - GNU AVR compiler | | | | +-dpp-qk-stk600-atmega2560/ - DPP example for AVR STK600-ATMEGA2560 | | | | | +-Debug/ - directory containing the Debug build | | | | | +-Release/ - directory containing the Release build | | | | | +-Spy/ - directory containing the Spy build | | | | | +-dpp-qk-stk600-atmega2560.cproj – Atmel Studio 6 project | | | | | +-dpp-qk-stk600-atmega2560.atsln – Atmel Studio 6 solution | | | | | +-dpp.h - the DPP header file | | | | | +-dpp.qm - QM model for the DPP application | | | | | +-bsp.c - Board Support Package for AVR | | | | | +-bsp.h - BSP header file | | | | | +-main.c - the main function | | | | | +-philo.c - the Philosopher active object | | | | | +-table.c - the Table active object | | | | | +-vanilla/ - “vanilla” examples | | | +-gnu/ - GNU AVR compiler | | | | +-dpp-stk600-atmega2560/ - DPP example for AVR STK600-ATMEGA2560 | | | | | +-Debug/ - directory containing the Debug build | | | | | +-Release/ - directory containing the Release build | | | | | +-Spy/ - directory containing the Spy build | | | | | +-dpp-stk600-atmega2560.cproj – Atmel Studio 6 project | | | | | +-dpp-stk600-atmega2560.atsln – Atmel Studio 6 solution | | | | | +-dpp.h - the DPP header file | | | | | +-dpp.qm - QM model for the DPP application | | | | | +-bsp.c - Board Support Package for AVR | | | | | +-bsp.h - BSP header file | | | | | +-main.c - the main function | | | | | +-philo.c - the Philosopher active object | | | | | +-dpp.h - the DPP header file | | | | | +-table.c - the Table active object

6 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

2.2 Building the QP Libraries

All QP components are deployed as libraries that you statically link to your application.-QP is deployed asa library that you statically link to your application. The pre-built QP libraries for various build configurations are provided inside the <qp>\ports\avr\ directory (see Listing 1). This section describes steps you need to take to rebuild the libraries yourself.

NOTE: To streamline and simplify the QP-library build process, Quantum Leaps software does not use the vendor-specific IDEs, such as the GNU Embedded Workbench IDE, for building the QP libraries. Instead, this QDK provides command-line build process based on simple batch scripts.The build process for your application is largely independent on the QP-library builds. In fact, once you have the QP libraries, you typically don’t need to rebuild them—at least not on the daily basis as you work on your application. This QDK uses the Atmel AVRStudio IDE to build the example applications, but you are free to use any other build strategy.

The code distribution contains all the batch file make_atmega2560.bat for building all the libraries locatedin <qp>\ports\avr\vanilla\gnu\ directory.

For example, to build the debug version of all the QP libraries for the AVR, with the GNU AVR compiler, you open a console window on a Windows PC, change directory to <qp>\ports\avr\vanilla\gnu\, and invoke the batch by typing at the command prompt the following command:

make_atmega2560

The make process should produce the QP libraries in the location: <qp>\ports\avr\vanilla\gnu\-dbg\. The make_atmega2560.bat assumes that the GNU-AVR toolset has been installed in the directory C:\tools\Atmel\Studio_6.0\extensions\Atmel\AVRGCC\3.4.0.65\AVRToolchain.

NOTE: You need to adjust the symbol GNU_AVR at the top of the make_atmega2560.bat file if you’ve installed the Atmel Studio in a different directory.

In order to take advantage of the Q-SPY instrumentation, you need to build the Spy version of the QP libraries. You achieve this by invoking the make_atmega2560.bat utility with the “spy” target, like this:

make_atmega2560 spy

The make process should produce the QP libraries in the directory: <qp>\ports\avr\vanilla\gnu\-spy\.

NOTE: The QP libraries and QP applications can be built in the following three build configurations:Debug - this configuration is built with full debugging information and minimal optimization. When the QP framework finds no events to process, the framework busy-idles until there are new events to process.Release - this configuration is built with no debugging information and high optimization. Single-stepping and debugging is effectively impossible due to the lack of debugging information and optimized code, but the debugger can be used to download and start the executable. When the QP framework finds no events to process, the framework puts the CPU to sleep until there are new events to process.Spy - like the debug variant, this variant is built with full debugging information and minimal optimization. Additionally, it is build with the QP's Q-SPY trace functionality built in. The on-board serial port and the Q-Spy host application are used for sending and viewing trace data. Like the Debug configuration, the QP framework busy-idles until there are new events to process.

You choose the build configuration by providing a target to the make_atmega2560.bat utility. The default target is “dbg”. Other targets are “rel”, and “spy” respectively. The following table summarizes the targets accepted by make_atmega2560.bat.

7 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

2.3 Building the Examples

The QDK contains the Atmel Studio project files located in <qp>\examples\avr\vanilla\gnu\dpp-stk600-atmega2560\dpp-stk600-atmega2560.avrsln for the “vanilla” version, and in <qp>\-examples\avr\qk\gnu\dpp-qk-stk600-atmega2560\dpp-qk-stk600-atmega2560.avrsln for the QK version, respectively.

Figure 4 The DPP example project opened in Atmel Studio.The build configuration is selected by the drop-down box.

8 of 23

Select buildconfiguration

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

2.4 Running the Examples

Figure 5 shows how to make internal connections on the STK600 board to enable the external LEDs and the external RS-232 port (see also Figure 1). The LEDs are connected to PORTD with the flat-band cable. The RS-232 SPARE port is connected with the PORTE pins 0 and 1 with a 2-wire cable as shownFigure 5.

After loading the DPP example to the board, the external LEDs connected to PORTD should start blinking. The LEDs 0-4 represent philosophers 0-4. An LED-on represents an “eating” philosopher. Extinguished LED represents philosopher “thinking” or “hungry”.

Figure 5 The connections on the STK600-ATMEGA2560.

The last LED 7 is used to visualize the idle loop activity. The LED 7 is rapidly toggled on and off from the idle callback, so its intensity is proportional to the frequency of idle loop.

As the application is running, the status LEDs at the bottom of the board (see Figure 1) should blink indicating the changing status of the Dining Philosophers. If you downloaded the Spy build configuration to the target board, you could launch the QSPY host utility to observe the output in the human-readable format.

NOTE: The QSPY host utility is now included in the Qtools collection, which is available for a separate download from www.state-machine.com/downlaods. The following discussion assumes that you have downloaded and installed Qtools, including adding the Qtools directory to the PATH variable on your system.

You launch the QSPY utility on a Windows PC as follows:

qspy –c COM5 –b 38400 –O2 –F2 –E1 –P1 –B1

This will start the QSPY host application to listen on COM5 serial port with baud rate 38400. (Please use the actual virtual COM port number on your PC.) The following screen shot shows the QSPY output from the DPP run:

9 of 23

PORTDto LEDs PORTE pins PE0/PE1 to

RS232 SPARE RXD/TXD

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

Figure 6 Screen shot from the QSPY output.

10 of 23

timestamp

QS trace record

command-lineoptions used

data lost due toinsufficient bandwidth

or too little filtering

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

3 The Vanilla QP PortThe “vanilla” port shows how to use Quantum Platform on a “bare metal” AVR-based system without any underlying multitasking kernel.

In the “vanilla” version of the QP, the only component requiring platform-specific porting is the QF. The other two components: QEP and QS require merely recompilation and will not be discussed here. Obviously, with the vanilla port you’re not using the QK component.

In case of AVR, the “vanilla” QF port is very similar to the generic “vanilla” port described in Chapter 9 of [PSiCC2].

3.1 Compiler Options Used

The most important GNU compiler options (used both for building the QP libraries and the final applicationimage) are as follows:

:: adjust these flags to the specific AVR variant that you're usingset MCU_TARGET=atmega2560. . .set CCFLAGS=-g -O2 -c -mmcu=%MCU_TARGET% -Wall

In particular, the device used was ATmega2560. You might want to replace this command file to match your AVR device.

NOTE: The most important parameter that you might want to customize for your application is the AVR device (MCU_TARGET). You need to change this symbol consistently in the make_2560.bat script for building the QP libraries as well as in the Atmel Studio project file for your application.

3.2 The qep_port.h Header File

AVR is a Harvard architecture that uses different program and data spaces as well as different CPU instructions to access these two address spaces (data memory is 8-bit wide while program memory is 16-bit wide). Consequently, one of the biggest concerns in AVR programs is to correctly allocate and access constant data in the program space (ROM) rather than the precious RAM. Since the ROM resides in a different address space, you need to tell the compiler to place variables there. You also need a way to access the data (i.e., the compiler has to use the lpm instruction.)

The GCC-AVR compiler supports data in the program memory only partially through the __attribute__((progmem)) extension. By tagging a variable with __attribute__((progmem)), you can force it to reside in the ROM. However, the compiler support ends at this point. It is your responsibility to correctly access the PROGMEM variables. The GCC-AVR provides merely a bunch of macros (all defined in the avr\pgmspace.h header file).

Listing 2 The mechanism of allocating and accessing data in ROM in qep_port.h

/* the macro 'PROGMEM' allocates const objects to ROM */ #define Q_ROM PROGMEM

/* the macro 'Q_ROM_BYTE' reads a byte from ROM */ #define Q_ROM_BYTE(rom_var_) pgm_read_byte_near(&(rom_var_))

#include <stdint.h> /* C99-standard exact-width integers */ #include <avr\pgmspace.h> /* accessing data in the program memory (PROGMEM) */ #include "qep.h" /* QEP platform-independent public interface */

11 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

As shown in Listing 2, the qep_port.h header file defines the mechanism of allocating and accessing byte-wide data in ROM by means of two macros Q_ROM and Q_ROM_BYTE(), respectively. Subsequently, all QP components (such as QEP, QF, QK, and QS) use the Q_ROM attribute and Q_ROM_BYTE() macro tocorrectly access the data in ROM.

3.3 The qf_port.h Header File

The QF header file for the AVR port with the GNU compiler is located in <qp>/ports/avr/vanilla/-gnu/qf_port.h. The most important aspect of the port is the interrupt locking/unlocking policy (QF critical section).

3.3.1 The QF Critical SectionThe AVR microcontrollers do not provide any advanced interrupt prioritization in hardware. In other words,if you enable interrupts at the CPU level (by setting the GIE flag in the SR), the AVR hardware will allow all interrupts, including the interrupt level currently being serviced. For that reason unlocking interrupts inside ISRs is not advisable, because ISRs are typically not reentrant. Consequently, QF services invokedfrom the ISRs (such as QF_tick(), QF_publish(), QActive_postFIFO, etc.) must not inadvertently unlock interrupts that are locked in hardware upon the entry to the interrupt processing. All this means that QF must use an interrupt locking/unlocking scheme that allows for nesting critical sections. QF provides such a policy called “saving and restoring interrupt status” and described in Chapter 7 of [PSiCC2].

Listing 3 The QF critical section defined in qf_port.h.

. . . /* QF interrupt disable/enable */ (1) #define QF_INT_DISABLE() cli() (2) #define QF_INT_ENABLE() sei()

/* QF critical section entry/exit */ (3) #define QF_CRIT_STAT_TYPE uint8_t (4) #define QF_CRIT_ENTRY(stat_) do { \ (stat_) = SREG; \ cli(); \ } while (0) (5) #define QF_CRIT_EXIT(stat_) (SREG = (stat_))

(4) #include <avr\io.h> /* SREG definition */ (5) #include <avr\interrupt.h> /* cli()/sei() */ . . .

(1) The interrupt-disable macro resolves to the CLI instruction on the AVR

(2) The interrupt-enable macro resolves to the SEI instruction on the AVR

(3) “saving and restoring interrupt status” interrupt locking policy requires specifying the type of the “interrupt lock key” (see Chapter 7 of [PSiCC2]). Here QF_CRIT_STAT_TYPE is defined as uint8_t (8-bit unsigned integer)

(4) The interrupt locking macro QF_CRIT_ENTRY() is first saving the SREG and then disabling interrupts. (Please note that the do {…} while (0) loop around the macro is only necessary for syntactically correct grouping of instructions.) The macro expands to the following assembly code (assuming that key_ is allocated in r24):

in r24,SREG ; (key_) = SREG

12 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

cli ; cli();

(5) The QF_CRIT_EXIT() macro restores the SREG saved before. The macro expands to the following assembly code (assuming that key_ is located in r24):

out SREG,r24 ; SREG = (key_)

Please note the following properties of this QF critical section implementation:

• The copying of the SREG is performed outside of critical section, so it does not add to the interrupt latency.The macro expands to very efficient machine code without any function-call overheads.

• If the interrupts were unlocked before QF_CRIT_ENTRY(), the QF_CRIT_EXIT() macro will set the I bit in the SREG. Conversely, if the interrupts are already locked (SREG.7 == 0) before QF_CRIT_ENTRY(), the QF_CRIT_EXIT() macro will not set the the I bit in the SREG, so the interrupts won’t be enabled.

13 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

3.4 BSP for AVR

The Board Support Package (BSP) for AVR is very simple. However, there are some important details that you need to pay attention to.

3.4.1 Board Initialization and the Timer TickThe BSP is minimal, but generic for most AVR devices. The most important step is initialization of Timer 0to deliver the time tick interrupt at the desired rate (BSP_TICKS_PER_SEC):

void BSP_init(void) { DDRD = 0xFF; /* All PORTD pins are outputs for LEDs */ PORTD = 0x00; /* trun off all LEDs */

if (QS_INIT((void *)0) == 0) { /* initialize the QS software tracing */ Q_ERROR(); } QS_RESET(); }

As you will see in the Timer0 interrupt initialization (in the file isr.c), Timer 0 is initialized with a prescalerof 1/1024. If you choose a different value of the prescaler, you’d need to adjust the OCR0A accordingly.

NOTE: At this point the TCCR0A register of the Timer0 is not written yet (should be 0 out of reset), tokeep the Timer0 stopped. This is done to keep the time stamp for QS instrumentation at zero, until the interrupts are configured and started.

3.4.2 Starting Interrupts in QF_onStartup()QP invokes the QF_onStartup() callback just before starting the event loop inside QF_run(). The QF_onStartup() function must configure and start the interrupts. In this BSP only the timer tick interrupt is started.

void QF_onStartup(void) { /* set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking */ TCCR2A = ((1 << WGM21) | (0 << WGM20)); TCCR2B = ((1 << CS22) | (1 << CS21) | (1 << CS20)); /* 1/2^10 */ ASSR &= ~(1 << AS2); TIMSK2 = (1 << OCIE2A); /* enable TIMER2 compare Interrupt */ TCNT2 = 0; OCR2A = ((F_CPU / BSP_TICKS_PER_SEC / 1024) - 1); /* keep last */ }

3.4.3 ISRsThe GNU-AVR compiler supports writing interrupts in C. In the “vanilla” port, the ISRs are identical as in the simplest of all “superloop” (main+ISRs), and there is nothing QP-specific in the structure of the ISRs. The only QP-specific requirement is that you provide a periodic time-tick ISR and you invoke QF_TICK() in it.

Listing 4 Time tick interrupt calling QF_tick() function to manage armed time events.

(1) ISR(TIMER2_COMPA_vect) { /* No need to clear the interrupt source since the Timer0 compare

14 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

* interrupt is automatically cleard in hardware when the ISR runs. */ #ifdef Q_SPY (2) l_tickTime += (F_CPU / BSP_TICKS_PER_SEC / 1024); #endif

(3) QF_TICK(&l_TIMER2_COMPA); }

(1) The definition of the interrupt function must begin with the ISR() macro.

(2) The update of the local l_tickTime variable is only performed in the SPY build configuration to provide a 32-bit time stamp for tracing (see also Section 5).

(3) The time-tick ISR must invoke QF_TICK(), and can also perform other actions, if necessary. The function QF_TICK() cannot be reentered, that is, it necessarily must run to completion and return before it can be called again. This requirement is automatically fulfilled, because here interrupts are locked throughout the interrupt processing.

3.5 QP Idle Loop Customization in QF_onIdle()

This QF port uses the standard QF_run() “vanilla” implementation described in Chapter 7 in the [PSiCC2]book. The standard QF_run() can very easily detect the situation when no events are available, in which case QF_run() calls the QF_onIdle() callback. You can use QF_onIdle() to suspended the CPU to save power, if your CPU supports such a power-saving mode. Please note that QF_onIdle() is called repetitively from the event loop whenever the event loop has no more events to process, in which case only an interrupt can provide new events. The QF_onIdle() callback is called with interrupts disabled, because the determination of the idle condition might change by any interrupt posting an event.

AVR supports several power-saving levels (consult the AVR data sheet for details). The following piece ofcode shows the QF_onIdle() callback that puts AVR into the idle power-saving mode. Please note that AVR architecture allows for very atomic setting the low-power mode and enabling interrupts at the same time.

Listing 5 QF_onIdle() for the “vanilla” AVR port

(1) void QF_onIdle() { /* NOTE: interrupts DISABLED */

LED_ON(7); LED_OFF(7);

(2) #ifdef Q_SPY /* use the idle cycles for QS transmission */

QF_INT_ENABLE();

if ((UCSR0A & (1 << UDRE0)) != 0) { uint16_t b;

QF_INT_DISABLE(); b = QS_getByte(); QF_INT_ENABLE();

if (b != QS_EOD) { UDR0 = (uint8_t)b; /* stick the byte to the TX UDR */ }

15 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

}

#elif defined NDEBUG (3) SMCR = (0 << SM0) | (1 << SE);/*idle sleep mode, adjust to your project */

/* never separate the following two assembly instructions, see NOTE01 */ (4) __asm__ __volatile__ ("sei" "\n\t" :: ); (5) __asm__ __volatile__ ("sleep" "\n\t" :: ); /* see Atmel AVR Datasheet */ (6) SMCR = 0; /* clear the SE bit */ #else QF_INT_ENABLE(); #endif /* Q_SPY */ }

(1) The function QF_onIdle() is always called with interrupts disabled.

(2) This part of the code is only used in the QSPY build configuration. In this case the idle callback is used to transmit the trace data using the USART of the AVR device.

(3) The SMCR register is loaded with the desired sleep mode (idle mode in this case) and the Sleep Enable (SE) bit is set. Please note that the sleep mode is not active until the SLEEP command.

(4) The interrupts are unlocked with the SEI instruction.

(5) The sleep mode is activated with the SLEEP instruction.

NOTE: The AVR datasheet is very specific about the behavior of the SEI-SLEEP instruction pair. Dueto pipelining of the AVR core, the SLEEP instruction is guaranteed to execute before entering any potentially pending interrupt. This means that enabling interrupts and activating the sleep mode is atomic, as it should be to avoid non-deterministic sleep.

(6) As recommended in the AVR datasheet, the SMCR register should be explicitly cleared upon the exit from the sleep mode.

3.6 Assertion Handling Policy in Q_onAssert()

As described in Chapter 6 of [PSiCC2], all QP components use internally assertions to detect errors in theway application is using the QP services. You need to define how the application reacts in case of assertion failure by providing the callback function Q_onAssert(). Typically, you would put the system in fail-safe state and try to reset. It is also a good idea to log some information as to where the assertion failed.

The following code fragment shows the Q_onAssert() callback for AVR. The function simply locks all interrupts and enters a for-ever loop. This policy is only adequate for testing, but probably is not adequatefor production release.

void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line) { QF_INT_DISABLE(); LED_ON_ALL(); /* light up all LEDs */ for (;;) { /* hang here in the for-ever loop */ } }

16 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

4 The QK PortThe QP port with the preemptive kernel (QK) is remarkably simple and very similar to the “vanilla” port. In particular, the interrupt locking/unlocking policy is the same, and the BSP is identical, except some small additions to the ISRs.

The DPP example for the QK port is provided in the directory <qp>\examples\avr\qk\gnu\dpp-qk-stk600-atmega2560.

4.1 Compiler and Linker Options Used

The compiler and linker used in the QK port are identical to those used in the “vanilla” port described earlier. The only additional consideration is the C-stack size, which in the QK application is generally larger than in the non-preemptive case of the “vanilla” port.

NOTE: The QK port uses the same compiler and linker options as the “vanilla” port. However, due to the increased stack usage with the preemptive kernel, you might need to adjust the stack size parameter.

4.2 The qk_port.h Header File

In the QK port, the only QP component requiring customization is the QK. You configure and customize QK through the header file qk_port.h, which is located in the QP ports directory <qp>\ports\avr\qk\gnu\.

4.2.1 The QK Critical SectionThe interrupt locking/unlocking policy in the QK port is the same as in the vanilla port (Section 3.3.1).

4.2.2 The QK Interrupt Entry and ExitThe QK port to AVR uses ISRs written in C, declared with the ‘ISR(TIMER2_COMPA_vect) macro, exactlyas described in Section 3.4.3 for the vanilla port. QK, however, requires a specific interrupt entry and exit actions that are performed in the following two QK macros defined in the qk_port.h header file:

Listing 6 QK interrupt entry and exit macros defined in the <qp>\ports\avr\qk\gnu\qk_port.h header file.

/* QK interrupt entry and exit */ (1) #define QK_ISR_ENTRY() (++QK_intNest_)

(2) #define QK_ISR_EXIT() do { \ (3) --QK_intNest_; \ (4) if (QK_intNest_ == (uint8_t)0) { \ (5) uint8_t p = QK_schedPrio_(); \ (6) if (p != (uint8_t)0) { \ (7) QK_sched_(p); \ } \ } \ } while (0)

#include "qk.h" /* QK platform-independent public interface */

17 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

(1) The macro QK_IRQ_ENTRY() is called with interrupts disabled. The macro increments the QK interrupt nesting level QK_intNest_, which prevents synchronous preemptions inside ISRs.

(2) The macro QK_IRQ_EXIT() is called with interrupts disabled, because interrupts are never unlocked throughout the ISR processing. The do {…} while (0) loop around the macro is the standard way of syntactically-correct grouping of instructions.

(3) The QK interrupt nesting level is reduced to account for leaving one interrupt level.

(4) Only when the QK interrupt nesting indicates that the ISR returns to the task level...

(5-6) The QK_schedPrio_() function returns the highest priority task ready to run or zero if no task has a higher priority than the current level.

(7) The QK scheduler is invoked to keep launching the high-priority tasks as long as they are above the currently serviced priority (see [PSiCC2], Figure 10.2, items (6) through (8)). The QK scheduler is designed to be called with with interrupts disabled and also returns with interrupts disabled, althoughit enables interrupts before launching any task.

4.3 The QK-specific Interrupt Processing in the BSP

As mentioned at the beginning to the QK port section, the BSP for the QK port is identical as the BSP for the “vanilla” port, except of adding the QK-specific interrupt entry and exit to each ISR.

Listing 7 The time tick ISR in the QK port.

(1) ISR(TIMER2_COMPA_vect) { (2) QK_ISR_ENTRY(); /* inform QK about entering the ISR */

/* No need to clear the interrupt source since the Timer0 compare * interrupt is automatically cleard in hardware when the ISR runs. */

(3) #ifdef Q_SPY (4) l_tickTime += (F_CPU / BSP_TICKS_PER_SEC / 1024); #endif

(5) QF_tick();

(6) QK_ISR_EXIT(); /* inform QK about exiting the ISR */ }

(1) The definition of the interrupt function must begin with the ISR() macro.

(2) The QK_ISR_ENTRY() macro informs QK about entering the ISR by incrementing the QK interrupt nesting level.

(3-4) The update of the local l_tickTime variable is only performed in the SPY build configuration to provide a 32-bit time stamp for tracing (see also Section 5).

(5) The time-tick ISR must invoke QF_tick(), and can also perform other actions, if necessary. The function QF_tick() cannot be reentered, that is, it necessarily must run to completion and return before it can be called again. This requirement is automatically fulfilled, because here interrupts are locked throughout the interrupt processing.

(6) The QK_ISR_EXIT() macro informs QK about exiting the ISR. The macro performs also invokes the QK scheduler to check for asynchronous preemptions.

18 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

4.4 Idle Loop Customization in the QK_onIdle()

If you’re using the QK preemptive kernel, the idle task is handled differently than in the “vanilla” port. In the absence of events to process, the QK invokes the QK_onIdle() callback function to give you an opportunity to perform some processing outside of the time-critical parts of the code or to give you an opportunity to conserve power by putting the CPU in a power-saving mode. In contrast to QF_onIdle(), QK_onIdle() is entered with interrupts already unlocked.

void QK_onIdle(void) {

QF_INT_DISABLE(); LED_ON(7); LED_OFF(7); QF_INT_ENABLE();

#ifdef Q_SPY /* use the idle cycles for QS transmission */

if ((UCSR0A & (1 << UDRE0)) != 0) { uint16_t b;

QF_INT_DISABLE(); b = QS_getByte(); QF_INT_ENABLE();

if (b != QS_EOD) { UDR0 = (uint8_t)b; /* stick the byte to the TX UDR */ } }

#elif defined NDEBUG

SMCR = (0 << SM0) | (1 << SE);/*idle sleep mode, adjust to your project */ __asm__ __volatile__ ("sleep" "\n\t" :: ); SMCR = 0; /* clear the SE bit */

#endif }

19 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

5 The QS Software Tracing InstrumentationThis QDK demonstrates how to use the QS software tracing instrumentation to generate real-time trace ofa running QP application. Normally, the QS instrumentation is inactive and does not add any overhead to your application, but you can turn the instrumentation on by defining the Q_SPY macro and recompiling thecode.

QS is a software tracing facility built into all QP components and also available to the Application code. QS allows you to gain unprecedented visibility into your application by selectively logging almost all interesting events occurring within state machines, the framework, the kernel, and your application code. QS software tracing is minimally intrusive, offers precise time-stamping, sophisticated runtime filtering of events, and good data compression (see Chapter 11 in PSiCC2 [PSiCC2]).

QS can be configured to send the trace data out of the serial port of the target device. On the AVR, QS uses the built-in USART to send the trace data to the host. The STK600 board has the RS232 level-shifter already installed and available as the serial port (see Figure 1). The QS platform-dependent implementation is located in the file bsp.c and looks as follows:

Listing 8 QSpy implementation to send data out of the USART serial port of the AVR.

(1) #ifdef Q_SPY

(2) #define QS_BUF_SIZE (256) (3) #define BAUD_RATE 38400

(4) static QSTimeCtr l_tickTime;

(5) uint8_t QS_onStartup(void const *arg) { (6) static uint8_t qsBuf[QS_BUF_SIZE]; /* buffer for Quantum Spy */ uint16_t n;

(7) QS_initBuf(qsBuf, sizeof(qsBuf));

n = BSP_CPU_HZ / 16 / BAUD_RATE - 1; /* Set baud rate */ UBRR0H = (uint8_t)(n >> 8); UBRR0L = (uint8_t)n;

/* enable transmitter in polled mode, no interrupts */ UCSR0B = (0 << RXEN0) | (1 << TXEN0) | (0 << RXCIE0) | (0 << UDRIE0);

/* Async. mode, 8N1 */ UCSR0C = (0 << UMSEL0) | (0 << UPM00) | (0 << USBS0) | (3 << UCSZ00) | (0 << UCPOL0);

return (uint8_t)1; /* indicate successful QS initialization */ } /*..........................................................................*/ (8) void QS_onCleanup(void) { } /*..........................................................................*/ (9) void QS_onFlush(void) { uint16_t b; /* note that uint16_t is used */ while ((b = QS_getByte()) != QS_EOD) { /* next QS trace byte available? */ while ((UCSR0A & (1 << UDRE0)) == 0) { /* TX UDR not empty? */ }

20 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

UDR0 = (uint8_t)b; /* stick the byte to the TX UDR */ } } /*..........................................................................*/ /* NOTE: getTime is invoked within a critical section (interrupts disabled) */(10) QSTimeCtr QS_onGetTime(void) {(11) if ((TIFR0 & (1 << OCF0A)) == 0) { /* output compare flag NOT set? */(12) return BSP_tickTime + (QSTimeCtr)TCNT0; } else { /* the output compare occured, but the ISR did not run yet */(13) return BSP_tickTime + (BSP_CPU_HZ / BSP_TICKS_PER_SEC / 1024) + (QSTimeCtr)TCNT0; } } #endif /* Q_SPY */

(1) The QS instrumentation is enabled only when the macro Q_SPY is defined

(2) You should adjust the QS buffer size (in bytes) to your particular application

(3) You might want to adjust the USART baud rate to your particular system (the 38400 baud rate workswell with the 8MHz clock)

(4) This l_tickTime variable is used to increment the 8-bit Timer0 used for ticking to 32-bits. The tick ISR increments l_tickTime by the number of hardware clock ticks, which is the value loaded to the OCR0A register plus one (see Section 3.4.1). The “plus one” value comes because after each output-compare the TCNT0 register rolls over to zero for the full hardware clock tick, as indicated in the timing diagrams in the AVR data sheet.

(5) The QS_onStartup() callback performs the initialization of QS

(6) The QS trace buffer is statically allocated to the specified size

(7) You always need to call QS_initBuf() from QS_onStartup() to initialize the trace buffer. This particular QS port initializes USART for data transfer at the given baud rate (BAUD_RATE = 38400 bits per second)

(8) The QS_onCleanup() callback performs the cleanup of QS. Here nothing needs to be done.

(9) The QS_onFlush() callback flushes the QS trace buffer to the host. Typically, the function busy-waits for the transfer to complete. It is only used in the initialization phase for sending the QS dictionary records to the host (see Chapter 11 in [PSiCC2])

(10) The QS_getTime() callback provides the time-stamp to the QS trace records. The QS time-stamping implementation uses the same Timer0 that is also providing the time tick interrupt. The idea is simple: count the number of the overflows in the time-tick interrupt and add on the number of hardware clock ticks from the TCNT0 register of Timer0.

(11) Here the OCF0A flag in the TIFR0 register is tested. This flag being set means that the Output Compare has occurred and the TCNT0 register rolled over to zero, but the time-tick ISR has not run yet (because interrupts are still locked).

(12) Most of the time the OCF0A flag is not set, and the time stamp is simply the sum of l_tickTime + (QSTimeCtr)TCNT0.

(13) If the OCF0A flag is set, the l_tickTime counter misses one period and must be incremented by (BSP_CPU_HZ / BSP_TICKS_PER_SEC / 1024).

21 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

6 Related Documents and References

Document Location[PSiCC2] “Practical UML Statecharts in C/C++, Second Edition”, Miro Samek, Newnes, 2008

Available from most online book retailers, such as amazon.com. See also: http://www.state-machine.com/psicc2.htm

[QP 08] “QP Reference Manual”, Quantum Leaps, LLC, 2008

http://www.state-machine.com/doxygen/qpn/

[QL AN-Directory 07] “Application Note: QP Directory Structure”, Quantum Leaps, LLC, 2007

http://www.state-machine.com/doc/AN_QP_Directory_Structure.pdf

[QL AN-DPP 08] “Application Note: Dining Philosopher Problem Application”, Quantum Leaps, LLC, 2008

http://www.state-machine.com/doc/AN_DPP.pdf

[Pardue 05] “C Programming for Microcontrollers”, Joe Pardue, Smiley Micros, 2005

http://www.smileymicros.com

“ATmega Data Sheet”, Atmel http://www.atmel.com/dyn/resources/ -prod_documents/doc2514.pdf

“GNU AVR C/C++ Compiler: Reference Guide”, GNU Systems

The PDF version of this document is included in the GNU Embedded Workbench for AVR.

[Samek 07a] “Using Low-Power Modes in Foreground/Background Systems”, Miro Samek,Embedded System Design, October 2007

http://www.embedded.com/design/202103425

22 of 23

Copyright © Quantum Leaps, LLC. All Rights Reserved.

QDK™AVR with Atmel Studio

www.state-machine.com/avr

7 Contact Information

Quantum Leaps, LLC103 Cobble Ridge DriveChapel Hill, NC 27516USA

+1 866 450 LEAP (toll free, USA only)+1 919 869-2998 (FAX)

e-mail: [email protected] WEB : http://www.quantum-leaps.com http://www.state-machine.com

“Practical UML Statecharts in C/C++,Second Edition: Event Driven Programming for Embedded Systems”,by Miro Samek,Newnes, 2008

23 of 23