Ocosii On Dspic

Download as pdf or txt
Download as pdf or txt
You are on page 1of 32

Micrim

Copyright 2008, Micrim


All Rights reserved

C/OS-II
and the

Microchip dsPIC33
Application Note
AN-1033

www.Micrium.com

C/OS-II and the Microchip dsPIC33

Table of Contents
1.00 Introduction ............................................................................................................. 3 2.00 The Microchip dsPIC33 programmers model ......................................................... 4 3.00 C/OS-II Port for dsPIC33 ..................................................................................... 7 3.01 Directories and Files................................................................................................ 7 3.02 OS_CPU.H .............................................................................................................. 8 3.02.01 OS_CPU.H, macros for externals ..................................................................... 8 3.02.02 OS_CPU.H, Data Types..................................................................................... 8 3.02.03 OS_CPU.H, Critical Sections ............................................................................. 9 3.02.04 OS_CPU.H, Stack growth .................................................................................. 9 3.02.05 OS_CPU.H, Task Level Context Switch ............................................................. 9 3.02.06 OS_CPU.H, Function Prototypes ..................................................................... 10 3.03 OS_CPU_C.C ....................................................................................................... 10 3.03.01 OS_CPU_C.C, OSInitHookBegin()................................................................... 10 3.03.02 OS_CPU_C.C, OSInitHookEnd() ..................................................................... 11 3.03.03 OS_CPU_C.C, OSTaskCreateHook() .............................................................. 11 3.03.04 OS_CPU_C.C, OSTaskStkInit() ....................................................................... 12 3.03.05 OS_CPU_C.C, OSTaskSwHook().................................................................... 15 3.03.06 OS_CPU_C.C, OSTimeTickHook() .................................................................. 15 3.04 OS_CPU_A.S........................................................................................................ 16 3.04.01 OS_CPU_A.S, OSStartHighRdy() .................................................................... 16 3.04.02 OS_CPU_A.S, OSCtxSw() ............................................................................... 17 3.04.03 OS_CPU_A.S, OSIntCtxSw() ........................................................................... 18 3.04.04 OS_CPU_A.S, T2Interrupt() ............................................................................. 18 3.04.05 OS_CPU_A.S, T4Interrupt() ............................................................................. 19 3.05 OS_CPU_UTIL_A.S .............................................................................................. 20 3.06 OS_DBG.C............................................................................................................ 21 4.00 Interrupt Handling.................................................................................................. 22 5.00 Application Code ................................................................................................... 26 5.01 APP.C, APP.H and APP_CFG.H........................................................................... 27 5.02 INCLUDES.H......................................................................................................... 29 5.03 OS_CFG.H ............................................................................................................ 29 6.00 BSP (Board Support Package).............................................................................. 30 7.00 Conclusion............................................................................................................. 31 Licensing ...................................................................................................................... 32 References.................................................................................................................... 32 Contacts ...................................................................................................................... 32

C/OS-II and the Microchip dsPIC33

1.00

Introduction

This application note describes how C/OS-II has been ported to the Microchip dsPIC33 family of processors. This application note does not assume any specific dsPIC33 derivative. Figure 1-1 shows a block diagram showing the relationship between your application, C/OS-II, the port code and the BSP (Board Support Package). Relevant sections of this application note are referenced on the figure.

Your Application
Section 5
APP.C APP.H APP_CFG.H INCLUDES.H OS CFG.H

C/OS-II
C/OS-II Book
OS_CORE.C OS_FLAG.C OS_MBOX.C OS_MEM.C OS_MUTEX.C OS_Q.C OS_SEM.C OS_TASK.C OS_TIME.C OS_TMR.C uCOS II.H

C/OS-II
Section 3

dsPIC33 Port
OS_CPU_C.C OS_CPU_A.S OS_CPU_UTIL_A.S OS_CPU.H OS DBG.C

BSP
BSP.C BSP.H

Section 6

Section 2

Explorer 16 / Target Board


Figure 1-1, Relationship between modules.

C/OS-II and the Microchip dsPIC33

2.00

The Microchip dsPIC33 programmers model

This section provides a brief description of the dsPIC33 programmers model. We present enough information in this section to provide a brief introduction. A complete description can be found in the Microchip documentation. The dsPIC33 family of CPU cores features proprietary Microchip architecture and is designed for controller applications using a 16-bit (data), DSP capable architecture. Below are some of the features of the dsPIC33 family of CPU cores: 16 General-purpose Working registers Dual 40 bit Accumulators, 17x17 Multiplier 24 bit variable length instruction word, 23 bit program counter. 40 bit barrel shifter capable of 16 bit shift left of shift right in 1 cycle. Dedicated 1KB of DMA Ram Hardware divider support Dual Address generators for accessing memory blocks X and Y. High speed deterministic interrupt processing

The dsPIC33 is a Little Endian architecture and thus the least significant byte of a value is placed at a lower memory location than its most significant byte. Figure 2-1 shows the register model of the dsPIC33 and consist of 16 Working registers, two 40 bit accumulators, the Data Table Page register, the Program Space Visibility Page Address register, a Repeat Loop Counter, three dedicated DO Loop registers, the Core Configuration register, and a CPU Status register.

W0-W15 (Working Registers)


Registers W0-W3 are general purpose scratch pad registers Registers W4-W7 are the DSP Operand registers Registers W8-W11 are the DSP address registers Register W12 doubles as the DSP Offset register Register W13 doubles as the DSP Write Back register Register W14 doubles as the Frame Pointer register Register W15 doubles as the CPU Stack Pointer register The stack pointer always points to the last pushed element. In other words, when an element is pushed onto the stack, the stack pointer is first decremented and the value placed at the current location where the stack is pointing to. The stack is popped by first reading the contents of the stack and then incrementing the stack pointer.

SPLIM (Stack Pointer Limit Register)


Causes a TRAP error when W15 is greater than SPLIM

ACCA / ACCB (DSP Accumulators)


Dual 40 bit accumulators for DSP operations

TBLPAGE (Table Page Register)


