03.11.2014 Views

QDK-nano PIC24/dsPIC-C30 - Quantum Leaps

QDK-nano PIC24/dsPIC-C30 - Quantum Leaps

QDK-nano PIC24/dsPIC-C30 - Quantum Leaps

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

QP state machine frameworks for <strong>PIC24</strong>/<strong>dsPIC</strong><br />

<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

Document Revision C<br />

October 2012<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 About QP-<strong>nano</strong> ........................................................................................................................................... 2<br />

1.2 About the QP-<strong>nano</strong> Port to <strong>PIC24</strong>/<strong>dsPIC</strong> .................................................................................................... 3<br />

1.3 What’s Included in the <strong>QDK</strong>-<strong>nano</strong>-<strong>PIC24</strong>-<strong>dsPIC</strong>? .......................................................................................... 4<br />

1.4 Licensing QP-<strong>nano</strong> ..................................................................................................................................... 5<br />

2 Getting Started ................................................................................................................................................ 6<br />

2.1 Software Installation ....................................................................................................................................... 6<br />

2.2 Building and Running the Examples .............................................................................................................. 8<br />

3 Non-Preemptive “Vanilla” Port ...................................................................................................................... 10<br />

3.1 The qpn_port.h Header File ........................................................................................................................... 10<br />

3.2 ISRs in the Non-Preemptive “Vanilla” Configuration ...................................................................................... 11<br />

3.3 QP Idle Loop Customization in QF_onIdle() ................................................................................................... 13<br />

4 Preemptive Configuration with QK-<strong>nano</strong> ................................................................................................... 15<br />

4.1 QK-<strong>nano</strong>-specific ISR Entry and Exit Macros (file qpn_port.h) ....................................................................... 15<br />

4.2 ISRs with the Preemptive QK-<strong>nano</strong> Kernel .................................................................................................... 17<br />

4.3 Idle Loop Customization in the QK-<strong>nano</strong> Port ................................................................................................ 20<br />

4.4 Testing QK Preemption Scenarios ................................................................................................................. 21<br />

5 Implementing “Zero Interrupt Latency” with the NMI .................................................................................. 24<br />

5.1 Communication between NMIs and QP-<strong>nano</strong> ................................................................................................ 24<br />

5.2 Implementation Considerations for the NMIs ................................................................................................. 24<br />

6 BSP for the Explorer 16 Board ....................................................................................................................... 25<br />

6.1 Setting the Sizes of Stack and Heap .............................................................................................................. 25<br />

6.2 The BSP header file bsp.h ............................................................................................................................. 26<br />

6.3 BSP initialization ............................................................................................................................................ 26<br />

6.4 Starting Interrupts in QF_onStartup() ............................................................................................................. 26<br />

6.5 Assertion Handling Policy in Q_onAssert() .................................................................................................... 27<br />

7 Related Documents and References ............................................................................................................. 28<br />

8 Contact Information ........................................................................................................................................ 29<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

i


1 Introduction<br />

This QP-<strong>nano</strong> Development Kit (<strong>QDK</strong>-<strong>nano</strong>) describes how to use the QP-<strong>nano</strong> state machine<br />

framework with the Microchip <strong>PIC24</strong>/<strong>dsPIC</strong> MCUs/DSCs and the Microchip <strong>C30</strong> compiler.<br />

USB cable<br />

to host PC<br />

Figure 1: Explorer 16 board with MPLAB ICD2 debugger<br />

MPLAB ICD2<br />

in-circuit debugger<br />

<strong>PIC24</strong>FJ128GA010<br />

or<br />

<strong>dsPIC</strong>33FJ256GP710<br />

target device<br />

Power LED<br />

9V DC power<br />

User LEDs<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

1 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

The actual hardware/software used to test this <strong>QDK</strong>-<strong>nano</strong> is described below (see Figure 1):<br />

1. Microchip Explorer 16 Development Board with <strong>PIC24</strong>FJ128GA010 or <strong>dsPIC</strong>33FJ256 daughter board<br />

2. Microchip MPLAB® ICD2 in-circuit debugger<br />

3. Microchip MPLAB® IDE v8.83<br />

4. Microchip MPLAB <strong>C30</strong> compiler v3.31<br />

5. QP-<strong>nano</strong> 4.5.02 or higher<br />

As shown in Figure 1, the Explorer 16 Development Board accepts <strong>PIC24</strong> and <strong>dsPIC</strong> daughter boards.<br />

The port has been tested with <strong>PIC24</strong>FJ128GA010 daughter board as well as with <strong>dsPIC</strong>33FJ256<br />

daughter board. However, the described QP port should be applicable to almost all Microchip 16-bit<br />

<strong>PIC24</strong> and <strong>dsPIC</strong> devices.<br />

1.1 About QP-<strong>nano</strong><br />

QP-<strong>nano</strong> is an ultra-lightweight, open source, state machine framework<br />

and RTOS for developing real-time embedded applications. QP-<strong>nano</strong> has<br />

been specifically designed to enable event-driven programming with<br />

concurrent hierarchical state machines (UML statecharts) on low-end 8- and<br />

16-bit single-chip MCUs and DSPs, such as PICmicro, <strong>PIC24</strong>/<strong>dsPIC</strong>, 8051,<br />

AVR, MSP430, 68HC08/11/12, R8C/Tiny, H8/S, TMS320C28x, Cypress<br />

PSoC, 8051 and others alike, with a few hundred bytes of RAM and a few<br />

kilobytes of ROM. With QP-<strong>nano</strong>, coding of modern state machines directly in<br />

C is a non-issue. No big design automation tools are needed.<br />

As shown in Figure 2, QP-<strong>nano</strong> consists of a universal UML-compliant event<br />

processor (QEP-<strong>nano</strong>), a highly portable event-driven framework (QF-<strong>nano</strong>),<br />

and a tiny run-to-completion kernel (QK-<strong>nano</strong>).<br />

Figure 2 QP-<strong>nano</strong> components and their relationship with the target hardware, board support<br />

package (BSP), and the application comprised of state machines<br />

State<br />

Machine<br />

State<br />

Machine<br />

State<br />

Machine<br />

State<br />

Machine<br />

Application<br />

(Your code)<br />

QEP-<strong>nano</strong> UML-Compliant<br />

Event Processor<br />

QF-<strong>nano</strong> Event-Driven<br />

Framework<br />

BSP<br />

QK-<strong>nano</strong> Preemptive Kernel,<br />

Cooperative Kernel,<br />

or other OS/RTOS<br />

Target<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

2 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

The QP-<strong>nano</strong> framework can manage up to 8 concurrently executing hierarchical state machines and<br />

requires only 1-2KB of code (ROM) and just several bytes of RAM. This tiny footprint, especially in RAM,<br />

makes QP-<strong>nano</strong> ideal for high volume, cost-sensitive applications, such as motor control, lighting control,<br />

capacitive touch sensing, remote access control, RFID, thermostats, small appliances, toys, power<br />

supplies, battery chargers, or just about any system-on-a-chip (SoC or ASIC) that contains a small<br />

processor inside.<br />

Also, because the event-driven paradigm naturally uses the CPU only when handling events and<br />

otherwise can very easily switch the CPU into a low-power sleep mode, QP-<strong>nano</strong> is particularly suitable<br />

for ultra-low power applications, such as wireless sensor networks or implantable medical devices.<br />

All versions of QP, including QP-<strong>nano</strong>, are described in detail in the book "Practical UML Statecharts in<br />

C/C++, 2nd Edition: Event-Driven Programming for Embedded Systems" [PSiCC2] published by Newnes<br />

in 2008 (see www.state-machine.com/psicc2). QP-<strong>nano</strong> has a strong user community and has been<br />

applied worldwide in industries, such as: consumer electronics, telecommunications, equipment, industrial<br />

automation, transportation systems, medical devices, and many more. Please refer to the www.statemachine.com<br />

website for more information.<br />

1.2 About the QP-<strong>nano</strong> Port to <strong>PIC24</strong>/<strong>dsPIC</strong><br />

Figure 3 shows the architecture of the QP-<strong>nano</strong> port to <strong>PIC24</strong>/<strong>dsPIC</strong>. The port takes advantage of the<br />

<strong>PIC24</strong>/<strong>dsPIC</strong> interrupt priority level (IPL) management hardware. In both cooperative and preemptive QP<strong>nano</strong><br />

ports the hardware priority levels are allocated as follows:<br />

1. All QP-<strong>nano</strong> tasks (active objects) as well as the QP idle loop execute at the lowest IPL of zero.<br />

2. IPL levels 1 through 6 are used for maskable interrupts. These maskable interrupts (IPL 1..6) can call<br />

