QDK-nano™ 8051-Keil Document Version D January 2007
quantum Leaps™, LLC www.quantum-leaps.com Copyright © 2002-2007
quantum Leaps, LLC. All Rights Reserved.
All parts of this document and the referenced software are protected by copyright law and can be used only with a valid license, as described in this document. Any other use or distribution of the referenced software or any part of this document is illegal.
Table of Contents 1
2
3
4
1.1 1.2 1.3
Introduction.................................................................................................... 1 What’s Included in the QDK-nano-8051-Keil? ........................................................ 2 Licensing QP-nano ............................................................................................ 2 Related Documentation and Resources................................................................. 2
Getting Started ............................................................................................... 3 2.1 Installation ...................................................................................................... 3 2.2 Building and Running the Examples ..................................................................... 4 2.2.1 Building and Executing the Time Bomb Examples on the ToolStick ..................... 4 2.2.2 Running and Stopping Code Execution ........................................................... 5 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10
Board Support Package for Keil C51: Non-Preemptive Configuration .............. 7 Compiler Options Used ...................................................................................... 7 Linker Options Used .......................................................................................... 7 Startup Code: STARTUP.A51 .............................................................................. 7 Configuration and Customizing QP-nano (qpn_port.h) ............................................ 8 The BSP header file bsp.h ................................................................................ 10 BSP initialization ............................................................................................. 10 Starting Interrupts in QF_start() ....................................................................... 11 ISRs ............................................................................................................. 11 QP-nano Idle Processing Customization in QF_onIdle() ......................................... 12 Assertion Handling Policy in Q_assert_handler() .................................................. 13
4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8
Board Support Package for Keil C51: Preemptive Configuration ................... 15 Compiler Options Used .................................................................................... 15 Linker Options Used ........................................................................................ 15 Startup Code: STARTUP.A51 ............................................................................ 15 Configuration and Customizing QP-nano for QK-nano ........................................... 16 The QK-nano Platform-Specific Code for 8051 ..................................................... 17 Handling Interrupts in QK-nano for 8051............................................................ 17 Starting Interrupts in QF_start() ....................................................................... 18 Writing ISRs for QK-nano ................................................................................. 19
5
Related Documents and References .............................................................. 20
6
Contact Information...................................................................................... 20
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
i
1 Introduction This Quantum Development Kit Nano™ (QDK-nano) describes how to use Quantum Platform Nano™ (QP-nano) with the Keil 8051 compiler. The actual hardware/software used in this QDKnano is described below: 1. USB Toolstick evaluation kit from Silicon Laboratories (the kit is based on the C8051F300). 2. SiLabs IDE (included ToolStick CD). 3. The free edition of the Keil C51 compiler (v7.10 included on the Toolstick CD) 4. QP-nano v1.5.06 or higher.
C8051F321 provides USB debug interface
Debug LEDs
Green LED (top) and Red LED (bottom) controlled by the “F300”
C8051F300 Target Device
Figure 1 Silicon Laboratories USB ToolStick. Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
1 of 20
QDK-nano™ 8051-Keil As shown in Figure 1, the USB ToolStick includes the integrated USB J-tag debugger and the target microcontroller C8051F300 with 256 bytes of SRAM and 8KB of Flash ROM. The evaluation kit includes a CD-ROM with the Silicon Labs IDE and the evaluation version of the Keil 8051 C compiler. Unfortunately, the evaluation version of the C51 Keil compiler is limited to only 2KB of code, which is not enough to demonstrate any non-trivial application of hierarchical state machines. However, it is still possible to use the Keil 8051 compiler with QP-nano configured for traditional, nonhierarchical FSMs. The Time Bomb example provided in this QDK-nano is designed specifically to demonstrate this option.
1.1 What’s Included in the QDK-nano-8051-Keil? This QDK-nano provides the Board Support Package (BSP) for the C8051F300 MCU and two example applications described in the “QP-nano Programmer's Manual”, implemented here with the Keil C51 compiler: 1. Simple Time Bomb example of a non-hierarchical FSM with non-preemptive scheduler built into QF-nano; and 2. Simple Time Bomb example of a non-hierarchical FSM with the preemptive QK-nano kernel.
1.2 Licensing QP-nano The Generally Available (GA) distribution of QDK-nano-8051-Keil™ available for download from the www.quantum-leaps.com/downloads website is offered with the following two licensing options: •
The GNU General Public License version 2 (GPL) as published by the Free Software Foundation and appearing in the file GPL.TXT included in the packaging of every Quantum Leaps software distribution. The GPL open source license allows you to use the software at no charge under the condition that if you redistribute the original software or applications derived from it, the complete source code for your application must be also available under the conditions of the GPL (GPL Section 2[b]).
•
One of several Quantum Leaps commercial licenses, which are designed for customers who wish to retain the proprietary status of their code and therefore cannot use the GNU General Public License. The customers who license Quantum Leaps software under the commercial licenses do not use the software under the GPL and therefore are not subject to any of its terms. NOTE: The preemptive configuration supported byt QDK-nan-8051-Keil requires the QK-nano component, which is not available in open source and is licensed only commercially. The QDKnano contains the pre-compiled QK-nano code, so you can experiment with the preemptive version.
1.3 Related Documentation and Resources This QDK-nano describes only elements of the implementation specific to 8051 and ToolStick with the particular 8051 compilers, but does not cover other related subjects, such as: QP-nano description, state machine basics, UML notation, state machine design, real-time concepts, and others. Please refer to the documents listed in the Section “References” for information about the related subjects. Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
2 of 20
QDK-nano™ 8051-Keil
2 Getting Started This section describes how to install, build, and use QDK-nano-8051-Keil based on one example. This information is intentionally included early in this document, so that you could start using QDKnano™ 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: Every QDK-nano™ contains only example(s) pertaining to the specific CPU and compiler, but does not include the platform-independent baseline code of QP-nano™, which is available for a separate download. It is strongly recommended that you read “QP-nano Programmer's Manual” before you start with this QDK-nano™.
2.1 Installation The QDK-nano code is distributed in a ZIP archive (qdkn_8051-Keil_
.zip, where stands for a specific QDK-nano version, such as 1.5.00). You can uncompress the archive into any directory. The installation directory you choose will be referred henceforth as QP-nano Root Directory . The following Listing 1 shows the directory structure and selected files included in the QP-nano distribution. (Please note that the QP directory structure is described in detail in a separate Application Note: “QP Directory Structure”) / - QP-nano Root Directory | +-examples\ - subdirectory containing the QP-nano example files | +-8051\ - examples for 8051 (the Toolstick) | | +-keil\ - examples compiled with the Keil C51 compiler | | | +-bomb-toolstick\ - the Time Bomb example (FSM only) non-preemptive case | | | | +-dbg\ - directory containing the debug build | | | | | +-bomb.hex - Intel-hex image of the application that you | | | | | can download to the Toolstick with the SiLabs IDE | | | | | +-bomb.map - map file of the application | | | | | | | | | +-bsp.c - Board Support Package for the Toolstick (non-preemptive) | | | | +-bsp.h - BSP header file | | | | +-main.c - the main function | | | | +-bomb.c - the Time Bomb state machine (FSM) | | | | +-bomb.h - the Time Bomb application header file | | | | +-bomb.wsp - Workspace file for the Silicon Labs IDE | | | | +-make.bat - batch file to build the application | | | | +-oper.c - the Operator state machine | | | | +-STARTUP.A51 - Customized startup code for this application | | | | +-qpn_port.h - QP-nano configuration for this application | | | | | | | +-bomb-qk-toolstick\ - the Time Bomb example preemptive case with QK-nano | | | | +-dbg\ - directory containing the debug build | | | | | +-bomb.hex - Intel-hex image of the application that you | | | | | can download to the Toolstick with the SiLabs IDE | | | | | +-bomb.map - map file of the application | | | | | | | | | +-bsp.c - Board Support Package for the Toolstick (non-preemptive) | | | | +-bsp.h - BSP header file | | | | +-main.c - the main function | | | | +-bomb.c - the Time Bomb state machine (FSM) | | | | +-bomb.h - the Time Bomb application header file Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
3 of 20
QDK-nano™ 8051-Keil | | | | +-bomb.wsp | | | | +-make.bat | | | | +-oper.c | | | | +-qkn_port.asm | | | | +-STARTUP.A51 | | | | +-qpn_port.h | | | | | | +-include\ | +-qassert.h | +-qepn.h | +-qfn.h | +-qkn.h | +-source/ | +-qepn.c | +-qfn.c | +-qkn.c
-
Workspace file for the Silicon Labs IDE batch file to build the application the Operator state machine QK-nano platform-specific code Customized startup code for this application QP-nano configuration for this application
-
subdirectory containing the QP-nano public interface embedded-systems-friendly assertions used in QP-nano The platform-independent QEP-nano header file The platform-independent QF-nano header file The platform-independent QK-nano header file
-
QP-nano source files QEP-nano implementation QF-nano implementation QK-nano implementation (requires commercial license)
Listing 1 Directories and files after installing QP-nano baseline code and the QDKnano-8051-Keil distribution. The highlighted directories and files are provided in the QDK-nano-8051-Keil ZIP file.
2.2 Building and Running the Examples This QDK-nano provides the Board two examples of a Simple Time Bomb implemented with the non-preemptive scheduler build into QF-nano as well as the fully preemptive QK-nano. The time bomb examples demonstrate QP-naon’s support for basic, non-hierarchical Finite State Machines (FSMs). Basic FSMs are a subset of HSMs, so any FSM can be easily coded as an HSM. However, the implementation of FSM is so small and efficient that it is provided as an option in the QEP-nano. You can use this option for performance-critical portions of your designs, such as inside interrupts or device drivers. Also, you can use FSMs in very ROM-constraint applications that simply cannot fit the full-featured HSMs. The support for FSM requires typically less than 100 bytes of code (ROM) and just a few bytes of RAM per state machine. (The RAM cost of a FSM is the same as an HSM.)
2.2.1 Building and Executing the Time Bomb Examples on the ToolStick For better control of the Keil compiler and linker options, the example project is based on a batch file make.bat, rather than the crude Silicon Labs IDE project features. The Silicon Labs IDE simply invokes the provided make.bat batch file. NOTE: You need to adjust the symbol KEIL_C51_DIR at the top of the make.bat file to the directory where you have installed the Keil C51 toolset. 1. Open the Silicon Labs IDE from the Start->Programs->Silicon Laboratories menu 2. Connect the ToolStick to a USB port on the PC 3. In the IDE, go to Project->Open Project 4. Browse to \examples\8051\keil\bomb-toolstick 5. Select bomb.wsp and click OK 6. In the IDE, select Project->Rebuild Project Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
4 of 20
QDK-nano™ 8051-Keil 7. Go to Options->Connection Options 8. Select “USB Debug Adapter” for the Serial Adapter and “C2” for the Debug Interface, and then click “OK” 9. Go to Debug->Connect 10. Download the code using the menu Debug->Download Object Code…, or the Load Button on the menu bar or use Alt-D Once these steps are completed, the firmware is built into an object file (step 6) and downloaded to the device (step 10). The device is now ready to begin executing code. If all of these steps were followed successfully, the “Go” option is enabled in the Debug menu. A green circle icon in the IDE toolbar also indicates that the device is ready to run. If one of the steps leads to an error, make sure that the ToolStick is properly inserted in a USB port and start again with step 3. To build the Time Bomb example with the preemptive QK-nano kernel, you follow exactly the same steps, except that you work in the directory \examples\8051\keil\bomb-qk-toolstick. NOTE: The preemptive example is just a few bytes too big for the 2KB-limited free evaluation version of the Keil C51 compiler that comes with the ToolStick. You need to use the unlimited toolset for building this version. You need to adjust the symbol KEIL_C51_DIR at the top of the make.bat file to the directory where you have installed the Keil C51 toolset.
2.2.2 Running and Stopping Code Execution Figure 2 shows the Time Bomb example running on the ToolStick. Here, the whole operation of the Time Bomb is visualized by means of two LEDs. The green LED is used for operator that arms the bomb every time the intensity of the green LED reaches its maximum. The red LED is used to show ticking of the time bomb by pulsing the LED at low intensity, and explosion by the turning the red LED to its maximum intensity. Once the IDE is connected to the device and the firmware is loaded, the IDE can start and stop the code execution. The following steps can be performed using the buttons on the toolbar or using the options in the Debug menu. 1. To start code execution, click the green “Go” button on the toolbar or use the Debug->Go menu option. The green and red LEDs on the ToolStick will start to flash. The Green LED will increase and decrease its brightness, while the red LED will blink at 1Hz with low intensity and turn to high intensity every few blinks (the bomb will explode). The debug commands on the IDE (single-step, multiple-step, set breakpoint, and others) are disabled when the device is running. 2. To stop code execution, click the red “Stop” button on the toolbar or use the Debug->Stop menu option. The device will halt code execution and all of the registers and pin on the device will hold their state. When the ToolStick firmware is stopped, one of the LEDs will be on and the other will be off. All debug windows and watch windows are refreshed when the device is stopped. If any of the values in these windows have changed since the last time the device was halted, the new value is shown in red text instead of black text.
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
5 of 20
QDK-nano™ 8051-Keil
Figure 2 The ToolStick executing the example code. Figure 2 shows the PELICAN example running on the ToolStick. Here, the whole operation of the PELICAN crossing must be visualized by means of only two LEDs. The green LED is used for the cars, the red LED is used for the Pedestrians. Additionally, the intensity of the LED (controlled via PWM generators of the C8051F300) is used for the “yellow” lights, as follows: Signal for Cars:
Green
Yellow
Red
Green LED
High intensity
Low intensity
Off
Signal for Pedestrians:
WALK
Blank
DON’T WALK
Red LED
Low intensity
Off
High intensity
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
6 of 20
QDK-nano™ 8051-Keil
3 Board Support Package for Keil C51: NonPreemptive Configuration The Board Support Package (BSP) for Keil C51 with the non-preemptive scheduler built into the QFnano is located in the directory: \examples\8051\keil\bomb-toolstick and consists of the following files: 1. qpn_port.h contains the platform-specific customization of QP-nano (the QP-port) 2. bsp.h contains the Board Support Package interface (BSP) 3. bsp.c contains the implementation of the BSP, which includes all ISRs and all platform-specific QP-nano callbacks.
3.1 Compiler Options Used You set the Keil C51 compiler options inside the make.bat batch file. The compiler options are as follows: SMALL ROM(COMPACT) NOINTPROMOTE DEBUG NOPRINT OBJECTEXTEND This set of options defines the SMALL memory model, but this QP-nano port should work with other memory models as well
3.2 Linker Options Used You set the linker options through the make.bat batch file. The most important linker options are as follows: NOOVERLAY NOINDIRECTCALL RAMSIZE(256) CODE(0 - 0x1DFF) IX
3.3 Startup Code: STARTUP.A51 The C51 compiler lets you customize the sizes of various memories, size of the stack(s) and so on by editing the STARTUP.A51 assembly module included with the examples. The following highlighted sections of the STARTUP.A51 file show the customizations for the ToolStick (C8051F300 MCU). Please refer to the “”
$NOMOD51 ;-----------------------------------------------------------------------------; This file is part of the C51 Compiler package ; Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc. ; ; Q u a n t u m L e a P s ; --------------------------; innovating embedded systems ; ; Adapted for the C8051F300 target device by Quantum Leaps, LLC ; ; IDATALEN EQU 100H ; 256 bytes of IDATA memory Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
7 of 20
QDK-nano™ 8051-Keil ; IBPSTACK EQU 1 ; small reentrant stack used ; . . . ;-----------------------------------------------------------------------------; ; User-defined Power-On Initialization of Memory ; ; With the following EQU statements the initialization of memory ; at processor reset can be defined: ; ; ; the absolute start-address of IDATA memory is always 0 IDATALEN EQU 100H ; the length of IDATA memory in bytes. ; XDATASTART EQU 0H ; the absolute start-address of XDATA memory XDATALEN EQU 0H ; the length of XDATA memory in bytes. ; PDATASTART EQU 0H ; the absolute start-address of PDATA memory PDATALEN EQU 0H ; the length of PDATA memory in bytes. ; ; Notes: The IDATA space overlaps physically the DATA and BIT areas of the ; 8051 CPU. At minimum the memory space occupied from the C51 ; run-time routines must be set to zero. ;-----------------------------------------------------------------------------; ; Reentrant Stack Initilization ; ; The following EQU statements define the stack pointer for reentrant ; functions and initialized it: ; ; Stack Space for reentrant functions in the SMALL model. IBPSTACK EQU 1 ; set to 1 if small reentrant is used. IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1. ;
3.4 Configuration and Customizing QP-nano (qpn_port.h) You configure and customize QP-nano through the header file qpn_port.h, which is included by the QP-nano source files (qepn.c, qfn.c, and qkn.c) as well as in all your application C modules. (1) #define Q_ROM
code
(2) #define Q_REENTRANT
reentrant
(3) #define Q_PARAM_SIZE (4) #define QF_TIMEEVT_CTR_SIZE
0 1
(5) #define Q_NHSM (6) #define QF_FSM_ACTIVE (7) #define QF_MAX_ACTIVE
4
/* interrupt locking policy for C8051F300/1/2/3/4/5, see NOTE01 */ (8) #define QF_INT_LOCK() (EA = 0, EA = 0) (9) #define QF_INT_UNLOCK() (EA = 1) (10) #define QF_ISR_NEST
/* interrupt nesting policy, see NOTE02 */
/* Exact-width types. WG14/N843 C99 Standard, Section 7.18.1.1 */ (11) typedef signed char int8_t; typedef signed int int16_t; Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
8 of 20
QDK-nano™ 8051-Keil typedef typedef typedef typedef
signed unsigned unsigned unsigned
long char int long
(12) #include (13) #include "qepn.h" (14) #include "qfn.h"
int32_t; uint8_t; uint16_t; uint32_t; /* SFR declarations for the F300 */ /* QEP-nano platform-independent public interface */ /* QF-nano platform-independent public interface */
/* shadow of the PCON register for atomic transition to Idle mode, NOTE03 */ (15) extern volatile uint8_t bdata QF_pcon; Listing 2 qpn_port.h header file for the USB ToolStick and Keil C51 compiler Listing 2(1) The Q_ROM macro is used inside the QP-nano source code to denote all constant objects that can be allocated in ROM. On CPUs with the Harvard architecture (such as the 8051), the code and data spaces are separate and are accessed through different instructions. The compilers often provide specific extended keywords to designate code or data space, such as the code extended keyword in the Keil 8051 compiler. To use QP-nano with a different MCU/compiler, you need to check how to allocate constants to ROM. On Von Neuman CPUs, you can always define the Q_ROM macro as empty (e.g., see the 80x86 version). Listing 2(2) The Keil C51 compiler, doesn't generate ANSI-C compliant reentrant functions because they are relatively inefficient on the 8051. C51 allows to dedicate specific functions to be reentrant with a special extended keyword “reentrant”. You need to define the macro Q_REENTRANT to “reentrant” to work with the Keil C51 compiler. NOTE: The macro Q_REENTRANT has been introduced in QP-nano version 1.5.06. The preemptive QK-nano port requires that you use this version or higher. Listing 2(3) The Q_PARAM_SIZE macro defines the size in bytes of the scalar event parameter. The valid values are 0, 1, 2, and 4, which correspond to no event parameter, uint8_t, uint16_t, and uint32_t parameter. You might also not define this macro at all, which is equivalent to specifying no parameter. Listing 2(4) The QF_TIMEEVT_CTR_SIZE macro defines the size in bytes of the time event downcounter (timer counter). The size of this counter determines the dynamic range of timeouts (in clock tick units). The valid values are 0, 1, 2, and 4, which correspond to no time event counter, uint8_t, uint16_t, and uint32_t counter. You might also not define this macro at all, which is equivalent to specifying no time event counter. Listing 2(5) The macro Q_NHSM eliminates the HSM code. You cannot use the advanced HSMs, only the simple FSMs. Limiting QP-nano to FSMs only is necessary to reduce the code size below the limitat of the evaluation version of the Keil compiler. Listing 2(6) The macro QF_FSM_ACTIVE specifies that active objects are the simple FSMs. The default is that active objects are HSMs. Obviously, QF_FSM_ACTIVE is not compatible with Q_NFSM. Listing 2(7) The macro QF_MAX_ACTIVE defines the maximum number of active objects. In QF-nano, this number cannot exceed 8. However, defining QF_MAX_ACTIVE as 4 or less slightly improves performance of the QF-nano scheduler. Listing 2(8-9) The macros QF_INT_LOCK() / QF_INT_UNLOCK() define the CPU and compiler-specific interrupt locking mechanism. For the 8051, clearing the IE.7 bit (the EA bit) locks the interrupts, and setting the IE.7 bit unlocks the interrupts. Also, the C8051F300 manual recommends Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
9 of 20
QDK-nano™ 8051-Keil always following interrupt locking with the 2-byte instruction, so the clearing the IE.7 bit is repeated. Listing 2(10) The 80x51 hardware supports interrupt prioritization and does not disable interrupts upon the entry to the interrupt (the global interrupt enable bit EA is not cleared). For this reason interrupts generally run with interrupts enabled. This is convenient for using the simple policy of unconditional unlocking of interrupts upon exit from a critical section, because there is no risk of nesting critical sections. NOTE: You should be careful not to introduce nesting of critical sections by not disabling interrupts before calling any QF-nano services, such as QF_postISR(), QF_tick().) Instead, you can always prioritize an interrupt to a higher level to prevent preemptions by other interrupts. Listing 2(11) The C99-standard exact-width integer types are defined for the Keil C51 compiler (the compiler does not provide the standard <stdint.h> header file). Listing 2(12) The MCU-specific header file is included for the definition of the SFRs, such as the IE register. Listing 2(13-14) The qpn_port.h header file must always include the platform-independent QEPnano header file qep.h and QF-nano header file qf.h. Listing 2(15) The variable QF_pcon holds the shadow of the PCON register (see Section 0).
3.5 The BSP header file bsp.h (1) #define BSP_MCK (2) #define BSP_TICKS_PER_SEC
(24500000/8) 10
(3) void BSP_init(void); (4) #define LED_RED(brightness_) (PCA0CPH0 = 0xFF - (brightness_)) (5) #define LED_GRN_ON() (P0 |= (1 << 3)) (6) #define LED_GRN_OFF() (P0 &= ~(1 << 3)) Listing 3 The bsp.h for the PELICAN crossing example. Listing 3(1-2) The BSP defines the MCK frequency for the ToolStick and the desired ticking rate. Listing 3(5) The BSP declares the board initialization interface Listing 3(4) The BSP declares the macros for setting the intensity of the ToolStick’s red LED Listing 3(5-6) The BSP declares the macros for switching the ToolStick’s green LED on and off.
3.6 BSP initialization The following BSP_init() function from the PELICAN crossing application for the ToolStick sets the internal oscillator, configures the PIO lines and PWM generators for the LEDs, and sets up the Timer2 to deliver the clock-tick interrupt: void BSP_init(void) { OSCICN = 0x04;
/* set SYSCLK as Int Osc 24.5/8 MHz */
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
10 of 20
QDK-nano™ 8051-Keil RSTSRC = 0x00;
/* disable missing clock detector */
PCA0CN = 0x40; PCA0MD = 0x00;
/* Enable PCA Counter/Timer (PCA0) */ /* Set PCA timebase = SYSCLK / 12, disable watchdog WDT */
PCA0CPH0 = 0x00; PCA0CPH1 = 0x00;
/* Duty cycle = (256 - PCA0CPHn) / 256 */ /* Config Mod0 PWM for a 100% initial duty cycle */ /* Config Mod1 PWM for a 100% initial duty cycle */
PCA0CPM0 = 0x42; PCA0CPM1 = 0x42;
/* Enable 8-bit PWM on CEX0 */ /* Enable 8-bit PWM on CEX1 */
/* Configure the Crossbar and GPIO ports. */ P0MDOUT = 0x0C; /* Config P0.2 & P0.3 as push-pull outputs XBR0 = 0x03; /* Skip P0.0 & P0.1 XBR1 = 0x80; /* Enable PCA Capture/Compare Outputs 0 & 1 XBR2 = 0x40; /* Enable Crossbar IP
= ~0;
/* set all interrupts to the high priority (no nesting) */
TMR2CN = 0x00; CKCON &= ~0x60; TMR2RL TMR2
*/ */ */ */
/* Stop Timer2; Clear TF2, SYSCLK/12 */ /* Timer2 clocked based on T2XCLK */
= 0x10000 - (BSP_MCK/12)/BSP_TICKS_PER_SEC; /* reload value */ = TMR2RL; /* Initialize Timer2 to reload value */
}
3.7 Starting Interrupts in QF_start() QP-nano invokes the QF_start() callback just before starting the background loop in the nonpreemptive case. Either way the QF_start() function must start the interrupts configured earlier. The following QF_start() function enables the Timer2 interrupts configured earlier in BSP_init() and starts the Timer2. void QF_start(void) { /* Set the priority of interrupts for your application by writing to the * IP and EIP1 registers. Here the pririties are left at the default * level (low) ... */
}
ET2 = 1; TR2 = 1;
/* Enable Timer2 interrupts */ /* Start Timer2 */
NOTE: The QF_start() function is also a good place to set interrupt priorities that best fit your application. In the non-preemptive QP-nano configuration you can assign different priorities to interrupts to allow interrupt nesting (8051 hardware, such as the “F300”, typically supports 2 levels of interrupt priority).
3.8 ISRs The Keil C51 compiler supports writing interrupts in C. In the non-preemptive case used in this QDK-nano, the ISRs are identical as in the simplest of all “super-loop” (main+ISRs), and there is nothing QP-specific in the structure of the ISRs. Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
11 of 20
QDK-nano™ 8051-Keil The only QP-specific requirement is that you provide a periodic time-tick ISR and you invoke QF_tick() in it. (1) void timer2_ISR (void) interrupt 5 { (2)
TF2H = 0;
(3)
QF_tick();
(4)
QF_pcon = PCON;
/* clear Timer2 interrupt flag */
/* disable Idle mode after retrun from the ISR, NOTE03 */
} Listing 4 Time tick interrupt for the ToolStick the QF_tick() function to generate Q_TIMEOUT events. Listing 4(1) The ISR in C is always compiler-specific, as the C standard does not define how to specify ISRs. In the case of the Keil compiler for 8051 interrupt must be preceded with the interrupt extended keyword and must define the vector number (5 for Timer2 interrupt). Listing 4(2) The Timer2 interrupt-pending flag needs to be explicitly cleared in software. Listing 4(3) The time-tick ISR must invoke QF_tick(), and can also perform other things, 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 QF_tick() is invoked with interrupts locked. Listing 4(4) The QF_pcon shadow register is updated to the current value of the PCON register. Please note that the IDL/PD bits in the PCON register are automatically cleared in hardware upon every ISR entry, so here these bits are guaranteed to be cleared in the QF_pcon shadow register. This, in turn, means that the Idle (or Power Down) mode is not entered, if the interrupt preempted the idle processing (see the next section).
3.9 QP-nano Idle Processing Customization in QF_onIdle() QP-nano 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 locked, because the determination of the idle condition might change by any interrupt posting an event. 80x51 architecture supports two power-saving levels (Idle and Power Down). These modes are activated by setting the IDL or PD bits in the PCON register (address 0x87). Writing to the IDL or PD bits stops the CPU immediately, and it must happen with interrupts enabled. This means that it is impossible to transition to the Idle (or Power Down) mode atomically. Any enabled interrupt can preempt the idle processing after the interrupts are enabled, but before the Idle mode is actually entered. To avoid any non-deterministic behavior such situation can cause, this QP-nano port uses a technique to disable the Idle mode transition in every interrupt. So, if an interrupt actually preempts the idle processing in the time window described above, the Idle mode transition won’t occur after the interrupt returns. To make this happen, the transition to the Idle mode must be a single machine instruction. (In particular the transition cannot be a non-atomic test-and-set operation.)
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
12 of 20
QDK-nano™ 8051-Keil To implement such a mechanism, this QP-nano port maintains a shadow of the PCON register (in the QF_pcon variable). The Idle mode bit is explicitly set only in the shadow register QF_pcon, still with interrupts disabled. Then interrupts get enabled and the register PCON is restored from the shadow. It is very important that the update of the PCON register happens as a single machine instruction. As it turns out, the 8051 compiler translates the simple assignment PCON = QF_pcon; into a single machine instruction such as MOV 87H,20H. The following piece of code shows the QF_onIdle() callback that puts 80x51 into the idle powersaving mode. The code includes the QS output, which will be discussed in the next section. (1) void QF_onIdle(void) { uint8_t volatile delay;
/* entered with interrupts locked */
(2)
LED_GRN_ON(); /* toggle green LED on and off, see NOTE01 */ for (delay = 10; delay != 0; --delay) { /* delay a bit to see the LED */ } LED_GRN_OFF();
(3) (4)
QF_pcon = PCON | 0x01; QF_INT_UNLOCK();
(5)
/* choose low-power mode for your app, NOTE03 */ /* unlock interrupts, see NOTE02 */
PCON = QF_pcon;
/* go to low-power, see NOTE03 */
} Listing 5 QF_onIdle() for the non-preemptive QF-nano configuration Listing 5(1) The QF_onIdle() function is called with interrupts locked and must unlock interrupts internally. Listing 5(2) This QP-nano port uses the Green LED of the Toolstick to visualize the idle loop activity. The LED is rapidly toggled on and off as long as the idle condition is maintained, so the brightness of the LED is proportional to the CPU idle time (the wasted cycles). Listing 5(3) The IDL bit is turned on in the shadow register QF_pcon. This happens still in the critical section. Listing 5(4) The interrupts are unlocked. Listing 5(5) The PCON register is updated from the shadow. This happens in just one atomic instruction (MOV 87H,20H). If an interrupt occurs even one machine instruction before this instruction gets executed, the shadow register QF_pcon will have the IDL/PD bits cleared and the PCON update will not cause the transition to the idle mode.
3.10 Assertion Handling Policy in Q_assert_handler() As described in “QP-nano Programmer's Manual”, QP-nano uses internally assertions to detect errors in the way application is using the QP-nano services. You need to define how the application reacts in case of assertion failure by providing the callback function Q_assert_handler(). 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_assert_handler() callback for the ToolStick. The function simply locks all interrupts, lights up the red LED, extinguishes the green LED, and enters a for-ever loop. This policy is only adequate for testing, but probably is not for production release. void Q_assert_handler(char const Q_ROM *file, int line) { Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
13 of 20
QDK-nano™ 8051-Keil file = file; /* avoid compiler warning line = line; /* avoid compiler warning QF_INT_LOCK(); /* make sure that interrupts are locked LED_RED(255); LED_GRN_ON(); for (;;) { /* NOTE: replace this loop with reset for the final version }
*/ */ */ */
}
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
14 of 20
QDK-nano™ 8051-Keil
4 Board Support Package for Keil C51: Preemptive Configuration This section describes how to use QP-nano on 8051 with fully preemptive QK-nano kernel. The benefits are very fast, fully deterministic task-level response. The downside is bigger RAM requirement for the stack. (see “QK-nano Programmer's Manual”.) NOTE: QK-nano provides responsiveness exceeding that of the RTX51 RTOS from Keil, at the fraction of the RAM/ROM footprint. The resource requirements of QK-nano are comparable to those of RTX51 Tiny, but QK-nano is much more powerful than RTX51 Tiny. The Board Support Package (BSP) for the 8051 with the QK-nano preemptive kernel is located in the directory: \examples\8051\keil\bomb-qk-toolstick and consists of the following files: 1. qpn_port.h contains the platform-specific customization of QP-nano (the QP-port) 2. qkn_port.asm QK-nano port to 8051 (contains QK_reti_() function) 3. bsp.h contains the Board Support Package interface (BSP) 4. bsp.c contains the implementation of the BSP, which includes all ISRs and all platform-specific QP-nano callbacks.
4.1 Compiler Options Used You set the compiler options through the make.bat batch file located in the directory \examples\8051\keil\bomb-qk-toolstick\. The compiler options are identical as in the nonpreemptive configuration (see Section 3.1):
4.2 Linker Options Used The linker options are identical as in the non-preemptive configuration (see Section 3.2), except that with the preemptive QK-nano kernel, you must be very careful to provide adequate stack size through the following option:
4.3 Startup Code: STARTUP.A51 The C51 compiler lets you customize the sizes of various memories, size of the stack(s) and so on by editing the STARTUP.A51 assembly module included with the examples. The STARTUP.A51 file for the QK-nano application is identical to the non-preemptive case (see Section 3.3). NOTE: The preemptive configuration with QK-nano requires the stack to be significantly bigger than the non-preemptive option. You need to adjust the size of this stack to be large enough for your application.
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
15 of 20
QDK-nano™ 8051-Keil
4.4 Configuration and Customizing QP-nano for QK-nano You configure and customize QP-nano through the header file qpn_port.h, which is included by the QP-nano source files (qepn.c, qfn.c, and qkn.c) as well as in all your application C modules. The following Listing 6 shows the qpn_port.h header file for the QK-nano port. Except for the highlighted fragments, the listing is identical as in the non-preemptive case. #define Q_ROM #define Q_REENTRANT
code reentrant
#define Q_PARAM_SIZE #define QF_TIMEEVT_CTR_SIZE
0 1
#define Q_NHSM #define QF_FSM_ACTIVE #define QF_MAX_ACTIVE
4
/* interrupt locking policy for C8051F300/1/2/3/4/5, see NOTE01 */ #define QF_INT_LOCK() (EA = 0, EA = 0) #define QF_INT_UNLOCK() (EA = 1) /* interrupt nesting policy, see NOTE02 */ #define QF_ISR_NEST (1) #define QK_ISR_ENTRY() (2) #define QK_ISR_EXIT() do { \ (3) QF_INT_LOCK(); \ (4) QK_reti_(); \ (5) QK_schedule(); \ (6) QF_INT_UNLOCK(); \ } while (0)
((void)0)
/* Exact-width types. WG14/N843 C99 Standard, Section 7.18.1.1 */ typedef signed char int8_t; typedef signed int int16_t; typedef signed long int32_t; typedef unsigned char uint8_t; typedef unsigned int uint16_t; typedef unsigned long uint32_t; #include #include "qepn.h" #include "qfn.h" (7) #include "qkn.h"
/* SFR declarations for the F300 */ /* QEP-nano platform-independent public interface */ /* QF-nano platform-independent public interface */ /* QK-nano platform-independent public interface */
/* shadow of the PCON register for atomic transition to Idle mode, NOTE03 */ (8) void QK_reti_(void); Listing 6 qpn_port.h header file for the preemptive QK-nano configuration and Keil C51 compiler Listing 6(1) The QK_ISR_ENTRY() macro is used to inform the QK-nano kernel that an interrupt has been entered. This macro is empty in the 8051 QK-nano port (see also Section 4.6). Listing 6(2) The QK_ISR_EXIT() macro is used to inform the QK-nano kernel that an interrupt has been exited. You must place this macro at the end of every ISR in your application. The do {…} while(0) loop around the macro is an idiom for guaranteeing syntactically-correct use of this macro in any context, including the dangling-else contexts. Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
16 of 20
QDK-nano™ 8051-Keil Listing 6(3) Interrupts are locked. Listing 6(4) The RETI instruction is issued to end the interrupt prioritization in the 8051 interrupt circuitry. At this point the thread becomes the task-level. As soon as interrupts get unlocked, the thread can be preempted by any interrupt (included the interrupt just serviced). Listing 6(5) The QK scheduler is invoked to perform the asynchronous preemptions. Listing 6(6) The interrupts are unlocked just before returning from the interrupt. Listing 6(7) The QK-nano port must include qkn.h header file. Listing 6(8) The prototype of the QK_reti_() function (see the next section).
4.5 The QK-nano Platform-Specific Code for 8051 The QK-nano port to 8051 requires a very small function QK_reti_() for executing the extra RETI instruction at the ISR exit (see Listing 6(2)). This function is defined in assembly in the file \examples\8051\keil\bomb-qk-toolstick\qkn_port.asm as follows: AME QK_NANO ?PR?QK_reti_?QK_NANO SEGMENT CODE PUBLIC QK_reti_ ; ; void reti(void) RSEG ?PR?QK_reti_?QK_NANO USING 0 QK_reti_: RETI END
4.6 Handling Interrupts in QK-nano for 8051 As described in “MCS 51 Microcontroller Family User’s Manual”, the 8051 enters an interrupt with interrupts enabled (IE bit set), and there is no way of detecting reliably in software that an interrupt preempted another interrupt. Without this information, a real-time kernel, such as QK-nano, cannot distinguish the last interrupt in the preemption chain to perform the asynchronous task switch. Scheduling a task upon exit from an interrupt that nests on another interrupt would lead to priority inversion (a task preempting an interrupt). For this reason, QK-nano for 8051 requires that interrupts do not nest (i.e., an interrupt cannot preempt another interrupt).
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
17 of 20
QDK-nano™ 8051-Keil priority
RETI
Interrupt Level
RETI
ISR
ISR No task preemption
High-priority task (1) Low-priority task
RETI
(2)
RETI
ISR
ISR
RETI ISR
(6) (4)
(5)
(3)
(7)
(9)
(10)
preempted (8)
QK-nano idle loop
preempted time
Figure 3 Interrupt handling in QK-nano. Red color indicates the part of ISR after calling the QK_ISR_EXIT() macro. Figure 3 illustrates how interrupts are handled in QK-nano. The first interrupt preempts the QKnano idle loop (1). At the end of the ISR, the QK_ISR_EXIT() macro (see Listing 6(2)) issues the RETI instruction, which terminates interrupt prioritization in hardware. After this the QK-scheduler determines that no tasks are ready-to-run and returns (via another RETI instruction) to the preempted idle loop (2). The second interrupt preempts the idle loop again (3), but the QK-scheduler detects that the Low-priority task has event(s) to process. The QK-scheduler unlocks interrupts and launches the Low-priority task (4). While this task is running, an interrupt preempts the task (5). The interrupt detects that the High-priority task is ready and launches this task (6). The Highpriority task returns (via RETI) to the preempted Low-priority task (7), which runs to completion and also returns to the preempted idle loop (8). The interrupt (8) preempt the idle loop. During processing of this interrupt, another one occurs, but interrupt nesting is not allowed. At the end of processing, the first interrupt issues the RETI instruction and eventually unlocks interrupts (see QF_INT_UNLOCK() in QK_ISR_EXIT() in Listing 2(2)). At this point, the second interrupt immediately preempts the first one (the RETI instruction terminates interrupt prioritization in the 8051 hardware). The second interrupt returns to the preempted context (10). NOTE: Even though interrupt nesting is not allowed, it actually happens in the case of back-toback interrupt processing, such as (9) and (10) in Figure 3. You need to budget adequate stack space to account for this possibility.
4.7 Starting Interrupts in QF_start() QP-nano invokes the QF_start() callback just before starting the background loop. The QF_start() function must start the interrupts. The following QF_start() function sets all interrupt priorities to high and enables the Timer2 interrupts configured earlier in BSP_init() and starts the Timer2. void QF_start(void) { /* set all interrupts to high priority (no interrupt nesting), NOTE01 */ IP = 0xFF; EIP1 = 0xFF; IE_bit.ET2 = 1;
/* Enable Timer2 interrupts */
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
18 of 20
QDK-nano™ 8051-Keil TMR2CN
= (1 << 2);
/* Start Timer2 */
} NOTE: Due to the particular implementation of hardware interrupt entry in 8051, you cannot let interrupt nest in the QK-nano configuration. You must disable interrupt nesting by setting all interrupts to the high priority level, which guarantees that interrupts will not preempt interrupts. You set the interrupt priority by writing to the IP register (and sometimes other registers, such as EIP1 for the “F300”). See also the next section).
4.8 Writing ISRs for QK-nano QK-nano must be informed about entering and exiting every ISR, so that it can perform asynchronous preemptions. You inform the QK-nano kernel about the ISR entry and exit through the macros QK_ISR_ENTRY() and QK_ISR_EXIT(), respectively (see Listing 6(1-2)). You need to call these macros in every ISR. The following listing shows an example of the time-tick ISR in the file \examples\8051\keil\pelican-qk-toolstick\bsp.c. void timer2_ISR(void) reentrant interrupt 5 { QK_ISR_ENTRY(); /* inform QK that an ISR has been entered */ TF2H = 0;
/* clear Timer2 interrupt flag */
QF_tick(); }
QK_ISR_EXIT();
/* inform QK that an ISR is exited */
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
19 of 20
QDK-nano™ 8051-Keil
5 Related Documents and References Document
Location
“MCS 51 Microcontroller Family User’s Manual”, Intel, February 1994.
http://www.intel.com/design/mcs51/manuals/27238302.pdf
“Cx51 Compiler User’s Guide”, Keil Software, 2001
Included in the Silicon Labs “F300” Toolstick, as document c51.pdf.
“C8051F300/1/2/3/4/5 Mixed Signal ISP Flash MCU Family”, Silicon Laboratories, 2005
http://www.silabs.com/public/documents/tpub_doc/dsheet/Microcontrollers/Small_Form_Factor/en/C8051F30x.pdf
“UML Statecharts at $10.99”, Dr. Dobb’s Journal, Miro Samek, May 2006
http://www.ddj.com/dept/embedded/188101799
“QP-nano Programmer’s Leaps, 2006
http://www.quantum-leaps.com/products/qpn.htm
Manual”,
Quantum
“QP Programmer’s Manual”, Quantum Leaps, 2005
http://www.quantum-leaps.com/doc/QP_Manual.pdf
6 Contact Information Silicon Laboratories, Inc. 4635 Boston Lane Austin, TX 78735 +1 (512) 416-8500 +1 (512) 416-9669 (FAX) 1-877-444-3032 (toll free, USA only) e-mail: [email protected] Web: www.silabs.com
Keil - An ARM Company 1501 10th Street, Suite 110 Plano, TX 75074 USA Toll Free: 800-348-8051 Phone: 972-312-1107 Fax: 972-312-1159 Sales: [email protected] Support: [email protected] WEB : www.Keil.com
Quantum Leaps, LLC 3452 South Court Palo Alto, CA 94306 USA +1 866 450 LEAP (toll free, USA only) +1 650 804 0385 +1 650 249 0323 (FAX) e-mail: [email protected] WEB : http://www.quantum-leaps.com
Copyright © 2002-2007, Quantum Leaps, LLC. All Rights Reserved.
20 of 20