The 8-bit Table Page register (TBLPAG) is used to define a 32K word region within the program space. This is concatenated with a 16-bit effective address in order to arrive at a full 24-bit program space address.

C/OS-II and the Microchip dsPIC33

PSVPAG (Program Space Visibility Page Address Register)


For remapping operations, the 8-bit Program Space Visibility register (PSVPAG) is used to define a 16K word page in the program space. When the Most Significant bit of the EA is 1, PSVPAG is concatenated with the lower 15 bits of the EA to form a 23-bit program space address. Unlike table operations, this limits remapping operations strictly to the user memory area.

RCOUNT (Repeat Loop Counter)


The RCOUNT register tracks the iteration count of an instruction that must be executed repetitively until its operation has been completed. Divide instructions are iterative instructions that make extensive use of the RCOUNT register.

DCOUNT (Do Loop Counter)


Counts the iteration number of a DO loop

DOSTART (Do Loop Start Address)


Holds the start address of a particular DO loop

DOEND (Do Loop End Address)s


Holds the end address of a particular DO loop

CORECON (Core Configuration Register)


Contains configuration bits pertaining to how the processor handles certain types of operations.

STATUS (CPU Status Register)


Contains status CPU status flags indicating the state of the CPU after a given instruction.

C/OS-II and the Microchip dsPIC33

Figure 2-1, dsPIC33 Programmers Model.

C/OS-II and the Microchip dsPIC33

3.00

C/OS-II Port for dsPIC33

C/OS-II has been ported to the Microchip C30, Hi-Tech dsPICC, and IAR iccDSPIC compilers. All ports currently utilize the MPLAB development environment v7.60 or greater. We strongly recommend checking with your compiler vendor to ensure that you have obtained the latest MPLAB IDE plug-in version available for the compiler of choice. We used an Explorer 16 EVB with a dsPIC33FJ256GP710 dsPIC33 derivative to test the described application. It is assumed that you have C/OS-II V2.82 or higher. Various tool-chains require different extensions for assembly files. This document refers to assembly files having the extension .s in order to be consistent with the Microchip C30 tool-chain. However, in practice, the Hi-Tech dsPICC compiler uses the extension .as while the IAR iccDSPIC compiler uses .s59. Files from different tool-chain ports are not interchangeable.

3.01

Directories and Files

The software that accompanies this application note is assumed to be placed within the following directory depending on the tool-chain used: C30: \Micrium\Software\uCOS-II\Ports\Microchip\PIC33FJ256\MPLAB_C30 dsPICC: \Micrium\Software\uCOS-II\Ports\Microchip\PIC33FJ256\MPLAB_HT-DSPICC iccDSPIC: \Micrium\Software\uCOS-II\Ports\Microchip\PIC33FJ256\MPLAB_IAR The source code for the C/OS-II dsPIC33 port is found in the following files: OS_CPU.H OS_CPU_C.C OS_CPU_A.S OS_CPU_UTIL_A.S OS_DBG.C Section 3.02 Section 3.03 Section 3.04 Section 3.05 Section 3.06

C/OS-II and the Microchip dsPIC33

3.02

OS_CPU.H

OS_CPU.H contains processor- and implementation-specific #defines constants, macros, and typedefs.

3.02.01 OS_CPU.H, macros for externals


OS_CPU_GLOBALS and OS_CPU_EXT allows us to declare global variables that are specific to this port. However, this port does not contain any global variables but the declarations have been included in case we need to add some in the future.

Listing 3-1, OS_CPU.H, Globals and Externs


#ifdef OS_CPU_GLOBALS #define OS_CPU_EXT #else #define OS_CPU_EXT extern #endif

3.02.02 OS_CPU.H, Data Types


Listing 3-2, OS_CPU.H, Data Types
typedef typedef typedef typedef typedef typedef typedef typedef typedef unsigned unsigned signed unsigned signed unsigned signed float long char char char int int long long double BOOLEAN; INT8U; INT8S; INT16U; INT16S; INT32U; INT32S; FP32; FP64; OS_STK; OS_CPU_SR;

(1)

(2) (3) (4)

typedef unsigned int typedef unsigned int L3-2(1)

If you were to consult the MPLab compiler documentation, you would find that an int is 16 bits, an long is 32 bits. Floating-point data types are included even though C/OS-II doesnt make use of floating-point numbers. A stack entry for the dsPIC33 processor is always 16 bits wide; thus, OS_STK is declared accordingly. All task stacks must be declared using OS_STK as its data type. The status register (STATUS) on the dsPIC33 processor is 16 bits wide. The OS_CPU_SR data type is used when OS_CRITICAL_METHOD #3 is used (described below). In fact, this port only supports OS_CRITICAL_METHOD #3 because its the preferred method for C/OS-II ports.

L3-2(2)

L3-2(3)

L3-2(4)

C/OS-II and the Microchip dsPIC33

3.02.03 OS_CPU.H, Critical Sections


C/OS-II, as with all real-time kernels, needs to disable interrupts in order to access critical sections of code and re-enable interrupts when done. C/OS-II defines two macros to disable and enable interrupts: OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL(), respectively. C/OS-II defines three ways to disable interrupts but, you only need to use one of the three methods for disabling and enabling interrupts. The book (MicroC/OS-II, The Real-Time Kernel) describes the three different methods. The one to choose depends on the processor and compiler. In most cases, the preferred method is OS_CRITICAL_METHOD #3. OS_CRITICAL_METHOD #3 implements OS_ENTER_CRITICAL() by writing a function that will save the status register of the CPU in a variable. OS_EXIT_CRITICAL() invokes another function to restore the status register from the variable. We recommend that you name the functions expected in OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() as OS_CPU_SR_Save() and OS_CPU_SR_Restore(), respectively. These routines are generally stored in OS_CPU_A.ASM, however, in this case, since the registers are memory mapped, it makes sense to simply write and read to and from SR directly from C code.

Listing 3-3, OS_CPU.H, OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()


#define #if #define #define #endif OS_CRITICAL_METHOD 3