QP-<strong>nano</strong> services, such as QF_tick() and QActive_postISR(). The maskable interrupts can nest<br />

on each other, which is the default in <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />

3. This QP-<strong>nano</strong> port never disables the IPL level 7, which is the Non-Maskable Interrupt (NMI) level.<br />

The NMI-level interrupt(s) cannot call any QP services. But the NMI can trigger a maskable interrupt,<br />

as shown by the dashed arrow in Figure 3. This triggered interrupt of IPL 1..6 can call QP-<strong>nano</strong><br />

services for signaling the QP tasks (active objects).<br />

This layered software architecture fits very naturally with the <strong>PIC24</strong>/<strong>dsPIC</strong> hardware. The QP ports (both<br />

cooperative and preemptive) allow nesting of interrupts, which is the default in <strong>PIC24</strong>/<strong>dsPIC</strong>. Both<br />

cooperative and preemptive QP ports can work with the interrupt service routines (ISRs) generated by the<br />

<strong>C30</strong> compiler, although the preemptive version requires special macros to be invoked upon entering and<br />

exiting ISRs.<br />

The interrupt locking policy is implemented very efficiently by means of the DISI instruction (see<br />

Section 3.1). As described in Section 8.2.3 of “<strong>PIC24</strong>F Family Reference Manual” [<strong>PIC24</strong> Ref], “The DISI<br />

instruction only disables interrupts with priority levels 1-6. Priority level 7 interrupts and all trap events still<br />

have the ability to interrupt the CPU when the DISI instruction is active.” This means that interrupts with<br />

IPL of 7 are never disabled and thus run with “zero interrupt latency”.<br />

NOTE: Obviously, true “zero interrupt latency” is impossible to achieve. The term means here that<br />

the NMI-level interrupt(s) execute with the minimal latency achievable in <strong>PIC24</strong>/<strong>dsPIC</strong> hardware and<br />

that there is no additional latency added to the NMI-level interrupt(s) because of interrupt locking in<br />

QP.<br />

The use of NMI to achieve “zero interrupt latency” is explained in Section 5.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

3 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

Figure 3: Architecture of the <strong>PIC24</strong>/<strong>dsPIC</strong> port<br />

(IPL = 7) Non-Maskable Interrupt (NMI)<br />

IPL Level 7<br />

(NMI level)<br />

(IPL = 6) User interrupt<br />

(IPL = 5) User interrupt<br />

(IPL = 4) User interrupt<br />

IPL Levels 1-6<br />

(interrupt level)<br />

(IPL = 3) User interrupt<br />

(IPL = 2) User interrupt<br />

(IPL = 1) User interrupt<br />

(QP prio = n) User task (active object)<br />

(QP prio = n-1) User task (active object)<br />

. . .<br />

(QP prio = 2) User task (active object)<br />

IPL Level 0<br />

(task level)<br />

(QP prio = 1) User task (active object)<br />

(QP prio = 0) Idle task<br />

1.3 What’s Included in the <strong>QDK</strong>-<strong>nano</strong>-<strong>PIC24</strong>-<strong>dsPIC</strong>?<br />

This <strong>QDK</strong> provides a basic Board Support Package (BSP) for <strong>PIC24</strong> and <strong>dsPIC</strong> MCUs and two versions<br />

of the PEdestrian LIght CONtroller (PELICAN) crossing example for each processor. The PELICAN<br />

application is described in Chapter 12 of [PSiCC2] as well as in the Application Note “PEdestrian LIght<br />

CONtroller (PELICAN) Crossing” [QL AN-PELICAN 08] (included in the <strong>QDK</strong>-<strong>nano</strong> distribution):<br />

<br />

<br />

<br />

<br />

The PELICAN crossing example with the cooperative “vanilla” kernel for <strong>PIC24</strong>FJ128GA010;<br />

The PELICAN crossing example with the preemptive QK-<strong>nano</strong> kernel for <strong>PIC24</strong>FJ128GA010;<br />

The PELICAN crossing example with the cooperative “vanilla” kernel for <strong>dsPIC</strong>33FJ256GP710; and<br />

The PELICAN crossing example with the preemptive QK-<strong>nano</strong> kernel for <strong>dsPIC</strong>33FJ256GP710.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

4 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

1.4 Licensing QP-<strong>nano</strong><br />

The Generally Available (GA) distribution of QP-<strong>nano</strong> available for download from the www.statemachine.com/downloads<br />

website is offered with the following two licensing options:<br />

<br />

<br />

The GNU General Public License version 2 (GPL) as published by the Free<br />

Software Foundation and appearing in the file GPL.TXT included in the packaging of<br />

every <strong>Quantum</strong> <strong>Leaps</strong> software distribution. The GPL open source license allows<br />

you to use the software at no charge under the condition that if you redistribute the<br />

original software or applications derived from it, the complete source code for your<br />

application must be also available under the conditions of the GPL (GPL Section<br />

2[b]).<br />

One of several <strong>Quantum</strong> <strong>Leaps</strong> commercial licenses, which are designed for<br />

customers who wish to retain the proprietary status of their code and therefore cannot<br />

use the GNU General Public License. The customers who license <strong>Quantum</strong> <strong>Leaps</strong><br />

software under the commercial licenses do not use the software under the GPL and<br />

therefore are not subject to any of its terms.<br />

For more information, please visit the licensing section of our website at: www.statemachine.com/licensing.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

5 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

2 Getting Started<br />

This section describes how to install, build, and use <strong>QDK</strong>-<strong>PIC24</strong>-<strong>dsPIC</strong>-<strong>C30</strong>.<br />

2.1 Software Installation<br />

The <strong>QDK</strong>-<strong>nano</strong> code is distributed in a ZIP archive (qdkn_pic24_dspic-c30_.zip, where <br />

stands for a specific <strong>QDK</strong>-<strong>nano</strong> version, such as 4.0.04). You can uncompress the archive into any<br />

directory. The installation directory you choose will be referred henceforth as . The following<br />

shows the directory structure and selected files included in the QP-<strong>nano</strong> distribution. (Please note that the<br />

QP-<strong>nano</strong> directory structure is described in detail in a separate <strong>Quantum</strong> <strong>Leaps</strong> Application Note: “QP<br />

Directory Structure”).<br />

NOTE: Every <strong>QDK</strong>-<strong>nano</strong> contains only example(s) pertaining to the specific MCU and compiler, but<br />

does not include the platform-independent baseline code of QP-<strong>nano</strong>, which is available for a<br />

separate download. It is strongly recommended that you read Chapter 12 in [PSiCC2] before you<br />

start with this <strong>QDK</strong>-<strong>nano</strong>.<br />

Listing 1: Selected QP directories and files after installing <strong>QDK</strong>-<strong>nano</strong>-<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

/<br />

- QP-<strong>nano</strong> Root Directory<br />

|<br />

+-doc\<br />

| +-AN_PELICAN.pdf - Application Note “PELICAN crossing example”<br />

| +-<strong>QDK</strong>n_<strong>PIC24</strong>-<strong>dsPIC</strong>.pdf – This <strong>QDK</strong>-<strong>nano</strong> Manual “<strong>QDK</strong>-<strong>nano</strong> <strong>PIC24</strong>-<strong>dsPIC</strong>-<strong>C30</strong>”<br />

|<br />

+-examples\<br />

- subdirectory containing the QP example files<br />

| +-pic24_dspic\ - <strong>PIC24</strong>/<strong>dsPIC</strong> examples<br />

| | +-mplab-c30\ - Microchip MPLAB <strong>C30</strong> compiler<br />

| | | +-pelican-explorer16_pic24\ - PELICAN example for Explorer 16 with <strong>PIC24</strong><br />

| | | | +-dbg\ - directory containing the Debug build<br />

| | | | | +-pelican-dbg.cof - image of the application<br />

| | | | | +-pelican-dbg.map - map file of the application<br />

| | | | +-rel\ - directory containing the Release build<br />

| | | | |<br />

| | | | +-bsp.c - BSP for Explorer 16 with <strong>PIC24</strong>FJ128GA010<br />

| | | | +-bsp.h - BSP header file<br />

| | | | +-main.c - the main function<br />

| | | | +-pelican.c - the PELICAN active objects<br />

| | | | +-pelican.h - the PELICAN application header file<br />

| | | | +-ped.c - the Pedestrian active object<br />

| | | | +-pelican-dbg.mcp – MPLAB project for Debug configuration<br />

| | | | +-pelican-rel.mcp – MPLAB project for Release configuration<br />

| | | | +-pelican-explorer16_pic24.mcw – MPLAB workspace for the application<br />

| | | | +-p24FJ128GA010.gld - Linker script for <strong>PIC24</strong>FJ128GA010<br />