OS_CRITICAL_METHOD == 3 OS_ENTER_CRITICAL() {cpu_sr = SR; SRbits.IPL = 7;} OS_EXIT_CRITICAL() {SR = cpu_sr;}

3.02.04 OS_CPU.H, Stack growth


The stacks on the dsPIC33 grows from low to high memory and thus, OS_STK_GROWTH is set to 0 to indicate this to C/OS-II.

Listing 3-4, OS_CPU.H, Stack Growth


#define OS_STK_GROWTH 0

3.02.05 OS_CPU.H, Task Level Context Switch


Task level context switches are performed when C/OS-II invokes the macro OS_TASK_SW(). Because context switching is processor specific, OS_TASK_SW() needs to execute an assembly language function. You should note that C/OS-II disables all interrupts before calling OSCtxSw().

Listing 3-5, OS_CPU.H, Task Level Context Switch


#define OS_TASK_SW() OSCtxSw()

C/OS-II and the Microchip dsPIC33

3.02.06 OS_CPU.H, Function Prototypes


As of V2.77, the prototypes for OSCtxSw(), OSIntCtxSw() and OSStartHighRdy() need to be placed in OS_CPU.H. In fact, it makes sense to do this since these are all port specific files.

Listing 3-8, OS_CPU.H, Function Prototypes


void void void OSCtxSw(void); OSIntCtxSw(void); OSStartHighRdy(void);

3.03

OS_CPU_C.C

A C/OS-II port requires that you write ten (10) fairly simple C functions: OSInitHookBegin() OSInitHookEnd() OSTaskCreateHook() OSTaskDelHook() OSTaskIdleHook() OSTaskStatHook() OSTaskStkInit() OSTaskSwHook() OSTCBInitHook() OSTimeTickHook() Typically, C/OS-II only requires OSTaskStkInit(). The other functions allow you to extend the functionality of the OS with your own functions.

IMPORTANT
You will also need to set the #define constant OS_CPU_HOOKS_EN to 1 in OS_CFG.H in order for the compiler to use the functions declared in this file.

3.03.01 OS_CPU_C.C, OSInitHookBegin()


This function is called by C/OS-IIs OSInit() at the very beginning of OSInit(). It gives the opportunity to add additional initialization code specific to the port. In this case, we initialize the global variable (global to OS_CPU_C.C) OSTmrCtr, which is used by the OS_TMR.C module if OS_TMR_EN is set to 1. Additionally, since C/OS-II does not currently support the stack checking feature of the dsPIC33, the SPLIM register is set to a maximum value in RAM for compiler libraries that pre-initialize the SPLIM register during the start-up code.

Listing 3-9, OS_CPU_C.C, OSInitHookEnd()


void OSInitHookBegin (void) { #if OS_TMR_EN > 0 OSTmrCtr = 0; #endif SPLIM } = 0xFFFE;

10

C/OS-II and the Microchip dsPIC33

3.03.02 OS_CPU_C.C, OSInitHookEnd()


This function is called by C/OS-IIs OSInit() at the very end of OSInit(). It gives the opportunity to add additional initialization code specific to the port. In this port, we stop measuring the interrupt disable time.

Listing 3-10, OS_CPU_C.C, OSInitHookEnd()


void OSInitHookEnd (void) { #if OS_CPU_INT_DIS_MEAS_EN > 0 OS_CPU_IntDisMeasInit(); #endif }

3.03.03 OS_CPU_C.C, OSTaskCreateHook()


This function is called by C/OS-IIs OSTaskCreate() or OSTaskCreateExt() when a task is created. OSTaskCreateHook() gives the opportunity to add code specific to the port when a task is created. In our case, we call an application hook defined within app.c or app_hooks.c. The placement of the application hooks is non critical and may vary depending on project developer.

Listing 3-11, OS_CPU_C.C, OSInitHookEnd()


#if OS_CPU_HOOKS_EN > 0 void OSTaskCreateHook (OS_TCB *ptcb) { #if OS_APP_HOOKS_EN > 0 App_TaskCreateHook(ptcb); #else (void)ptcb; #endif } #endif

11

C/OS-II and the Microchip dsPIC33

3.03.04 OS_CPU_C.C, OSTaskStkInit()


Recall (from the C/OS-II documentation) that a task is declared as shown in listing 3-12.

Listing 3-12, C/OS-II Task


void MyTask (void *p_arg) { /* Do something with p_arg, optional */ while (1) { /* Task body */ OSTimeDlyHMSM(0, 0, 1, 0); } }

The code in Listing 3-13 initializes the stack frame for the task being created. The task received an optional argument p_arg. Most compilers pass a single argument in register W0 and thus, p_arg is passed in W0 when the task is created. The Task Body MUST call either one of the OS???Pend() functions or OSTimedly??() functions. In other words, a task MUST always be waiting for an event to occur. An event can be the reception of a signal or a message from another task or ISR. An event can also be to wait for the passage of time.

12

C/OS-II and the Microchip dsPIC33

Listing 3-13, OS_CPU_C.C, OSTaskStkInit()


OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt) { INT16U x; INT8U pc_high; pc_high = *ptos++ *ptos++ *ptos++ = = = 0; (OS_STK)task; (OS_STK)pc_high; (OS_STK)task; // Push the PC (Starting Address for the new task) // Upper byte word of start address // Simulate an Interrupt, Push the PC // Set SR to enable all interrupts // Check the interrupt level mask bit IPL3, == 1? // Save the value of IPL3 in x, bit 7.

x = 0; if (CORCONbits.IPL3) { x |= 0x0080; } *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ *ptos++ }

= (OS_STK)(x | (INT16U)pc_high); // Push the SR Low, CORCON IPL3 and PC (22..16) = x; = = = = = = = = = = = = = = = (OS_STK)p_arg; 0x1111; 0x2222; 0x3333; 0x4444; 0x5555; 0x6666; 0x7777; 0x8888; 0x9999; 0xAAAA; 0xBBBB; 0xCCCC; 0xDDDD; 0xEEEE; // Push SR Low and CORCON IPL3 on to the stack // // // // // // // // // // // // // // // W0, Push a dummy argument on to the stack W1 W2 W3 W4 W5 W6 W7 W8 W9 W10 W11 W12 W13 W14

= ACCAL; = ACCAH; = ACCAU; = ACCBL; = ACCBH; = ACCBU; = TBLPAG; = PSVPAG; = RCOUNT; = = = = = DCOUNT; DOSTARTL; DOSTARTH; DOENDL; DOENDH;

// Accumulator A // Accumulator A // Accumulator A // Accumulator B // Accumulator B // Accumulator B // Table Page // Program Space Visibility Page Address // Repeat Loop Counter // // // // // Do Do Do Do Do Loop Loop Loop Loop Loop Counter Start Address Start Address End Address End Address

= 0; = CORCON;

// Status Register (contents irrelevant) // Core Configuration Register // Return the address to the top of the stack

return (ptos);

13

C/OS-II and the Microchip dsPIC33 Figure 3-1 shows how the stack frame is initialized for each task when its created.

Low Memory PC [15:0] (TaskAddr) PC [22:16](TaskAddr) PC [15:0] (TaskAddr) SR Low | IPL3 | PC [22:16] W0 W1 W2 W3 W4 W5 W6 W7 W8 W9 W10 W11 W12 W13 W14 = p_arg = 0x1111 = 0x2222 = 0x3333 = 0x4444 = 0x5555 = 0x6666 = 0x7777 = 0x8888 = 0x9999 = 0xAAAA = 0xBBBB = 0xCCCC = 0xDDDD = 0xEEEE ACCAL ACCAH ACCAU ACCBL ACCBH ACCBU TBLPAG PSVPAG RCOUNT DCOUNT DOSTARTL DOSTARTH DOENDL DOENDH SR (Initialized to 0) CORCON ptos (Top Of Stack) High Memory Stack Bottom

Simulated Interrupt

Figure 3-1, The Initial Stack Frame for each Task.


When the task is created, the final value of ptos is placed in the OS_TCB of that task by the C/OS-II function that calls OSTaskStkInit() (i.e. OSTaskCreate() or OSTaskCreateExt()).

14

C/OS-II and the Microchip dsPIC33

3.03.05 OS_CPU_C.C, OSTaskSwHook()


OSTaskSwHook() is called when a context switch occurs. This function allows the port code to be extended and do things such as measuring the execution time of a task, output a pulse on a port pin when a context switch occurs, etc. In our case, we call an application hook defined within app.c or app_hooks.c. The placement of the application hooks is non critical and may vary depending on project developer.

Listing 3-14, OS_CPU_C.C, OSCtxSwHook()


#if OS_CPU_HOOKS_EN > 0 void OSTaskSwHook (void) { #if OS_APP_HOOKS_EN > 0 App_TaskSwHook(); #endif } #endif

3.03.06 OS_CPU_C.C, OSTimeTickHook()


OSTimeTickHook() is called at the very beginning of OSTimeTick(). This function allows the port code to be extended. Much like the other hooks, this function calls a user defined application hook placed within app.c or app_hooks.c depending on the preference of the project developer. Additionally, OSTimeTickHook() determines whether its time to update the C/OS-II timers. This is done by signaling the timer task.

Listing 3-15, OS_CPU_C.C, OSTimeTickHook()


void OSTimeTickHook (void) { #if OS_VIEW_MODULE > 0 App_TimeTickHook(); #endif #if OS_TMR_EN > 0 OSTmrCtr++; if (OSTmrCtr >= (OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC)) { OSTmrCtr = 0; OSTmrSignal(); } #endif }

15

C/OS-II and the Microchip dsPIC33

3.04

OS_CPU_A.S

A C/OS-II port requires that you write five fairly simple assembly language functions. These functions are needed because you normally cannot save/restore registers from C functions. The five functions are: OS_CPU_SR_Save() OS_CPU_SR_Restore() OSStartHighRdy() OSCtxSw() OSIntCtxSw() It should be noted that since the dsPIC33 has memory mapped registers, the first two functions, namely OS_CPU_SR_Save() and OS_CPU_SR_Restore() have been implemented as C macros. Since the remaining functions involve modifying the program counter, they have been implemented in assembly language in order to leverage off of the RETFIE instruction. The RETFIE instruction will restore the PC from data located on the current tasks stack. This is why an interrupt is simulated during task stack initialization.

3.04.01 OS_CPU_A.S, OSStartHighRdy()


OSStartHighRdy() is called by OSStart() to start running the highest priority task that was created before calling OSStart(). OSStart() sets OSTCBHighRdy to point to the OS_TCB of the highest priority task.

Listing 3-18, OSStartHighRdy()


_OSStartHighRdy: call _OSTaskSwHook mov #0x0001, w0 mov.b wreg, _OSRunning mov mov _OSTCBHighRdy, w0 [w0], w15
; (1) Call user defined task switch hook ; (2) Set OSRunning to TRUE

; (3) Resume Stack Pointer

OS_REGS_RESTORE retfie L3-18(1)

; (4) Restore Context ; (5) Run Task

Before starting the highest priority task, we call OSTaskSwHook() in case a hook function has been declared (see OS_CPU_C.C). The C/OS-II flag OSRunning is set to TRUE indicating that C/OS-II will be running once the first task is started. We then get the pointer to the tasks top-of-stack (was stored by OSTaskCreate() or OSTaskCreateExt()). See figure 3-1 (ptos is stored in the OS_TCB of the created task). We then pop all the registers from the tasks stack. This is done by calling an assembly language macro which is declared in OS_CPU_UTIL_A.S and will be described later. By executing a return from interrupt instruction, the dsPIC33 pops the PC and the STATUS register from the stack and thus, the dsPIC33 will start executing the tasks code.

L3-18(2) L3-18(3)

L3-18(4)

L3-18(5)

16

C/OS-II and the Microchip dsPIC33

3.04.02 OS_CPU_A.S, OSCtxSw()


The code to perform a task level context switch is shown below in pseudo-code. OSCtxSw() is called when a higher priority task is made ready to run by another task or, when the current task can no longer execute (e.g. it calls OSTimeDly(), OSSemPend() and the semaphore is not available, etc.). A task level context switch occurs when C/OS-II invokes the macro OS_TASK_SW() which, in the case of the dsPIC33 port, corresponds to calling OSCtxSw(). Normally, it is ideal to use a software interrupt or TRAP in order to enter OSCtxSw(), however, since a software interrupt is not available, code must adjust the stack frame in order to simulate an interrupt such that the RETFIE instruction may be used to restore the PC, SR Low, and CORCON IPL3.
OS_TASK_SW() OSCtxSw: Add SRL, IPL3 and the upper 7 bits of the return PC to simulate an ISR Save the CPU registers onto the old tasks stack; OSTCBCur->OSTCBStkPtr OSTaskSwHook(); OSPrioCur OSTCBCur SP = SP; = OSPrioHighRdy; = OSTCBHighRdy; = OSTCBHighRdy->OSTCBStkPtr; /* (1) */ /* (2) */ /* /* /* /* /* (3) (4) (5) (6) (7) */ */ */ */ */

Restore the CPU registers from the new tasks stack; Return from Interrupt;

/* (8) */ /* (9) */

The actual code for the task level context switch is shown in Listing 3-19.

Listing 3-19, OSCtxSw()


_OSCtxSw: mov.b sl btsc bset ior mov SRL, wreg w0, #8, w0 CORCON, #IPL3 w0, #7; w0, [--w15], w0 w0, [w15++] ; (1) Simulate an interrupt, save SRL and CORCON IPL3

OS_REGS_SAVE mov mov call mov mov mov.b mov.b mov _OSTCBCur, w0 w15, [w0] _OSTaskSwHook _OSTCBHighRdy, w1 w1, _OSTCBCur _OSPrioHighRdy, wreg wreg, _OSPrioCur [w1], w15

; (2) Save context of interrupted task ; (3) OSTCBCur->OSTCBStkPtr = SP ; (4) Call user defined task switch hook ; (5) OSPrioCur = OSPrioHighRdy ; (6) OSTCBCur = OSTCBHighRdy ; (7) SP = OSTCBHighRdy->OSTCBStkPtr ; (8) Restore context of interrupted task ; (9) Return from interrupt

OS_REGS_RESTORE retfie

17

C/OS-II and the Microchip dsPIC33

3.04.03 OS_CPU_A.S, OSIntCtxSw()


When an ISR (Interrupt Service Routine) completes, OSIntExit() is called to determine whether a more important task than the interrupted task needs to execute. If thats the case, OSIntExit() determines which task to run next and calls OSIntCtxSw() to perform the actual context switch to that task. You will notice that OSIntCtxSw() is identical to the second half of OSCtxSw(). The reason we have these as two separate functions is to simplify debugging. Specifically, if you wanted to set a breakpoint in OSIntCtxSw(), you would hit the breakpoint during a task level context switch (if OSIntCtxSw() was just a label in OSCtxSw()). Of course this would make debugging a bit difficult.

Listing 3-20, OSIntCtxSw()


_OSIntCtxSw:
call mov mov mov.b mov.b mov _OSTaskSwHook _OSTCBHighRdy, w1 w1, _OSTCBCur _OSPrioHighRdy, wreg wreg, _OSPrioCur [w1], w15 ; Call user defined task switch hook ; OSPrioCur = OSPrioHighRdy ; OSTCBCur ; W15 (SP) = OSTCBHighRdy = OSTCBHighRdy->OSTCBStkPtr

OS_REGS_RESTORE retfie

; Restore context of interrupted task ; Switch to the new context

3.04.04 OS_CPU_A.S, T2Interrupt()


Since the dsPIC uses a vectored interrupt controller, ALL ISRs must follow the following format. The example provided below is the actual OS Time Tick ISR for C/OS-II. Generally, it is common practice to initialize the OS Tick timer from C code (BSP.C) and service the interrupt in assembly (BSP_A.S). From assembly, a C ISR Handler function may be called in order to handle more complex operations. Note: T2Interrupt() is one of two possible OS Time Tick Interrupt vectors. The option to choose 1 of 2 possible timers to generate the OS Tick exists from within (BSP.H). If an alternate timer is selected the ISR code below will not run. See T4Interrupt below.
; 1) Save processor registers ; 2) Call OSIntEnter() or increment OSIntNesting ; 3) Check OSIntNesting. if OSIntNesting == 1, ; then save the stack pointer, otherwise jump ; to T2_Cont.