| | | | +-qpn_port.h - QP-<strong>nano</strong> port to <strong>PIC24</strong>FJ128GA010 with Vanilla kernel<br />

| | | | |<br />

| | | +-pelican-explorer16_dspic\ - PELICAN example for Explorer 16 with <strong>dsPIC</strong><br />

| | | | +-dbg\ - directory containing the Debug build<br />

| | | | | +-pelican-dbg.cof - image of the application<br />

| | | | | +-pelican-dbg.map - map file of the application<br />

| | | | +-rel\ - directory containing the Release build<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

6 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

| | | | |<br />

| | | | +-bsp.c - BSP for Explorer 16 with <strong>PIC24</strong>FJ128GA010<br />

| | | | +-bsp.h - BSP header file<br />

| | | | +-main.c - the main function<br />

| | | | +-pelican.c - the PELICAN active objects<br />

| | | | +-pelican.h - the PELICAN application header file<br />

| | | | +-ped.c - the Pedestrian active object<br />

| | | | +-pelican-dbg.mcp – MPLAB project for Debug configuration<br />

| | | | +-pelican-rel.mcp – MPLAB project for Release configuration<br />

| | | | +-pelican-explorer16_dspic.mcw – MPLAB workspace for the application<br />

| | | | +-p33FJ256GP710.gld - Linker script for <strong>dsPIC</strong>33FJ256GP710<br />

| | | | +-qpn_port.h - QP-<strong>nano</strong> port to <strong>dsPIC</strong>33FJ256GP710 with Vanilla kernel<br />

| | |<br />

| | | +-pelican-qk-explorer16_pic24\ - PELICAN example for Explorer 16 with QK<br />

| | | | +-dbg\ - directory containing the Debug build<br />

| | | | | +-pelican-dbg.cof - image of the application<br />

| | | | | +-pelican-dbg.map - map file of the application<br />

| | | | +-rel\ - directory containing the Release build<br />

| | | | |<br />

| | | | +-bsp.c - BSP for Explorer 16 with <strong>PIC24</strong>FJ128GA010<br />

| | | | +-bsp.h - BSP header file<br />

| | | | +-main.c - the main function<br />

| | | | +-pelican.c - the PELICAN active objects<br />

| | | | +-pelican.h - the PELICAN application header file<br />

| | | | +-ped.c - the Pedestrian active object<br />

| | | | +-pelican-dbg.mcp – MPLAB project for Debug configuration<br />

| | | | +-pelican-rel.mcp – MPLAB project for Release configuration<br />

| | | | +-pelican-qk-explorer16_pic24.mcw – MPLAB workspace for the application<br />

| | | | +-p24FJ128GA010.gld - Linker script for <strong>PIC24</strong>FJ128GA010<br />

| | | | +-qpn_port.h - QP-<strong>nano</strong> port to <strong>PIC24</strong>FJ128GA010 with QK-<strong>nano</strong> kernel<br />

| | | |<br />

| | | +-pelican-qk-explorer16_dspic\ - PELICAN example for Explorer 16 with QK<br />

| | | | +-dbg\ - directory containing the Debug build<br />

| | | | | +-pelican-dbg.cof - image of the application<br />

| | | | | +-pelican-dbg.map - map file of the application<br />

| | | | +-rel\ - directory containing the Release build<br />

| | | | |<br />

| | | | +-bsp.c - BSP for Explorer 16 with <strong>PIC24</strong>FJ128GA010<br />

| | | | +-bsp.h - BSP header file<br />

| | | | +-main.c - the main function<br />

| | | | +-pelican.c - the PELICAN active objects<br />

| | | | +-pelican.h - the PELICAN application header file<br />

| | | | +-ped.c - the Pedestrian active object<br />

| | | | +-pelican-dbg.mcp – MPLAB project for Debug configuration<br />

| | | | +-pelican-rel.mcp – MPLAB project for Release configuration<br />

| | | | +-pelican-qk-explorer16_dspic.mcw – MPLAB workspace for the application<br />

| | | | +-p33FJ256GP710.gld - Linker script for <strong>dsPIC</strong>33FJ256GP710<br />

| | | | +-qpn_port.h - QP-<strong>nano</strong> port to <strong>dsPIC</strong>33FJ256GP710 with QK-<strong>nano</strong> kernel<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

7 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

2.2 Building and Running the Examples<br />

The examples included in this <strong>QDK</strong>-<strong>nano</strong> are based on the standard PELICAN crossing application<br />

implemented with active objects (see <strong>Quantum</strong> <strong>Leaps</strong> Application Note: “PEdestrian LIght CONtrolled<br />

(PELICAN) Crossing Example” [QL AN-PELICAN 08] included in this <strong>QDK</strong>-<strong>nano</strong>). The example directory<br />

contains the MPLAB workspaces and project file that you can load into the MPLAB IDE, as shown in<br />

Figure 4.<br />

Figure 4: The MPLAB IDE with the PELICAN example<br />

Program<br />

target device<br />

button<br />

Select project<br />

for the build<br />

configuration<br />

2.2.1 Building the Example Projects in the MPLAB IDE<br />

1. Connect the Explorer 16 board to the MPLAB ICD 2 In-Circuit debugger and to the PC as described<br />

in the Quick Start Guide.<br />

2. Connect the USB cable between the MPLAB ICD 2 In-Circuit Debugger and the PC.<br />

3. Open the MPLAB IDE and open the project pelican-dbg.mcp (located in \examples\-<br />

pic24_dspic\mplab-c30\pelican-explorer16_pic24\). Figure 4 shows the screen shot of the<br />

MPLAB IDE after opening the project.<br />

4. Build the project by select Project->Make menu or by pressing F10.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

8 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

2.2.2 Programming the Example to Run under the MPLAB Debugger<br />

After a successful build, you can load the program to the target device by pressing the “Program Target<br />

Device” button, which is shown in Figure 4.<br />

2.2.3 Programming the Example to Run Standalone<br />

The DPP example application comes with the pelican-rel.mcp project, which is intended for<br />

standalone execution on the Explorer 16 board without the debugger. Once you activate and build this<br />

project in the MPLAB IDE, you need to de-select the ICD 2 as the Debugger and select ICD 2 as the<br />

Programmer (you cannot configure ICD 2 as a programmer and debugger simultaneously). To program<br />

the code into the <strong>PIC24</strong>/<strong>dsPIC</strong> device, you click the “Program Target Device” icon. After the programming<br />

is done, you can release the device out of reset by clicking the appropriate icon. You can also disconnect<br />

the ICD 2 completely and power-cycle the board. The programmed PELICAN application should start<br />

running standalone.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

9 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

3 Non-Preemptive “Vanilla” Port<br />

The example of using QP-<strong>nano</strong> with the cooperative “Vanilla” kernel is located in the directory: \-<br />

examples\pic24_dspic\mplab-c30\pelican-explorer16_pic24\ for <strong>PIC24</strong> and \examples\-<br />

pic24_dspic\mplab-c30\pelican-explorer16_dspic\ for <strong>dsPIC</strong>. This section describes the generic<br />

QP-<strong>nano</strong> configuration, which consist of the qpn_port.h header file. The board-specific elements are<br />

common for both the non-preemptive and preemptive (QK-<strong>nano</strong>) configuration and will be covered in<br />

Section 6.<br />

3.1 The qpn_port.h Header File<br />

You configure and customize QP-<strong>nano</strong> through the header file qpn_port.h, which is included by the QP<strong>nano</strong><br />

source files (qepn.c and qfn.c) as well as in all your application C modules.<br />

NOTE: The QP-<strong>nano</strong> port to the cooperative “Vanilla” kernel qpn_port.h is generic and should not<br />

need to change (except for the QF_MAX_ACTIVE definition) for other <strong>PIC24</strong>/<strong>dsPIC</strong> applications.<br />

Listing 2: qpn_port.h header file for the non-preemptive QP-<strong>nano</strong> configuration and <strong>C30</strong> compiler<br />

(1) #define Q_NFSM<br />

(2) #define Q_PARAM_SIZE 2<br />

(3) #define QF_TIMEEVT_CTR_SIZE 2<br />

/* maximum # active objects--must match EXACTLY the QF_active[] definition */<br />

(4) #define QF_MAX_ACTIVE 2U<br />

/* task-level interrupt nesting policy, see NOTE01 */<br />

(5) #define QF_INT_LOCK() __asm__ volatile("disi #0x3FFF")<br />

(6) #define QF_INT_UNLOCK() __asm__ volatile("disi #0x0000")<br />

/* ISR-level interrupt locking policy for <strong>PIC24</strong>/<strong>dsPIC</strong>, see NOTE02 */<br />