__T2Interrupt: OS_REGS_SAVE inc.b dec.b bra mov mov T2_Cont: call call _OSIntNesting _OSIntNesting, wreg nz, T2_Cont _OSTCBCur, w0 w15, [w0] _OS_Tick_ISR_Handler _OSIntExit

; 4) Call YOUR ISR Handler (May be a C function). ; In this case, the OS Tick ISR Handler ; 5) Call OSIntExit() ; 6) Restore registers

OS_REGS_RESTORE retfie

18

C/OS-II and the Microchip dsPIC33

3.04.05 OS_CPU_A.S, T4Interrupt()


Since the dsPIC uses a vectored interrupt controller, ALL ISRs must follow the following format. The example provided below is the actual OS Time Tick ISR for C/OS-II. Generally, it is common practice to initialize the OS Tick timer from C code (BSP.C) and service the interrupt in assembly (BSP_A.S). From assembly, a C ISR Handler function may be called in order to handle more complex operations. Note: T4Interrupt() is one of two possible OS Time Tick Interrupt vectors. The option to choose 1 of 2 possible timers to generate the OS Tick exists from within (BSP.H). If an alternate timer is selected the ISR code below will not run. See T2Interrupt above.
; 1) Save processor registers ; 2) Call OSIntEnter() or increment OSIntNesting ; 3) Check OSIntNesting. if OSIntNesting == 1, ; then save the stack pointer, otherwise jump ; to T2_Cont.

__T4Interrupt: OS_REGS_SAVE inc.b dec.b bra mov mov T4_Cont: call call _OSIntNesting _OSIntNesting, wreg nz, T4_Cont _OSTCBCur, w0 w15, [w0] _OS_Tick_ISR_Handler _OSIntExit

; 4) Call YOUR ISR Handler (May be a C function). ; In this case, the OS Tick ISR Handler ; 5) Call OSIntExit() ; 6) Restore registers

OS_REGS_RESTORE retfie

19

C/OS-II and the Microchip dsPIC33

3.05

OS_CPU_UTIL_A.S

OS_CPU_UTIL_A.S is an assembly language file that declares two macros used to save the context of the CPU. These macros are called OS_REGS_SAVE and OS_REGS_RESTORE and are declared as shown below.
.macro OS_REGS_SAVE push.d w0 push.d w2 push.d w4 push.d w6 push.d w8 push.d w10 push.d w12 push w14 push push push push push push push push push push push push push push push push .endm ACCAL ACCAH ACCAU ACCBL ACCBH ACCBU TBLPAG PSVPAG RCOUNT DCOUNT DOSTARTL DOSTARTH DOENDL DOENDH SR CORCON

.macro OS_REGS_RESTORE pop CORCON pop SR pop pop pop pop pop pop pop pop pop pop pop pop pop pop pop pop.d pop.d pop.d pop.d pop.d pop.d pop.d .endm DOENDH DOENDL DOSTARTH DOSTARTL DCOUNT RCOUNT PSVPAG TBLPAG ACCBU ACCBH ACCBL ACCAU ACCAH ACCAL w14 w12 w10 w8 w6 w4 w2 w0

20

C/OS-II and the Microchip dsPIC33

IMPORTANT
You MUST include a reference to this file when you write your ISRs (see section 4.00, Interrupt Handling). This is done by using the assembler directive #include as follows: .include os_cpu_util_a.s

3.06

OS_DBG.C

OS_DBG.C is a file that has been added in V2.62 to provide Kernel Aware debugger to extract information about C/OS-II and its configuration. Specifically, OS_DBG.C contains a number of constants that are placed in ROM (code space) which the debugger can read and display. Unfortunately, the MPLab debugger is not C/OS-II aware and thus this file is not needed but should be included in all builds for future reference.

21

C/OS-II and the Microchip dsPIC33

4.00

Interrupt Handling

The dsPIC33 contains an interrupt and exception vector table which contains up to 54 entries. Each of these entries point to an interrupt or exception handler. For C/OS-II, each of those interrupt handlers MUST be written in assembly language. In fact, only a portion must be in assembly language as shown in listing 4-1. Note that you ONLY need to change the portion in RED for your own ISR. The rest of the code is IDENTICAL from one ISR to the next. Of course, you will need to give a unique name to your ISRs. Additionally, every tool-chain vendor has a unique method for placing interrupt code and setting interrupt vector table entries. At the time of this writing, the following methods are employed by each of the three tool-chain vendors supported. You may use bsp_a.s as an interrupt example if necessary. C30: .text .global __ISR_Name __ISR_Name: code (1) (2) (3) (4)

1. Place the remaining code below this point within the TEXT section 2. Prototype the name of the ISR Handler. Note that the ISR handler name has two underscores. One provided by the ISR routine programmer, and another by Microchip as part of the ISR naming convention. 3. Declare the ISR with the name specified in the Microchip MPLAB C30 Users Guide. (Document DS51284F, Page 100, table 7-1). This will tell the compiler to add the constant ISR address to the vector table by means of adding carefully placed constant data to the application binary. 4. Include the ISR code under the ISR name label. dsPICC: GLOBAL psect ORG DDW psect __ISR_Name vectors,class=VECTORS,delta=2 01Ch __ISR_Name text,global,reloc=4,class=CODE,delta=2 (1) (2) (3) (4) (5) (6) (7)

__ISR_Name: code

1. Prototype the name of the ISR. 2. Specify the placement of the defined double word (4) to be within the VECTORS section. 3. Specify the address to place ISR vector address. This value must be the vector address as specified in the dsPIC33 datasheet, minus six. The subtraction of six is necessary since ORG directive is NOT absolute and is relative to the current section (VECTORS) which starts from an offset of 0x06 in the memory map. In this case 01Ch represents vector address 0x22, the Timer2 ISR vector address. 4. Declare a double word representing the address of the ISR to be placed in the specified address. 5. Place the remainder of the code within the file in the CODE segment. 6. Declare the ISR with the name of your choice. 7. Include the ISR code under the ISR name label.

22

C/OS-II and the Microchip dsPIC33 iccDSPIC: COMMON INTVEC:CODE:ROOT(2) ORG __ISR_Address * 2 DL __ISR_Name / 2 RSEG CODE:CODE:ROOT(2) PUBLIC __ISR_Name

(1) (2) (3) (4) (5) (6) (7)

__ISR_Name: code

1. Place the defined long data word in the INTVEC section. 2. Specify the address to store the following long word (24 bit). This must be the ISR vector address as specified in the datasheet, multiplied by two. 3. Declare a long word whos value is that of the ISR_Handler defined within the same file below. The address to be placed in the above memory address must be that of the ISR_Name divided by two. Additional vectors may be declared at this time before step 4. 4. Place the remainder of the code within the file in the CODE segment. 5. Prototype the ISR Name. Note that the ISR name has two underscores appended to the beginning. This is to maintain consistency with the C30 compiler port, but is not required. 6. Declare the ISR with the name of your choice. 7. Include the ISR code under the ISR name label.

23

C/OS-II and the Microchip dsPIC33

Listing 4-1, Assembly Language ISR


_My_ISR: OS_REGS_SAVE inc.b dec.b bra mov mov _My_ISR_1: call _OSIntNesting _OSIntNesting, wreg nz, _My_ISR_1 _OSTCBCur, w0 w15, [w0] ; 1) Save processor registers ; 2) Call OSIntEnter() or increment OSIntNesting ; 3) Check OSIntNesting. if OSIntNesting == 1, ; then save the stack pointer, otherwise jump ; to _My_ISR_1