(7) #define QF_ISR_NEST<br />

/* Exact-width types. WG14/N843 C99 Standard, Section 7.18.1.1 */<br />

(8) typedef signed char int8_t;<br />

typedef signed int int16_t;<br />

typedef signed long int32_t;<br />

typedef unsigned char uint8_t;<br />

typedef unsigned int uint16_t;<br />

typedef unsigned long uint32_t;<br />

(9) #include "qepn.h" /* QEP-<strong>nano</strong> platform-independent public interface */<br />

(10) #include "qfn.h" /* QF-<strong>nano</strong> platform-independent public interface */<br />

(1) Defining the macro Q_NFSM eliminates the code for the simple non-hierarchical FSMs.<br />

(2) The macro Q_PARAM_SIZE defines the size (in bytes) of the scalar event parameter. The allowed<br />

values are 0 (no parameter), 1, 2, or 4 bytes. Here the value of Q_PARAM_SIZE is set to 2, so that<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

10 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

QP-<strong>nano</strong> will use uint16_t. If you don’t define this macro in qpn_port.h, the default of 0 (no<br />

parameter) will be assumed.<br />

(3) The macro QF_TIMEEVT_CTR_SIZE defines the size (in bytes) of the time event down-counter. The<br />

allowed values are 0 (no time events), 1, 2, or 4 bytes. Here the value of QF_TIMEEVT_CTR_SIZE is<br />

set to 2, so that QP-<strong>nano</strong> will use uint16_t. If you don’t define this macro in qpn_port.h, the<br />

default of 0 (no time events) will be assumed.<br />

(4) You must define the QF_MAX_ACTIVE macro as the exact number of active objects used in the<br />

application. The provided value must be between 1 and 8 and must be consistent with the definition<br />

of the QF_active[] array in main.c.<br />

(5) The interrupt locking macro resolves to the single instruction DISI #0x3FFF, which disables<br />

interrupts for 16383 instruction cycles. The number of cycles is much longer than any actual critical<br />

section in QP-<strong>nano</strong>.<br />

NOTE: The DISI instruction only disables interrupts with priority levels 1-6. Priority level 7 interrupts<br />

and all trap events still have the ability to interrupt the CPU when the DISI instruction is active. This<br />

means that from the perspective of QP, the level 7 interrupts are treated as non-maskable interrupts<br />

(NMIs). Such non-maskable interrupts cannot call any QP services. In particular, they cannot post<br />

events (see also Section 5).<br />

(6) The DISI #0 instruction is then used to unconditionally unlock the interrupts at the end of the critical<br />

section.<br />

(7) The interrupt nesting is allowed, as it is the default for <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />

(8) The C99-standard exact-width integer types are defined explicitly, because the MPLAB <strong>C30</strong><br />

compiler does not provide the standard header file.<br />

(9) The qpn_port.h must include the QEP-<strong>nano</strong> event processor interface qepn.h.<br />

(10) The qpn_port.h must include the QF-<strong>nano</strong> real-time framework interface qfn.h.<br />

3.2 ISRs in the Non-Preemptive “Vanilla” Configuration<br />

The MPLAB <strong>C30</strong> compiler supports writing interrupts in C. In the non-preemptive “vanilla” port, the ISRs<br />

are identical as in the simplest of all “superloop” (main+ISRs), and there is nothing QP-<strong>nano</strong>-specific in<br />

the structure of the ISRs. The only QP-specific requirement is that you provide a periodic system clock<br />

tick ISR and you invoke QF_tick() in it. The ISRs are located in the bsp.c file found in the application<br />

directory.<br />

NOTE: The non-preemptive “vanilla” kernel allows interrupts nesting, as the interrupt preemptions<br />

are handled entirely by the <strong>PIC24</strong>/<strong>dsPIC</strong> hardware.<br />

Listing 3: The system tick interrupt calling QF_tick() function to manage armed time events.<br />

(1) void __attribute__((__interrupt__,<br />

(2) auto_psv))<br />

(3) _T2Interrupt(void)<br />

{<br />

(4) _T2IF = 0; /* clear Timer 2 interrupt flag */<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

11 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

(5) QF_tick(); /* handle all armed time events in QF */<br />

}<br />

/* perform other work, if necessary */<br />

(1) In the MPLAB <strong>C30</strong> The ISR must be declared with the attribute __interrupt__.<br />

(2) The auto_psv attribute will cause the compiler to preserve the previous contents of PSVPAG and<br />

set it to section .const. Upon exit, the previous value of PSVPAG will be restored. Alternatively, if<br />

the ISR does not access the PSVPAG access, the attribute no_auto_psv can be used (see Section<br />

8.10 in [MPLAB <strong>C30</strong>] for more details).<br />

NOTE: If you don’t explicitly specify the no_auto_psv attribute, the compiler will err on the safe side<br />

and will implicitly insert the auto_psv atribute.<br />

(3) Every ISR must be a void (void) function.<br />

(4) Most ISRs in the <strong>PIC24</strong>/<strong>dsPIC</strong> architecture must explicitly clear the interrupt flag for the interrupt<br />

source.<br />

(5) The system clock tick ISR must invoke QF_tick(), and can also perform other actions, if<br />

necessary. The function QF_tick() cannot be reentered, that is, it necessarily must run to<br />

completion and return before it can be called again. This requirement is automatically fulfilled,<br />

because the same interrupt priority cannot preempt the running priority in <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />

3.2.1 PSV Usage in ISRs<br />

This QP port to <strong>PIC24</strong>/<strong>dsPIC</strong> can work with ISRs that preserve the PSVPAG register as well as ISRs that<br />

don’t. The preserving of the PSVPAG register is controlled by the attributes auto_psv and no_auto_psv<br />

that the MPLAB <strong>C30</strong> compiler supports for this purpose (see Listing 3(2)).<br />

If an ISR references const variables or string literals using the constants-in-code memory model, the<br />

auto_psv attribute should be added to the ISR definition. This attribute will cause the compiler to<br />

preserve the previous contents of PSVPAG and set it to section .const. Upon exit, the previous value of<br />

PSVPAG will be restored. Alternatively, if the ISR does not access the PSVPAG access, the attribute<br />

no_auto_psv can be used (see Section 8.10 in [MPLAB <strong>C30</strong>] for more details). The no_auto_psv<br />

attribute results in slightly faster ISR with one register less saved on the stack.<br />

3.2.2 Shadow Registers Usage in ISRs<br />

The non-preemptive “vanilla” QP-<strong>nano</strong> port to <strong>PIC24</strong>/<strong>dsPIC</strong> can work with ISRs that preserve the context<br />

in the shadow registers available in the <strong>PIC24</strong>/<strong>dsPIC</strong> CPU. To request the compiler to use the fast context<br />

save (using the push.s and pop.s instructions), tag the function with the __shadow__ attribute. For<br />

example:<br />

void __attribute__((__interrupt__, __shadow__)) _T2Interrupt(void);<br />

NOTE: Generally, shadow registers should be used at one IPL level only (typically the highest).<br />

When interrupt nesting is allowed, which is the default in the <strong>PIC24</strong>/<strong>dsPIC</strong> architecture, the shadow<br />

registers can get corrupted if they are used by interrupts of two or more different IPL levels.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

12 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

3.2.3 Assigning/Changing Interrupt Priorities in Software<br />

Each ISR in <strong>PIC24</strong>/<strong>dsPIC</strong> runs at a pre-configured IPL level. The default IPL level assigned to all ISRs out<br />

of reset is 4. It is strongly recommended, however, to assign priority to each used interrupt explicitly<br />

before enabling the interrupt. For example, the highlighted code in Listing 4 shows assigning the priority<br />

to the Timer2 interrupt in function QF_onStartup():<br />

Listing 4: Configuring interrupts in function QF_onStartup() (file bsp.c)<br />

#define TIMER2_ISR_PRIO 4<br />

. . .<br />

void QF_onStartup(void) { /* entered with interrupts locked */<br />

T2CON = 0; /* Use Internal Osc (Fcy), 16 bit mode, prescaler = 1 */<br />

TMR2 = 0; /* Start counting from 0 and clear the prescaler count */<br />

PR2 = BSP_TMR2_PERIOD - 1; /* set the timer period */<br />

_T2IP = TIMER2_ISR_PRIO; /* set Timer 2 interrupt priority */<br />

_T2IF = 0; /* clear the interrupt for Timer 2 */<br />

_T2IE = 1; /* enable interrupt for Timer 2 */<br />

T2CONbits.TON = 1; /* start Timer 2 */<br />

}<br />

NOTE: All ISRs that call QP services must have priorities not exceeding 6 (1 through 6). Priority 7 is<br />