_My_ISR_Handler

; 4) Call YOUR ISR Handler (May be a C function). ; You MUST clear the interrupt source either ; from within, or just after your ISR Handler. ; 5) Call OSIntExit() ; 6) Restore registers ; 7) Return to the interrupted task

call

_OSIntExit

OS_REGS_RESTORE retfie

L4-1(1) L4-1(2) L4-1(3)

You then MUST save all of the registers using the OS_REGS_SAVE macro. You MUST increment C/OS-IIs interrupt nesting counter (OSIntNesting) You MUST check to see whether this is the first nested ISR by checking if OSIntNesting got incremented to 1. If this is the first nested ISR level then you MUST save the stack pointer into the current tasks OS_TCB. You can now call your actual ISR handler which could be written in C. You dont have to write the handler in C but its generally more readable and portable. You may optionally choose to clear the interrupt source from within your ISR handler, or you may do it before calling OSIntExit() from assembly. When you are done handling the ISR (i.e. the code returns from My_ISR_Handler()), you MUST call OSIntExit(). OSIntExit() checks to see if this is the last nested ISR. If it is then OSIntExit() checks to see if a more important task has been made ready-to-run by the ISR (or any other nested ISRs). If a more important task is ready-to-run, OSIntExit() doesnt return but instead context switches to the more important task. If the interrupted task is still the most important task to run then OSIntExit() returns and we simply need to restore the saved CPU registers in order to return to the interrupted task. To restore the registers, you MUST invoke the OS_REGS_RESTORE macro. The RETFIE instruction MUST be executed to return program execution back to the interrupted task in the event that OSIntExit()does not force a context switch.

L4-1(4)

L4-1(5)

L4-1(6)

L4-1(7)

The pseudo-code for the C ISR handler is shown in Listing 4-2.

24

C/OS-II and the Microchip dsPIC33

Listing 4-2, C ISR Handler


void My_ISR_Handler (void) { /* (1) Dont forget to clear the interrupt source */ /* (2) Enable interrupts if you want to allow nested interrupts */ /* (3) Handle the interrupt using C */ /* (4) Disable interrupts (if you enabled them) */ }

L4-2(1)