reserved for the NMI, as described in Section 5.<br />

3.3 QP Idle Loop Customization in QF_onIdle()<br />

The cooperative “vanilla” kernel can very easily detect the situation when no events are available, in<br />

which case QF_run() calls the QF_onIdle() callback. You can use QF_onIdle() to suspended the CPU<br />

to save power, if your CPU supports such a power-saving mode. Please note that QF_onIdle() is called<br />

repetitively from the event loop whenever the event loop has no more events to process, in which case<br />

only an interrupt can provide new events. The QF_onIdle() callback is called with interrupts locked,<br />

because the determination of the idle condition might change by any interrupt posting an event.<br />

The <strong>PIC24</strong>/<strong>dsPIC</strong> CPU supports several power-saving levels (consult the data sheets of particular<br />

<strong>PIC24</strong>/<strong>dsPIC</strong> devices for details). The following piece of code shows the QF_onIdle() callback that puts<br />

<strong>PIC24</strong>/<strong>dsPIC</strong> into the IDLE power-saving mode. Please note that <strong>PIC24</strong>/<strong>dsPIC</strong> architecture allows for an<br />

atomic setting the low-power mode and enabling interrupts at the same time.<br />

Listing 5: QF_onIdle() for the non-preemptive (“vanilla”) QP-<strong>nano</strong> port to <strong>PIC24</strong>/<strong>dsPIC</strong><br />

(1) void QF_onIdle(void) { /* entered with int locked, NOTE01 */<br />

(2) LED_ON (IDLE_LED); /* blink the IDLE LED, see NOTE02 */<br />

(3) LED_OFF(IDLE_LED);<br />

(4) #ifdef NDEBUG<br />

(5) __asm__ volatile("disi #0x0001");<br />

(6) Idle(); /* transition to Idle mode, see NOTE03 */<br />

#else<br />

(7) QF_INT_UNLOCK(dummy); /* unlock interrupts, see NOTE01 */<br />

#endif<br />

}<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

13 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

(1) The QF_onIdle() callback is always called with interrupts locked to prevent any race condition<br />

between posting events from ISRs and transitioning to the sleep mode.<br />

(2-3) The LED D10 on the Explorer 16 board is used to visualize the idle loop activity. The LED is toggled<br />

on and off, which causes the LED to “glow” at the intensity proportional to the rate of invocations of<br />

the idle callback. The idle LED is toggled with interrupts locked, which means that the intensity of the<br />

LED “glow” is not skewed by preemptions from interrupts or tasks.<br />

(4) The low-power mode stops the CPU clock, so it can interfere with the debugger. Here, the lowpower<br />

mode is activated only in the Release build configuration when the macro NDEBUG is<br />

defined.<br />

(5) Interrupts are locked for just one following machine instruction, which ensures that interrupts will<br />

unlock after the IDLE mode.<br />

(6) The IDLE mode is activated by executing the IDLE instruction.<br />

NOTE: The <strong>PIC24</strong>/<strong>dsPIC</strong> allows for atomic transition to the Idle or Sleep modes with interrupts<br />

locked, as it should be done for a deterministic transition to the low-power mode (see [Samek 07a]).<br />

(7) When the idle/sleep mode is not used, interrupts are simply unlocked.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

14 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

4 Preemptive Configuration with QK-<strong>nano</strong><br />

The QP-<strong>nano</strong> port to <strong>PIC24</strong>/<strong>dsPIC</strong> with the preemptive kernel (QK-<strong>nano</strong>) is similar to the “vanilla” port. In<br />

particular, the interrupt locking/unlocking policy is the same, and the BSP is almost identical, except for<br />

some extra considerations for the ISRs.<br />

4.1 QK-<strong>nano</strong>-specific ISR Entry and Exit Macros (file qpn_port.h)<br />

The preemptive QP-<strong>nano</strong> port to <strong>PIC24</strong>/<strong>dsPIC</strong> also allows writing ISRs in C, so no explicit assembly<br />

programming is necessary. However, the preemptive QK kernel requires (as all preemptive kernels do)<br />

notifying the kernel about entering and exiting the ISR, so that the kernel can avoid context switching<br />

inside ISRs, but knows when to perform asynchronous preemptions when the last nested ISR returns to<br />

the task level.<br />

While working with the compiler-generated ISRs, the biggest problem for a preemptive kernel is to<br />

reliably detect that a given ISR is not nesting on top of another ISR, so that only the last interrupt<br />

returning to the task level performs context switch, if necessary.<br />

NOTE: Unlike most other processors, <strong>PIC24</strong>/<strong>dsPIC</strong> does not disable interrupts upon the entry to the<br />

interrupt service routine. <strong>PIC24</strong>/<strong>dsPIC</strong> merely raises the current IPL to the level of currently serviced<br />

interrupt. This means that the currently serviced interrupt can be preempted by a higher-level<br />

interrupt even at the very first instruction. Consequently, incrementing the interrupt nesting counter<br />

(QK_intNest_) is not a reliable way of accounting for interrupt nesting, because it can miss an<br />

interrupt nesting level no matter how early in the ISR it is performed.<br />

For <strong>PIC24</strong>/<strong>dsPIC</strong>, a reliable way of detecting nesting of interrupts is to check the preempted status<br />

register SR, which is saved on the interrupt stack as shown in Figure 5. Only if the preempted IPL<br />

(bits SR) is non-zero, the current interrupt preempts the task level and performing a context switch<br />

is allowed.<br />

Figure 5: Interrupt stack frame for <strong>PIC24</strong>/<strong>dsPIC</strong><br />

Low memory<br />

High memory<br />

15<br />

PC<br />

SR<br />

PC<br />

Register saved by the <strong>C30</strong> compiler<br />

Register saved by the <strong>C30</strong> compiler<br />

…<br />

Register saved by the <strong>C30</strong> compiler<br />

0<br />

IPL3 status bit<br />

(CORCON).<br />

Stack pointer<br />

(w15)<br />

While checking the stacked SR is quite easy in assembly, it is much trickier for the compiler-generated<br />

ISRs. The compiler generates different stack frames depending on the actually clobbered registers in the<br />

body of the ISR as well as the attributes of the ISR and even the optimization level used to compile the<br />

code. Consequently, it is difficult to know the offset of the stacked SR from the current stack pointer w15<br />

that is accessible inside the ISR, (again see Figure 5).<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

15 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

The solution used in this QK port is to use the __preprologue__ attribute of the compiler-generated ISR<br />

to check the stacked SR before the compiler pushes any registers to the stack and alters the stack<br />

pointer. The IPL extracted from the stacked SR is then stored in the global variable QK_intNest_, which<br />

in this case is not used as the interrupt nesting counter, but rather as a bitmask (a set) of preempted IPL<br />

levels. More precisely, the QK-<strong>nano</strong>-specific ISR entry code added before any compiler-generated code<br />

checks the stacked IPL level in SR and sets the bit corresponding to the preempted IPL in the<br />

QK_intNest_ bitmask, as illustrated in Figure 6. In the QK-specific ISR exit code, the current IPL is<br />

extracted from the SR and the bit corresponding to the current IPL is cleared in the QK_intNest_<br />

bitmask.<br />

Figure 6: Storing the preempted IPLs in the QK_intNest_ bitmask<br />

Bit number<br />

7<br />

6<br />

5<br />

4<br />

3<br />

2<br />

1<br />

0<br />

0<br />

0<br />

1<br />

0<br />

0<br />

0<br />

1<br />

1<br />

QK_intNest_<br />

Unused level (IPL = 7)<br />

Preempted IPL = 5<br />

Preempted IPL = 1<br />

Preempted IPL = 0<br />

The QK-<strong>nano</strong>-specific interrupt entry and exit sequence defined in the header file qk_port.h shown in<br />

Listing 6 demonstrates one possible solution to this problem. The QK port header file for the <strong>PIC24</strong>/<strong>dsPIC</strong><br />

port is located in \ports\pic24-dspic\qk\mplab-c30\qk_port.h. The upcoming Section “ISRs<br />

with the Preemptive QK-<strong>nano</strong> Kernel” explains how to use the macros QK_ISR_ENTRY() /<br />

QK_ISR_EXIT() and how these macros work at the assembly level.<br />

Listing 6: qpn_port.h header file for the preemptive QP port with QK<br />

. . .<br />

#define QF_ISR_NEST<br />

/* QK interrupt entry and exit */<br />

(1) #define QK_ISR(psv_) \<br />

(2) void __attribute__((__interrupt__(__preprologue__( \<br />

"push RCOUNT \n" \<br />

"push.d w0 \n" \<br />

"mov.w [w15-8],w0 \n" \<br />

"lsr.w w0,#13,w1 \n" \<br />

"mov.w #1,w0 \n" \<br />

"sl w0,w1,w0 \n" \<br />

"ior.b _QK_intNest_\n" \<br />

"bra .+6 ")) \<br />

, psv_))<br />

(3) #define QK_ISR_EXIT() do { \<br />

register uint16_t this_sr; \<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

16 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

__asm__ volatile ( \<br />

"mov.w SR,%0 \n" \<br />

"lsr %0,#5,w0 \n" \<br />

"and.w w0,#7,w0 \n" \<br />

"mov.w #1,w1 \n" \<br />

"sl w1,w0,w0 \n" \<br />

"ior.b #1,w0 \n" \<br />

"com.b w0,w0 \n" \<br />

"disi #0x3FFF \n" \<br />

"and.b _QK_intNest_" : "=r"(this_sr) : : "w0", "w1"); \<br />

if (QK_intNest_ == 0) { \<br />

uint8_t p = QK_schedPrio_(); \<br />

if (p != (uint8_t)0) { \<br />

__asm__ volatile ("clr.b SR"); \<br />

QK_sched_(p); \<br />

__asm__ volatile ("mov.w %0,SR" : : "r"(this_sr)); \<br />

} \<br />

} \<br />

__asm__ volatile ("disi #0x0000"); \<br />

} while (0);<br />

. . .<br />

#include "qepn.h" /* QEP-<strong>nano</strong> platform-independent public interface */<br />

#include "qfn.h" /* QF-<strong>nano</strong> platform-independent public interface */<br />

#include "qkn.h" /* QK-<strong>nano</strong> platform-independent public interface */<br />

(1) The macro QK_ISR() defines the ISR attributes, as required by the MPLAB <strong>C30</strong> compiler. The<br />

macro is designed to be before the actual ISR signature, as will be illustrated in Section 4.2. The<br />

argument psv_ controls PSV usage in the ISR and can be either auto_psv or no_auto_psv (see<br />

Section 3.2.1).<br />

(2) The macro QK_ISR() then uses the attribute __preprologue__ to define the specific interrupt entry<br />

sequence in assembly, which the compiler copies verbatim before any generated ISR code.<br />

(3) The macro QK_ISR_EXIT() must be invoked as the last instruction of every ISR of IPL 1..6. Again,<br />

the usage of this macro will be illustrated in Section 4.2.<br />

4.2 ISRs with the Preemptive QK-<strong>nano</strong> Kernel<br />

Even though the QK-<strong>nano</strong> interrupt entry/exit macros are somewhat involved, their use is actually very<br />

simple and enables very straightforward coding ISRs in C, without any need to use assembly. The<br />

following Listing 7 shows how to write ISRs for the preemptive QK-<strong>nano</strong> kernel:<br />

NOTE: The QK-<strong>nano</strong> interrupt entry and exit macros QK_ISR() and QK_ISR_EXIT() work only<br />

with the optimization level O1 or higher (O2, O3, or Os) in the <strong>C30</strong> compiler. The MPLAB <strong>C30</strong><br />

compiler will report a compile-time error “'asm' operand requires impossible reload” when no<br />

optimization is used.<br />

Listing 7: T2Interrupt ISR for QK-<strong>nano</strong><br />

(1) QK_ISR(no_auto_psv) _T2Interrupt() {<br />

_T2IF = 0; /* clear Timer 2 interrupt flag */<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

17 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

QF_tick(); /* handle all armed time events in QF */<br />

(2) QK_ISR_EXIT(); /* inform QK about exiting the ISR */<br />

}<br />

(1) The macro QK_ISR() is used before the ISR signature. Here the PSV control is set to no_auto_ps,<br />

which avoids saving and restoring the PSVPAG register. If your ISR body references const variables<br />

or string literals using the constants-in-code memory model, you need to define this argument to<br />

auto_psv.<br />

(2) The macro QK_ISR_EXIT() is invoked at the end of every ISR to perform the asynchronous context<br />

switch, if necessary.<br />

4.2.1 Explanation of ISR code for the preemptive QK kernel<br />

Listing 8 shows the disassembled code for the _T2Interrupt ISR from Listing 7. The highlighted code is<br />

generated by the macros QK_ISR() and QK_ISR_EXIT(). The explanation section immediately following<br />

the listing clarifies the most important points.<br />

Listing 8: Disassembled code for T2Interrupt ISR<br />

55: QK_ISR(no_auto_psv) _T2Interrupt() {<br />

(1) 008C8 F80036 push.w 0x0036<br />

(2) 008CA BE9F80 mov.d 0x0000,[0x001e++]<br />

(3) 008CC 97B84F mov.w [0x001e-8],0x0000<br />

(4) 008CE DE00CD lsr 0x0000,#13,0x0002<br />

(5) 008D0 200010 mov.w #0x1,0x0000<br />

(6) 008D2 DD0001 sl 0x0000,0x0002,0x0000<br />

(7) 008D4 B76880 ior.b 0x0880<br />

(8) 008D6 370002 bra 0x0008dc<br />

(9) 008D8 F80036 push.w 0x0036<br />

(10) 008DA BE9F80 mov.d 0x0000,[0x001e++]<br />

(11) 008DC BE9F82 mov.d 0x0004,[0x001e++]<br />

(12) 008DE BE9F84 mov.d 0x0008,[0x001e++]<br />

(13) 008E0 BE9F86 mov.d 0x000c,[0x001e++]<br />

(14) 008E2 781F88 mov.w 0x0010,[0x001e++]<br />

56: _T2IF = 0; /* clear Timer 2 interrupt flag */<br />

008E4 A9E084 bclr.b 0x0084,#7<br />

57: QF_tick(); /* handle armed time events in QF-<strong>nano</strong> */<br />

008E6 07FFD1 rcall 0x00088a<br />

58:<br />

59: QK_ISR_EXIT(); /* inform QK-<strong>nano</strong> about exiting ISR */<br />

(15) 008E8 800218 mov.w 0x0042,0x0010<br />

(16) 008EA DE4045 lsr 0x0010,#5,0x0000<br />

(17) 008EC 600067 and.w 0x0000,#7,0x0000<br />

(18) 008EE 200011 mov.w #0x1,0x0002<br />

(19) 008F0 DD0800 sl 0x0002,0x0000,0x0000<br />

(20) 008F2 B34010 ior.b #0x1,0x0000<br />

(21) 008F4 EAC000 com.b 0x0000,0x0000<br />

(22) 008F6 FC3FFF disi #16383<br />

(23) 008F8 B66880 and.b 0x0880<br />

(24) 008FA E24880 cp0.b 0x0880<br />

(25) 008FC 3A0005 bra nz, 0x000908<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

18 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

(26) 008FE E24885 cp0.b 0x0885<br />

(27) 00900 320003 bra z, 0x000908<br />

(28) 00902 EF6042 clr.b 0x0042<br />

(29) 00904 0700A0 rcall 0x000a46<br />

(30) 00906 880218 mov.w 0x0010,0x0042<br />

(31) 00908 FC0000 disi #0<br />

60: }<br />

(32) 0090A 78044F mov.w [--0x001e],0x0010<br />

(33) 0090C BE034F mov.d [--0x001e],0x000c<br />

(34) 0090E BE024F mov.d [--0x001e],0x0008<br />

(35) 00910 BE014F mov.d [--0x001e],0x0004<br />

(36) 00912 BE004F mov.d [--0x001e],0x0000<br />

(37) 00914 F90036 pop.w 0x0036<br />

(38) 00916 064000 retfie<br />

(1) The RCOUNT register (at address 0x36) is pushed to the stack. This is done to establish the same<br />

stack layout as the MPLAB <strong>C30</strong> compiler does for all ISRs.<br />

(2) The register pair w0-w1 is pushed to the stack. Now these registers can be clobbered.<br />

(3) The stacked SRCORCONPC bits (see Figure 5) are loaded from the stack to w0.<br />

Please note that after pushing RCOUNT and w0,w1 pair, this information is 8 bytes away from the<br />

current stack pointer in w15.<br />

(4) The register w0 is left-shifted by 13 bits, so that the stacked IPL ends up in w1.<br />

(5) The register w1 is compared with zero.<br />

(6) The stacked IPL indicates that an ISR has been preempted, so the corresponding bit in the<br />