Dont forget to clear the interrupting device (i.e. acknowledge that you serviced the interrupt). Failure to do this will cause the ISR to be re-entered which may not be what you want. As indicated, you may enable interrupts (by lowering the IPL level in the SR) if you want to allow nested interrupts. You can now service the interrupting device using the C programming language (instead of doing that in assembly language). If you disabled interrupts (see step #1) then you should disable them before returning to the caller of this function.

L4-2(2)

L4-2(3)

L4-2(4)

25

C/OS-II and the Microchip dsPIC33

5.00

Application Code

Your application code can make use of the port presented in this application note as described in this section. Figure 5-1 shows a block diagram of the relationship between your application, C/OS-II, the C/OS-II port, the BSP (Board Support Package), the dsPIC CPU and the target hardware.

Your Application
Section 5
APP.C APP.H APP_CFG.H INCLUDES.H OS_CFG.H

C/OS-II
C/OS-II Book
OS_CORE.C OS_FLAG.C OS_MBOX.C OS_MEM.C OS_MUTEX.C OS_Q.C OS_SEM.C OS_TASK.C OS_TIME.C OS_TMR.C uCOS II.H

C/OS-II
Section 3

dsPIC Port
OS_CPU_C.C OS_CPU_A.ASM OS_CPU_I.ASM OS_CPU.H OS DBG.C

BSP
BSP.C BSP.H

Section 6

Section 2

Explorer 16 / Target Board


Figure 5-1, Relationship between modules.

26

C/OS-II and the Microchip dsPIC33

5.01

APP.C, APP.H and APP_CFG.H

For sake of discussion, your application is placed in files called APP.C, APP.H and APP_CFG.H. Of course, your application (i.e. product) can contain many more files. APP.C would be where you would place main() but, of course, you can place main() anywhere you want. APP.H contains #define constants, macros, prototypes, etc. that are specific to your application. This file may NOT exist in YOUR application and it is not included in examples provided for the PIC. APP_CFG.H contains #define constants to configure the application. We placed task stack sizes task priorities and other #defines in this file. This allows you to locate task priorities and sizes in one place. APP.C is a standard test file for C/OS-II examples. The two important functions are main() (listing 5-1) and AppStartTask() (listing 5-2).

Listing 5-1, main()


void main (void) { INT8U err; BSP_IntDisAll(); OSInit(); OSTaskCreateExt(AppStartTask, (void *)0, (OS_STK *)&AppStartTaskStk[TASK_STK_SIZE-1], TASK_START_PRIO, TASK_START_PRIO, (OS_STK *)&AppStartTaskStk[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); #if OS_TASK_NAME_SIZE > 11 OSTaskNameSet(TASK_START_PRIO, "Start Task", &err); #endif OSStart(); } (1) (2) (3)

(4) (5)

L5-1(1)

A BSP function called BSP_IntDisAll() is called to disable ALL interrupts. You would typically prevent the interrupt controller from issuing interrupts until your application is ready to service them. As with all C/OS-II based applications, you need to initialize C/OS-II by calling OSInit(). You need to create at least one task. In this case, we created the task using the extended task create call. This allow C/OS-II to have more information about your task. We can now give a name for our task. In order to start multitasking, you need to call OSStart(). Note that OSStart() will not return from this call.

L5-1(2)

L5-1(3)

L5-1(4) L5-1(5)

27

C/OS-II and the Microchip dsPIC33

Listing 5-2, AppStartTask()


static void AppStartTask (void *p_arg) { (void)p_arg; BSP_Init(); #if OS_TASK_STAT_EN > 0 OSStatInit(); #endif (1) (2)

#if (uC_PROBE_OS_PLUGIN > 0) || (uC_PROBE_COM_MODULE > 0) AppProbeInit(); #endif AppTaskCreate(); while (TRUE) { for (i = 0; i < 4; i++) { for (j = 1; j <= 8; j++) { LED_On(j); OSTimeDlyHMSM(0, 0, 0, 25); LED_Off(j); } for (j = 7; j >= 2; j--) { LED_On(j); OSTimeDlyHMSM(0, 0, 0, 25); LED_Off(j); } } for (i = 0; i < 4; i++) { LED_On(0); OSTimeDlyHMSM(0, 0, 0, 25); LED_Off(0); OSTimeDlyHMSM(0, 0, 0, 25); } } } (3) (4)

(5)

L5-2(1)

If you decided to implement a BSP (see section 6, Board Support Package) for your target board, you would initialize it here. If you enabled the statistic task by setting OS_TASK_STAT_EN in OS_CFG.H to 1) then, you need to call it here. Please note that you need to make sure that you initialized and enabled the C/OS-II clock tick because OSStatInit() assumes the presence of clock ticks. In other words, if the tick ISR is not active when you call OSStatInit(), your application will end up in C/OS-IIs idle task and not be able to run any other tasks. At this point, you can create additional tasks. We decided to place all our task initialization in one function called AppTaskCreate() but, you are certainly welcome to use a different technique. You can now perform whatever additional function you want for this task. We decided to toggle an LED by calling a BSP function called LED_Toggle(). It is in fact these nested loops that perform the LED sweep functionality provided in many of our example applications. Each of your tasks MUST invoke one of the C/OS-II functions that will wait for an event to occur. We decided to use OSTimeDlyHMSM() which suspends the task for a specified amount of time.

L5-2(2)

L5-2(3)

L5-2(4)

L5-2(5)

28

C/OS-II and the Microchip dsPIC33

5.02

INCLUDES.H

INCLUDES.H is a master include file and is found at the top of all .C files. INCLUDES.H allows every .C file in your project to be written without concern about which header file is actually needed. The only drawbacks to having a master include file are that INCLUDES.H may include header files that are not pertinent to the actual .C file being compiled and the compilation process may take longer. These inconveniences are offset by code portability. You can edit INCLUDES.H to add your own header files, but your header files should be added at the end of the list. Listing 5-3 shows the typical contents of INCLUDES.H. Of course, you can add your own header files as needed.

Listing 5-3, INCLUDES.H


#ifndef #define #include #include #include #include #include #include #include #include #include #include #include INCLUDES_H INCLUDES_H <stdio.h> <string.h> <ctype.h> <stdlib.h> <p33FJ256GP710.h> <cpu.h> <ucos_ii.h> <bsp.h> <lib_def.h> <lib_str.h> <lib_mem.h> /* Header file name varies with tool-chain */

#if (uC_PROBE_OS_PLUGIN > 0) #include <os_probe.h> #endif #if (uC_PROBE_COM_MODULE > 0) #include <probe_com.h> #if (PROBE_COM_METHOD_RS232 > 0) #include <probe_rs232.h> #endif #endif #endif /* End of File */

5.03

OS_CFG.H

Every C/OS-II requires that you configure the RTOS for your own application. The configuration of C/OS-II allows to specify how many tasks your application will have, how many semaphores (if any), how many message queues (if any), etc. Configuring C/OS-II allows C/OS-IIs footprint to be only as big as it needs to be.

29

C/OS-II and the Microchip dsPIC33

6.00

BSP (Board Support Package)

It is often convenient to create a Board Support Package (BSP) for your target hardware. A BSP could allow you to encapsulate the following functionality: Timer initialization (OS Ticker and View) PLL Initialization ISR Handlers LED control functions Reading switches Setting up communication channels Etc. A BSP consist of 2 files: BSP.C and BSP.H. For example, because a number of evaluation boards are equipped with LEDs, we decided to create LED control functions as follows: void void void void LED_Init(void); LED_On(INT8U led); LED_Off(INT8U led); LED_Toggle(INT8U led);

In this case, LEDs are referenced logically instead of physically. When you write the BSP, you determine which LED is LED #1, which is LED #2, etc. When you want to turn on LED #1, you simply call LED_On(1). If you want to toggle LED #2, you simply call LED_Toggle(2). In fact, you can (and should) associate names to your LEDs using #defines. You could thus specify LED_Off(LED_PM). Where LED_PM could be defined as LED #2. Each BSP should contain a BSP initialization function. We called ours BSP_Init() and should be called by your application code. We decided to encapsulate the C/OS-II clock tick ISR handler and its initialization function in the BSP because they ideally belong in your application code and not as part of C/OS-II. Doing this makes it easier to adapt the C/OS-II port to different target hardware since you could simply change the BSP to select whichever timer or interrupt source for the clock tick that your application requires. The clock tick ISR is in BSP_A.S which in turn calls the OS_Tick_ISR_Handler() that is found in BSP.C.

30

C/OS-II and the Microchip dsPIC33

7.00

Conclusion

This application note presented a generic port for the dsPIC33 processor. Of course, if you use C/OS-II and use the port on actual hardware, you will need to initialize and properly handle hardware interrupts.

31

C/OS-II and the Microchip dsPIC33

Licensing
If you intend to use C/OS-II in a commercial product, remember that you need to contact Micrim to properly license its use in your product. The use of C/OS-II in commercial applications is NOT-FREE. Your honesty is greatly appreciated.

References
MicroC/OS-II, The Real-Time Kernel, 2nd Edition Jean J. Labrosse CMP Technical Books, 2002 ISBN 1-5782-0103-9

Contacts
CMP Books, Inc. 6600 Silacci Way Gilroy, CA 95020 USA Phone Orders: 1-800-500-6875 or 1-408-848-3854 Fax Orders: 1-408-848-5784 e-mail: [email protected] WEB: http://www.cmpbooks.com Micrim 949 Crestview Circle Weston, FL 33327 USA 954-217-2036 954-217-2037 (FAX) e-mail: [email protected] WEB: www.Micrium.com Microchip Technology Inc. 2355 West Chandler Blvd. Chandler, Arizona 85224-6199 USA 480-792-7200 WEB: www.MicroChip.com

32

You might also like