QK_intNest_ bitmask needs to be set. Here the bitmask (1


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

(15) The QK-specific ISR exit starts with placing the current SR into the this_sr local variable, which<br />

the <strong>C30</strong> compiler decided to place in the w8 register.<br />

(16) The IPL bits from the current SR are right-shifted into w0.<br />

(17) All other SR bits, except the IPL bits in w0 are masked off in w0.<br />

(18-19) The bitmask (1


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

include any preemptions by interrupts and tasks). The transition to the Idle mode happens in a very<br />

straightforward way, because transition to idle mode is always safe under a preemptive priority-based<br />

kernel, such as QK.<br />

void QK_onIdle(void) {<br />

Listing 9: QK_onIdle() callback for <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />

QF_INT_LOCK();<br />

LED_ON (IDLE_LED); /* blink the IDLE LED, see NOTE01 */<br />

LED_OFF(IDLE_LED);<br />

QF_INT_UNLOCK();<br />

#ifdef NDEBUG<br />

Idle(); /* transition to Idle mode */<br />

#endif<br />

}<br />

4.4 Testing QK Preemption Scenarios<br />

The technique described in this section will allow you to use the MPLAB debugger to trigger an interrupt<br />

at any machine instruction and observe the preemptions it causes. The interrupt used for the testing<br />

purposes is the GPIOA interrupt (INTID == 0). The ISR for this interrupt is shown below:<br />

The DPP example application for the preemptive QK-<strong>nano</strong> kernel includes special ISR (INT0 ISR) for<br />

convenient testing of various preemption scenarios, defined as follows:<br />

#define INT0_ISR_PRIO 6<br />

QK_ISR(auto_psv) _INT0Interrupt() {<br />

_INT0IF = 0;<br />

QActive_postISR(&AO_Ped, PEDS_WAITING_SIG, 0);<br />

QK_ISR_EXIT(); /* inform QK about exiting the ISR */<br />

}<br />

The INT0 ISR, is assigned priority 6, which is higher than the priority of the system clock tick ISR.<br />

Figure 7 shows how to trigger the INT0 interrupt from the MPLAB debugger. From the debugger you need<br />

to first open the “File Registers” window (menu View | File Registers) as shown in Figure 7. You scroll to<br />

the IFS0 register at address 0x0084. To trigger the INT0 interrupt you need to write 1 to the leastsignificant<br />

bit (bit #0) of the IFS0 register.<br />

The general testing strategy is to break into the application at an interesting place for preemption, set<br />

breakpoints to verify which path through the code is taken, and trigger the INT0 interrupt, as described<br />

above. Next, you need to free-run the code (don’t use single stepping) so that the <strong>PIC24</strong>/<strong>dsPIC</strong> CPU can<br />

perform prioritization. You observe the order in which the breakpoints are hit. This procedure will become<br />

clearer after a few examples.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

21 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

Figure 7: Triggering the INT0 interrupt from the MPLAB debugger<br />

4.4.1 Interrupt Nesting Test<br />

The first interesting test is verifying that the preemptive scheduler QK_schedule_() is not called in a<br />

nested interrupt, but is called when the interrupt returns to the task level.<br />

To test this scenario, you place a breakpoint inside the _INT0Interrupt() and also inside the<br />

_T2Interrupt() ISR. When the breakpoint in the _T2Interrupt() is hit, you remove the original<br />

breakpoint and place another breakpoint at the very next machine instruction (use the Disassembly<br />

window) and also another breakpoint on the first instruction of the QK_schedule_() function. Next you<br />

trigger the INT0 interrupt per the instructions given in the previous section. You hit the Run button.<br />

The pass criteria of this test are as follows:<br />

1. The first breakpoint hit is the one inside the _INT0Interrupt() function, which means that the INT0<br />

ISR preempted the _T2Interrupt() ISR.<br />

2. The second breakpoint hit is the one in the _T2Interrupt(), which means that the Timer2 ISR<br />

continues after the _INT0Interrupt() completes.<br />

3. The last breakpoint hit is the one in QK_schedule_(), which means that the scheduler is called only<br />

after all interrupts are processed.<br />

You need to remove all breakpoints before proceeding to the next test.<br />

4.4.2 Task Preemption Test<br />

The next interesting test is verifying that tasks can preempt each other. You set a breakpoint anywhere in<br />

the Philosopher state machine code. You run the application until the breakpoint is hit. After this<br />

happens, you remove the original breakpoint and place another breakpoint at the very next machine<br />

instruction (use the Disassembly window). You also place a breakpoint inside the _INT0Interrupt()<br />

interrupt handler and on the first instruction at the first instruction of the QK_schedule_() function. Next<br />

you trigger the INT0 interrupt per the instructions given earlier. You hit the Run button.<br />

The pass criteria of this test are as follows:<br />

1. The first breakpoint hit is the one inside the _INT0Interrupt() function, which means that INT0 ISR<br />

preempted the Philospher task.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

22 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

2. The second breakpoint hit is the one in QK_schedule_() function, which means that the QK<br />

scheduler is activated before the control returns to the preempted Philosopher task.<br />

3. After hitting the breakpoint in QK_schedule_() function, you must first disable the Timer2 interrupts<br />

by clearing bit #7 in the IEC0 register at address 0x0094, in the “File Registers” window, in the similar<br />

way as you set the bit #0 in the IFS0 register to trigger INT0.<br />

4. Next, you single step into the QK_schedule_(). (NOTE: you might want to close the “File Registers”<br />

window to speed up the single stepping.) You verify that the scheduler invokes a state handler of the<br />

Table state machine. This proves that the Table task preempts the Philosopher task.<br />

5. After this you free-run the application and verify that the next breakpoint hit is the one inside the<br />

Philosopher state machine. This validates that the preempted task continues executing only after<br />

the preempting task (the Table state machine) completes.<br />

4.4.3 Other Tests<br />

Other interesting tests that you can perform include changing priority of the INT0 ISR to be lower than the<br />

priority of Timer2 ISR to verify that the QK-<strong>nano</strong> scheduler is still called only after all interrupts complete.<br />

In yet another test you could post an event to Philosopher active object rather than Table active object<br />

from the INT0 ISR to verify that the QK-<strong>nano</strong> scheduler will not preempt the Philosopher task by itself.<br />

Rather the extra event will be queued and the Philosopher task will process the queued event only after<br />

completing the current event processing.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

23 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

5 Implementing “Zero Interrupt Latency” with the NMI<br />

As noted in Section 3.1, the interrupt locking policy in this QP port locks only interrupts with IPL from 1 to<br />

6. The highest-level interrupts with IPL=7 are not locked at all. In other words, IPL=7-interrupts are nonmaskable<br />

(they are NMIs). This interrupt locking policy has two important consequences.<br />

First, the IPL=7-interrupts cannot call any QP-<strong>nano</strong> services (e.g., NMIs cannot post events to QP-<strong>nano</strong><br />

tasks), because any system call from an NMI would run the risk of corrupting the internal QP data since<br />

the protection by critical sections does not apply for NMIs.<br />

On the flip side, however, the IPL=7-interrupts run completely “free” with the interrupt latency determined<br />

only by the underlying hardware and independent on the critical sections implemented in QP-<strong>nano</strong>. This is<br />

what is meant here by the term “Zero Interrupt Latency”.<br />

Such NMIs (IPL=7-interrupts, in this case) have many potential uses. They are very useful when the<br />

software must perform some simple operations very fast, but most of the time the software does not need<br />

to communicate with the task-level of QP-<strong>nano</strong> active objects. Consider for example a fast interrupt-driven<br />

UART interface. The ISR that receives bytes must remove the bytes from the FIFO very fast or else the<br />

FIFO will quickly overflow. However, the ISR might buffer the bytes and only need to notify the task level<br />

when the buffer fills up, which is relatively infrequently.<br />

NOTE: NMIs (IPL=7-interrupts, in this case) can be equally well used in the non-preemptive QP port<br />

as well as in the preemptive port with the QK kernel.<br />

5.1 Communication between NMIs and QP-<strong>nano</strong><br />

From the previous discussion it is obvious that the NMIs must be able to communicate from time to time<br />

with the task level (active objects in QP-<strong>nano</strong>). The question is how to achieve such communication<br />

without calling QP-<strong>nano</strong> services directly?<br />

The solution is to call QP services indirectly, by triggering a regular (maskable) interrupt from the NMI. In<br />

the <strong>PIC24</strong>/<strong>dsPIC</strong> architecture you can very easily trigger an ISR by explicitly setting the corresponding<br />

interrupt flag (the same that you explicitly clear in the ISR). The triggered interrupt can call QP services,<br />

such as posting or publishing events to QP-<strong>nano</strong> active objects.<br />

The subtle, but important consideration is the communication between the NMI and the triggered<br />

maskable interrupt. Typically, such communication must occur by means of atomically-accessed shared<br />

variables. In the <strong>PIC24</strong>/<strong>dsPIC</strong> CISC architecture most 8- and 16-bit variables are read and written<br />

atomically (in one machine instruction), so implementing this way of communication is quite easy.<br />

5.2 Implementation Considerations for the NMIs<br />

Because the NMIs run completely “free” and outside the QP-<strong>nano</strong> framework, they don’t need to follow<br />

any conventions associated with regular maskable ISRs that can call QP services. Generally, the NMIs<br />

should run as fast as possible, because the interrupt latency for NMIs is determined typically by the<br />

execution time of the NMI itself. (In this QP port, only one IPL level 7 is available for NMIs, so NMIs<br />

cannot preempt each other).<br />

NMIs can be coded in C, and in fact the <strong>C30</strong> compiler offers options to make these interrupts very<br />

efficient. On of these options is __shadow__, which uses fast context save and restore to the shadow<br />

registers. Obviously, you can use only one such fast ISR in your system to avoid corruption of the shadow<br />

registers. And finally, you should generally avoid calling any functions from the NMI, as this would force<br />

the compiler to save and restore w0-w7.<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

24 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

6 BSP for the Explorer 16 Board<br />

The Board Support Package (BSP) for <strong>PIC24</strong>/<strong>dsPIC</strong> and Explorer 16 board (see Figure 1) with the nonpreemptive<br />

“vanilla” scheduler is located in the directory: \examples\pic24_dspic\mplabc30\pelican-explorer16_pic24\<br />

for <strong>PIC24</strong> and \examples\pic24_dspic\mplabc30\pelican-explorer16_dspic\<br />

for <strong>dsPIC</strong>. The BSP consists of the following files:<br />

1. bsp.h contains the Board Support Package interface (BSP)<br />

2. bsp.c contains the implementation of the BSP, which includes all ISRs and all platform-specific QP<br />

callbacks.<br />

3. p24FJ128GA010.gld contains the linker command file for locating the application.<br />

6.1 Setting the Sizes of Stack and Heap<br />

You set the sizes of stack and heap through the MPLAB IDE, as shown in Figure 8.<br />

Figure 8: Setting the size of heap and stack<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

25 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

6.2 The BSP header file bsp.h<br />

The BSP header file for the PELICAN application defines the desired ticking rate. This constant is useful<br />

for defining timeouts, which are always specified in units of clock ticks:<br />

#include /* for declaration of the SRBits register */<br />

#define BSP_TICKS_PER_SEC<br />

100UL<br />

/* the system tick rate [Hz] */<br />

/* street signals ..........................................................*/<br />

enum BSP_CarsSignal {<br />

CARS_RED, CARS_YELLOW, CARS_GREEN, CARS_OFF<br />

};<br />

enum BSP_PedsSignal {<br />

PEDS_DONT_WALK, PEDS_BLANK, PEDS_WALK<br />

};<br />

void BSP_init(void);<br />

void BSP_signalCars(enum BSP_CarsSignal sig);<br />

void BSP_signalPeds(enum BSP_PedsSignal sig);<br />

#define BSP_showState(prio_, state_) ((void)0)<br />

6.3 BSP initialization<br />

The following BSP_init() function for the Explorer 16 board configures the DSP and peripherals as,<br />

shown in Listing 10. This file has been designed to be easily modifiable for other applications.<br />

Listing 10: The BSP_init() implementation in the bsp.c for the DPP example.<br />

void BSP_init(void) {<br />

RCONbits.SWDTEN = 0; /* disable Watchdog */<br />

}<br />

TRISA = 0x00; /* set LED pins as outputs */<br />

PORTA = 0x00; /* set LEDs drive state low */<br />

6.4 Starting Interrupts in QF_onStartup()<br />

QP invokes the QF_onStartup() callback just before starting the background loop. The<br />

QF_onStartup() function must configure and start interrupts. At the minimum, QF_onStartup() must<br />

start the system clock tick interrupt. The following QF_onStartup() function enables the CPU Timer 0.<br />

void QF_onStartup(void) { /* entered with interrupts locked */<br />

T2CON = 0; /* Use Internal Osc (Fcy), 16 bit mode, prescaler = 1 */<br />

TMR2 = 0; /* Start counting from 0 and clear the prescaler count */<br />

PR2 = BSP_TMR2_PERIOD - 1; /* set the timer period */<br />

_T2IP = TIMER2_ISR_PRIO; /* set Timer 2 interrupt priority */<br />

_T2IF = 0; /* clear the interrupt for Timer 2 */<br />

_T2IE = 1; /* enable interrupt for Timer 2 */<br />

T2CONbits.TON = 1; /* start Timer 2 */<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

26 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

}<br />

6.5 Assertion Handling Policy in Q_onAssert()<br />

As described in Chapter 6 of [PSiCC2], all QP-<strong>nano</strong> components use internally assertions to detect errors<br />

in the way application is using the QP services. You need to define how the application reacts in case of<br />

assertion failure by providing the callback function Q_onAssert(). Typically, you would put the system in<br />

a fail-safe state and try to reset. It is also a good idea to log some information as to where the assertion<br />

failed.<br />

The following code fragment shows the Q_onAssert() callback for <strong>PIC24</strong>/<strong>dsPIC</strong>. The function keeps<br />

locking interrupts in an endless loop. You can then break into the code with the debugger and inspect the<br />

cause of the assertion by backtracking the call stack.<br />

NOTE: This policy is only adequate for testing, but is not adequate for production release.<br />

void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line) {<br />

(void)file; /* avoid compiler warning */<br />

(void)line; /* avoid compiler warning */<br />

for (;;) {<br />

QF_INT_LOCK(dummy); /* make sure that interrupts are locked */<br />

}<br />

}<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

27 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

7 Related Documents and References<br />

Document<br />

[PSiCC2] “Practical UML Statecharts in C/C++,<br />

Second Edition”, Miro Samek, Newnes, 2008<br />

[QP-<strong>nano</strong> 08] “QP-<strong>nano</strong> Reference Manual”,<br />

<strong>Quantum</strong> <strong>Leaps</strong>, LLC, 2008<br />

[QL AN-Directory 07] “Application Note: QP<br />

Directory Structure”, <strong>Quantum</strong> <strong>Leaps</strong>, LLC, 2007<br />

[QL AN-PELICAN 08] “Application Note: PELICAN<br />

Crossing Application”, <strong>Quantum</strong> <strong>Leaps</strong>, LLC, 2008<br />

[MPLAB-<strong>C30</strong>] “MPLAB® C Compiler for <strong>PIC24</strong><br />

MCUs and <strong>dsPIC</strong>® DSCs User’s Guide”,<br />

Microchip, 2008.<br />

[<strong>PIC24</strong>] “<strong>PIC24</strong>FJ128GA010 Family Data Sheet<br />

64/80/100-Pin General Purpose, 16-Bit Flash<br />

Microcontrollers”, Microchip, 2007.<br />

[<strong>dsPIC</strong>] “<strong>dsPIC</strong>30F/33F Programmer’s Reference<br />

Manual High-Performance Digital Signal<br />

Controllers”, Microchip, 2005.<br />

[QKernel] “Q.Kernel for <strong>PIC24</strong> and <strong>dsPIC</strong> User<br />

Guide Version 2.1-1089”, Quasarsoft Ltd, 2009<br />

[AVIX] AVIX RTOS for Microchip 16-bit and 32-bit<br />

micro controllers belonging to the <strong>PIC24</strong>F,<br />

<strong>PIC24</strong>H, <strong>dsPIC</strong>30F, <strong>dsPIC</strong>33F and PIC32 families.<br />

[Samek 07a] “Using Low-Power Modes in<br />

Foreground/Background Systems”, Miro Samek,<br />

Embedded System Design, October 2007<br />

http://www.state-machine.com/doxygen/qpn/<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.statemachine.com/doc/AN_QP_Directory_Structure.pdf<br />

http://www.statemachine.com/doc/AN_PELICAN.pdf<br />

Microchip literature #DS51284H, available on the<br />

Explorer 16 kit and online at www.microchip.com<br />

Microchip literature #DS39747D, available on the<br />

Explorer 16 kit and online at www.microchip.com<br />

Microchip literature #DS70157B, available on the<br />

Explorer 16 kit and online at www.microchip.com<br />

Document available for download from<br />

www.quasarsoft.com<br />

http://www.avix-rt.com/<br />

http://www.embedded.com/design/202103425<br />

Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />

28 of 29


<strong>QDK</strong>-<strong>nano</strong><br />

<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />

www.state-machine.com/pic<br />

8 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<br />

Statecharts in C/C+<br />

+, Second Edition:<br />

Event Driven<br />

Programming for<br />

Embedded<br />

Systems”,<br />

by Miro Samek,<br />

Newnes, 2008<br />

Microchip, Inc.<br />

Web: http://www.microchip.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!