142
UNIVERSITY OF GLAMORGAN FACULTY OF ADVANCED TECHNOLOGY Student: Mr Khaled Sobaihi Email: [email protected] Course name: Electronic Products Design Student Signature: Supervisor Signature: Academic Year: 2006-2007

Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

  • Upload
    skalyd

  • View
    240

  • Download
    1

Embed Size (px)

Citation preview

Page 1: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

UNIVERSITY OF GLAMORGAN

FACULTY OF ADVANCED TECHNOLOGY

Student: Mr Khaled Sobaihi

Email: [email protected]

Course name: Electronic Products Design

Student Signature: Supervisor Signature:

Academic Year: 2006-2007

Page 2: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Page 1

ABSTRACT

The Renesas M16C 16 bit microcontrollers are used extensively throughout our

undergraduate and MSc courses. The main aim of this project consists of porting the

µC/OS-II Multitasking Real-Time Kernel to the Renesas M16C microcontrollers, which

means make this RTOS operational on these microcontrollers, so it can be used by the

students to develop multitask real time applications.

The next step requires developing a Real Time Data Monitor, based on the ported

µC/OS-II; this application makes a demonstration tool to show the power and the

benefits of this RTOS. To make the demonstration more attractive the Kernel Monitor of

the µC/OS-II called µC/OS-View will be ported to the M16C microcontrollers, this viewer

will show what is happening inside the µC/OS-II in real time.

As long the µC/OS-II will be tested on the MSA0654 development board, the different

peripherals drivers of this board need to be written and should be compatible with this

RTOS regarding the used compiler.

Page 3: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Page 2

ACKNOWLEDGEMENTS

This project would not have been possible without the guidance and

support of my supervisor, and the people who participated to make the

project succeed

Page 4: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Introduction

Page 3

INTRODUCTION

Modern control systems applications are often built on top of a real time operating system

(RTOS) which provides the multitask scheduling and the necessary hardware abstraction and

other services as well. Several open source RTOS solutions are publicly available, which is very

attractive, both from an economic (low licensing fees) as well as from technical (control over

the source code) point of view. [8]

The µC/OS-II is priority based preemptive multitasking kernel, very scalable and highly portable,

this RTOS has been ported to hundreds of microcontrollers, in addition, µC/OS-II is very simple

to use and to implement but very effective in terms of to price/performance ratio.

The aim of this project consists of porting the µC/OS-II to MSA0654MEAUST development

board and interfacing daughter board which is widely used throughout the undergraduate and

MSc courses. To achieve this goal, the µC/OS-II should be first ported to the M16C62P

microcontroller that makes the core of the MSA0654 development board. And secondly,

various peripherals’ drivers should be written to be used for any µC/OS-II application that

targets this board.

In order to finalize this project and make under test the ported µC/OS-II, a Data Monitor

application will be developed to demonstrate the multitasking mechanism and real time

responsiveness of the µC/OS-II. In addition, different features of the µC/OS-II (inter-task

communication and synchronization, memory management, etc.) will be used in this

application.

Page 5: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 4

CHAPTER I

Real Time Operating System (RTOS)

INTRODUCTION

This chapter will introduce brief notions of the operating system and especially the real

time operating system (RTOS), in addition, we will make brief comparison between using

the traditional programming and use the RTOS platform to develop a real time application.

1. The Operating System

An operating system (OS) is the program that, after being initially loaded into the computer

by a boot program, manages all the other programs in a computer. The other programs are

called applications or application programs. The application programs make use of the

operating system by making requests for services through a defined application program

interface (API). In addition, users can interact directly with the operating system through a

user interface such as a command language or a graphical user interface (GUI). [9]

Page 6: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 5

Operating systems can be classified as follows:

Multi-user: Allows two or more users to run programs at the same time. Some

operating systems permit hundreds or even thousands of concurrent users.

Multiprocessing: Supports running a program on more than one CPU.

Multitasking: Allows more than one program to run concurrently.

Multithreading: Allows different parts of a single program to run concurrently.

Real time: Responds to input instantly. General-purpose operating systems, such as

DOS and UNIX, are not real-time.

2. Real Time Operating System

The Real Time Operating System and embedded systems operate in constrained

environments in which computer memory and processing power are limited. They must

provide their services within strict time deadlines to their users and to the surrounding

world to which they interface. It is these memory, speed and timing constraints that dictate

the use of real-time operating systems in embedded software. [6]

The kernel of the Real Time Operating System (RTOS) provides an "abstraction layer” that

hides from application software the hardware details of the processor upon which the

application software will run. In doing so, it supplies five main categories of basic services

to application software. Figure 1.1 shows these services.

Page 7: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 6

Figure 1.1 Basic services provided by a Real Time Operating System

The Task Management is the most important service provided by the RTOS, this allows to

software developers to design their application as a number of separate tasks each one

handles a distinct topic with its own deadline. In parallel, the RTOS provides the tasks

scheduling regarding the tasks’ priorities during the runtime of the embedded system.

The second service is inter-task communication and synchronization, this service allows

the tasks to exchange information without danger to be corrupted, in addition, the tasks

can productively synchronize their activities or cooperate between each other, without the

help of these RTOS services, tasks might well communicate corrupted information or

otherwise interfere with each other. [6]

The third service is the Timing service; this service includes the task delay functions and

Timers timeouts.

Some RTOS provide the dynamic allocation of the memory, in this case a memory partition

can be used and reused many times by different tasks to store large size of data as long it

can be allocated and freed in runtime. Some RTOS provides also Device I/O which provides

a uniform framework for organizing and accessing the many hardware device drivers that

are typical of an embedded system. [6]

Task Management

Intertask communication & synchronization

Dynamic memory allocation

Timers

Device I/O Supervisor

Page 8: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 7

In addition of these basic services, RTOS can offer other optional services such as: File

system management, Network communication, Database management, User Interface

graphic etc.

2.1. Multitasking

The multitasking concept was born from the observation that computers spent much of

their time waiting for slow peripheral devices to either store or retrieve data; this leads to

misusing the power of the processors as long waiting for an I/O is unproductive time. [2]

Multitasking is the process of scheduling and switching the CPU (Central Processing Unit)

between several tasks; a single CPU switches execution from one task to another to ensure

each task is given processing time when the respective task needs the CPU according to the

task priority.

Multitasking is like foreground/background with multiple backgrounds. Multitasking

maximizes the utilization of the CPU and also provides for modular construction of

applications. One of the most important aspects of multitasking is that it allows the

application programmer to manage complexity inherent in real-time applications.

Application programs are typically easier to design and maintain if multitasking is used. [1]

The multitasking is done by the kernel which is the principal part of an operating system

that provides the most basic services to application software running on the processor.

According to the manner in which the multitasking is achieved, we distinguish two types of

real time kernels:

2.2. Non-Preemptive Kernel

Called also Cooperative Multitasking, In this case the tasks cooperate with each other to

share the CPU, hence, each task runs until it decides to gives up voluntary the CPU to

another task, in addition, when the interrupt service routine (ISR) interrupts the current

Page 9: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 8

running task it returns to the same task after accomplished, in this case this interrupt can

be used to make the next task ready to run. Figure 1.2 shows the mechanism of the kernel.

The advantage of the non-preemptive kernel is the safety when using the shared variables

or non re-entrant functions (non re-entrant functions don’t allow to be called more than

one task before the first caller achieve using this function), in this case there is no risk of

corrupting the shared variables as long each task gives up the CPU only when finished

manipulating the shared variables or when returns from a non re-entrant function.

The most disadvantage of the non-preemptive kernel is their responsiveness, where the

highest priority that has been made ready to run may wait for long time to run, waiting the

current running task to give up the CPU.

Page 10: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 9

Figure 1.2 Non-Preemptive kernel

2.3. Preemptive Kernel

In this kernel the highest priority ready task is immediately re-launched when an interrupt

occurs or when the current running task enters in waiting state, therefore, upon

completion of an ISR, the kernel will resume execution to the highest priority task ready to

run (not the interrupted task). Most of commercial RTOS use the preemptive kernel;

therefore, the RTOS based on this kernel will be the subject of this thesis.

The advantage of this multitasking kernel is that the execution of the highest priority task

ready to run is deterministic which leads to the best responsiveness time.

In contrast, we should be careful when using the shared variables or non-reentrant

functions, to avoid any corruption that may occur the interrupts should be disabled before

manipulating such variables. Figure 1.3 shows the principle of this kernel.

Task#1 Low

priority

Task#2 High

priority

ISR Time

Task #1 interrupted by an ISR

Task #1 resumed after completion of

the ISR

Task #1 gives control of the CPU

to Task #2

Task #1 gives back control to Task #2

Page 11: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 10

Figure 1.3 Preemptive kernel

3. Context Switch

The Context Switch called also Task Switch is achieved by the kernel when decides to give

control to another task, which starts by saving the current task’s context (CPU registers)

into the current task’s task, each task has its own stack area in memory. When this

operation performed the kernel restores the context of the highest priority task ready to

run into the CPU registers and gives control to this task by executing the return from

interrupt instruction (REIT in case of M16C assembly language). The top pointer of each

task’s stack is stored in a structure called Task Control Block (TCB) owned by each task,

along with other information such as priority, name, next TCB pointer, previous TCB

pointer, etc.

Figure 1.4 illustrates the context switch routine by taking an example of the M16C CPU

registers, when the Context Switch routine is called; which starts by storing all the registers

onto stack memory area of Task #1 using the PUSHM instruction, after that it assigns the

stack’s pointer of the high priority ready task to the stack pointer CPU register (ISP), the

Task#1 Low

priority

Task#2 High

priority

ISR Time

Task #1 interrupted by an ISR

The highest priority ready Task #2 resumed after

completion of the ISR

Kernel gives control to Task#2 when

Task#1 waiting for an event

Page 12: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 11

next step will restore the registers of this task to the CPU registers by using the POPM

instruction.

Figure 1.4 Context Switch mechanism (M16C Example)

/* Save all the registers into current Task’s Stack */ PUSHM R0,R1,R2,R3,A0,A1,SB,FB /* Assign the ready task’s stack pointer to stack pointer register (ISP) */ MOV.W OSTCBHighRdy, A0 LDC [A0], ISP /* Restore all register to the next Task’s Stack */ POPM R0,R1,R2,R3,A0,A1,SB,FB /* return from interrupt leads to resume task #2 */ REIT

Task#1 Low

priority

Task#2 High

priority

Context Switch routine Time

Page 13: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 12

4. RTOS Vs Infinite Loop

An example is the best way to demonstrate the benefits brought by the real time operating

system (RTOS) against the traditional infinite loop. Let’s take an example of a data logger

system; this example usually includes ADC converter, Keypad, LCD Display and RS232

Serial communication.

In the case of infinite loop each function is executed to completion, in this case many

configurations can be designed; the simplest solution is calling each function one by one

within the infinite loop, the first function gives control to the second function when

achieved, executing all functions in one loop iteration will take too long time by calling

functions that don’t need to be called,

More intelligence can be introduced by using the state machine; this method will make

each loop shorter by calling one function by loop iteration, the loop can be called within

short regular intervals by using a Timer interrupt, this timer should short enough to ensure

that function gets called at frequency the meets its timing requirements, this method can

be improved by calling more frequently the function that has the high priority.

In the other hand, using the RTOS platform begins by creating each task that performs the

desired function, when the RTOS starts running, the CPU switches between all tasks,

therefore, it seems that tasks are executed in same time. Table bellow makes structural

comparison when using an infinite loop and the RTOS.

Page 14: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 13

Table 1.1 Using or not using RTOS structure comparison Without using RTOS Using RTOS

void TimerInterrupt(void) { TimerExpire = true; } void main(void) { State = 0; while(1) { if (TimerExpire){ Switch(State){ case 0: if (KeyPressed) ScanKeyPad(); State = 1;

break;

case 1: if (CharAvailable) ProcessRS232(); State = 2; break; case 2: if (AdcReady) ProcessAdc(); State = 0; break; } TimerExpire = false; } else /* do something else */ } }

void ScanKeyPadTask() { while(1){ /* ScanKeyPadTask Code goes here */ Delay(); }} void ProcessRS232Task() { while(1){ /* ProcessRS232Task Code goes here */ Delay(); }} void ProcessADCTask() { while(1){ /* ProcessADCTask Code goes here */ Delay(); }} void main(void) { OSCreateTask(ScanKeyPadTask, KeyPadPrio); OSCreateTask(ProcessRS232Task, RS232Prio); OSCreateTask(ProcessADCTask, ADCPrio); /* Run the RTOS */ OSRun(); }

When looking at both structures, the first thing we note is the total independences between

tasks in case of RTOS structure, in contrast, we can see the interference that exists between

task’s functions in case of infinite loop, this will lead to an important inertia to execute each

function as long the previous should be entirely executed.

Take an example if the user is typing command on the keypad, at this time an Analog input

comes ready to be converted, this analog sample will be missed as long the infinite loop is

trapped by the ScanKeyPad() function, this problem can be solved by using a complex

Page 15: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

CHAPTER I Real Time Operating System (RTOS)

Page 14

interrupt service routine for each function (Keypad ISR, Serial UART ISR, ADC ISR etc… ),

therefore, using an important number of interrupts would increase the system latency,

also, using such numbers of interrupts push to create a task scheduling to give each

interrupt its optimal priority, in this case the system gets closer to an RTOS.

In contrast case of using the RTOS, the user can continue uses the keypad while the

analogue samples are converted, this is the result of the high frequency tasks switching

that makes like all tasks are running in same time.

CONCLUSION

The RTOS provides an excellent and reliable solution to handle events within a rigorous

deadline, especially when there are many events and tasks to manage with timing

constraints. In addition, the RTOS provides additional features, such as Task’s priority,

semaphores to access to shared resources, Mailboxes and Messages Queues for inter-task

communication, etc.

Page 16: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 15

CHAPTER II

µC/OS-II Real Time Operating System

INTRODUCTION

µC/OS-II (Micro-Controller Operating System Version II) is an open source preemptive

multitasking real time operating system mainly intended for embedded systems written in

ANSI C. Highly portable, this RTOS can be ported on various microcontrollers. It is also very

robust and reliable and suitable for use in safety critical systems common to aviation and

medical products.

This chapter will introduce the latest version of µC/OS-II V2.83, its multitasking strategy

and how it can be used to develop a multitasking application. We will then focus on

different features provided by this RTOS such as semaphores, mailboxes, memory

management etc.

1. Context Switch in µC/OS-II

The kernel of the µC/OS-II performs the context switching or task switch in two levels, the

first one is the task level context switch done by OSCtxSw() function, the second is the

interrupt level task switch done by OSIntCtxSw() function.

Page 17: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 16

1.1. Task Level Context Switch

The kernel calls the task level context switch function OSCtxSw() when the current running

task enters in the waiting state or when suspended or even deleted by itself. Listing 2.1

shows the pseudo code of this function, the context switch function carries out the

following steps:

Save the CPU registers onto the current task stack;

Save the stack pointer in the current TCB stack pointer;

Call the user definable function OSTaskSwHook(), this function is called to inform the

user’s application whenever a context switch occurs, this function is useful to monitor task

switching;

Assign the TCB of the highest priority task and ready to run to the current task TCB, and

assign its priority to the current task priority;

Get the stack pointer of the task to resume, which is the stack pointer of the highest

priority ready task;

Reload the saved registers of this task onto the CPU registers;

To resume this task, we simply call the return from interrupt instruction.

void OSCtxSw(void) { Save processor registers; Save the current task’s stack pointer into the current task’s OS_TCB: OSTCBCur->OSTCBStkPtr = Stack pointer; Call user definable OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; Get the stack pointer of the task to resume: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; Restore all processor registers from the new task’s stack; Execute a return from interrupt instruction; }

Listing 2.1 OSCtxSw() routine Pseudo code

Page 18: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 17

1.2. Interrupt Level Context Switch

The interrupt level context switch is performed by OSIntCtxSw(), this function is called by

OSIntExit() which determines the next ready task to run. The pseudo code of this function is

almost the same as OSCtxSw() exception in that, there is no need to save the CPU’s context

as long as it has been called from an ISR, this ISR will perform the saving of the context

when called. The listing 2.2 illustrates the pseudo code of this function, and we can

distinguish the following steps:

1) Save the stack’s pointer to the current task stack’s pointer;

2) Call the user definable function: OSTaskSwHook();

3) Transfer the TCB of the next ready to run task to the current TCB and assign its stack

pointer to CPU stack pointer;

4) Pop the current task registers to the CPU registers;

5) Execute the return from interrupt instruction which resume the current task.

void OSIntCtxSw(void) { Save the current task’s stack pointer into the current task’s OS_TCB: OSTCBCur->OSTCBStkPtr = Stack pointer; Call user definable OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; Get the stack pointer of the task to resume: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; Restore all processor registers from the new task’s stack; Execute a return from interrupt instruction; }

Listing 2.2 OSIntCtxSw() Pseudo code

1.3. µC/OS-II Tasks States

Figure 2.1 shows the five states that can be taken by a task, at the beginning when the

multitasking starts, all tasks are in the ready state; hence the kernel executes the highest

priority task.

Page 19: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 18

Figure 2.1 The five possible states of the µC/OS-II tasks

When the running task wait for a delay to expire or for a message by calling OSTimeDly(),

OSMboxPend() etc… or even suspended by itself when calling OSTaskSuspend(), this task

will be placed in waiting state until the delay expires or the message waiting for is present,

at this time the task placed in ready list waiting its turn to run.

The running task can also be preempted by an ISR when interrupts enabled, therefore, an

ISR may make one or more tasks ready to run, in this case before returning from the ISR

the kernel checks if there is a higher priority task ready to run, then the new higher priority

task is resumed otherwise the interrupted task is resumed.

The Dormant state correspond to the deleted task by calling OSTaskDel() or not available to

µC/OS-II yet done by OSCreatTask() or OSCreatTaskExt(), in this case the task stays residing

in memory but without any effect. When there is no task ready to run the kernel executes

the idle task OSTaskIdle().

Page 20: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 19

1.4. Task Control Blocks (OS_TCBs)

A task control block is a data structure that is used by μC/OS-II to maintain the state of a

task when it is preempted. When the task regains control of the CPU the task control block

allows the task to resume execution exactly where it left off. All OS_TCBs reside in RAM. [1]

The following structure describes each field in the OS_TCB data structure in case µC/OS-II

V2.83:

OS_TCB

typedef struct os_tcb { OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */ #if OS_TASK_CREATE_EXT_EN > 0 void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */ OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */ INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */ INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */ INT16U OSTCBId; /* Task ID (0..65535) */ #endif struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */ struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */ #if OS_EVENT_EN OS_EVENT *OSTCBEventPtr;/* Pointer to event control block */ #endif #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */ #endif #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) #if OS_TASK_DEL_EN > 0 OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */ #endif OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */ #endif INT16U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */ INT8U OSTCBStat; /* Task status */ BOOLEAN OSTCBPendTO; /* Flag indicating PEND timed out (OS_TRUE == timed out) */ INT8U OSTCBPrio; /* Task priority (0 == highest) */ INT8U OSTCBX; /* Bit position in group corresponding to task priority */ INT8U OSTCBY; /* Index into ready table corresponding to task priority */ #if OS_LOWEST_PRIO <= 63 INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */ INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */ #else INT16U OSTCBBitX; /* Bit mask to access bit position in ready table */ INT16U OSTCBBitY; /* Bit mask to access bit position in ready group */ #endif #if OS_TASK_DEL_EN > 0 INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */ #endif #if OS_TASK_PROFILE_EN > 0 INT32U OSTCBCtxSwCtr; /* Number of time the task was switched in */ INT32U OSTCBCyclesTot; /*Total number of clock cycles the task has been running*/

Page 21: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 20

INT32U OSTCBCyclesStart;/* Snapshot of cycle counter at start of task resumption */ OS_STK *OSTCBStkBase; /* Pointer to the beginning of the task stack */ INT32U OSTCBStkUsed; /* Number of bytes used from the stack */ #endif #if OS_TASK_NAME_SIZE > 1 INT8U OSTCBTaskName[OS_TASK_NAME_SIZE]; #endif } OS_TCB;

OSTCBStkPtr: Contains a pointer to the top of the respective task stack pointer, µC/OS-II allows to each task to have its own stack with any size, this will lead to minimize the ram area allocated to stacks,

OSTCBExtPtr: This pointer is used for user extension of the TCB without changing the whole structure of the µC/OS-II TCB. This field is used only when the task created by OSTaskCreateExt() with OS_TASK_CREATE_EXT_EN = 1,

OSTCBStkBottom: This pointer points on the bottom valid stack location OSTCBStkBottom is used by OSTaskStkChk() to check the size of a task’s stack at run-time in order to determine the amount of free stack space available for each stack. This field is used only when the task created by OSTaskCreateExt() with OS_TASK_CREATE_EXT_EN = 1,

OSTCBStkSize: This is variable that holds the size of the stack in number of elements instead of bytes. This means that if a stack contains 1000 entries and each entry is 32-bit wide then the actual size of the stack is 4000 bytes. OSTCBStkSize is used by OSTaskStkChk(), this field is valid when OS_TASK_CREATE_EXT_EN set to 1.

OSTCBOpt: This Variable holds options passed to OSTaskCreateExt() when a task is created by the extended task create function,

OSTCBId: Variable used to hold an identifier for the task. This field is currently not used and has only been included for future expansion.

OSTCBNext, OSTCBPrev: Contains pointers to previous and next TCB, because the tasks’ TSBs are doubly linked nodes this will make easy the update of each field of the TCBs.

OSTCBEventPtr, OSTCBMsg: Contain pointers to an Event Control Block and to the message sent to the task respectively, these will be described in inter-task communication;

OSTCBDly: This variable contains how many clock ticks left to get ready to run when the task delayed for a certain number of clocks ticks or waits for event with certain a timeout;

OSTCBStat: contains the state of the task. When equals to 0, the task is ready to run;

OSTCBPendTO: Indicates if the task is no more pending on an event (semaphore, messagebox etc...), =OS_TRUE if true (timed out);

Page 22: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 21

OSTCBPrio: Contains the task priority. A high priority task has a low OSTCBPrio;

OSTCBDelReq: Indicates if the task has been requested to delete itself, used to free the resources owned by this task before deletion;

OSTCBCtxSwCtr, OSTCBCyclesTot, OSTCBCyclesStart, OSTCBStkBase and OSTCBStk are used to profile the respective task, useful to keep track of each status of each task by the debugger programs;

OSTCBTaskName: String to hold the task’s name, length = OS_TASK_NAME_SIZE;

OSTCBX, OSTCBY, OSTCBBitX and OSTCBBitY: These variables are used to accelerate the process of making a task ready to run, or to make a task wait for an event (to avoid computing these values at runtime). The values for these fields are computed when the task is created or when the task's priority is changed. The values are computed as follows:

OSTCBY = priority >> 3; OSTCBBitY = OSMapTbl[priority >> 3]; OSTCBX = priority & 0x07; OSTCBBitX = OSMapTbl[priority & 0x07];

Table 2.1 OSMapTbl[8] values Index Bit mask (Binary)

0 00000001 1 00000010 2 00000100 3 00001000 4 00010000 5 00100000 6 01000000 7 10000000

1.5. Ready list

µC/OS-II is a priorities based RTOS, therefore, each task is assigned a unique priority used

as identifier in many functions, the µC/OS-II V2.83 allows up to 255 tasks fixed by the

variable OS_LOWEST_PRIO = Number of tasks - 1, in order to optimize the size of the used

RAM it is preferable to fix this variable to the number of used tasks plus the idle task which

has the lowest priority.

The ready tasks are placed in the ready list consisting of two variables; OSRdyTbl and

OSRdyGrp. OSRdyTbl is 8x8 bits or 16x16 bits to support up to 63 Tasks or 255 tasks

respectively, this table contains states’ bits of each task (0:Not ready, 1:Ready task).

Page 23: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 22

The tasks’ priorities are grouped in OSRdyGrp (8 tasks per group or 16 tasks in case of 63

and 255 tasks respectively), each bit in this variable is set to 1 when at least one task that

belongs to this group is ready.

The state of each task is easily located in the OSReadyTbl with X equals to the first 3 or 4

LSB bits, and Y equals to the remaining 3 or 4 MSB bits, again depends to the numbers of

supported tasks (63 or 255). Figure 2.2 illustrates the bits map of these variables.

Figure 2.2 Ready to run tasks table and ready group bits map

Lowest priority Task

[0] 7 6 5 4 3 2 1 0

[1] 15 14 13 12 11 10 9 8

[2] 23 22 21 20 19 18 17 16

[3] 31 30 29 28 27 26 25 24

[4] 39 38 37 36 35 34 33 32

[5] 47 46 45 44 43 42 41 40

[6] 55 54 53 52 51 50 49 48

[7] 63 62 61 60 59 58 57 56

8-Bits length OSRdyGrp

7 6 5 4 3 2 1 0

Highest priority Task

8-Bits length Task's Priority Byte

0 0 Y Y Y X X X

X Bits Position in OSRdyTbl

X Bits Position in OSRdyTbl

Page 24: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 23

This configuration allows µC/OS-II to accelerate the operations of make task ready, remove

task from ready list or get the highest ready to run task.

The following code place a task in the ready list: OSRdyGrp |= OSMapTbl[prio >> 3]; OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07]; To remove a task from the ready list: if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) OSRdyGrp &= ~OSMapTbl[prio >> 3];

In order to get the priority of the highest priority task ready to run:

y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; prio = (y << 3) + x; OSUnMapTbl is a lookup table to find the highest priority task ready to run rather than

scanning through the table starting with OSRdyTbl[0], OSUnMapTbl[256] is declared is

follow:

INT8U const OSUnMapTbl[256] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */ };

Page 25: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 24

For example, if OSRdyGrp contains 01101000 then:

Y = OSUnMapTbl[OSRdyGrp] = 3; Assume that OSRdyTbl[3] = 11100100; X = = OSUnMapTbl[OSRdyTbl[3]] = 2; The highest priority task ready to run (prio) would then be 26 (3 * 8 + 2).

Getting a pointer to the OS_TCB for the corresponding task is done by indexing into

OSTCBPrioTbl[] using the task's priority.[1]

2. Task Scheduling

The scheduler, also called the dispatcher, is the part of the kernel responsible for

determining which task will run next. µC/OS-II kernel is priority based. Each task is

assigned a priority based on its importance. In a priority-based kernel, control of the CPU

will always be given to the highest priority task ready-to-run. [1]

As we have seen before, µC/OS-II makes the context switch in two levels; task level, done

by OSSched() function and interrupt level performed by OSIntExit().

2.1. OSSched() Context Switch function

Listing 2.3 illustrates the OSShed() code, the scheduling starts by disabling all interrupts by

calling OS_ENTER_CRITICAL() macro (1), the next code is a critical section and doesn’t

accept to be interrupted. The test statement tests if the scheduling enabled (OSLockNesting

== false) and if the OSShed() was not called from an interrupt (OSIntNesting == false) (2), if

the both conditions are true then the priority of the highest priority task ready to run is

computed using the part of code (3), the next if statement tests if this task is not the

current running task (4), if the case no need to make an useless context switch, if not the

TCB’s pointer of this task is assigned to the highest priority task ready to run

OSTCBHighRdy TCB (5). Next, OSSched() increments the context switch counter OSCtxSwCtr

which keeps track the number of the performing context switch (6), finally, the context

switch achieved by calling the macro OS_TASK_SW() (7) and re-enable the interrupts by

calling OS_EXIT_CRITICAL().

Page 26: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 25

void OSSched (void) { INT8U y; OS_ENTER_CRITICAL(); (1) if ((OSLockNesting | OSIntNesting) == 0) { (2) y = OSUnMapTbl[OSRdyGrp]; (3) OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); if (OSPrioHighRdy != OSPrioCur) { (4) OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; (5) OSCtxSwCtr++; (6) OS_TASK_SW(); (7) } } OS_EXIT_CRITICAL(); (8) }

Listing 2.3 Task level Scheduling function OSSched()

2.2. OSIntExit() Context Switch function

Listing 2.4 shows the code of this function, as we can note OSIntExit() looks like OSSched()

except for some differences, the first one is that OSIntExit() should decrement the interrupt

nesting variable OSIntNesting, this variable was incremented by OSIntEnter() function

called at the entry of each interrupt code, OSIntExit() verifies that OSIntNesting = 0 after a

decrement which means that this interrupt has interrupted a task rather than another

interrupt, in the case and if the scheduling enabled (OSLockNesting == false) then the same

procedures are performed as in OSSched() (3), (4) and (5) except that in this case

OSIntExitY is declared as global variable to avoid allocating a local variable on the stack

which needs to be accounted for in interrupt level context switch OSIntCtxSw().

Instead of calling OSCtxSw() to perform the context switching, OSIntExit() calls

OSIntCtxSw() firstly because the ISR has already saved the CPU registers, secondly because

OSIntCtxSw() should do some stack adjustment to remove the return address to itself and

OSIntExit().

Page 27: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 26

void OSIntExit (void) { OS_ENTER_CRITICAL(); (1) if ((--OSIntNesting | OSLockNesting) == 0) { (2) OSIntExitY = OSUnMapTbl[OSRdyGrp]; (3) OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { (4) OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; (5) OSCtxSwCtr++; (6) OSIntCtxSw(); (7) } } OS_EXIT_CRITICAL(); (8) }

Listing 2.4 Interrupt level Scheduling function OSIntExit()

3. Interrupts Under µC/OS-II

The µC/OS-II requires to write the ISRs in assembly language to get access to CPU registers,

unless the compiler support the inline assembly, listing 2.5 shows the pseudo code of an

ISR under µC/OS-II.

ISR identifier: Save all CPU registers; (1) Call OSIntEnter() or, increment OSIntNesting directly; (2) Execute user code to service ISR; (3) Call OSIntExit(); (4) Restore all CPU registers; (5) Execute a return from interrupt instruction; (6)

Listing 2.5 Pseudo code of an interrupt service routine under µC/OS-II

The first that should be done is saving all CPU registers (1), next, calling OSIntEnter() or

increment OSIntNesting directly in order to notify that an ISR has occurred (2), at this

moment the ISR routine can be serviced by calling the respective call-back function (3),

when the ISR’s call-back function achieved the ISR calls OSIntExit() to perform the context

switching and decrementing the interrupt nesting variable OSIntNesting (4), the next step

restores the saved CPU registers (5) in order to return to the interrupted task, ISR

interrupt or the highest priority ready task (depends of what OSIntExit() has performed) by

Page 28: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 27

executing the return from interrupt instruction (6). The listing bellow illustrates an

example of an ISR code in case of M16C microcontroller:

TIMER_A1_ISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;Save current CPU context registers INC.B OSIntNesting ;OSIntNesting++ JSR TIMER_A1_CALLBACK ;Call the interrupt call-back function JSR OSIntExit ;Call OSIntExit() POPM R0,R1,R2,R3,A0,A1,SB,FB ;Restore the CPU context registers REIT ;Return from the interrupt

Listing 2.6 Sample of an interrupt service routine ISR in case of M16C under µC/OS-II

4. Clock Ticks

µC/OS-II needs a temporal reference source in order to keep track of the time delays and

timeouts, this can be achieved by hardware timer or an extern pulses (Ex. 50/60Hz main

supply frequency), The faster the tick rate, the higher the overhead imposed on the system.

Depends of the microcontroller performance and desired tick resolution, the ticks

frequency can be between 50 and 100Hz.

As described before the Clock Ticks Timer is serviced like any ISR, The call-back function

serviced by this interrupt should be OSTimeTick(), the listing bellow shows the pseudo

code of this ISR interrupt:

ISR identifier: Save all CPU registers; Call OSIntEnter() or, increment OSIntNesting directly; Execute user code to service ISR; Call OSTimeTick ; Restore all CPU registers; Execute a return from interrupt instruction;

Listing 2.7 Pseudo code of the Clock Ticks ISR

Listing 2.8 shows the code that services OSTimeTick() call-back function. OSTimeTick()

starts by calling a user definable function OSTimeTickHook() which can be used to extend

the functionality of OSTimeTick() (1).

Page 29: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 28

Most of the work done by OSTimeTick() basically consist of decrementing the OSTCBDly

field for each OS_TCB (if it’s nonzero). OSTimeTick() follows the chain of OS_TCB starting at

OSTCBList (2) until it reaches the idle task. When the OSTCBDly field of a task's OS_TCB is

decremented to zero, the task is made ready to run (4). The task is not readied, however, if

it was explicitly suspended by OSTaskSuspend() (5). The execution time of OSTimeTick() is

directly proportional to the number of tasks created in an application. [1]

void OSTimeTick (void) { OS_TCB *ptcb; OSTimeTickHook(); (1) ptcb = OSTCBList; (2) while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { (3) OS_ENTER_CRITICAL(); if (ptcb->OSTCBDly != 0) { if (--ptcb->OSTCBDly == 0) { if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { (5) OSRdyGrp |= ptcb->OSTCBBitY; (4) OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; } else { ptcb->OSTCBDly = 1; } } } ptcb = ptcb->OSTCBNext; OS_EXIT_CRITICAL(); } OS_ENTER_CRITICAL(); (7) OSTime++; (6) OS_EXIT_CRITICAL(); }

Listing 2.8 OSTimeTick() Code

5. Task Management

µC/OS-II’s Task has the format of any C void with no return value, also it should be an

infinite loop otherwise deletes itself before exiting the task’s void, listings bellow show the

template of any task:

Page 30: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 29

void TASK (void *pdata) { /* Task code */ while(1) /* infinite loop */ { /* Task code */ } }

Listing 2.9 µC/OS-II Task template in case of infinite loop code

void TASK (void *pdata) { /* user code */ /* delete task */ }

Listing 2.10 µC/OS-II Task template without infinite loop code

pdata argument contains the data pointer passed to the task when executed first time.

5.1. Creating µC/OS-II Task

Before µC/OS-II carries out a declared task it should be created in other word assign a TCB

to this task and place it in ready task list. A task can be created before launching the

µC/OS-II or during the runtime. There are two functions that can be used to create a task,

one simple OSTaskCreate() or the extended version OSTaskCreateExt():

INT8U OSTaskCreate(void(*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio) /* Arguments: Task is a pointer to the task's code p_arg is a pointer to an optional data area which can be used to pass parameters to the task when the task first executes. ptos is a pointer to the task's top of stack. prio is the task's priority. A unique priority MUST be assigned to each task and thenlower the number, the higher the priority. Returns: OS_NO_ERR if the function was successful. OS_PRIO_EXIT if the task priority already exist (each task MUST have a unique priority). OS_PRIO_INVALID if the priority you specify is higher than the maximum allowed (i.e. >= OS_LOWEST_PRIO) OS_ERR_TASK_CREATE_ISR if you tried to create a task from an ISR.*/

Listing 2.11 µC/OS-II Create Task function

Page 31: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 30

INT8U OSTaskCreateExt (void(*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio, INT16U id, OS_STK *pbos, INT32U stk_size, void *pext, INT16U opt) /* Arguments : Task is a pointer to the task's code p_arg is a pointer to an optional data area which can be used to pass parameters to the task when the task first executes. ptos is a pointer to the task's top of stack. prio is the task's priority. A unique priority MUST be assigned to each task and then lower the number, the higher the priority. id is the task's ID (0..65535) pbos is a pointer to the task's bottom of stack. stk_size is the size of the stack in number of elements. If OS_STK is set to INT8U, 'stk_size' corresponds to the number of bytes available. If OS_STK is set to INT16U, 'stk_size' contains the number of 16-bit entries available. pext is a pointer to a user supplied memory location which is used as a TCB extension. opt contains additional information (or options) about the behaviour of the task. * OS_TASK_OPT_STK_CHK Stack checking to be allowed for the task OS_TASK_OPT_STK_CLR Clear the stack when the task is created OS_TASK_OPT_SAVE_FP If the CPU has floating-point registers, save them during a context switch. Returns: OS_NO_ERR if the function was successful. OS_PRIO_EXIT if the task priority already exist (each task MUST have a unique priority). OS_PRIO_INVALID if the priority you specify is higher than the maximum allowed (i.e. >= OS_LOWEST_PRIO) OS_ERR_TASK_CREATE_ISR if you tried to create a task from an ISR. */

Listing 2.12 Extended version of the µC/OS-II create task function

In µC/OS-II a task can be deleted, suspended, and resumed etc..., by using the task

manipulation functions summarised in Table 2.1.

Page 32: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 31

Table 2.1 Task Manipulation functions Tasks manipulation functions Description

OSTaskChangePrio (INT8U oldprio, INT8U newprio) Change the priority of a task to newprio

OSTaskDel (INT8U prio) Delete the task with priority prio

OSTaskdelreq (INT8U prio) Request that a task delete itself OSTaskNameSet (INT8U prio, INT8U *pname, INT8U *err) Set a name to the task with priority prio

OSTaskNameGet (INT8U prio, INT8U *pname, INT8U *err) Get the name of a task with priority prio

OSTaskSuspend (INT8U prio) Suspend a task with priority prio

OSTaskResume (INT8U prio) Resume a suspended task with priority prio

By default µC/OS-II creates the Idle Task (OSTaskIdle()) which is executed when no task is ready to

run, µC/OS-II creates another task when enabled, called Statistic Task(OSTaskStat()) which

performs every second the computing of the percentage of CPU usage .

6. Inter-task Communication & Synchronization

When variables are shared among two or more tasks can allows to these tasks to interact

between each other. For example, one way to communicate information between tasks is

for one task to read a value written by another task.

In order to get exclusive access to shared variables and avoid that more than one task get

access to the resource in same time which leads to corrupt these variables, µC/OS-II

provides the two macros OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() which are called

respectively before starting and after finishing the manipulation of the shared resource,

these will disable interrupts and leads to stop the multitasking. Another method can be

used, by Locking and unlocking µC/OS-II’s scheduler with OSSchedLock() and

OSSchedUnlock() respectively the multitasking can be stopped as well.

Page 33: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 32

In a real time system this could lead to performance problems due to the disruption of the

precision of the timing. For these reasons a different technique is used for critical section

protection based on a construct known as a semaphore (a word which means signal or

alternatively a device which is used to send signals). [2]

In order to perform the inter-task communication and synchronization, µC/OS-II provides

a shared structure called ECB (Event Control Blocks) which can take the form of a

semaphore to synchronize tasks, messages mailbox or messages queues to make the inter-

task communication. The listing bellow shows the data structure of an ECB.

typedef struct { void *OSEventPtr; /* Pointer to message or queue structure */ INT8U OSEventTbl[OS_EVENT_TBL_SIZE];/* Wait list for event to occur */ INT16U OSEventCnt; /* Count (when event is a semaphore) */ INT8U OSEventType; /* Event type */ INT8U OSEventGrp; /* Group for wait list */ } OS_EVENT;

Listing 2.13 OS_EVENT Structure members

OSEventPtr: This pointer is used when the ECB is assigned to a mailbox or a messages

queue. In this case, OSEventPtr points to the message when used for a mailbox or a pointer

to a message queue data.

OSEventTbl[] and OSEventGrp are similar to OSRdyTbl[ ] and OSRdyGrp used in tasks’

states respectively except that they contain a list of tasks waiting on the event instead of

being a list of tasks ready-to-run.

OSEventCnt is used to hold the semaphore count when the ECB is used for a

semaphore.

OSEventType contains the type associated with the ECB and can have the following

values: OS_EVENT_SEM, OS_EVENT_TYPE_MBOX or OS_EVENT_TYPE_Q. This field is used to

make sure you are accessing the proper object when you perform operations on these

objects through µC/OS-II’s service calls. [1].

Page 34: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 33

6.1. Semaphores

A semaphore is a key that your code acquires in order to continue its execution. If the

semaphore is already in use, the requesting task is suspended until the semaphore is

released by its current owner. In other words, the requesting task says: "Give me the key. If

someone else is using it, I am willing to wait for it!”. [1]

In order to use the µC/OS-II’s semaphore we need to create it first using the function

OSSemCreate (Assign an ECB to this semaphore) as follow:

OS_EVENT *MySemaphore; MySemaphore = OSSemCreate (INT16U cnt) /* Argument : cnt : Is the initial value for the semaphore. If the value is 0, no resource is available (or no event has occurred). You initialize the semaphore to a non-zero value to specify how many resources are available (e.g. if you have 10 resources, you would initialize the semaphore to 10) */ /* Returns: != (void *)0 is a pointer to the event control clock (OS_EVENT) associated with the created semaphore == (void *)0 if no event control blocks were available */

Listing 2.13 Function to create a semaphore

Wait for a semaphore, when a task requesting access to a semaphore the function

OSSemPend is called, this function will make the called task in waiting list until the

semaphore is available or the specified timeout finished, the listing bellow show the

prototype of this function:

void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) /* Arguments : pevent is a pointer to the event control block associated with the desired semaphore. timeout is an optional timeout period (in clock ticks). If non-zero, your task will wait for the resource up to the amount of time specified by this argument. If you specify 0, however, your task will wait forever at the specified semaphore or, until the resource becomes available (or the event occurs). err is a pointer to where an error message will be deposited. Possible error messages are: OS_NO_ERR The call was successful and your task owns the resource or, the event you are waiting for occurred. OS_TIMEOUT The semaphore was not received within the specified timeout. OS_ERR_EVENT_TYPE If you didn't pass a pointer to a semaphore.

Page 35: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 34

OS_ERR_PEND_ISR If you called this function from an ISR and the result would lead to a suspension. OS_ERR_PEVENT_NULL If 'pevent' is a NULL pointer. Returns: none */

Listing 2.14 Function to wait on semaphore to be released

Signal a semaphore (release a semaphore), after get access to a semaphore and we want

release it, the OSSemPost function is called, this will signal to the task waiting for this

semaphore that the semaphore is free to be taken, the prototype of this function is as

follow:

INT8U OSSemPost (OS_EVENT *pevent) /* Arguments: pevent is a pointer to the event control block associated with the desired semaphore. Returns: OS_NO_ERR The call was successful and the semaphore was signaled. OS_SEM_OVF If the semaphore count exceeded its limit. In other words, you have signaled the semaphore more often than you waited on it with either OSSemAccept() or OSSemPend(). */ /* Return: OS_ERR_EVENT_TYPE If you didn't pass a pointer to a semaphore OS_ERR_PEVENT_NULL If 'pevent' is a NULL pointer. */

Listing 2.15 Function to release a semaphore

Another interesting function which checks if a semaphore is available or not without

waiting, OSSemAccept(), unlike OSSemPend(), OSSemAccept() does not suspend the calling

task if the resource is not available or the event did not occur. The listing bellow illustrates

this function:

INT16U OSSemAccept (OS_EVENT *pevent) /* Arguments: pevent is a pointer to the event control block Returns: > 0 if the resource is available or the event did not occur the semaphore is decremented to obtain the resource. == 0 if the resource is not available or the event did not occur or if 'pevent' is a NULL pointer or, if you didn't pass a pointer to a semaphore */

Listing 2.16 Function to check the availability of a semaphore

Page 36: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 35

6.1.1. Get exclusive access to a resource using semaphore

Figure 2.3 illustrates an example when using the semaphore as key to get exclusive access

to a resource, Task 1 starts by creating the semaphore by assigning an ECB to it, when this

task is the first task that calls OSSemPend() function, this resource is available to task 1

until it release it by calling OSSemPost(), at this moment the task 2 which was waiting for

the semaphore to be signalled takes the control of this semaphore or in other word the LCD

Display until it releases it to the Task 1 when calling OSSemPost().

Figure 2.3 Semaphore used to get exclusive access to LCD Display resource

void Task_2(void *pdata) { INT8U err; while(1) { OSSemPend (LcdSem, 0, &err); /* Wait until the Lcd display available */ LcdWrite(“Task 2: Hello!”); /* Display the task 1’s message */ OSTimeDly(2000); /* keep the message for 2000 clock ticks */ OSSemPost(LcdSem); /* Release the Lcd Display to other tasks */ OSTimeDly(1000); /* Make a delay of 1000 ticks */ } }

OS_EVENT* LcdSem ; /* ECB to be assigned to semaphore */ void Task_1(void *pdata) {INT8U err; LcdSem = OSSemCreate(1); /* Create the LCD semaphore with one access */ while(1) { OSSemPend (LcdSem, 0, &err);/* Waite until the Lcd display available */ LcdWrite(“Task 1: Hello!”); /* Display the task 1’s message */ OSTimeDly(2000); /* keep the message for 2000 clock ticks */ OSSemPost(LcdSem); /* Release the Lcd Display to other tasks */ OSTimeDly(1000); /* Make a delay of 1000 ticks */ } }

Calling OSSemPost in Task 2 releases the LcdSem semaphore and OSSemPend terminates the waiting time of Task 1

Calling OSSemPost in Task 1 releases the LcdSem semaphore and OSSemPend terminates the waiting time of Task 2

Page 37: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 36

6.1.2. Using A Semaphore To Synchronize Two Tasks

A Semaphore can be used also to synchronize a task with another task or an ISR, in this

case the semaphore is initialized to zero (no resource), depends on whether one task or an

ISR signals to another task unilateral rendezvous or two tasks signal to each other bilateral

rendezvous, two tasks can synchronize their activities with each other. In this case the

semaphore is represented as flag; Figure 2.4 illustrates different synchronization cases.

Figure 2.4 Semaphore used for: Unilateral synchronization (Unilateral rendezvous), Bilateral

synchronization (Bilateral rendezvous)

Unilateral rendezvous Bilateral rendezvous

TASK1 ISR

TASK2

Semaphore

OSSemPost() OSSemPend()

TASK1 TASK2

Semaphore

OSSemPost() OSSemPend()

OSSemPost() OSSemPend()

Page 38: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 37

Figure 2.5 Two Tasks synchronization example, Task 1 signals Task 2 to perform the waiting instructions.

6.2. Mutual Exclusion Semaphores

Mutual Exclusion Semaphores (Mutex) are used by tasks to gain exclusive access to a

resource. Mutexes have additional features beyond the normal semaphores in order to

resolve the problem of priority inversion.

The priority inversion problem is illustrated by Figure bellow, at (1) Task 3 that has the

lowest priority gets access to the semaphore, at (2) this task preempted by Task 1 (because

Task 1 has the higher priority). At (3), Task 1 requests the semaphore, but this semaphore

is still owned by Task 3, therefore, Task 1 will be placed in waiting list until the semaphore

is released, at this time Task 3 takes the control of the CPU and continues manipulating the

semaphore’s resources, at (4) Task 2 preempts Task 3 and runs until (5), where the CPU

void Task_2(void *pdata) { while(1) { OSSemPend(Sem, 0, &err);/* Wait until signal received from Task 1 */ /* Perform the operations waiting for the signal */ /*…………………………………………………………….*/ OSTimeDly(1000); /* Make a delay of 1000 ticks */ }}

void Task_1(void *pdata) { Sem = OSSemCreate(0);/* Create a semaphore with no shared resource*/ while(1) { OSSemPost(Sem); /* Send semaphore’s Signal to task 2 */ OSTimeDly(1000); /* Make a delay of 1000 ticks */ }}

Task 1 sends signal to task 2 by calling OSSemPost(), this signal will be detected by OSSemPend in Task 2.

Page 39: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 38

will be given to Task 3 instead of Task 1 (Highest priority) which still awaits for the

semaphore to be released by Task 3. At (6) Task 3 releases this semaphore and Task 1

obtains it after a long waiting.

This situation makes a virtual reduction of the Task 1 priority to Task 2 and Task 3,

because it was waiting for a semaphore which has been owned by lowest priority task

(Task 3), this would lead to an important execution delay of the Task 1.

Figure 2.6 Priority inversions problem when using semaphores.

To avoid this problem, we can raise the priority of Task 3 over the others Tasks and restore

the original priority when releases the semaphore, this will lead to give more CPU cycles to

this Task, therefore, the semaphore will be released more quickly and task 1 does not have

to wait for a long time for the semaphore. These Operations will be done automatically by

using the mutual exclusion semaphore instead of the normal semaphore.

SEM TASK 3 (Lowest priority)

TASK 2 (Medium priority)

TASK 1 (Highest priority)

Task 3 Gets Semaphore

Task 3 preempted by Task 1

Task 3 resumed

SEM

Task 3 pre-empted by Task 2

Task 3 preempted by Task 2

SEM

SEM

Task 3 releases the Semaphore

Task 1 Gets Semaphore

Priority inversions problem Task 1 (Highest priority) wait for Task

2 or Task 3 (Lowest priority)

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

Page 40: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 39

Like the normal semaphores, the Mutex should be created before its using, almost the same

function as the normal semaphores are used to wait for, release or check the status of the

Mutex. The table bellow summarizes these functions in the case of the Mutexes:

Table 2.2 Mutual Exclusion semaphores functions

Mutexes functions Description OSMutexCreate (INT8U prio,

INT8U *err) creates a mutual exclusion semaphore prio is the priority to use when accessing the mutual exclusion semaphore. In other words, when the semaphore is acquired and a higher priority task attempts to obtain the semaphore then the priority of the task owning the semaphore is raised to this priority. It is assumed that you will specify a priority that is LOWER in value than ANY of the tasks competing for the mutex.

OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)

Pend on mutual exclusion semaphore.

OSMutexPost (OS_EVENT *pevent)

Post signal to a mutual exclusion semaphore

OSMutexAccept (OS_EVENT*pevent, INT8U *err)

Checks the mutual exclusion semaphore is available.

OSMutex_RdyAtPrio (OS_TCB *ptcb, INT8U prio)

Restore a task back to its original priority Arguments: ptcb is a pointer to OS_TCB of the task to make ready, prio is the desired priority,

6.3. Message Mailboxes

µC/OS-II allows tasks to exchange messages between each other by using the Mailbox ECB,

the message has the form of pointer which points on any type of messages (string,

numbers, structures etc…), as for any ECB object, the Mailbox should be created before use

it, to create a mailbox the following function is requested:

OS_EVENT *OSMboxCreate (void *msg) /* Arguments : msg is a pointer to a message that you wish to deposit in the mailbox. Returns : != (OS_EVENT *)0 is a pointer to the event control clock (OS_EVENT) associated with the created mailbox == (OS_EVENT *)0 if no event control blocks were available */

Listing 2.17Function to create a Mailbox

Page 41: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 40

After created the Mailbox, a message can be posted into it by using the following function: INT8U OSMboxPost (OS_EVENT *pevent, void *msg) /* Arguments: pevent is a pointer to the event control block associated with the desired mailbox msg is a pointer to the message to send. You MUST NOT send a NULL pointer. Returns: OS_NO_ERR: The call was successful and the message was sent OS_MBOX_FULL: If the mailbox already contains a message. You can can only send one message at a time and thus, the message MUST: be consumed before you are allowed to send another one. OS_ERR_EVENT_TYPE: If you are attempting to post to a non mailbox. OS_ERR_PEVENT_NULL: If 'pevent' is a NULL pointer OS_ERR_POST_NULL_PTR: If you are attempting to post a NULL pointer */

Listing 2.18 Function to post a message into a Mailbox

To read the message sent, two functions can be used depend if we want pending on

mailbox until the message is sent, in this case the OSMboxPend() is used, or we can check if

there is message in the mailbox, if exists then read the massage otherwise continue the

execution of the program by using OSMboxAccept(), the following listings illustrate these

functions:

/* Pend on the mailbox within a timeout if specified until a message is available */ void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err); /* Get the message if exists without waiting */ void *OSMboxAccept (OS_EVENT *pevent); /* Arguments : pevent is a pointer to the event control block associated with the desired mailbox timeout is an optional timeout period (in clock ticks). If non-zero, your task will wait for a message to arrive at the mailbox up to the amount of time specified by this argument. If you specify 0, however, your task will wait forever at the specified mailbox or, until a message arrives. err is a pointer to where an error message will be deposited. Possible error messages are: OS_NO_ERR The call was successful and your task received a message. OS_TIMEOUT A message was not received within the specified timeout OS_ERR_EVENT_TYPE Invalid event type OS_ERR_PEND_ISR If you called this function from an ISR and the result would lead to a suspension. OS_ERR_PEVENT_NULL If 'pevent' is a NULL pointer Returns : != (void *)0 is a pointer to the message received == (void *)0 if no message was received or, if 'pevent' is a NULL pointer or, if you didn't pass the proper pointer to the event control block.*/

Listing 2.19 Functions to pickup the sent message

Page 42: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 41

Figure 2.7 Inter-task communication using Mailbox

Figure 2.8 Task 1 sends Text Message to task 2 through a mailbox

void Task_2 (void *data) { char* ReceivedMsg; INT8U err; while(1) { /* Pend on Mailbox until the message available then read it */ ReceivedMsg = (char*)OSMboxPend(MailBox, 0, &err); printf(“%s \n”, ReceivedMsg); /* Print the received message */ OSTimeDly(1000); /* Make a delay of 1000 ticks */ }}

void Task_1 (void *data) { char SentMsg = “HELLO!”; INT8U err; MailBox = OSMboxCreate(TextMsgPtr); while(1) { OSMboxPend(MailBox, 0, &err); /* Wait until the Mailbox is empty */ OSMboxPost(Mailbox, (void *)&SentMsg);/* send the text message */ OSTimeDly(1000); /* Make a delay of 1000 ticks */ }}

Task 1 sends Text Message to MailBox by OSMboxPost(), Task 2 receives this message by calling OSMboxPend().

TASK

Mailbox

» OSMboxPost()

ISR » OSMboxPost()

» OSMboxPend()

TASK

» OSMboxAccept()

TASK ISR

Page 43: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 42

6.4. Messages Queues

A Message Queue is built as chain of Mailboxes organized in queue, therefore, a messages

queue can hold more than one message, these messages are sent and received in FIFO (first

input first output) priority. As we have seen in Semaphores and Mailboxes the messages

queue should be created using the following function:

OS_EVENT *OSQCreate (void **start, INT16U size) /* Arguments: start is a pointer to the base address of the message queue storage area. The storage area MUST be declared as an array of pointers to 'void' as follows: void *MessageStorage[size] size is the number of elements in the storage area Return: != (OS_EVENT *)0 is a pointer to the event control clock (OS_EVENT) associated with the created queue == (OS_EVENT *)0 if no event control blocks were available or an error was detected */

Listing 2.20 Function to create a Messages Queue

To send (inject) a message at the end of the massage queue, OSQPost() is called, otherwise

if we want to send the message at the front of the message queue OSQPostFront() function

is called, the following listing illustrates the prototypes of these functions:

/* post message at the end of the queue */ INT8U OSQPost (OS_EVENT *pevent, void *msg); /* post message at the front instead of the end of the queue */ INT8U OSQPostFront (OS_EVENT *pevent, void *msg); /* Arguments: pevent is a pointer to the event control block associated with the desired queue msg is a pointer to the message to send. Returns: OS_NO_ERR The call was successful and the message was sent OS_Q_FULL If the queue cannot accept any more messages because it is full. OS_ERR_EVENT_TYPE If you didn't pass a pointer to a queue. OS_ERR_PEVENT_NULL If 'pevent' is a NULL pointer */

Listing 2.21 Function to post message into messages queue

Page 44: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 43

The same situation as in mailboxes, there are two function to get message from the queue,

OSQPend() to pend on the message queue until a message is present or OSQAccept() to

check if there is a message in the queue, if the case, read this message or leave the function,

the following listing shows these functions:

/* Wait for a message to be sent to a queue */ void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) /* pickup the message if available or leave if not */ void *OSQAccept (OS_EVENT *pevent, INT8U *err) /* Arguments: pevent is a pointer to the event control block associated with the desired queue timeout is an optional timeout period (in clock ticks). If non-zero, your task will wait for a message to arrive at the queue up to the amount of time specified by this argument. If you specify 0, however, your task will wait forever at the specified queue or, until a message arrives. err is a pointer to where an error message will be deposited. Possible error messages are: OS_NO_ERR The call was successful and your task received a message. OS_TIMEOUT A message was not received within the specified timeout OS_ERR_EVENT_TYPE You didn't pass a pointer to a queue OS_ERR_PEVENT_NULL If 'pevent' is a NULL pointer OS_ERR_PEND_ISR If you called this function from an ISR and the result would lead to a suspension. OS_ERR_PEND_LOCKED If you called this function with the scheduler is locked Returns: != (void *)0 is a pointer to the message received == (void *)0 if you received a NULL pointer message or, if no message was received or, if 'pevent' is a NULL pointer or, if you didn't pass a pointer to a queue.*/

Listing 2.22 Functions to pickup a message from a queue

Figure 2.9 Messages posting and receiving in queue structure

TASK ISR

Messages Queue

» OSQPost()

» OSQPend() OSQAccept()

TASK ISR

» OSQPostFront()

TASK ISR

Front of the queue

End of the queue

Page 45: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 44

Figure 2.10 Messages Queue example, Task 1 and Task 2 send Messages to the queue every 1000 Clock Ticks, Task 3 reads these messages and prints them

const QSize = 64;/* Messages queue’s size */ void* TxtPtr[QSize]; /* Pointers that hold messages */ /* this task gets messages from queue and print them */ void Task_3 (void *data) { Char* ReceivedMsg;/* Pointer to contain the Received Message */ INT8U err; /* Create the queue size = QSize */ MsgQ = OSQCreate(&TxtPtr[0], QSize); while(1) { /* Pend on Messsage Queue until a message available */ ReceivedMsg = (char*)OSQPend(MsgQ, 0, &err); printf(“%s \n”, ReceivedMsg); /* Print the received message */ OSTimeDly(1000); /* Make a delay of 1000 ticks */ }}

void Task_2 (void *data) { char* SentMsg = “Task 2 Message”; while(1) { /* send SentMsg message to the Queue */ OSQPost(MsgQ, (void *)SentMsg); OSTimeDly(1000); /* Make a delay of 1000 ticks */ }}

Task 2 sends Text Message to MsgQ by OSQPost()

void Task_1 (void *data) { char* SentMsg = “Task 1 Message”; while(1) { /* send SentMsg message to the Queue */ OSQPost(MsgQ, (void *)SentMsg); OSTimeDly(1000); /* Make a delay of 1000 ticks */ }}

Task 1 sends Text Message to MsgQ by OSQPost()

Page 46: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 45

6.5. Event Flags (µC/OS-II V2.51 and higher)

Event flags are used when a task needs to synchronize with the occurrence of multiple

events. The task can be synchronized when any of the events have occurred. This is called

disjunctive synchronization (logical OR). A task can also be synchronized when all events

have occurred. This is called conjunctive synchronization (logical AND). [4]

Figure bellow shows the Disjunctive and Conjunctive synchronization using Flag Events:

Figure 2.11 Conjunctive and Disjunctive synchronization

In µC/OS-II the flags are grouped in a set of 8, 16 or 32 flags (fixed in compile time) called

Event Flags Group, each event flag is represented by a bit which can be Set or Clear by a

task or an ISR, in the other side a task can synchronize its activities by waiting (Pend) on or

check these Event flags states.

Figure 2.12 µC/OS-II Event Flags service

Conjunctive synchronization

TASK Post Events

TASK

ISR

AND (ALL) TASK/ISR TASK

Pend on Events

Disjunctive synchronization

TASK Post Events

TASK

ISR

TASK/ISR TASK

Pend on Events

OR

(ANY)

TASK

Set/Clear Event Flags OSFlagPost()

Wait Events OSFlagPend()

Event Flags Group 0 1 1 1 0 0 1 1

Check Events OSFlagAccept() Set/Clear Event

Flags OSFlagPost()

ISR

TASK

ISR

Page 47: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 46

Before using µC/OS-II Event Flags a group of flags should be created by using

OSFlagCreate(), described as follow:

OS_FLAG_GRP *OSFlagCreate (OS_FLAGS flags, INT8U *err) /* Arguments: flags Contains the initial value to store in the event flag group. err is a pointer to an error code which will be returned to your application: OS_NO_ERR if the call was successful. OS_ERR_CREATE_ISR if you attempted to create an Event Flag from an ISR. OS_FLAG_GRP_DEPLETED if there are no more event flag groups Returns: A pointer to an event flag group or a NULL pointer if no more groups are available.*/

Listing 2.23 Functions to create the Event Flags

To Set/Clear some bits in the Event Flags Group, the function OSFlagPost() is called as

follow:

OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err) /* Arguments: pgrp is a pointer to the desired event flag group. flags If 'opt' (see below) is OS_FLAG_SET, each bit that is set in 'flags' will set the corresponding bit in the event flag group. e.g. to set bits 0, 4 and 5 you would set 'flags' to: 0x31 (note, bit 0 is least significant bit) If 'opt' (see below) is OS_FLAG_CLR, each bit that is set in 'flags' will CLEAR the corresponding bit in the event flag group. e.g. to clear bits 0, 4 and 5 you would specify 'flags' as: 0x31 (note, bit 0 is least significant bit) opt indicates whether the flags will be Set(OS_FLAG_SET)or Cleared (OS_FLAG_CLR) err is a pointer to an error code and can be: OS_NO_ERR The call was successful OS_FLAG_INVALID_PGRP You passed a NULL pointer OS_ERR_EVENT_TYPE you are not pointing to an event flag group OS_FLAG_INVALID_OPT You specified an invalid option Returns: The new value of the event flags bits that are still set. */

Listing 2.24 Functions to post (Set) some flags bits

Page 48: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 47

In order to synchronize another Task the function OSFlagPend() is used to wait for a

combination of bit to be set or clear, the listing bellow describe how to use this function:

OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err) /* Arguments: pgrp is a pointer to the desired event flag group flags Is a bit pattern indicating which bit(s) (i.e. flags) you wish to wait for. The bits you want are specified by setting the corresponding bits in 'flags'. e.g. if your application wants to wait for bits 0 and 1 then 'flags' would contain 0x03. wait_type specifies whether you want ALL bits to be set or ANY of the bits to be set. You can specify the following argument: OS_FLAG_WAIT_CLR_ALL You will wait for ALL bits in 'mask' to be clear (0) OS_FLAG_WAIT_SET_ALL You will wait for ALL bits in 'mask' to be set (1) OS_FLAG_WAIT_CLR_ANY You will wait for ANY bit in 'mask' to be clear (0) OS_FLAG_WAIT_SET_ANY You will wait for ANY bit in 'mask' to be set (1) NOTE: Add OS_FLAG_CONSUME if you want the event flag to be 'consumed' by the call. Example, to wait for any flag in a group AND then clear the flags that are present, set 'wait_type' to: OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME timeout is an optional timeout (in clock ticks) that your task will wait for the desired bit combination. If you specify 0, however, your task will wait forever at the specified event flag group or, until a message arrives. err is a pointer to an error code and can be: OS_NO_ERR The desired bits have been set within the specified 'timeout'. OS_ERR_PEND_ISR If you tried to PEND from an ISR OS_FLAG_INVALID_PGRP If 'pgrp' is a NULL pointer. OS_ERR_EVENT_TYPE You are not pointing to an event flag group OS_TIMEOUT The bit(s) have not been set in the specified 'timeout'. OS_FLAG_ERR_WAIT_TYPE You didn't specify a proper 'wait_type' argument. Returns: The flags in the event flag group that made the task ready or, 0 if a timeout or an error occurred.*/

Listing 2.25 Functions to pend on the flags to be Set/Clear

The example bellow shows a demonstration of the Event Flags, firstly the task

Hold_Flag_Task create the Flags group, when it is time to rise the flag, this task calls

OSFlagPost() and specifies that the first flag (Flags = 0x01) equal to one (OS_FLAG_SET).

The task that watches the risen Flags (Watch_Flags_Task) wait until the event occurs by

sticking (Pend) on the OSFlagPend() function until the task Hold_Flag_Task signals the flag.

Page 49: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 48

Figure 2.13 Flag Events application example 7. Time Management

µC/OS-II provides a set of timing functions to delay a task a certain amount of time, the

time resolution depends of the Clock Tick frequency fixed by the constant

OS_TICKS_PER_SEC, the table bellow summarizes the Task delaying functions:

Table 2.3 Different Time Management functions TASK DELAYING FUNCTION DESCRIPTION

OSTimeDly(INT16U ticks) Delay the currently running task until the number of ticks expires

OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT16U milli)

Delay the currently running task until the Time specified (Hours:Min:Sec:MSec) expires

OSTimeDlyResume (INT8U prio)

Resume a task that has been delayed by OSTimeDly() or OSTimeDlyHMSM(), or task waiting for an event with timeout. This would make the task look like a timeout occurred.

OSTimeGet (void) Get the current value of the 32-bit counter which keeps track of the number of clock ticks.

OSTimeSet (INT32U ticks) Set the 32-bit counter which keeps track of the number of clock ticks.

void Hold_Flag_Task(void *pdata) { INT8U err; OS_FLAG_GRP * Flag; /* Create the Flags */ Flag = OSFlagCreate(0x00, &err); while(1) { /* Test if is Time to rise the flag… If yes, rise the first Flag (Set to 1 first bit)*/ OSFlagPost(Flag, 0x01, /*First Flag */ OS_FLAG_SET,/*Set the flag to 1 */ &err); } }

void Watch_Flags_Task(void *pdata) { INT8U err; OS_FLAG_GRP * Flag; while(1) { /* Wait until the Flag raised */ OSFlagPend( Flag, /* Flags Pointer */ 0x01, /* Watch the first flag */ /* Consume the Flag (Decline it) */ OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME, 0, /* Wait forever, until the flag rises */ &err); } }

Page 50: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 49

8. Timers Management

Instead of using the hardware Timers which can affects the responsiveness of the µC/OS-II

the latest versions of µC/OS-II provides timers facilities, managed by the Timers task

OSTmr_Task, µC/OS-II’s Timers can be settled in repeat or in one-shout mode, before using

a Timer we should create it by using the function described by the following listing: OS_TMR *OSTmrCreate (INT32U dly, INT32U period, INT8U opt, OS_TMR_CALLBACK callback, void *callback_arg, INT8U *pname, INT8U *perr) /* Arguments: dly Initial delay. If the timer is configured for ONE-SHOT mode, this is the timeout used, If the timer is configured for PERIODIC mode, this is the first timeout to wait for before the timer starts entering periodic mode, period The 'period' being repeated for the timer. If you specified 'OS_TMR_OPT_PERIODIC' as an option, when the timer expires, it will automatically restart with the same period. opt Specifies either: OS_TMR_OPT_ONE_SHOT The timer counts down only once OS_TMR_OPT_PERIODIC The timer counts down and then reloads itself callback Is a pointer to a callback function that will be called when the timer expires. The callback function must be declared as follows: void MyCallback (OS_TMR *ptmr, void *p_arg); callback_arg Is an argument (a pointer) that is passed to the callback function when it is called. pname Is a pointer to an ASCII string that is used to name the timer. Names are useful for debugging. perr Is a pointer to an error code. '*perr' will contain one of the following: OS_NO_ERR OS_ERR_TMR_INVALID_DLY you specified an invalid delay OS_ERR_TMR_INVALID_PERIOD you specified an invalid period OS_ERR_TMR_INVALID_OPT you specified an invalid option OS_ERR_TMR_ISR if the call was made from an ISR OS_ERR_TMR_NON_AVAIL if there are no free timers from the timer pool OS_ERR_TMR_NAME_TOO_LONG if the timer name is too long to fit Returns: A pointer to an OS_TMR data structure. This is the 'handle' that you application will use to reference the timer created/started. */

Listing 2.26 Functions to create a Timer

Page 51: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 50

To start the created Timer, OSTmrStart() is used as follow: OSTmrStart (OS_TMR *ptmr, INT8U *perr) /* Arguments: ptmr Is a pointer to an OS_TMR perr Is a pointer to an error code. '*perr’ will contain one of the following: OS_NO_ERR OS_ERR_TMR_INVALID OS_ERR_TMR_INVALID_TYPE ‘ptmr’ is not pointing to an OS_TMR OS_ERR_TMR_ISR if the call was made from an ISR OS_ERR_TMR_INACTIVE if the timer was not created OS_ERR_TMR_INVALID_STATE the timer is in an invalid state Returns: OS_TRUE if the timer was started OS_FALSE if an error was detected */

Listing 2.27 Function to start the created Timer

This Timer can be stopped and deleted if requested by using the following function: OSTmrStop (OS_TMR *ptmr, INT8U opt, void *callback_arg, INT8U *perr) /* Arguments: ptmr Is a pointer to the timer to stop and delete. opt Allows you to specify an option to this functions which can be: OS_TMR_OPT_NONE Do nothing special but stop the timer OS_TMR_OPT_CALLBACK Execute the callback function, pass it the callback argument specified when the timer was created. OS_TMR_OPT_CALLBACK_ARG Execute the callback function, pass it the callback argument specified in THIS function call callback_arg Is a pointer to a 'new' callback argument that can be passed to the callback function instead of the timer's callback argument. In other words, use 'callback_arg' passed in THIS function INSTEAD of ptmr->OSTmrCallbackArg perr Is a pointer to an error code. '*perr' will contain one of the following: OS_NO_ERR OS_ERR_TMR_INVALID 'ptmr' is a NULL pointer OS_ERR_TMR_INVALID_TYPE 'ptmr' is not pointing to an OS_TMR OS_ERR_TMR_ISR if the function was called from an ISR OS_ERR_TMR_INACTIVE if the timer was not created OS_ERR_TMR_INVALID_OPT if you specified an invalid option for 'opt' OS_ERR_TMR_STOPPED if the timer was already stopped OS_ERR_TMR_INVALID_STATE the timer is in an invalid state OS_ERR_TMR_NO_CALLBACK if the timer does not have a callback function defined Returns: OS_TRUE If the call was successful (if the timer is already stopped, we also return OS_TRUE) OS_FALSE If not */

Listing 2.28 Functions to stop the Timer

Page 52: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 51

9. Memory Management

In order to exploit dynamically a memory partition, µC/OS-II provides dynamic memory

allocation and releasing functions of a fixed size partition made of a contiguous memory

area with fixed blocks size.

To achieve this and to manage the partition, a memory control block data structure is

associated to each created partition, the figure bellow shows the structure of the Memory

partition with the respective Control Block:

Figure 2.14 Memory partition and its Memory Control Blocks OSMemAddr: Points to the first block (base) of the memory partition, it contains the

partition handle as well.

OSMemFreeList: pointer used by µC/OS-II to point to either the next free memory control

block or to the next free memory block.

OSMemBlkSize: The size of each memory blocks in the partition.

OSMemNBlks: Total number of memory blocks that the partition contains.

OSMemNFree: Number of the available blocks from the memory partition.

typedef struct { void *OSMemAddr; void *OSMemFreeList; INT32U OSMemBlkSize; INT32U OSMemNBlks; INT32U OSMemNFree; } OS_MEM;

Memory Partition

Memory Block

Free blocks OSMemNFree

Total blocks OSMemNBlks

Page 53: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 52

Before using a memory partition we should create a partition from an array type and assign

it to the Partition Control Blocks, to achieve this OSMemCreate() is called regarding the

following declaration:

OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err) /* Arguments: addr is the starting address of the memory partition nblks is the number of memory blocks to create from the partition. blksize is the size (in bytes) of each block in the memory partition. err is a pointer to a variable containing an error message which will be set by this function to either: OS_NO_ERR if the memory partition has been created correctly. OS_MEM_INVALID_ADDR you are specifying an invalid address for the memory storage of the partition or, the block does not align on a pointer boundary OS_MEM_INVALID_PART no free partitions available OS_MEM_INVALID_BLKS user specified an invalid number of blocks (must be >= 2) OS_MEM_INVALID_SIZE user specified an invalid block size - must be greater than the size of a pointer - must be able to hold an integral number of pointers Returns: != (OS_MEM *)0 is the partition was created == (OS_MEM *)0 if the partition was not created because of invalid arguments or no free partition is available. */

Listing 2.29 Functions to create a memory partition

Upon the memory partition created, we can dynamically allocate and freed each block of

the partition by calling OSMemGet() and OSMemPut() functions.

/* Allocate a memory block from a partition */ void *OSMemGet (OS_MEM *pmem, INT8U *err); /* Arguments: pmem is a pointer to the memory partition control block err is a pointer to a variable containing an error message which will be set by this function to either: OS_NO_ERR if the memory partition has been created correctly. OS_MEM_NO_FREE_BLKS if there are no more free memory blocks to allocate to caller OS_MEM_INVALID_PMEM if you passed a NULL pointer for 'pmem' Returns: A pointer to a memory block if no error is detected A pointer to NULL if an error is detected */

Listing 2.30 Functions to allocate a memory block

Page 54: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 53

/* free a memory block to a partition */ INT8U OSMemPut (OS_MEM *pmem, void *pblk) /* Arguments: pmem is a pointer to the memory partition control block blk is a pointer to the memory block being released. Returns: OS_NO_ERR if the memory block was inserted into the partition OS_MEM_FULL if you are returning a memory block to an already FULL memory partition (You freed more blocks than you allocated!) OS_MEM_INVALID_PMEM if you passed a NULL pointer for 'pmem' OS_MEM_INVALID_PBLK if you passed a NULL pointer for the block to release.*/

Listing 2.31 Functions to free the allocated block

The following listing illustrates the memory management example, the memory partition is

used to store the converted analogue data, and the partition blocks will be freed when

these data are sending and no longer need to save them:

INT16U Data[128][32];/*128 blocks with 32bytes each, devoted for the memory partition */ OS_MEM* DataMem; /* Memory Control Blocks to manage the partition */ void* MemBlkPtr; /* Pointer contains a partition block */ void DataStore_Task(void *pdata) { INT8U err; /* Create the Memory Control Blocks associated with the declared partition */ DataMem = OSMemCreate(Data, 128, 32, &err); while(1) {

MemBlkPtr = OSMemGet(DataMem, &err); /* Allocate a block memory */ if (err == OS_NO_ERR) /* if the block allocated successfully */

GetData(MemBlkPtr); /* Store the data into the allocated block */ if (SendReq) {

SendData(MemBlkPtr); /* Send the block of the stored data */ OSMemPut(DataMem, MemBlkPtr); /* freed the allocated block */ } OSTimeDly(100); }}

Listing 2.32 Dynamic memory allocation example

Page 55: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter II µC/OS-II Real Time Operating System

Page 54

CONCLUSION

In this chapter we have seen all the features provided by the µC/OS-II, these includes Tasks

management, Inter-task synchronization and communication, memory management etc.

Therefore, the µC/OS-II stills useless without making the next bottom layer, this layer

interfaces between the µC/OS-II functions and the hardware of the target microcontroller,

this operation is called Porting the µC/OS-II to the specific microcontroller, this will be the

topic of the next chapter.

Page 56: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 55

CHAPTER III

Porting µC/OS-II to the M16C Microcontrollers

INTRODUCTION

µC/OS-II is a real time operating system that can work on several Processors and

Microcontrollers. However, µC/OS-II needs to write some processors specific codes in C

and assembly language, especially codes that manipulate CPU registers and interrupts.

Porting the µC/OS-II to a given processor consists of defining the specific type’s length,

write the context switch routines and the Time track ticks generator (Timer).

A processor can run µC/OS-II if it satisfies the following general requirements [1]:

1. You must have a C compiler for the processor and the C compiler must be able to

produce reentrant code.

2. You must be able to disable and enable interrupts.

3. The processor must support interrupts and you need to provide an interrupt that

occurs at regular intervals (typically between 10 to 100 Hz).

4. The processor must support a hardware stack, and the processor must be able to store

a fair amount of data on the stack (possibly many Kbytes).

5. The processor must have instructions to load and store the stack pointer and other

CPU registers either on the stack or in memory.

Page 57: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 56

Table 3.1 µC/OS-II Hardware and Software structures

µCOS-II Application µC/OS-II Source files

Processor independent files µC/OS-II specific application file

File Function File Function os_core.c Real-time kernel functions

<os_cfg.h>

Include/exclude the µC/OS-II functions, keep only the function needed by the application to optimize the output code size

ucos_ii.h µC/OS-II functions prototype os_mbox.c Message mailbox management os_q.c Message queue management os_sem.c Semaphore management os_task.c Tasks management os_time.c Time management <app_cfg.h> Application configuration os_flag.c Event flag management

<includes.h> This file includes all the header files needed by the application

os_mutex.c Mutual exclusion semaphore os_tmr.c Timer management os_mem.c Memory management

Ported µC/OS-II files Processor dependent files

File Function os_cpu.h Redefining data type and macros os_cpu_c.c Task’s frame stack initialization function OSTaskStkInit() os_cpu_a.asm µC/OS-II assembly functions

Hardware Table 3.1 shows the different files that constitute the µC/OS-II RTOS, the µC/OS-II source

files are processors independent and work on any suitable processor. There is no subject to

modify these file, the specific application header files can be modified depends on the

application needs, <os_cfg.h> contains the options to include or exclude different service

provided by the µC/OS-II. The files to be programmed are <os_cpu.h>, <os_cpu_c.c> and

<os_cpu_a.asm>, these files are specific to each processor.

Page 58: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 57

1. Compiler Specific Data Types and Macros, <OS_CPU.H>

µC/OS-II’s code never makes use of C’s short, int and, long data types because they are

inherently non-portable. Instead, the file <os_cpu.h> contains the processor specific type

that represent the INT8U, INT8S, INT16U etc., it contain also the OS_ENTER_CRITICAL() and

OS_EXIT_CRITICAL() macros definitions to enable and disable the interrupts, it contains

also OS_TASK_SW() macro which performs the task level context switch and should be

referred to vector #0 interrupt.

Listing 3.1 shows the implementation of this file in case of M16C microcontroller:

<OS_CPU.H> /***************************************************************************/

/******* DATA TYPES ******/ /******* (Compiler Specific) *******/

/***************************************************************************/ typedef unsigned char BOOLEAN; typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ typedef signed char INT8S; /* Signed 8 bit quantity */ typedef unsigned int INT16U;/* Unsigned 16 bit quantity */ typedef signed int INT16S;/* Signed 16 bit quantity */ typedef unsigned long INT32U;/* Unsigned 32 bit quantity */ typedef signed long INT32S;/* Signed 32 bit quantity */ typedef float FP32; /* Single precision floating point */ typedef double FP64; /* Double precision floating point */ typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */ typedef INT16U OS_CPU_SR; /* Type of CPU status register */ /***************************************************************************/

/******* INTERRUPTS ENABLE/DISABLE MACROS ******/ /******* (processors Specific) *******/

/***************************************************************************/

#define OS_ENTER_CRITICAL() asm("FCLR I") /* Disable interrupts */ #define OS_EXIT_CRITICAL() asm("FSET I") /* Enable interrupts */ /***************************************************************************/

/******* STACK GROWTH, CONTEXT SWITCH INTERRUPT MAPPING ******/ /******* (processors Specific) *******/

/***************************************************************************/ #define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory */ #define OS_TASK_SW() asm("INT #0") /* Mapped to the software interrupt 0 */

Listing 3.1 os_cpu.h, Compiler specific data types

Page 59: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 58

2. Stack Initialization And User Hook Function, OS_CPU_C.C

This file contains the body of the stack frame initialization function (OSTaskStkInit()), this

function will be called when a task is created in order to simulate an interrupt call and all

the processor registers were pushed onto the stack.

When a task created by either OSTaskCreate() or OSTaskCreateExt(), we should pass to

this function the start address of the task, a pointer called pdata, Task’s top-of-stack and

the task’s priority.

OSTaskStkInit() needs the Task’s address, pdata pointer and Task top-of-stack, Figure 3.1

shows the stack frame configuration that should be achieved by OSTaskStkInit() function in

case of M16C microcontroller.

Figure 3.1 Stack frame initialization of the M16C

The configuration of the figure 3.1 done by OSTaskStkInit(), firstly the address of the task is

pushed at beginning of the stack, next steps will simulate a task call like any other function,

start by pushing the argument pdata, the following would be pushing the return address if

it was normal function, but as the task doesn’t return and considered as infinite loop, there

Low memory

FLG(H) PC(H) FLG(L) PC(L)

FB SB A1 A0 R3

pdata(H) (R2) pdata(L) (R1)

ptos (R0)

High memory

Stack pointer SP

Task address

Stack growth

Page 60: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 59

is no need to push the return address of the caller, the next step pushes all the remaining

CPU registers R3, A0, A1, SB, FB and PC(L), FLG(L), PC(H) and FLH(H) are pushed at the top

of the stack which represents the stack pointer or the stack handle.

The listing 3.2 shows the OSTaskStkInit() function that performs this configuration.

os_cpu_c.c

OS_STK *OSTaskStkInit(void(*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { INT16U *pstk16; INT16U flag; flag = 0x0040; pstk16 = (INT16U *)ptos; pstk16--; /* Simulate ISR entry */ *pstk16-- = (flag & 0x00FF)/* The lowest byte of the FLAG register */ INT32U)task >> 8) & 0x00000F00)/*The highest nibble of the PC register */ | ((flag << 4) & 0xF000); /* The highest nibble of the FLAG register */ *pstk16-- =(((INT32U)task)&0x0000FFFF);/* The lowest bytes of the PC register */ /* Save registers onto stack frame */ *pstk16-- = (INT16U)0xFBFB; /* ... FB register */ *pstk16-- = (INT16U)0x3B3B; /* ... SB register */ *pstk16-- = (INT16U)0xA1A1; /* ... A1 register */ *pstk16-- = (INT16U)0xA0A0; /* ... A0 register */ *pstk16-- = (INT16U)0x3333; /* ... R3 register */ *pstk16-- = (INT32U)pdata >> 16L /* ... Pass argument in R2 register */ *pstk16-- = (INT32U)pdata & 0x0000FFFFL; /*... Pass argument in R1 register*/ *pstk16 = (INT16U)0x0000; /* ... R0 register */ return ((OS_STK *)pstk16); /* Return the top address of the Stack */ }

Listing 3.2 Stack initialization function, OSTaskStkInit

In addition of the OSTaskStkInit, µC/OS-II porting requires to write the some user

functions, these functions are used by the µC/OS-II kernel awareness called µCOS-VIEW

which monitors all the activities of the kernel, these function are:

Page 61: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 60

OSTaskCreateHook(), this function is called whenever a task is created, therefore, it

calls OSView_TaskCreateHook() owned by the OS-VIEW module,

OSTaskDelHook(), called when a task going to be deleted,

OSTaskSwHook(), called when a context switch performed, allows the user to perform

other operations during a context switch, this function should calls OSView_TaskSwHook()

to aware the OS-VIEW that a context switch has occurred.

OSTaskStatHook(), This function is called every second by µC/OS-II's statistics task,

this allows to add new functionality to the statistics task.

OSTaskIdleHook(), Called by the idle task, this hook has been added to allow the user to

do such things as STOP the CPU to conserve power.

OSTCBInitHook(), Called by OS_TCBInit() after setting up most of the TCB.

OSTimeTickHook(), This function is called every tick, used to call OSView_TickHook, this

function is also used to increment the µC/OS-II V2.83 Timers counter.

3. Assembly Language Functions File, OS_CPU_A.ASM

This file contains the functions written in assembly language and the interrupt table of the

µC/OS-II, these functions are:

3.1. OSStartHighRdy()

called by the µC/OS-II when starts running, this function load the stack of the highest

priority ready to run task, assumed that OSTCBHighRdy points on the TCB of the highest

priority ready to run task, this function call the hook function OSTaskSwHook() to inform

the user that the multitasking has just starting before making OSRunning = TRUE.

Page 62: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 61

The listing below show the assembly code of this function in case of the M16C

microcontroller:

OS_CPU_A.ASM

;';;;;;;;;;;;;;;;;;; Run the highest priority task ready to run ;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;; void OSStartHighRdy(void);;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OSStartHighRdy: JSR OSTaskSwHook ;'Call OSTaskSwHook() FCLR U ;'Software interrupt knowledge ;'Point on the stack pointer of the highest priority ready task... MOV.W OSTCBHighRdy, A0 ;'ISP = OSTCBHighRdy->OSTCBStkPtr LDC [A0], ISP MOV.B #01H, OSRunning ;'OSRunning = TRUE ;'Load the highest priority ready task registers into CPU registers POPM R0,R1,R2,R3,A0,A1,SB,FB REIT ;'Launch the highest priority ready task

Listing 3.3 Function called when µC/OS-II start to run the highest priority ready to run task

3.2. OSCtxSw(),

This function performs the task level context switch, as we have seen in chapter II this

routine starts by saving the context registers, loads the context of the highest priority ready

task and launches this task, the listing bellow shows the pseudo code of this function:

void OSCtxSw(void) { Save processor registers; Save the current task’s stack pointer into the current task’s OS_TCB: OSTCBCur->OSTCBStkPtr = Stack pointer; Call user definable OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; Get the stack pointer of the task to resume: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; Restore all processor registers from the new task’s stack; Execute a return from interrupt instruction; }

Listing 3.4 Pseudo code of the function that performs Context Switch function

Page 63: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 62

The implementation of this function in case of M16C microcontroller is shown by listing

3.5, we should note that this routine is mapped in interrupt vector number 0, thus it will be

called by “INT#O” assembly instruction symbolized by OSCtxSw() macro.

OS_CPU_A.ASM ;';;;;;;;;;;;;;;; Performs the task level context switch ;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;; void OSCtxSw(void) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OSCtxSw: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Push the CPU Regs onto current task stack MOV.W OSTCBCur, A0 ;'OSTCBCur->OSTCBStkPtr = SP STC ISP, [A0] JSR OSTaskSwHook ;'Call OSTaskSwHook() MOV.W OSTCBHighRdy, OSTCBCur ;'OSTCBCur = OSTCBHighRdy MOV.W OSPrioHighRdy, OSPrioCur;'OSPrioCur = OSPrioHighRdy MOV.W OSTCBHighRdy, A0 ;'SP = OSTCBHighRdy->OSTCBStkPtr LDC [A0], ISP ;'Restore all processor registers from the new tasks stack POPM R0,R1,R2,R3,A0,A1,SB,FB REIT ;'Launch the highest priority ready task

Listing 3.5 Assembly code of the function that performs task level Context Switch function

3.3. OSIntCtxSw(),

This function performs the interrupt level context switch, which means that is called when

an interrupt occurs, therefore, no need to save the current context as long it has been

already done by the calling interrupt, this what makes difference with the OSCtxSw()

function.

Page 64: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 63

The listing bellow shows the assembly code of this function:

OS_CPU_A.ASM ;';;;;;;;;;;;;; Performs the interrupt level context switch ;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;; void OSIntCtxSw(void) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OSIntCtxSw: JSR OSTaskSwHook ; 'Call OSTaskSwHook() MOV.W OSTCBHighRdy, OSTCBCur ; 'OSTCBCur = OSTCBHighRdy MOV.W OSPrioHighRdy, OSPrioCur ; 'OSPrioCur = OSPrioHighRdy MOV.W OSTCBHighRdy, A0 ; 'SP = OSTCBHighRdy->OSTCBStkPtr LDC [A0], ISP ;'Restore all processor registers from the new tasks stack POPM R0,R1,R2,R3,A0,A1,SB,FB REIT ; 'Launch the highest priority ready task

Listing 3.6 Assembly code of the function that performs the interrupt level Context Switch function

3.4. OSTickISR(),

This ISR is considered as Clock Ticks generator, required by µC/OS-II to keep track of

delays and timeout, any hardware Timer or external pulses can be used to invoke this

routine periodically, the frequency of these pulses should be between 10 and 100Hz,

making too high this frequency would overhead the CPU, in contrast, making it too low will

decrease the reactivity of the µC/OS-II application.

The listing below show the pseudo code of this routine [1]:

void OSTickISR(void) { Save processor registers; Call OSIntEnter() or increment OSIntNesting; Call OSTimeTick(); Call OSIntExit(); Restore processor registers; Execute a return from interrupt instruction; }

Listing 3.7 Pseudo code of the Clock Ticks interrupt

Page 65: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 64

In case of M16C microcontroller, this function can be implemented as the following

assembly code:

OS_CPU_A.ASM ;';;;;;;;;;;;;;;;;;;;; Clock Ticks Generator Timer ;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;; void OSTickISR (void) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OSTickISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ; 'Save current task registers INC.B OSIntNesting ; 'OSIntNesting++ ;' Make sure that this ISR does not interrupt another ISR... CMP.B #1,OSIntNesting ; 'if (OSIntNesting == 1) JNE OSTickISR1 ;'If yes assign the highest priority ready task stack pointer to the current TCB stack pointer MOV.W OSTCBCur, A0 ; 'OSTCBCur->OSTCBStkPtr = SP STC ISP, [A0] OSTickISR1: JSR OSTimeTick ; 'Call OSTimeTick() JSR OSIntExit ; 'Call OSIntExit() to achieve the context switch POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers from the new tasks stack REIT

Listing 3.8 Assembly code of the Clock Tick ISR

Page 66: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter III Porting µC/OS-II to the M16C Microcontrollers

Page 65

To achieve the os_cpu_a.asm file we need to add the interrupt vectors table for OSCtxSw

and OSTickISR ISR, the listing shows such interrupts table in case of IAR C Compiler for

M16C:

OS_CPU_A.ASM ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;; INTERRUPT VECTOR TABLE ;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .COMMON INTVEC ;'Context Switch Interrupt Vector ORG 0 .LWORD OSCtxSw ;'Clock Ticks Generator TIMER A0 ORG 21*4 .LWORD OSTickISR ;'Tx UART Interrupt vector, Used by µCOS-VIEW Module (Optional) .ORG 17*4 .LWORD OSView_TxISR ;'Rx UART Interrupt vector, Used by µCOS-VIEW Module (Optional) .ORG 18*4 .LWORD OSView_RxISR .END

Listing 3.9 Interrupts’ vector table of the os_cpu_a.asm file

CONCLUSION

Once the porting of the µCOS-II to the M16C microcontrollers achieved, any real time

applications that targets the M16C can be built upon this RTOS. These applications will get

the benefits and the facilities that characterize the µCOS-II. This will be the topic addressed

in the next chapter.

Page 67: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 66

CHAPTER IV

µC/OS-II Demonstration Example

INTRODUCTION

In this chapter we will use the µC/OS-II operating system to develop a real time embedded

application, in order to demonstrate the way how to use the features brought by the

µC/OS-II, this application needs to be a multitask system with inter-task communication

using Mailboxes, Message Queues and Events Flag etc.

To achieve this goal I have chosen a data acquisition system of a hermitically closed area,

this application displays in real time the Temperatures, Pressure and the Gates status

(Open/Close) of this area, if the temperature exceeds or the pressure is bellow a certain

predefined values or one of the gates are open, the application makes visual and audible

alarm and display the nature of the emergency on the LCD Display.

Before start programming this application, I have made a µC/OS-II V2.83 Template Project

which should be the start point of our application or any µC/OS-II based application that

targets the M16C Microcontrollers, in all programs that follow the IAR Embedded

Workbench for Mitsubishi V1.36 Compiler and Linker are used.

Page 68: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 67

1. µC/OS-II Project Template

Figure bellow shows the IAR Project of the template, we can see that the project’s files are

grouped in the project file structure and the folders structure as well in four groups, the

first group contains µC/OS-II sources files (Chip independent files), these file constitute the

µC/OS-II core which contain all its features grouped by categories, these file are:

Table 4.1 µC/OS-II Source file µC/OS-II Source file Functions OS_CORE.C µC/OS-II Core functions (Required) OS_TASK.C Tasks management functions (Required) OS_MBOX.C Mailboxes management functions (Optional) OS_SEM.C Semaphores management functions (Optional) OS_MUTEX.C Mutual exclusion semaphore functions (Optional) OS_Q.C Message queues management functions (Optional) OS_MEM.C Memory management functions (Optional) OS_FLAG.C Events Flag management functions (Optional) OS_TIME.C Delay Time functions (Ex. OSTimeDly()) (Optional) OS_TMR.C Timers management functions (Optional) All the µC/OS-II variables, data structures and all the prototypes functions are declared in

ucos_ii.h file, this file should be included in any µC/OS-II application.

The file os_cfg.h contains the directives to include or exclude different features provided by

the µC/OS-II; these directives are useful to optimize the application code by keeping only

the needed functions.

The next important group of files is the µC/OS-II port files; these files are chip specific and

depend of the microcontroller, the first file os_cpu_a.asm contains the functions written in

assembly language, these functions perform the context switch operations (OSCtxSw,

OSIntCtxSw etc…) which require to access to CPU’s registers. The second file ported to the

M16C Microcontrollers is os_cpu_c.c which contains the Task’s Stack initialization and calls

to the user’s hook functions.

Page 69: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 68

Figure 4.1 µC/OS-II Template project and its directories structures

The µC/OS-II project need to be compiled under far memory model, therefore, this model

has been chosen within the IAR Embedded Workbench Options as shown in the Figure 4.2.

In addition, we should include the directory paths that contain the header files of the

project as shown in Figure 4.3.

Communication Setup for the MSA0654 board

µC/OS-II Source directory files

µCOS-View Module directory files

M16C target Port Files

µC/OS-II Application directory files

Page 70: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 69

Figure 4.2 Far memory model used to compile the project

Far memory model

Page 71: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 70

Figure 4.3 Include directories added to the project

The third group contains the embedded part of the µC/OS-View software; this embedded

module communicates with a Microsoft Windows application via RS232 serial port. µC/OS-

View provides an important GUI tool to debug the µC/OS-II RTOS by showing the status of

each managed Task, the name of each task, CPU cycles consumed by each task etc.

OS_VIEWa.ASM and OS_VIEWc.C files depend of the microcontroller and contain the serial

communication interrupts declaration and UART’s registers initialization respectively.

OS_VIEW.C makes the core of µC/OS-View and it is independent of the processor.

The files that belong to the application are grouped in same folder named ‘µC/OS

Application’, depend of the size of the application all the tasks can be written in same file or

make to each task its own file, this folder contains also the file <includes.h> which contains

all the header files used by the application and <ucos_ii.h> file.

Extra include directories of the µC/OS-II Project

Page 72: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 71

Another header file included by µC/OS-II anyway is <app_cfg.h>, this file can be used to

define the constants or data structures for the application (Ex. Tasks priorities, Tasks

Stacks size etc…) without creating another application header file.

The following listing shows the previous file of the project template:

<includes.h>

#define Chip_3062x /* Define the Target Chip */ #include <iom16c62.h> /* include the Target Chip file */ /* Add your application need header file Here */ /* Ex.. #include <string.h> */ /* uCOS-II Header File */ #include <uCOS_II.H> /* uCOS-II OS Viewer header files */ #include <OS_VIEWc.H> #include <OS_VIEW.H>

<app_cfg.h> /* Define Tasks’ priorities here */ #define STARTUP_PRIO 40 #define TASK1_PRIO 5 #define TASK2_PRIO 10 /* Tasks’ Stack size */ #define TASK_STK_SIZE 128 /* CPU Clock frequency */ #define CPU_CLK_FREQ 16000000 /* Timer initialization of the µC/OS-II Clock Ticks Generator */ void InitTick(void);

Page 73: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 72

The following listing contains the template of a µC/OS-II application:

Main.C /* include the global header file */ #include <includes.h> /* Startup Task’s Stack declaration */ OS_STK STARTUP_STK[TASK_STK_SIZE]; /* Other Tasks’ Stacks declaration go here */ OS_STK TASK1_STK[TASK_STK_SIZE]; /* Task1’s Stack */ OS_STK TASK2_STK[TASK_STK_SIZE]; /* Task2’s Stack */ /* Tasks’ Core Go Here */ /*********************** Task 1 ************************/ void TASK1(void *pdata) { INT32U X = 1, Y = 0; /* Task 1 Core */ while(1) { Y += sqrt(X++^2+2); OSTimeDlyHMSM(0, 0, 0, 100); } } /*********************** Task 2 ***********************/ void TASK2(void *pdata) { INT32U Z = 0, W = 1; /* Task 2 Core */ while(1) { W += sqrt(Z++) + 1; OSTimeDlyHMSM(0, 0, 0, 200); } } /* Startup Task’s Core */ void STARTUP_TASK(void *pdata) { INT8U err; InitTick(); /* Initialize the Clock Ticks Timer */ OSStatInit(); /* Initialize the Statistic Task (Optional) */ OSView_Init(); /* Initialize the OS Viewer Module (Optional) */ /* Use OS-View function to send text to the UART Terminal */ OSView_TxStr(“ UCOS-II V2.83\n DEMONSTRATION\n PROGRAM\n “,0); /* Other initialization functions go here... */ /* Create Other Tasks Here */ /* Create Task 1 */ OSTaskCreateExt(TASK1, (void *)0, &TASK1_STK[TASK_STK_SIZE-1], TASK1_PRIO, TASK1_PRIO, &TASK1_STK[0], TASK_STK_SIZE,(void*)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to the created Task (TASK1) */ OSTaskNameSet(TASK1_PRIO, ”TASK1”, &err); /* Create Task 2 */

Page 74: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 73

OSTaskCreateExt(TASK2, (void *)0, &TASK2_STK[TASK_STK_SIZE-1], TASK2_PRIO, TASK2_PRIO, &TASK2_STK[0], TASK_STK_SIZE,(void*)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to the created Task (TASK2) */ OSTaskNameSet(TASK2_PRIO, ”TASK2”, &err); while(1) { /* Startup Task’s Core */ /*.....................*/ OSTimeDlyHMSM(0, 0, 1, 0); /* Delay the Task for 1 Sec */ } } /* Timer initialization of the µC/OS-II Clock Ticks Generator */ void InitTick(void) { /* Make reload value to generate OS_TICKS_PER_SEC Ticks/Sec */ TA0 = (INT16U)(( 2000000 / OS_TICKS_PER_SEC – 1)); TA0MR = 0x40; /* F8 as clock source = 500ns */ TA0IC = 0x01; /* Timer interrupt register, priority 1 (lowest)*/ TABSR |= 0x01; /* Set Timer count start flag */ PRCR = 0x01; /* Enable write to Processor mode */ CM0 &= 0x1F; /* main clock division register: no division */ CM1 &= 0x3F; PRCR = 0x00; /* Disable access to processor and clock mode */ } void main (void) { OSInit(); /* Initialize the uC/OS-II */ /********** Create the StartUp Task ***********/ OSTaskCreateExt(STARTUP_TASK, /* Task’s Void */ (void *)0, /* No value passed to the task */ &STARTUP_STK[TASK_STK_SIZE – 1],/* Top of the Task’s Stack */ STARTUP_PRIO, /* Task Priority */ STARTUP_PRIO, /* Task ID */ &STARTUP_STK[0], /* Bottom of the Task’s Stack */ TASK_STK_SIZE, /* Task’s Stack Size */ (void*)0, /* No TCB Extension */ /* Enable stack checking + clear stack */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /*Give a Name to the Startup Task */ OSTaskNameSet(STARTUP_PRIO,”STARTUP TASK”, (void*)0); OSStart(); /* Start running the operating system */ }

Page 75: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 74

In the previous application I have used the low priority “Startup” Task to initialize the Clock

Ticks Timer and create Task1 and Task2, Task1 and Task2 make some mathematical

operations to consume some CPU cycles.

2. µC/OS-View Module

The µC/OS-View is a combination of an embedded module that resides in the target

microcontroller with µC/OS-II application and a Windows application, the Windows

application communicate with the embedded module via the rs232 port and allows to view

the status of the tasks managed by the µC/OS-II. The µC/OS-View V1.10 allows to shows the

following information: [7]

The address of the TCB of each task;

The name of each task;

The status (Ready, delayed, waiting on event) of each task;

The number of ticks remaining for a timeout if a task is delayed or wait on timed events;

The amount of stack space used and left for each task;

The percentage of CPU time used by each task relative to all the tasks;

The number of times each task has been switched to;

The execution profile of each task;

‘Suspend’ the tick interrupt from decrementing delays and timeouts of tasks. However,

you can ‘step’ on tick at a time by pressing the F8 key from the Windows application. The

F6 key cancels this mode, the F7 key enables this mode and the F8 key enables one tick to

be processed.

Pass keystrokes to your application from the ‘Terminal’ window. In other words, you

can now send commands to your product from the Windows application. You determine

the command structure.

Output ASCII strings from the target to the ‘Terminal’ window. These ASCII strings are

target specific and thus you can define those specific to your product.

Page 76: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 75

To use the µC/OS-View terminal, these two functions are used to send or receive ASCIL

character respectively:

To send data:

OSView_TxStr(char *s, INT16U dly);/* Send ASCII string to terminal window */ /* dly: Time delay to send the string s */

To receive data:

/* Install the function that receive the data */ OSView_TerminalRxSetCallback(TerminalCallback); /* The code for the callback looks as follows: */ void TerminalCallback (INT8U data) { /* The received data will be carried by ‘data’ variable */ }

After many experiences, I have noted that writing all interrupts as described in Chapter II

causes the crash of the application when these interrupt are often triggered, therefore, I

have kept this format only in the case of Clock Ticks Timer (OSTickISR) in order to perform

the interrupt level context switch, the other interrupts notify the µC/OS-II that an interrupt

has occurred without making the context switch by incrementing OSIntNesting when enter

to interrupt and decrements this variable when excite the interrupt, the listing bellow

shows the new format to write an interrupt (Ex. Serial Port data reception interrupt Tx).

OSView_TxISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save the current running task registers INC.B OSIntNesting ;'OSIntNesting++, JSR OSView_TxISRHandler ;'Call OSView_TxISRHandler() DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers from the new task stack REIT

Page 77: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 76

By using the µC/OS-View application we can show the Tasks’ status and the CPU’s cycles

consumed by each task, we can show also how often the context switches performed to a

certain task. µC/OS-View displays the Task’s stack size and the amount left, Figure 4.4

shows the µC/OS-View windows that provide Tasks’ status for the previous sample. In this

example the number of Clock Ticks has been fixed to 100 (OS_TICKS_PER_SEC = 100,

located in <os_cfg.h>).

Figure 4.4 Tasks’ Status and µC/OS-II running information given by µC/OS-View

µCOS-View Terminal window

CPU

’s T

ime

cons

umed

Tim

e re

mai

n to

ta

sk to

be

read

y

Task

’s S

tatu

s

Task

’s T

CB

Addr

ess

Num

ber

of

Cont

ext S

wit

ches

to

eac

h ta

sk s

o fa

r

Stac

k us

ed

/Sta

ck S

ize

and

Stac

k ad

dres

s

Task

’s N

ame

Task

‘s p

rior

ity

Even

t’s a

ddre

ss

wai

ted

for

Page 78: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 77

In order to make the CPU load of the “Task1”, “Task2” and “Startup” Task visible on the

µC/OS-II CPU Load graph (very low compared to the idle task’s CPU load), I have multiplied

the CPU load of each task by 100 in os_view.c file as follow:

os_view.c …………………………………………… OSView_TxStoINT32U((INT32U)ptcb->OSTCBEventPtr);/*Pointer to event task is waiting for*/ OSView_TxStoINT32U((INT32U)ptcb->OSTCBDly);/*Timeout (i.e. ticks of delayed task)*/

OSView_TxStoINT32U(ptcb->OSTCBCyclesTot*100);/* ExecTime */ OSView_TxStoINT32U(ptcb->OSTCBCtxSwCtr); /* NumActivations */ ……………………………………………..

Figure 4.5 Graph of the CPU percentage consumed by each task across the time

TASK1’s Activity

TASK2’s Activity

Startup Task‘s Activity

Page 79: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 78

The graph of the figure 4.5 provides useful information about the CPU consumption of each

task, the first note we can see is that 98.53% of the time is spent in the idle Task which

means the rest time of the µC/OS-II, in contrast, “Task1” and “Task2” consume only 0.63

and 0.65% of CPU time respectively which is very low, this means that still plenty of CPU

time to add more Tasks and Codes without the risk to overload the CPU. Therefore, these

statistics proves the benefit of the µC/OS-II regarding the CPU load optimization.

3. µC/OS-II Demonstration Example

The previous project template will be used to develop the data acquisition system

described briefly at the beginning of this chapter, to get benefit of the most important

feature of the µC/OS-II, which is the multitasking, the whole system will be divided into

tasks, each task performs its job independently of other tasks, therefore, the system uses

µC/OS-II Inter-task communications tools to make different tasks interact with each other

in order to achieve the main functions of this system. Figure 4.6 shows the block diagram of

this system:

Page 80: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 79

Figure 4.6 Block diagram of the developed system

LCD MESSAGES TASK

Computer

SOUND ALARM TASK

Mailbox

Emergency Mailbox

Mails Queue

DATA CAPTURE TASK

SERIAL COM TASK

Suspend /Resume

Mails Queue

RS232

Messages Store

Memory

Temperature Sensor

Gates Open/Close

Sensors

Pressure Sensor

LCD Semaphore

Messages contain system status (Temperature, Pressure, Gates Close/Open)

Emergency Messages

Resume sending the

stored messages

when memory 90%

full

Stored Messages to be sent to PC

Message to make Leds blink when

emergency

Sent Audible Message when

emergency

LEDS ALARM TASK

Mailbox

CONFIG MENU TASK

1 2 3 F

4 5 6 E

7 8 9 D

A 0 B C

Event Flag

Keypad

Buzzer

Page 81: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 80

The data acquisition system of the precedent block diagram contains the following tasks:

1. “Sound Alarm” Task: This task drives the Buzzer to generate the warning alarm

sounds, this task’s program contains a Mailbox to receive messages from outside, and this

message carries the code pattern of the desired sound.

2. “LEDs Alarm” Task: To pay the intention of the user when the system in emergency, I

have added a visual alarm to the previous audio alarm, therefore, the “Leds Alarm” Task

performs this job by controlling the eight Leds of the board, this task contains a mailbox to

receive the emergency message which contains the pattern of the Leds that should be

bright and if these Leds blink or bright in permanence.

3. “LCD Messages” Task: This task drives the LCD Display in order to show the received

messages, thus, this task contains a Messages Queue as long these messages are displayed

one by one with certain time between each two messages. I have added to this task an

Emergency Mailbox in which this task receives the emergency messages that they can’t

wait on the queue and should be displayed immediately. The messages posted to this task

contain two text lines of 16 characters each to be displayed on the 2-Line 16-Charaters LCD

Display. These messages will be stored on a Memory Partition with limited size, therefore,

this partition should be used dynamically and freed each time it is 90% full, the messages

stored in the freed area wont be lost, but send to the Personal Computer via the Serial Port

where they can be stored on files, this is will be the mission of the next Task which is

“Serial Com” Task.

4. “Serial Com” Task: The job of this task is to send the stored messages to the PC via

Serial Port when the memory area is 90% full, each block sent of the memory partition will

be freed by this task, to achieve this a messages queue is used by this task which contain a

pointer to each stored message, this task will be suspended by the “LCD Message” Task

until the time to freed the memory comes in which it will be resumed and suspended again

when 90% of the memory is free.

Page 82: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 81

5. “Config Menu” Task: The role of this task is to manage different configuration menus of

this application, this menu contain two major items, the Setup menu; in this menu the user

can setup different parameters of the system (Ex. Maximum allowed Temperature,

Minimum allowed Pressure, Alarmed/Disarmed gates, Time and Date). The second menu is

the View item, by selecting this menu the user can display the status of the µC/OS-II, the

items that can be displayed are: CPU Usage (%), Free Messages Memory (%), Number of

Clock Tick and Context Switches, Time/Date. This task waits for an Event Flag to be

signaled by the keypad’s interrupt when any button pressed, at this time this task becomes

ready to run and ask for the LCD Display via the LCD Display Semaphore in order to display

the Menu’s items.

6. “Data Capture” Task: This task is the most important task in the system and the

responsiveness of the system depends of the responsiveness of this task, thus, this task was

given the highest priority among all tasks. By the means of Temperature and Pressure

sensors and the eight switches, this task picks up the Temperature, Pressure and Status of

the eight gates, these values are stamped by the Time and Date and send to the “LCD

Message” Task into its Messages Queue in order to be displayed one by one on the LCD

Display, in case when the Temperature is above the predefined value or the pressure is

bellow the minimum pressure or either one gate or more are open, this task posts an

emergency message into the emergency Mailbox of the “LCD Message” Task to notify the

user about the nature of the emergency problem (Temperature too high, Pressure too low

or which gate is open). In addition, this task sends messages to “Leds Alarm” Task and

“Sound Alarm” Task to their Mailboxes in order to make a visual and an audible alarm, so

the emergency will more severe.

Page 83: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 82

The LCD Semaphore will be used to get exclusive access to the LCD Display, This Display is

shared by the “Config Menu” Task and the “LCD Message” Task, therefore, when one task of

them gets access to this resource the other task should wait until released. This will avoid

the mixture of the displayed characters (Menu’s items and Messages) of both tasks.

4. MSA0654 Development Board

The application designed in this chapter will be tested on the MITSUBISHI (MSA0654-

MEAUST) development board with its daughter board; this board contains the M16C62

microcontroller as core. In addition, this board was built with many Input/Output

peripherals (LCD Displays, LEDs, Keypad, and Buzzer etc.). Figure 4.7 shows this

development board connected with its daughter board.

Page 84: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 83

Figure 4.7 MSA0654 development board, used to run the application

Main board

Daughter board

M16C62P

Page 85: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 84

5. Test and Demonstration

Figure 4.8 shows the Application’s project designed with the IAR IDE V1.36, the previous

project template was used to develop this application.

Figure 4.8 The µC/OS-II Application project

Application’s directory

MSA0654 Board Drivers

Page 86: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 85

Figure 4.9 Tasks and µC/OS-II status viewed by µC/OS-View

In addition of the tasks shown by the Figure 4.8, the “Clock” Task has been added to provide

Real Time Clock in order to stamp the stored messages by the time, which is a necessity in

any archiving process.

µC/OS-II MSA0654 DRIVERS directory contains the MSA0654 board driver functions, and

provides the routines to use different peripherals of this board (Ex. LCD, Keypad, Leds, AD

Converters etc…). The assembly file uCOS-II-MSA0654-DRIVERS-ASM.ASM contains the

interrupt service routines and the interrupt vectors table needed by MSA0654 Board

Drivers and should be written in assembly language.

Page 87: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 86

5.1. Normal Running of the Application

By using the µC/OS-View kernel debugger, we can monitor the profile of the µC/OS-II and each task

of the application, Figures bellow shows that profile when the Application runs in the usual time

(Temperature, Pressure and Gates status displayed on the LCD, no emergency, no message sent via

RS232 Port, no menus).

Figure 4.10 Graphs shows Tasks’ CPU load in normal operation of the application

CPU Overload when LCD‘s

functions called

Page 88: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 87

From the Tasks’ information given by Figure 4.9 and Figure 4.10 we can comment on the

status of each task as follow:

“Data Capture” Task: This task spend the majority of time delayed and consumes a

small amount of CPU cycles when picking up the data or sending messages;

“LCD Message” Task: This is the most one that loads the CPU; this is caused by LCD

Display’s functions that should call the dummy function to simulate a short delay to follow

the LCD module responsiveness. In addition, this task checks the Message Queue and the

emergency Mailbox if a message was posted to display it on the LCD display;

“LEDS Alarm” Task: By lighting the LEDs one by one to show which gates are alarmed,

this task loads slightly the CPU (0.01%), in addition, this task check its Mailbox if a message

was sent in case of emergency to light the LEDs according the message content;

“Serial Com” Task: Suspended until the memory is 90% full where will be resumed by

the “LCD Message” Task;

“Config Menu” Task: This task waits for an Event Flags to be signaled by the means of

the keypad’s interrupt when the user presses any button. Therefore, this task spends all its

time in waiting state and it doesn’t load the CPU while in this state.

“Sound Alarm” Task: This task waits for a message to be posted in its Mailbox, which

carries the emergency sound Alarm, this task doesn’t consume any CPU time while waiting

for this message;

“Clock” Task: This task delays it self by one second to generate the time and date,

therefore, this task doesn’t consume too much CPU time.

By observing the CPU load of the whole system, we can note that the great CPU time

(99.63%) is consumed by the Idle Task; the statistic task consumes 0.32% used to compute

the µC/OS-II parameters and different Task’s profiles.

From these results we can conclude that CPU stills not full used and many other tasks can

be added to the µC/OS-II without overload the CPU or miss the deadline of each task.

Page 89: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 88

5.2. When Keypad’s button pressed

In this case the Event Flags which waited to be set by the “Config Menus” Task will be

signaled by the Keypad’s interrupt, this will lead to make ready to run this task, after asking

for the LCD Display via the LCD Display Semaphore the “Config Menu” Task displays the

menu’s items and the user can navigates through different items to change the system

parameters or view some information about the µC/OS-II or display time and date.

When “Config Menu” task takes over the LCD Display, the “LCD Message” task loads less the

CPU as long the LCD is not available to call its functions. The graphs of the figure bellow

demonstrate this case:

Figure 4.11 “Config Menu” Task comes active to manage the menus items when keypad

pressed

“Config Menu” Task comes active when keypad pressed

“Config Menu” Task goes back to wait state when exit menu

Page 90: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 89

5.3. When the Messages memory is full

When the memory partition where all messages are stored is 90% full. These messages

should be sent to the Computer via the RS232 port in order to be saved in files, this will be

achieved by the “Serial Com” Task which will be resumed after was suspended by the “LCD

Messages” Task, the graphs of the figure bellow shows this scenario.

Figure 4.12 “Serial Com” Task resumed to empty the memory when it is 90% full

“Serial Com” Resumed when

memory full

“Serial Com” Suspended

when memory emptied

Messages sent by the “Serial Com”

Task

Page 91: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 90

5.4. In Case of emergency

The Emergency case happened when the temperature is too high or the pressure is too low

or either any of the eight gates is open, in this case the system emits loud alarm sound

along with LEDs blinking done by the “Alarm Sound” and “LEDs Alarm” Tasks respectively,

in addition, the “LCD Messages” Task sends to the LCD the nature of the emergency. The

Figures 4.13 shows the activities of each task involved in the Emergency state.

Figure 4.13 CPU load of the involved Tasks in case of emergency

“LCD Messages” and “DATA Capture” Tasks load more the CPU

“Sound Alarm” Task comes active and plays the Emergency Alarm

sound

Page 92: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 91

Figure 4.13 shows what is happened in the case of emergency regarding the activities of

different tasks. The “Sound Alarm” Task which was waiting for a message starts running

and emits the Alarm sound; at that time this task loads the CPU slightly while emitting this

sound. The “DATA Capture” and “LCD Messages” Tasks consume much more CPU’s cycles

because the Emergency message will be sent and received by these tasks respectively more

frequently as long the emergency state stills present.

When there is no emergency the LCD Displays shows the temperature, pressure and the

gates status (Open/Close). Otherwise, in case of emergency it displays the nature of this

emergency (Temperature too high, Pressure too low or which gate has been broken), Table

4.2 shows the messages displayed and LEDs status for different cases.

Page 93: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 92

Table 4.2 LCD’s Messages displayed Messages displayed

and LEDs status Description

Message to display the actual Temperature degree Value

Message to display the actual Pressure Value

Status of the all Gates of the closed area

Emergency Message when the Temperature is higher than the maximum temperature preset value. All LEDs blinking and Alarm

sound emitted

Emergency Message when the Pressure is lower than the minimum preset value, four

LEDs blinking and Alarm Sound emitted

Emergency Message when any of the armed gate is open, the number of the gate

displayed, the respective LED blinking and Alarm Sound emitted

Page 94: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 93

The LCD Display can show other parameters of the µC/OS-II or the application (CPU usage,

Amount of the free memory, number Clock ticks and Context Switches etc). These

parameters can be shown by selecting the View item of the Main Menu. By selecting the

setup menu the user can set different parameters of the application (Max Temperature, Min

Pressure, Alarms/Disarms each gate, set Time/Date). Table 4.3 summarizes the displayed

parameters.

Table 4.3 Setup and View Menu items

Setup or View Menu Items Description

Setup Menu to change the Maximum Allowed Temperature

Setup Menu to change the Minimum Allowed Pressure

Setup Menu to Enable/Disable the desired Gates

Time/Date View Menu item, view the Real Time Clock

CPU Usage View Menu item, Display the global CPU Load of the application

Clock Ticks/Context Switches View Menu item, Display the number of Clock Ticks and

Context Switches

Free Mem View Menu item, Display the amount of the remaining memory, used to

store the messages

Page 95: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Chapter IV µC/OS-II Demonstration Example

Page 94

CONCLUSION

The application built in this chapter has well demonstrated the advantages of the µC/OS-II

and the facilities brought by this RTOS to design a real time multitasking application, these

advantages can be summarized as follow:

1. Total independence between different programmed Tasks, in other word, there is

no limit made by other tasks when developing a task, especially regarding the

waiting time or the inertia made by other tasks;

2. Tasks can easily interact with each other or exchange information using different

means provided by µC/OS-II (Mailbox, Messages Queue, Event flags, etc.), the

functions that manipulate these services are very trivial and easy to use;

3. No more worries about the resources sharing and the risk of corruption, this

problem can be resolved efficiently by using the semaphore or mutual exclusion

semaphore and they are manipulated with very simple functions.

The robustness and the reliability of the developed application have been tested as well, by

keeping this application running for many hours. Therefore, no crash was noted along with

trying different circumstances that may occur.

Page 96: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Conclusion

Page 95

Conclusion In this project, porting the µC/OS-II to the M16C microcontrollers has been achieved with

full success. to illustrate this; I have developed and tested the ported files by developing a

real time Data Monitor on the µC/OS-II platform that targets the M16C62P microcontroller.

In addition to the real time responsiveness of this application, I have used all the features

provided by the µC/OS-II (Semaphore, Mailbox, Event flags etc) to give a practical

demonstration on how to use these features.

Further more, in order to make the ported files useful for future works or for students who

wish to develop µC/OS-II applications I have created on the joint CD a fully commented

template project to make it easy to use. Two templates have been designed to be compiled

under M16C IAR Workbench for V1.36 and V2.12.

With the different Input/Output (LCD, LEDs, Switches etc) built within the MSA0654

developing board, the real time responsiveness of the µC/OS-II has been well

demonstrated, by observing the delay between an action made on the input (Keypad,

Potentiometer that simulates the temperature or pressure, switches to simulate the Gates

status) and the reaction that results from these actions which can be observed on the LCD,

LEDS and the Buzzer speaker, and this in different experimental conditions The delays

measured were very short and imperceptible by humans and were therefore, acceptable.

To achieve this work, I felt the necessity to make an attractive demonstration about what is

happening inside the µC/OS-II and the status of each application task. Luckily, the µC/OS-II

has a Monitor called µC/OS-View, made from a windows application and an embedded

module built within the µC/OS-II project, the embedded files should be ported to the M16C

microcontrollers by writing the hardware initialization of the serial communication and the

Transmission/Reception interrupts for a specific microcontroller.

Page 97: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Conclusion

Page 96

The µC/OS-View works along with its graphical windows, and provides the status (Ready,

Wait for a delay to expire or Semaphore or Message etc) and shows graphically the activity

(CPU Load) of each task, this makes an excellent tool to debug the µC/OS-II application by

the step-by-step execution and to optimize the global CPU load by knowing the CPU load of

each task.

The global CPU load of the developed application which doesn’t exceed 9% demonstrates

the major benefit gained when using the µC/OS-II thanks to the optimized way that µC/OS-

II uses the CPU, where tasks can be added without affecting the responsiveness of other

tasks.

In other word, someone who uses the µC/OS-II for the first time will never go back to use

the traditional way to develop a medium or large applications, because of the new

attractive structure of this RTOS, and very easy to use functions provided.

The disadvantage that maybe considered when using the µC/OS-II is the extra memory

needed to run this RTOS, which is about 20Kb when all the features are enabled.

Page 98: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

References

Page 97

REFERENCES

[1] Microc/OS-II: The Real-Time Kernel, Second Edition, 2002, Jean J. Labrosse

[2] Introduction to Real-Time operating systems (2001) Robert Betz

[3] μC/OS-II and the Renesas M16C Processors, Application Note AN-1019 Jean J. Labrosse

[4] μC/OS-II and Event Flags, Application Note AN-1007A Jean J. Labrosse

[5] μC/OS-II and Mutual Exclusion Semaphores, Application Note AN-1002 Jean J. Labrosse

[6] Introduction to Real-Time Operating Systems, David Kalinsky, Ph.D. Enea Embedded

Technology, San Jose, California USA

[7] μC/OS-View V1.10 User’s Manual, Micriμm, Inc.

[8] Open Source Real Time Operating Systems Overview T. Straumann, SSRL, Menlo Park,

USA

[9] http://www.whatis.com/

[10] μC/OS-View V1.10 User’s Manual, Micriμm, Inc.

Page 99: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 98

Appendix I

Programs listings of the Project Application

Main.C

/****************************************************************************/ /* Main.C */ /* This file contains the "Startup" Task to create all of the other tasks */ /* initialize the MSA0654 board hardware and the Ticks Timer */ /* */ /****************************************************************************/ #include <includes.h> /* Include the globale header file */ /* void pointer to each Task managed by uCOS-II */ extern void LCDMESSAGES_TASK(void *pdata); extern void DATACAPTURE_TASK(void *pdata); extern void LEDSALARM_TASK(void *pdata); extern void SERIALCOM_TASK(void *pdata); extern void CONFIGMENU_TASK(void *pdata); extern void SOUNDALARM_TASK(void *pdata); extern void CLOCK_TASK(void *data); /* uCOS-II's Clock Tick Timer initialization */ void InitTick(void); /* Stack of each Task managed by uCOS-II */ OS_STK STARTUP_STK[TASK_STK_SIZE]; OS_STK DATACAPTURE_STK[TASK_STK_SIZE]; OS_STK LEDSALARM_STK[TASK_STK_SIZE]; OS_STK SERIALCOM_STK[TASK_STK_SIZE]; OS_STK CONFIGMENU_STK[TASK_STK_SIZE]; OS_STK SOUNDALARM_STK[TASK_STK_SIZE]; OS_STK LCDMESSAGES_STK[TASK_STK_SIZE]; OS_STK CLOCK_STK[TASK_STK_SIZE];

Page 100: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 99

/* Startup Task */ /* This Task initializes the Clock Tick timer and different MSA0654 board peripherals and ceates the other tasks, at the end it deletes itself */ static void STARTUP_TASK(void *pdata) { INT8U err; InitTick(); /* Initialize the Clock Tick Timer */ LcdDspInit(); /* Initialize the LCD Display */ ADCInit(); /* Initialize the ADC Converter */ OSStatInit(); /* Initialize the Statistic Task */ OSView_Init(); /* Initialize the uCOS-VIEW Module */ /* Dispplay the Welcome Screen */ LcdDspChars(0, 0, " uC/OS-II V2.83 ", 16); LcdDspChars(1, 0, " DATA MONITOR ", 16); /* Create the "DATA Capture" Task */ OSTaskCreateExt(DATACAPTURE_TASK, (void *)0, &DATACAPTURE_STK[TASK_STK_SIZE - 1], DATACAPTURE_PRIO, DATACAPTURE_PRIO, &DATACAPTURE_STK[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to "Data Capture" Task */ OSTaskNameSet(DATACAPTURE_PRIO, "DATA CAPTURE", &err); /* Create the "LCD Messages" Task */ OSTaskCreateExt(LCDMESSAGES_TASK, (void *)0, &LCDMESSAGES_STK[TASK_STK_SIZE - 1], LCDMESSAGES_PRIO, LCDMESSAGES_PRIO, &LCDMESSAGES_STK[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to "LCD Messages" Task */ OSTaskNameSet(LCDMESSAGES_PRIO, "LCD MESSAGES", &err); /* Create the "LEDs Alarm" Task */ OSTaskCreateExt(LEDSALARM_TASK, (void *)0, &LEDSALARM_STK[TASK_STK_SIZE - 1], LEDSALARM_PRIO, LEDSALARM_PRIO, &LEDSALARM_STK[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to "LEDs Task" Task */ OSTaskNameSet(LEDSALARM_PRIO,"LEDS ALARM", &err); /* Create the "Serial Com" Task */ OSTaskCreateExt(SERIALCOM_TASK, (void *)0, &SERIALCOM_STK[TASK_STK_SIZE - 1], SERIALCOM_PRIO, SERIALCOM_PRIO,

Page 101: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 100

&SERIALCOM_STK[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to "Serial Communicator" Task */ OSTaskNameSet(SERIALCOM_PRIO,"SERIAL COM", &err); /* Create the "Config Menu" Task */ OSTaskCreateExt(CONFIGMENU_TASK, (void *)0, &CONFIGMENU_STK[TASK_STK_SIZE - 1], CONFIGMENU_PRIO, CONFIGMENU_PRIO, &CONFIGMENU_STK[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to "Config Menus" Task */ OSTaskNameSet(CONFIGMENU_PRIO,"CONFIG MENUS", &err); /* Create the "Sound Alarm" Task */ OSTaskCreateExt(SOUNDALARM_TASK, (void *)0, &SOUNDALARM_STK[TASK_STK_SIZE - 1], SOUNDALARM_PRIO, SOUNDALARM_PRIO, &SOUNDALARM_STK[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Give a name to "Sound Alarm" Task */ OSTaskNameSet(SOUNDALARM_PRIO,"SOUND ALARM", &err); /* Create the "Clock" Task */ OSTaskCreateExt(CLOCK_TASK, (void *)0, &CLOCK_STK[TASK_STK_SIZE-1], CLOCK_PRIO, CLOCK_PRIO, &CLOCK_STK[0], TASK_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); OSTaskNameSet(CLOCK_PRIO,"CLOCK", &err); /* Give a name to "Startup" Task */ OSTaskNameSet(STARTUP_PRIO, "STARTUP", &err); OSTaskDel(OS_PRIO_SELF);/* Delete the Startup task */ } /* Timer initialization of the µCOS-II Clock Ticks Generator */ void InitTick(void) /* Make reload value to generate OS_TICKS_PER_SEC Ticks/Sec */ { TA0 = (INT16U)(( 2000000 / OS_TICKS_PER_SEC - 1)); TA0MR = 0x40; /* F8 as clock source = 500ns */ TA0IC = 0x01; /* Timer interrupt register, priority 1 (lowest) */ TABSR |= 0x01; /* Set Timer count start flag */ PRCR = 0x01; /* Enable write to Processor mode */ CM0 &= 0x1F; /* main clock division register: no division */

Page 102: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 101

CM1 &= 0x3F; PRCR = 0x00; /* Disable access to processor and clock mode */ } void main (void) { OSInit(); /* Initialize the uCOS-II */ /********** Create the StartUp Task ***********/ OSTaskCreateExt(STARTUP_TASK, /* Task's Void */ (void *)0, /* No value passed to the task */ &STARTUP_STK[TASK_STK_SIZE - 1], /* Top of the Task's Stack */ STARTUP_PRIO, /* Task Priority */ STARTUP_PRIO, /* Task ID */ &STARTUP_STK[0], /* Bottom of the Task's Stack */ TASK_STK_SIZE, /* Task's Stack Size */ (void*)0, /* No TCB Extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Enable stack checking + clear stack */ OSTaskNameSet(STARTUP_PRIO,"STARTUP TASK", (void*)0); /* Give a name to Startup Task */ OSStart(); /* Start running the operating system */ }

Main.h

/* Message Format received by "LCD Messages" Task */ typedef struct { char TimeStamp[21];/* Time Stamp */ char Line_0[18]; /* First line of the LCD Display */ char Line_1[18]; /* Second line of the second line */ } LcdMsg; /* Message Format received by "LEDs Alarm" Task */ typedef struct { INT8U Pattern; /* LEDs pattern to be light */ BOOLEAN Blink; /* Specify if LEDs blink or not */ } LedMsg; /* Decimal to String conversion function prototype */ void DecToStr (INT8U *Str, INT32U Dec, INT8U digits); /* String to Decimal conversion function prototype */ INT32U StrToDec(INT8U *Str, INT8U Digits); /* Picking up the time function prototype */ char* GetTime(char* Time); /* Picking up the date function prototype */ char* GetDate(char* Date);

include.h

#define Chip_3062x /* Define the Target Chip */ #include <iom16c62.h> /* include the Target Chip file */

Page 103: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 102

/* Application needded header file Here */ #include <string.h> /* uCOS-II Header File */ #include <uCOS_II.H> /* MSA0654 board drivers */ #include "uCOS-II-MSA0654-DRIVERS.H" /* Application header file */ #include "main.h" /* uCOS-II OS Viewer header files */ #include "OS_VIEWc.H" #include "OS_VIEW.H"

app_cfg.h

/* Priority of each Task */ #define STARTUP_PRIO 8 #define DATACAPTURE_PRIO 10 #define LCDMESSAGES_PRIO 20 #define LEDSALARM_PRIO 30 #define SERIALCOM_PRIO 35 #define CONFIGMENU_PRIO 40 #define SOUNDALARM_PRIO 45 #define CLOCK_PRIO 50 #define TASK_STK_SIZE 128 /* Same stack size for all task */ #define CPU_CLK_FREQ 16000000 /* CPU Frequency */ /* memory management constant */ #define MsgLogNBlk 64 /* # of the memory partition block */ #define MsgLogMinNBlk 10 /* # of minimum free blks */ #define MsgLogMaxNBlk 60 /* # of maximum used blks */ #define MsgLogBlkSize 64 /* Size of each block in byte */ /* Sound code for each emergency */ #define HighTempSoundAlarm 0 #define LowPressSoundAlarm 1 #define GateOpenSoundAlarm 2 #define SoundPaternSize 4 /* numbers of tones in sound pattern */ /* LCD Messages message displaying period */ #define MsgPeriod 40 /* number of messages that can wait on LCD Queue */ #define LcdQueueSize 8 /* number of memory blocks that can stored in the Queue to be sent via rs232 */ #define SerialComQueueSize MsgLogNBlk

uCOS-TASK1-LCDMESSAGES.C

/****************************************************************************/ /* uCOS-TASK1-LCDMESSAGES.C */

Page 104: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 103

/* This file contains the "Lcd Message" Task, "Lcd Message" receives */ /* LCD Messages, display them on the LCD and stores them in the */ /* memory partition. */ /****************************************************************************/ #include <includes.h> /* Include the globale header file */ void* LcdMsgQPTR[LcdQueueSize]; /* Queue of pointers to the queued Messages */ OS_EVENT *LcdDspSem; /* LCD Semaphore to access to LCD Display */ OS_EVENT *LcdMsgQ; /* Messages Queue to queue the messages received by this task */ OS_EVENT *LcdEmergMBox; /* Emergency Mailbox to receive the emergency messages */ OS_MEM *MsgLogMem; /* Memory partition to store the received messages */ extern OS_EVENT *SerialComMsgQueue; /* Pointer to Messages Queue of serial Com Task */ /* Memory area to be managed by the "MgLogMem" memory partition */ static char MsgLog[MsgLogNBlk][MsgLogBlkSize]; void *MsgLogBlkPTR; /* Pointer used to allocate a memory block to store messages */ extern char CLOCK[19]; /* String that holds Time and Date */ /********* "LCD Message" Task Core ***********/ void LCDMESSAGES_TASK(void *pdata) { LcdMsg* Msg; /* Variable to hold the LCD Message format */ INT8U err; /* Create the LCD Semaphore and give it a Name */ LcdDspSem = OSSemCreate(1); OSEventNameSet (LcdDspSem, "LCD Semaphore", &err); /* Create the Messages Queue and give it a Name */ LcdMsgQ = OSQCreate(&LcdMsgQPTR[0], LcdQueueSize); OSEventNameSet (LcdMsgQ, "LCD Msg Queue", &err); /* Create the Emergency Messagebox and give it a Name */ LcdEmergMBox = OSMboxCreate((void*)0); OSEventNameSet (LcdEmergMBox, "LCD Mailbox", &err); /* Create the Memory partition and give it a Name, MsgLogNBlk: #Blocks, MsgLogBlkSize: Block size */ MsgLogMem = OSMemCreate(MsgLog, MsgLogNBlk, MsgLogBlkSize, &err); OSMemNameSet(MsgLogMem, "MsgLog Memory", &err); /* LCD Messages Task's infinit loop */ while(1) { /* Wait for an Emegency message within OS_TICKS_PER_SEC/10 secs */ Msg = (LcdMsg*)OSMboxPend(LcdEmergMBox, OS_TICKS_PER_SEC/10, &err); if (Msg != (void*)0){ /* If Emergency message received... Request the LCD Display within OS_TICKS_PER_SEC/50 secs */ OSSemPend(LcdDspSem, OS_TICKS_PER_SEC/50, &err); if (err == OS_NO_ERR){ /* If the LCD Display aquired by this task... /* Display the Emergency Message */ LcdDspChars(0, 0, Msg->Line_0, 16); LcdDspChars(1, 0, Msg->Line_1, 16); /* Release the LCD Display */ OSSemPost(LcdDspSem);} } else {/* if there is no emergency message.... Check the Messages Queue within OS_TICKS_PER_SEC/10 if any message waits */ Msg = (LcdMsg*)OSQPend(LcdMsgQ, OS_TICKS_PER_SEC/10, &err); if (Msg != (void*)0) {/* If a message was picked...

Page 105: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 104

Request the LCD Display within OS_TICKS_PER_SEC/50 secs */ OSSemPend(LcdDspSem, OS_TICKS_PER_SEC/50, &err); if (err == OS_NO_ERR){/* If the LCD Display aquired by this task... /* Display the Emergency Message */ LcdDspChars(0, 0, Msg->Line_0, 16); LcdDspChars(1, 0, Msg->Line_1, 16); /* Release the LCD Display */ OSSemPost(LcdDspSem);} }} /* Messages storing code */ if (Msg != (void*)0) {/* if any messages has been received... Allocate a memory partition block */ MsgLogBlkPTR = OSMemGet(MsgLogMem, &err); if (err == OS_NO_ERR){/* if the allocation succeed... copy the message to the allocated memory block */ memcpy(MsgLogBlkPTR, Msg, sizeof(LcdMsg)); /* Send the block pointer to the Serial Com task Msg queue to be sent */ OSQPost(SerialComMsgQueue, (void*)MsgLogBlkPTR); } if (MsgLogMem->OSMemNFree < MsgLogMinNBlk)/*if the#memory blocks<minimal #block /* Resume the "Serial Com" task to send the messages via the rs232 port */ OSTaskResume(SERIALCOM_PRIO); else if(MsgLogMem->OSMemNFree > MsgLogMaxNBlk)/*when #free blocks>#Max free blocks Suspend the "Serial Com" task */ OSTaskSuspend(SERIALCOM_PRIO); } } } /********* END of the "LCD Messages" Task *******************/

uCOS-TASK2-DATACAPTURE.C

/****************************************************************************/ /* uCOS-TASK2-DATACAPTURE.C */

Page 106: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 105

/* Containts "DATA Capture" Task */ /* "DATA Capture" Task picks up the Temperature, Pressure and Gates' */ /* Statues and send these info bto other Tasks within messages. */ /****************************************************************************/ #include <includes.h> /* Include the globale header file */ INT8U MaxTemperature = 150; /* Maximum Allowed Temperature */ INT8U MinPressure = 20; /* Minimum Allowed Pressure */ INT8U GatesMask = 0xFF; /* Gates Enabled/Disabled Mask */ extern OS_EVENT* LcdMsgQ; /* Pointer to LCD Messages Task Queue */ extern OS_EVENT* LcdEmergMBox; /* Emergency Mailbox of the LCD Messages Task */ extern OS_EVENT* LedMbox; /* Pointer to Mailbox of the LEDs Task */ extern OS_EVENT* SoundMbox; /* Pointer to Mailbox of the Sound Task */ /***************************************************************************/ /************** Different Messages come out from this task *****************/ LcdMsg TempNormalLcdMsg = {"YYYY-MM-DD HH:MM:SS\r\n", /* Temperature Values */ "==TEMPERATURES==\r\n", /* Message */ "=== XXX Degs ===\r\n"}, TempEmergeLcdMsg = {"YYYY-MM-DD HH:MM:SS\r\n",/* High Temperature */ "=== WARNINGS ===\r\n", /* Emergency Message */ " TEMPS TOO HIGH \r\n"}, PressureNormalLcdMsg = {"YYYY-MM-DD HH:MM:SS\r\n",/* Pressure Values */ "====PRESSURE====\r\n", /* Message */ "=== XXX Bars ===\r\n"}, PressureEmergeLcdMsg = {"YYYY-MM-DD HH:MM:SS\r\n",/* Low pressure */ "=== WARNINGS ===\r\n", /* Emergency Message */ "PRESSURE TOO LOW\r\n"}, GatesNormalLcdMsg = {"YYYY-MM-DD HH:MM:SS\r\n",/* Gates Open/Close */ "==GATES STATUS==\r\n", /* Message */ "== ALL CLOSED ==\r\n"}, GatesEmergeLcdMsg = {"YYYY-MM-DD HH:MM:SS\r\n",/* Gates Penetration */ "====WARNINGS====\r\n", /* Emergency Message */ "= GATE #X OPEN =\r\n"}; LedMsg TempEmergeLedMsg = {0xFF, OS_TRUE},/* High Temp Message sent to LEDs Task */ PressureEmergeLedMsg = {0xF0, OS_TRUE},/* Low Press Message sent to LEDs Task */ GatesEmergeLedMsg = {0x00 ,OS_TRUE}; /* Gate Message sent to LEDs Task */ void DATACAPTURE_TASK(void *pdata) { INT16U TEMPERATURE = 0; INT16U PRESSURE = 0; INT8U GATES, OPENGATE = 1; INT8U SoundMsg = 1; INT16U MsgCount = 0; ADCInit(); /* Initialize the ADC Converter */ while(1) { TEMPERATURE = ADCGetValue(0); /* Pick Up the temperature through the ADC */ if (TEMPERATURE > MaxTemperature)/* If the Temperature > Preset Max temp... */ {/* Stamp the Hight Temp Emergency Message with Time and Date */ GetDate(&TempEmergeLcdMsg.TimeStamp[0]); GetTime(&TempEmergeLcdMsg.TimeStamp[11]); /* Post the Emergency Message into the LCD Messages Task's Emergency Mailbox */

Page 107: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 106

OSMboxPost(LcdEmergMBox, (void*)&TempEmergeLcdMsg); /* Post an Emergency Message into the LEDs Task's Mailbox */ OSMboxPost(LedMbox, (void*)&TempEmergeLedMsg); SoundMsg = HighTempSoundAlarm; /* Select the nature of the sound Alarm... Post the Alarm Emergency Message into the Sound Task's Mailbox */ OSMboxPost(SoundMbox, (INT8U*)&SoundMsg); } else /* Else, if the Temperature in the normal limits... */ if (MsgCount == MsgPeriod)/* is it time to post message to LCD Messages Task?*/ {/* Stamp the Message with Time and Date */ GetDate(&TempNormalLcdMsg.TimeStamp[0]); GetTime(&TempNormalLcdMsg.TimeStamp[11]); /* Convert the Temperature value to string */ DecToStr(&TempNormalLcdMsg.Line_1[4], TEMPERATURE, 3); /* Post the Message to LCD Messages Task's Queue */ OSQPost(LcdMsgQ, (void*)&TempNormalLcdMsg); } /* Pick up the pressure (Simulated by dividing Ad value / 3 ) */ PRESSURE = ADCGetValue(0) / 3; if (PRESSURE < MinPressure) /* if the pressure < Presete value... */ {/* Stamp the Emergency message with Time and Date */ GetDate(&PressureEmergeLcdMsg.TimeStamp[0]); GetTime(&PressureEmergeLcdMsg.TimeStamp[11]); /* Post the Message into the LCD messages Task's Emergency Mailbox */ OSMboxPost(LcdEmergMBox, (void*)&PressureEmergeLcdMsg); /* Post an Emergency Message into the LEDs Task's Mailbox */ OSMboxPost(LedMbox, (void*)&PressureEmergeLedMsg); SoundMsg = LowPressSoundAlarm; /* Select the sound Alarm... Post the Alarm Emergency Message into the Sound Task's Mailbox */ OSMboxPost(SoundMbox, (INT8U*)&SoundMsg); } else /* Else, if the Pressure in the normal limits... */ if (MsgCount == 2*MsgPeriod)/* is it time to post message to LCD Messages Task?*/ {/* Stamp the Message with Time and Date */ GetDate(&PressureNormalLcdMsg.TimeStamp[0]); GetTime(&PressureNormalLcdMsg.TimeStamp[11]); /* Convert the Pressure value to string */ DecToStr(&PressureNormalLcdMsg.Line_1[4], PRESSURE, 3); /* Post the Message to LCD Messages Task's Queue */ OSQPost(LcdMsgQ, (void*)&PressureNormalLcdMsg); } /**********************************************************************/ GATES = SwGetStatus() & GatesMask; /* Get the Gates' switches status */ if (GATES != 0x00) /* If Any gate is open... */ { GatesEmergeLedMsg.Pattern = GATES; /* Save the gates' switches status */ OPENGATE = 1; /* Detect which gate is open By... */ while(GATES = GATES >> 1) OPENGATE++; /*..decoding the Gates' Switches status */ /* Convert the open gate number to char and insert it in the LCD Messages */ GatesEmergeLcdMsg.Line_1[8] = OPENGATE%10 + '0'; /* Stamp the Message with Time and Date */ GetDate(&GatesEmergeLcdMsg.TimeStamp[0]); GetTime(&GatesEmergeLcdMsg.TimeStamp[11]); /* Post the Message into the LCD messages Task's Emergency Mailbox */ OSMboxPost(LcdEmergMBox, (void*)&GatesEmergeLcdMsg);

Page 108: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 107

/* Post an Emergency Message into the LEDs Task's Mailbox */ OSMboxPost(LedMbox, (void*)&GatesEmergeLedMsg); SoundMsg = GateOpenSoundAlarm;/* Select the sound Alarm... Post the Alarm Emergency Message into the Sound Task's Mailbox */ OSMboxPost(SoundMbox, (INT8U*)&SoundMsg); } else /* Else, if All gate are close...*/ if (MsgCount > 3*MsgPeriod)/* is it time to send message about gates status */ {/* if yes Stamp the LCD messages with time and date */ GetDate(&GatesNormalLcdMsg.TimeStamp[0]); GetTime(&GatesNormalLcdMsg.TimeStamp[11]); /* Post the Gates' status message into the LCD Messages Task's Queue */ OSQPost(LcdMsgQ, (void*)&GatesNormalLcdMsg); MsgCount = 0; /* initialize the Massages periode variable */ } MsgCount++;/* increment the Massages periode variable */ OSTimeDly(OS_TICKS_PER_SEC/10); /* Make delay of 100ms */ } } /********* END of the "DATA CAPTURE" Task *******************/

uCOS-TASK3-CONFIGMENU.C

/****************************************************************************/ /* uCOS-TASK3-CONFIGMENU.C */ /* Containts "Config Menus" Task */ /* "Config Menus" Manages different configuration Menus' Items */ /****************************************************************************/ #include <includes.h> /* Include the globale header file */ typedef struct { /* Menu's Map Structure */ INT8U ItemState[3][10]; /* Munu's Items States Machine */ INT8U MaxItems[3]; /* Maximum Items of each Submenu */ INT8U XPos, YPos; /* X, Y Position in the Menu's Map */ char *MenuName[3]; /* SubMenus' Names */ char *ItemName[3][10]; /* SubMenus' Items Names */ } MenuMap; MenuMap MainMenu = { /* Instance of the used Menu */ /* State Machine's State of each menu's item */ 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, /* Max Items of the respective Submenu */ 3, 6, 5, /* Initial X and Y position in Menu */ 0, 0, /* SubMenus' Names */ "|MAIN| E:ENTER ", "|SETUP| E:ENTER ", "|VIEW| E:ENTER ", /* SubMenus' Items Names */ {"<B SETUP C>", "<B VIEW C>", "<B EXIT C>", "", "", "", "", "", "", "",

Page 109: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 108

"<B MAX TEMP C>", "<B MIN PRESS C>", "<B EN/DI GATE C>", "<B SET TIME C>", "<B SET DATE C>","<B EXIT C>", "", "","","", "<B CPU USAGE C>", "<B FREE MEM C>", "<B Tick/CtxSw C>", "<B TIME/DATE C>", "<B EXIT C>", "","","","",""} }; extern INT8U MaxTemperature;/* Link to Maximum Temperature */ extern INT8U MinPressure; /* Link to Minimum Pressure */ extern INT8U GatesMask; /* Link to Gates Alarme/Disarme Mask */ extern INT8U SEC, MIN, HR, DAY, MONTH; /* Link to Clock's parametres */ extern INT16U YEAR; extern OS_MEM *MsgLogMem; /* link to Memory partition used to store Messages */ extern OS_EVENT *LcdDspSem; /* Semaphore to access LCD Display */ OS_FLAG_GRP *MenuFlag; /* Event Flags, Sigaled when keypad pressed */ /* User interface Text Input function via Keypad and LCD */ INT16U GetInputValue(char* Label, INT8U Lenght); /* User interface function to Enable/Disable gates via keypad and LCD */ INT8U EnableDisableGates(void); INT8U SetTime(); /* User interface function to set Time */ INT8U SetDate(); /* User interface function to set Date */ void KeyPadPressed(void)/* Function called when keypad pressed */ { INT8U err; /* Set the bit 0 of the "MenuFlag" Event flag */ OSFlagPost(MenuFlag, 0x01, OS_FLAG_SET, &err); } /************************** CONFIGURATION MENU TASK *****************************/ void CONFIGMENU_TASK(void *pdata) { INT8U err, Key, TextMsg[16]; INT32U InValue = 0; BOOLEAN QuitMenu = OS_TRUE; INT8U State = 1; /* Initialize Keypad hardware with "KeyPadPressed" callback function */ KeyPadInit(KeyPadPressed); MenuFlag = OSFlagCreate (0x00, &err); /* Create the Menu Event Flag */ OSFlagNameSet(MenuFlag, "Menu Flag Evants", &err);/* Give it name */ while(1) {/* Pend on the Event Flag until set, consume it when setted */ OSFlagPend(MenuFlag, 0x01, OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME, 0, &err); OSSemPend(LcdDspSem, 0, &err);/* Ask Access for the LCD via LCD Semaphore */ QuitMenu = OS_FALSE; /* we have just enter to Menu... */ State = 1; /*...in the first State */ GetKey();/* Consume the pressed key */ while(!QuitMenu){ /* while user didn't exit menu */ switch (State) /* Menu's State Machine */ { case 0 : QuitMenu = OS_TRUE;/* User quites Menu... then Clear the 1st bit of the Event Flag */ OSFlagPost(MenuFlag, 0x01, OS_FLAG_CLR, &err); OSSemPost(LcdDspSem);/*... and release the LCD Display */ break; case 1 :{ /* Display the Selected Submenu's Item */ LcdDspChars(0, 0, MainMenu.MenuName[MainMenu.YPos], 16); LcdDspChars(1, 0, MainMenu.ItemName[MainMenu.YPos][MainMenu.XPos], 16);

Page 110: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 109

/* wait until any key pressed */ while ((Key = GetKey()) == 0xFF) OSTimeDly(OS_TICKS_PER_SEC / 50); switch(Key) /* regarding the key pressed...*/ { case 'E' : /* E: User select to Enter into the submenu then... Set the next state to state of the slected Item */ State = MainMenu.ItemState[MainMenu.YPos][MainMenu.XPos];break; case 'B' : /* B: User select to shift left the Menu */ if (MainMenu.XPos == 0) MainMenu.XPos = MainMenu.MaxItems[MainMenu.YPos] - 1; else MainMenu.XPos--; State = 1; break; case 'C' : /* C: User select to shift right the Menu */ if (MainMenu.XPos == MainMenu.MaxItems[MainMenu.YPos] - 1) MainMenu.XPos = 0; else MainMenu.XPos++; State = 1; break; default:break; }}break; case 10 : MainMenu.XPos = 0;/* Set X and Y to "Setup" submenu Position */ MainMenu.YPos = 1; State = 1; /* Go to "Setup" submenus */ break; case 11 : MainMenu.XPos = 0;/* Set X and Y to "View" submenu Position */ MainMenu.YPos = 2; State = 1; /* Go to "View" submenus */ break; case 12 : State = 0; /* User slected to quit menus */ break; case 20 : /* User selects to change the Max Temperature... Call the User interface Function to capture the input data */ InValue = GetInputValue("MAX TEMP:", 3); if (InValue != -1) {/* if the user valadates typed value */ LcdDspCls(); /* Clear LCD Display */ MaxTemperature = (INT8U)InValue; /* Change the Max Temp */ LcdDspChars(0, 2, "DATA UPDATED", 12); /* Data Updated */ LcdDspChars(1, 2, "WITH SUCCESS", 12); OSTimeDlyHMSM(0, 0, 1, 0); } State = 1; /* Go back the previous menu */ break; case 21 : /* User selects to change the Min Pressure... Call the User interface Function to capture the input data */ InValue = GetInputValue("MIN PRESS:", 3); if (InValue != -1) {/* if the user valadates typed value */ LcdDspCls();/* Clear LCD Display */ MinPressure = (INT8U)InValue; /* Change the Min Pressure */ LcdDspChars(0, 2, "DATA UPDATED", 12); /* Data Updated */ LcdDspChars(1, 2, "WITH SUCCESS", 12); OSTimeDlyHMSM(0, 0, 1, 0); }

Page 111: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 110

State = 1; /* Go back the previous menu */ break; case 22 : /* User selects to Enable/Disable some gates... Call the function that manages this */ GatesMask = EnableDisableGates(); State = 1;/* Go back the previous menu */ break; case 23 : SetTime(); /* Call the function that allows the user to Set time */ State = 1; /* Go back the previous menu */ break; case 24 : SetDate(); /* Call the function that allows the user to Set date */ State = 1; /* Go back the previous menu */ break; case 25 : MainMenu.XPos = 0; /* Select to root of the Menu */ MainMenu.YPos = 0; State = 1; /* Go to the selected Munu's item */ break; case 30 : /* User select to View the CPU Usage */ LcdDspCls(); /* Clear LCD */ strncpy(TextMsg, "XXX%", 4); LcdDspChars(0, 0, "== CPU USAGE: ==", 16); while (Key = GetKey() == 0xFF){ /* while no key pressed */ /* Get CPU Usage computed by Statistic Task */ InValue = (INT32U)OSCPUUsage; /* Convert the value to string */ TextMsg[0] = (InValue / 100) + '0'; TextMsg[1] = (InValue % 100)/10 + '0'; TextMsg[2] = (InValue % 10) + '0'; LcdDspChars(1, 6 ,TextMsg, 4); /* Display CPU Usage */ OSTimeDly(OS_TICKS_PER_SEC / 2); /* Update value each 500ms */ } State = 1; /* go to previous menu */ break; case 31 : /* User select to View the Free Memory used to Store messages */ LcdDspCls(); /* Clear LCD */ TextMsg[3] = '%'; LcdDspChars(0, 1, "FREE MEMORY(%)", 14); while (Key = GetKey() == 0xFF){ /* while no key pressed */ /* Get exclusive access to MsgLogMem->OSMemNBlks */ OS_ENTER_CRITICAL(); /* Compute the % of the remaining memory in the partition */ InValue = (100 * MsgLogMem->OSMemNFree)/MsgLogMem->OSMemNBlks; OS_EXIT_CRITICAL(); /* Convert it to string */ TextMsg[0] = (InValue / 100) + '0'; TextMsg[1] = (InValue % 100)/10 + '0'; TextMsg[2] = (InValue % 10) + '0'; LcdDspChars(1, 6, &TextMsg[0], 4); /* Display that value */

Page 112: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 111

OSTimeDly(OS_TICKS_PER_SEC);}; /* Update value each 1sec */ State = 1; /* go to previous menu */ break; case 32 : /* View the number of Clock Ticks and Context switches */ LcdDspCls();/* Clear LCD */ while (Key = GetKey() == 0xFF){/* while no key pressed */ strncpy(TextMsg, "#Ticks:", 7);/* Copy the #Ticks label */ InValue = (INT32U)OSTime; /* Pickup the # Clock Ticks */ DecToStr(&TextMsg[7], InValue, 8);/* Convert it to string */ LcdDspChars(0, 0, TextMsg, 15);/* display it on LCD */ strncpy(TextMsg, "#CtxSw:", 7);/* Copy the #Ctx Sw label */ InValue = (INT32U)OSCtxSwCtr;/* Pickup the # Cntx Sw */ DecToStr(&TextMsg[7], InValue, 8);/* Convert it to string */ LcdDspChars(1, 0, TextMsg, 15);/* display it on LCD */ OSTimeDly(OS_TICKS_PER_SEC);} /* Update value each 1sec */ State = 1;/* go to previous menu */ break; case 33 : /* User select to View the Time/Date */ LcdDspCls();/* Clear LCD */ while (Key = GetKey() == 0xFF){/* while no key pressed */ GetTime(TextMsg); /* Pickup the time */ LcdDspChars(0, 4, TextMsg, 8); /* Display the time */ GetDate(TextMsg); /* Pickup the date */ LcdDspChars(1, 3, TextMsg, 10); /* Display the date */ OSTimeDlyHMSM(0, 0, 1, 0); }; /* Update value each 1sec */ State = 1;/* go to previous menu */ break; case 34 :MainMenu.XPos = 2;/* Select the View submenu */ MainMenu.YPos = 0; State = 1; /* Go back to SubMenu */ break; default: State = 1; break; } } } } /********* user interface text input function *******************/ INT16U GetInputValue(char* Label, INT8U Lenght) { INT8U Text[8] = " ", Key; INT8U indx = 0; INT32U InputValue = 0; LcdDspCls();/* Clear LCD Display */ LcdDspChars(0, 0, "E:Go|F:Exit|D:<-", 16); LcdDspChars(1, 0, Label, strlen(Label)); LcdDspMoveTo(1, strlen(Label)); LcdDspCursorBlink(); while (1) { switch(Key = GetKey()) {/* When 0..9 key pressed */ case '0':case'1':case'2':case'3':case'4': case '5':case'6':case'7':case'8':case'9':

Page 113: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 112

if (indx < Lenght) { Text[indx] = Key;/* Store the pressed # in buffer*/ LcdSendData(Text[indx]);/* make an echo on the LCD of number */ indx++;} /* increment the buffer position */ break; case 'D':/* Backspace key pressed */ if (indx > 0) { LcdDspBackSpace(); /* send Back the LCD cursor */ indx--;}/* decrement the buffer position */ else indx = 0;/* we got the limit */ break; case 'E': /* user presses Enter key to validate the input value...*/ InputValue = StrToDec(Text, Lenght);/*..Convert it to decimal */ LcdDspCursorOff();/* Hide LCD cursor */ return InputValue; /* return the typed value */ case 'F': /* user wants exit without validate the typed value */ LcdDspCursorOff();/* Hide LCD cursor */ return -1;/* indicate the used didnt validate the typed value */ default:break; } OSTimeDly(OS_TICKS_PER_SEC / 50); } } /********* user interface Enable/Disable Gates function *******************/ INT8U EnableDisableGates(void) { char Key, GatesCheckBox[10] = "|XXXXXXXX|"; INT8U indx, Mask = 0xFE; LcdDspCls(); for (indx = 1; indx < 9; indx++)/* Initialize the checkbox regarding...*/ { if ((Mask | GatesMask) == 0xFF) /*...the enabled or disabled gates */ GatesCheckBox[indx] = 'X'; else GatesCheckBox[indx] = ' '; Mask = (Mask <<= 1)|0x01; } LcdDspChars(0, 0, "Gate#:|12345678|", 16);/* display gate order */ LcdDspChars(1, 0, "En/Di:", 6); LcdDspChars(1, 6, GatesCheckBox, 10);/* Display the check box */ while (1) { switch(Key = GetKey()) {/* When key 1...8 pressed... */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': indx = Key - '0';/* Convert the pressed number to decimal */ if (GatesCheckBox[indx] == 'X') /* if the checkbox checked before then...*/ GatesCheckBox[indx] = ' '; /* uncheck it */ else GatesCheckBox[indx] = 'X'; /* else check it */ LcdDspChars(1, 6, GatesCheckBox, 10);/* display the new checkbox status */ break; case 'E': /* User has activated the entered configuration */ for (indx = 1; indx < 9; indx++)/* Transfer the checkbox config to...*/ {Mask = Mask >>= 1; /*...the Enable/Disable Gates */ if (GatesCheckBox[indx] == 'X') Mask |= 0x80; else Mask &= 0x7F;

Page 114: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 113

} return Mask; /* return the entered Mask */ case 'F': /* The user has canceled the entered Mask */ return GatesMask; /* return the old Mask */ default:break; } OSTimeDly(OS_TICKS_PER_SEC / 50); } } /********* user interface Set Time function *******************/ INT8U SetTime(void) { INT8U Key, indx = 0, Time[8]; LcdDspCls();/* Clear the LCD */ LcdDspChars(0, 0, "E:Go|F:Exit|D:<-", 16);/* Display the Menu */ GetTime(Time); /* import the current Time */ LcdDspChars(1, 4, Time, 8); /* Display the current Time */ LcdDspMoveTo(1, 4); /* Move LCD cursor to begining of the Time string */ LcdDspCursorBlink(); /* Make Cursor blinking */ while (1) { switch(Key = GetKey()) {/* When a number pressed...*/ case '0':case'1':case'2':case'3':case'4': case '5':case'6':case'7':case'8':case'9': if (indx < 8) {/* Capture the lcd cursor inside the displayed Time string */ Time[indx] = Key; /* Buffer the pressed number */ LcdSendData(Time[indx]); /* Make an echo of the prssed number */ if (Time[indx+1] == ':') { indx++; LcdSendData(':');} /* rewrite : as well */ indx++; } /* go to next digit */ break; case 'D': /* Backspace key pressed */ if (indx > 0) { LcdDspBackSpace();/* Backspace the LCD cursor */ if (Time[indx-1] == ':') { indx--; LcdDspBackSpace();}/* Skip the : character */ indx--;} /* decrement the buufer's cursor */ else indx = 0; /* freez the cursor when we get the left limit */ break; case 'E': /* user presses Enter key to validate the input value...*/ OS_ENTER_CRITICAL();/* Get exclusive access to clock (Sec,Min,Hr)(stop interrupt)*/ HR = (Time[0] - 48)*10 + (Time[1] - 48); MIN = (Time[3] - 48)*10 + (Time[4] - 48); SEC = (Time[6] - 48)*10 + (Time[7] - 48); OS_EXIT_CRITICAL();/* Release the interrupt clock (Sec, Min, Hr) */ LcdDspCursorOff();/* Hide cursor */ return 0;/* return 0 to indicates that the user has validate the time */ case 'F': /* user wants exit without validate the typed value */ LcdDspCursorOff(); /* Hide LCD cursor */ return -1;/* indicate the used didnt validate the typed value */ default:break; } OSTimeDly(OS_TICKS_PER_SEC / 50);

Page 115: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 114

} } /********* user interface Set Date function *******************/ INT8U SetDate(void) { INT8U Key, indx = 0, Date[10]; LcdDspCls();/* Clear LCD Display */ LcdDspChars(0, 0, "E:Go|F:Exit|D:<-", 16);/* Display the top Menu */ GetDate(Date); /* import the current Date */ LcdDspChars(1, 3, Date, 10); /* Display the current Date */ LcdDspMoveTo(1, 3); /* Move LCD cursor to begining of the Time string */ LcdDspCursorBlink(); /* Make Cursor blinking */ while (1) { switch(Key = GetKey()) {/* When a number pressed...*/ case '0':case'1':case'2':case'3':case'4': case '5':case'6':case'7':case'8':case'9': if (indx < 10) {/* Capture the lcd cursor inside the displayed Time string */ Date[indx] = Key; /* Buffer the pressed number */ LcdSendData(Date[indx]); /* Make an echo of the prssed number */ if (Date[indx+1] == '-') { indx++; LcdSendData(':');}/* rewrite : as well */ indx++; } /* go to next digit */ break; case 'D': /* Backspace key pressed */ if (indx > 0) { LcdDspBackSpace();/* Backspace the LCD cursor */ if (Date[indx-1] == '-') { indx--; LcdDspBackSpace();}/* Skip the '-' character */ indx--;} /* decrement the buufer's cursor */ else indx = 0; /* freez the cursor when we get the left limit */ break; case 'E': /* user presses Enter key to validate the input value...*/ OS_ENTER_CRITICAL(); /* Get exclusive access to clock (Sec,Min,Hr)(stop interrupt)*/ YEAR = (Date[0] - 48)*1000+(Date[1] - 48)*100+(Date[2] - 48)*10+(Date[3] - 48); MONTH = (Date[5] - 48)*10+(Date[6] - 48); DAY = (Date[8] - 48)*10+(Date[9] - 48); OS_EXIT_CRITICAL();/* Release the interrupt clock (Sec, Min, Hr) */ LcdDspCursorOff();/* Hide cursor */ return 0;/* return 0 to indicates that the user has validate the time */ case 'F':/* user wants exit without validate the typed value */ LcdDspCursorOff(); /* Hide LCD cursor */ return -1;/* indicate the used didnt validate the typed value */ default:break; } OSTimeDly(OS_TICKS_PER_SEC / 50); } } /********* Decimal to string conversion function *******************/ void DecToStr(INT8U *Str, INT32U Dec, INT8U Digits) { INT8U indx;

Page 116: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 115

INT32U Mult; Mult = 1; /* Convert each digit one by one, regarding its order */ for (indx = 0; indx < (Digits - 1); indx++) Mult *= 10; while (Mult > 0) { *Str++ = Dec / Mult + '0'; Dec %= Mult; Mult /= 10; } } /********* String to Decimal conversion function *******************/ INT32U StrToDec(INT8U *Str, INT8U Digits) { INT8U indx; INT32U Mult, Dec; Mult = 1; Dec = 0; for (indx = 0; indx < (Digits - 1); indx++) Mult *= 10; /* Convert each digit one by one, regarding its order */ for (indx = 0; indx < (Digits); indx++) { Dec += (Str[indx] - '0')*Mult; Mult /= 10; } return Dec; }

uCOS-TASK4-SERIALCOM.C

/****************************************************************************/ /* uCOS-TASK4-SERIALCOM.C */ /* Containts "SERIALCOM" Task */ /* "SERIALCOM" Sends the stored Messages via the RS232 Port to the PC */ /* and free the memory block where the message was stored */ /****************************************************************************/ #include <includes.h> /* Include the globale header file */ /* Pointers to carry the received messages */ void* SerialComMsgQueuePTR[SerialComQueueSize]; OS_EVENT* SerialComMsgQueue; /* Serial Com Task Messages Queue */ extern OS_MEM *MsgLogMem; /* Link to Memorry Partition */ void SERIALCOM_TASK(void *pdata) { INT8U err; LcdMsg *MsgLog; /* Create the Messages Queue, and Give it a name */ SerialComMsgQueue = OSQCreate(&SerialComMsgQueuePTR[0], SerialComQueueSize); OSEventNameSet(SerialComMsgQueue, "Serial Queue", &err); /* super loop of the "Serial Com" Task */ while(1) {/* Check if there is message on the queue */ MsgLog = (void*)OSQAccept(SerialComMsgQueue, &err); if (MsgLog != (void*)0){ /* if yes ...*/ OSView_TxStr("\n", 10); /* insert line on the terminal */ OSView_TxStr((char*)MsgLog, 10); /* Send the message to the terminal */

Page 117: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 116

/* Free the memory block carried the sent message */ OSMemPut(MsgLogMem, MsgLog); } OSTimeDly(OS_TICKS_PER_SEC/50); /* 50ms delay between each message sent */ } }

uCOS-TASK5-SOUNDALARM.C

/****************************************************************************/ /* uCOS-TASK5-SOUNDALARM.C */ /* This file contains the "Sound Alarm" Task, "Sound Alarm" Plays Alarm */ /* Sound when it receives Emergency Messages */ /****************************************************************************/ #include <includes.h> /* Include the globale header file */ OS_EVENT* SoundMbox; /* "Sound Alarm" Task's Mailbox */ /* the Played note typedef by the Buzzer */ typedef struct {INT16U Periode; /* Period of the emited signal */ INT16U Delay; /* for how long this signal emited */ } SoundNote; /* Sound Alarm Pattern, Table of Consecutive Notes */ static const SoundNote AlarmSound[SoundPaternSize] = {{0x2FFF, OS_TICKS_PER_SEC/4}, {0x1FFF, OS_TICKS_PER_SEC/4}, {0x0FFF, OS_TICKS_PER_SEC/4}, {0x0EFF, OS_TICKS_PER_SEC/4}}; /************ Sound Alarm Task ****************/ void SOUNDALARM_TASK(void *pdata) { INT8U Cnt, err, *SoundTypeMsg; /* Create the Soun Alarm MessageBox and give it a Name */ SoundMbox = OSMboxCreate((void*)0); OSEventNameSet(SoundMbox, "Sounds Mailbox", &err); /* Initialize the Sound timer connected to the Buzzer */ SoundInit(); /* Task infinit loop */ while(1) { /* Wait forever for a Message to be posted */ SoundTypeMsg = (INT8U*)OSMboxPend(SoundMbox, 0, &err); /* When getting the Message, depends of Sound's ID sent ...*/ switch (*SoundTypeMsg){ case HighTempSoundAlarm:/* if High temperature emergency */ case LowPressSoundAlarm:/* if low pressure emergency */ case GateOpenSoundAlarm:/* if a gate is open */ for (Cnt=0; Cnt<SoundPaternSize; Cnt++) /* Play the Sound Alarm Pattern */ Sound(AlarmSound[Cnt].Periode, AlarmSound[Cnt].Delay); break;

Page 118: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 117

default: break; } }} /************ Sound Alarm Task End **************/

uCOS-TASK6-LEDSALARM.C

/****************************************************************************/ /* uCOS-TASK6-LEDSALARM.C */ /* This file contains the "Leds Alarm" Task, "Leds Alarm" indicates which */ /* gates are Alarmed and Makes visual Alarm by leds when emergency */ /****************************************************************************/ #include <includes.h> /* Include the globale header file */ extern INT8U GatesMask; /* Alarmed/Disarmed gates Mask */ OS_EVENT *LedMbox; /* LEDs Task's Mailbox */ void LEDSALARM_TASK(void *pdata) { INT8U err, Count = 0, LedPattern = 0x01; LedMsg* Msg; /* Leds Message format received by LEDs Mailbox */ LedsInit(); /* initialize the eight LEDs hardware */ LedMbox = OSMboxCreate((void*)0); /* Create the LEDs Mailbox */ OSEventNameSet(LedMbox, "LEDs Mailbox", &err);/* Give name to this Mailbox */ while(1) {/* Wait for message to be posted within 100ms (OS_TICKS_PER_SEC/10) */ Msg = (LedMsg*)OSMboxPend(LedMbox, OS_TICKS_PER_SEC/10, &err); if (Msg != (void*)0) /* if Message received ...*/ { LedPattern = Msg->Pattern; /* read the light LEDs pattern */ while(Count++ < 10){/* within certain time...*/ if(Msg->Blink) /* if we should blink the LEDs...*/ LedPattern = (~LedPattern) & Msg->Pattern; /* blink LEDs */ else LedPattern = Msg->Pattern; /* permanent light LEDs */ LedsLight(LedPattern); /* Light LEDs */ OSTimeDly(OS_TICKS_PER_SEC/10);/* Make delay of 100ms */ } Count = 0; } else { /* else, If no message received ...*/ LedPattern = LedPattern << 1; /* Shift left the Leds Pattern */ if (LedPattern == 0x00) LedPattern = 0x01; LedsLight(GatesMask & LedPattern); /* Light the LEDs regarding the Mask*/ } } } /************ LEDS ALARM Task End **************/

uCOS-TASK7-CLOCK.C

/****************************************************************************/ /* uCOS-TASK7-CLOCK.C */ /* This file contains the "Clock" Task, "Clock" Task provides a Real Time */ /* Clock to the system to be used as Stamp when store messages */ /****************************************************************************/

Page 119: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 118

#include <includes.h> /* Include the globale header file */ char CLOCK[19] = "YYYY-MM-DD HH:MM:SS"; /* String to hld Date and Time */ INT8U SEC = 0, MIN = 58, HR = 23, DAY = 20, MONTH = 8; INT16U YEAR = 2007; /* Number of day of each month */ static INT8U MONTHDAYS[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; BOOLEAN UpdateClock(void); void UpdateDate (void); /* Clock Task (increment seconds using the OSTimeDlyHMSM delay function */ void CLOCK_TASK (void *pdata) { while(1) { if (UpdateClock()) UpdateDate();/* if 24Hours occured Update the Date */ OSTimeDlyHMSM(0, 0, 1, 0); } } static BOOLEAN UpdateClock(void) { BOOLEAN NEWDAY; NEWDAY = OS_FALSE; /* Still not completed one day yet */ if (SEC >= 59) { /* One minute completed ? */ SEC = 0; /* Yes, clear seconds */ if (MIN >= 59){/* One hour completed ? */ MIN = 0; /* Yes, clear minutes */ if (HR >= 23) { /* One day Completed */ HR = 0; /* Yes, clear hours */ NEWDAY = OS_TRUE; /* we have New day */ } else HR++; /* No, increment hours */ } else MIN++; /* No, increment minutes */ } else SEC++; /* No, increment seconds */ return (NEWDAY); } static BOOLEAN IsLeapYear(INT16U YEAR) {/* The year not dividable by 4 and dividable by 100 or not dividable by 400 ...*/ if (!(YEAR % 4) && (YEAR % 100) || !(YEAR % 400)) { return OS_TRUE; /* We have leap year */ } else { return OS_FALSE;/* else we have normal year */ } } static void UpdateDate (void) { BOOLEAN newmonth; newmonth = OS_TRUE; if (DAY >= MONTHDAYS[MONTH]) { /* Last day of the month? */ if (MONTH == 2) { /* Is this February? */ if (IsLeapYear(YEAR) == OS_TRUE) {/* Yes, Is this a leap year? */ if (DAY >= 29) { /* Yes, Last day in february? */ DAY = 1; /* Yes, Set to 1st day in March*/ } else { DAY++; newmonth = OS_FALSE; }

Page 120: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix I

Page 119

} else { DAY = 1; } } else { DAY = 1; } } else { DAY++; newmonth = OS_FALSE; } if (newmonth == OS_TRUE) { /* We have completed a month */ if (MONTH >= 12) { /* Yes, Is this december ? */ MONTH = 1; /* Yes, set month to january */ YEAR++; /* we have a new year */ } else { MONTH++; /* No, increment the month */ } } } char* GetTime(char* Time) { OS_ENTER_CRITICAL();/* Disable interrupts to get exclusive access to SEC, MIN, HR */ CLOCK[18] = SEC%10 + '0'; /* Convert Time to String format */ CLOCK[17] = SEC/10 + '0'; CLOCK[15] = MIN%10 + '0'; CLOCK[14] = MIN/10 + '0'; CLOCK[12] = HR %10 + '0'; CLOCK[11] = HR /10 + '0'; OS_EXIT_CRITICAL(); /* Enable interrupt again */ return (strncpy(Time, &CLOCK[11], 8)); /* Copy the Time into the Clock variable */ } char* GetDate(char* Date) { INT16U YEAR1 = YEAR; OS_ENTER_CRITICAL();/* Disable interrupts to get exclusive access to Year, Month,Day*/ CLOCK[0] = YEAR1 / 1000 + '0';/* Convert Date to String format */ YEAR1 = YEAR1 % 1000; CLOCK[1] = YEAR1 / 100 + '0'; YEAR1 = YEAR1 % 100; CLOCK[2] = YEAR1 / 10 + '0'; CLOCK[3] = YEAR1 % 10 + '0'; CLOCK[5] = MONTH / 10 + '0'; CLOCK[6] = MONTH % 10 + '0'; CLOCK[8] = DAY / 10 + '0'; CLOCK[9] = DAY % 10 + '0'; OS_EXIT_CRITICAL();/* Enable interrupt again */ return (strncpy(Date, &CLOCK[0], 10));/* Copy the Time into the Clock variable */ } /************ Clock Task End **************/

Page 121: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 120

Appendix II MSA0654 Development

Board Drivers uCOS-II-MSA0654-DRIVERS.H

/********************************************************************************/ /* FILE: uCOS-II-MSA0654-DRIVERS.H */ /* CREATED: 19/07/07 */ /* LAST MODIFICATION: 19/07/07 */ /* CREATED BY: Khaled Sobaihi */ /********************************************************************************/ /* Derective to include/exclude the needed drivers */ #define SevSegEn 1 #define LcdDspEn 1 #define LedsEn 1 #define SwitchesEn 1 #define KeyPadEn 1 #define ADCEn 1 #define SwitchesIntEn 1 #define SoundEn 1 #define TenBitAdcEn 0 #define SevenSegScanDly 10 /* Seven Seg digits scan delay (ms) */ #define SevenSegDigitNb 2 /* Number of seven segments Digits MSA0654 case = 2 */ /* Ports names redefinition */ #define PORT_0 P0 #define PORT_0D PD0 #define PORT_1 P1 #define PORT_1D PD1 #define PORT_2 P2 #define PORT_2D PD2 #define PORT_3 P3 #define PORT_3D PD3 #define PORT_4 P4 #define PORT_4D PD4 #define PORT_5 P5 #define PORT_5D PD5 #define PORT_10 P10

Page 122: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 121

#define PORT_10D PD10 /* LCD Command values */ #define LcdCmd_Cfg 0x3B /*Cfg: 8-bit data, 1/16 duty, 5x8 dots */ #define LcdCmd_Cls 0x01 /*Clear Display, Cursor Home */ #define LcdCmd_Inc 0x06 /*Set Incrument Mode */ #define LcdCmd_Set 0x0C /*Set LCD: Dsp: ON, Cursor OFF, Blink: Off */ #define LcdCmd_CursorOn 0x0E #define LcdCmd_CursorOff 0x0C #define LcdCmd_CursorBlink 0x0D #define LcdCmd_On 0x0C #define LcdCmd_Off 0x08 #define LcdCmd_BckSpace 0x10 #define LcdCmd_FwdSpace 0x14 #define LcdCmd_PanLeft 0x18 #define LcdCmd_PanRight 0x1C /* LCD Macros for quick execution */ #define LcdDspCls() LcdSendCmd(LcdCmd_Cls) #define LcdDspOn() LcdSendCmd(LcdCmd_On) #define LcdDspOff() LcdSendCmd(LcdCmd_Off) #define LcdDspBackSpace() LcdSendCmd(LcdCmd_BckSpace) #define LcdDspFwdSpace() LcdSendCmd(LcdCmd_FwdSpace) #define LcdDspMoveScrLeft() LcdSendCmd(LcdCmd_PanLeft) #define LcdDspMoveScrRight() LcdSendCmd(LcdCmd_PanRight) #define LcdDspCursorOn() LcdSendCmd(LcdCmd_CursorOn) #define LcdDspCursorOff() LcdSendCmd(LcdCmd_CursorOff) #define LcdDspCursorBlink() LcdSendCmd(LcdCmd_CursorBlink) #define KeyRptStartDly 1000 /* x 976.56us */ #define KeyRptDly 400 /* x 976.56us */ #if SevSegEn > 0 void SevenSegInit(void); /* Initialize 7-Segs display */ /* Display DIG_0 and DIG_1 on the 7-Segs Display */ void SevenSegDsp(INT8U DIG_0, INT8U DIG_1); /* Light specific segements on DIG_0 and DIG_1 */ void SevenSegActive(INT8U SEG_0, INT8U SEG_1); #endif #if LcdDspEn > 0 void LcdDspInit(void);/* initialize LCD Display hardware */ /* Move cursor to specified position */ void LcdDspMoveTo(INT8U Row, INT8U Col); /* Display the String in Row, Col position with Chars Nb length */ void LcdDspChars(INT8U Row, INT8U Col, char* String, INT8U CharsNb); void LcdSendCmd(INT8U Cmd);/* Send command to LCD routine */ void LcdSendData(INT8U DATA);/* Send displayed char to LCD routine */ #endif #if LedsEn > 0 void LedsInit(void); /* initialize LEDs hardware */ void LedsLight(INT8U Pattern);/* Light the leds with the pettern */ #endif

Page 123: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 122

#if SwitchesEn > 0 void SwitchesInit(void); /* initialize Switches hardware */ INT8U SwGetStatus(void); /* Get the status of the Switches */ #endif #if KeyPadEn > 0 void KeyPadInit(void(*CallBack)(void)); /* initialize keypad hardware */ INT8U GetKey(void); /* pickup the pressed function */ #endif #if ADCEn > 0 void ADCInit(void); INT16U ADCGetValue(INT8U Channel); #endif #if SwitchesIntEn > 0 void SwIntInit(); #endif #if SoundEn > 0 void SoundInit(void); void Sound(INT16U SoundPeriod, INT16U Dly); #endif uCOS-II-MSA0654-DRIVERS.C

/*************************************************************************/ /* */ /* FILE: uCOS-II-MSA0654-DRIVERS.C */ /* CREATED: 19/07/07 */ /* LAST MODIFICATION: 30/08/07 */ /* CREATED BY: Khaled Sobaihi */ /*************************************************************************/ /* This File contains the MSA0654 Board hardware Drivers functions */ /*************************************************************************/ #include <includes.h> #include "uCOS-II-MSA0654-DRIVERS.H" void dummy(void){}/* Dummy void to simulate short delay */ /*******************************************************************************/ /* KEYPAD DRIVER */ /*******************************************************************************/ #if SevSegEn > 0 const INT8U SevenSegCode[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80, 0x90}; INT8U SevenSegDigit[SevenSegDigitNb]; INT8U DigitIndex; void SevenSegInit(void) /* Initialize 7-Segs display and timer used to scan 2 digits */ { PORT_0D = 0xFF; PORT_1D = 0xFF; TA2IC = 0x02; /* Setup Timer A2 interrupt reg */ TA2MR = 0x80; /* Timer A2 Moder Register: f1 * 32 = 2 us. */

Page 124: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 123

TA2 = SevenSegScanDly * 500; /* Timer A2: Initialize to wait SevenSegScanDly msec*/ TABSR |= 0x04; /* Start timer A2. */ } /* Display DIG_0 and DIG_1 on the 7-Segs Display */ void SevenSegDsp(INT8U DIG_0, INT8U DIG_1) { SevenSegDigit[0] = SevenSegCode[DIG_0]; SevenSegDigit[1] = SevenSegCode[DIG_1]; } /* Light specific segements on DIG_0 and DIG_1 */ void SevenSegActive(INT8U SEG_0, INT8U SEG_1) { SevenSegDigit[0] = ~SEG_0; SevenSegDigit[1] = ~SEG_1; } #endif /* Timer A2 Callback function to scan the 2 7-Segs digits */ void TIMER_A2_CALLBACK(void) { #if SevSegEn > 0 PORT_3D = 0xFF; PORT_0 = SevenSegDigit[DigitIndex];/* Get to pattern of the digit */ if (DigitIndex == SevenSegDigitNb-1) { DigitIndex = 0; PORT_1 = 0xFE;/* Active the first digit */ } else { DigitIndex = 1; PORT_1 = 0xFD;/* Active the second digit */ } #endif } /********************************************************************************/ /* KEYPAD DRIVER */ /********************************************************************************/ #if KeyPadEn > 0 BOOLEAN KeyDown = OS_FALSE; BOOLEAN KeyHot = OS_FALSE; BOOLEAN KeyUnRead = OS_FALSE; INT8U KeyCode = 0xFF; /* keypad interrupt callback function */ void(*KeyCallBack)(void) = (void*)0; INT8U GetKey(void) /* pickup the pressed function */ { INT8U KeyChar; /* Convert the code of pressed key to the respective char */ if (KeyUnRead) { switch (KeyCode){ case 0xEE : KeyChar = '1'; break; case 0xDE : KeyChar = '4'; break; case 0xBE : KeyChar = '7'; break; case 0x7E : KeyChar = 'A'; break;

Page 125: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 124

case 0xED : KeyChar = '2'; break; case 0xDD : KeyChar = '5'; break; case 0xBD : KeyChar = '8'; break; case 0x7D : KeyChar = '0'; break; case 0xEB : KeyChar = '3'; break; case 0xDB : KeyChar = '6'; break; case 0xBB : KeyChar = '9'; break; case 0x7B : KeyChar = 'B'; break; case 0xE7 : KeyChar = 'F'; break; case 0xD7 : KeyChar = 'E'; break; case 0xB7 : KeyChar = 'D'; break; case 0x77 : KeyChar = 'C'; break; default : KeyChar = 0xFF; break; } } else KeyChar = 0xFF; KeyUnRead = OS_FALSE; return KeyChar; } /* initialize keypad hardware */ void KeyPadInit(void(*CallBack)(void)) { PORT_10D = 0x0F; PORT_10 = 0x00; PUR2 = 0x20; /* PULL UP resistors enabled on P10.4-P10.7 */ KUPIC = 0x04; /* Enable interrupt on keypad with priority = 4 */ PD8 &= 0x3F; /* Xc pins as input */ PRCR = 0x01; /* Enable write to Processor mode */ CM0 |= 0x10; /* Xc Circuit Enabled */ CM0 &= 0xF7; PRCR = 0x00; /* Disable access to processor and clock mode */ TA3 = 0x01; /* initial reload = 976.56us */ TA3MR = 0xC2; /* One shoot mode timer, fc32 src =976.56us */ TA3IC = 0x02; /* Priority = 2 */ TABSR|= 0x08; /* Enable TA3 Timer */ KeyCallBack = CallBack; /* Assign the callback function */ } void KEYPAD_CALLBACK(void)/* when any key pressed */ { TA3 = 40; KeyDown = OS_TRUE; ONSF |= 0x08; /* Start Timer A3 */ /* Call the keypad callback if not null */ if ((void*)(KeyCallBack) != (void*)0) KeyCallBack(); } #endif /* Timer A3 callback function, used for the auto repeat */ void TIMER_A3_CALLBACK(void) { INT8U Shift; static INT8U Code; KeyHot = OS_TRUE;

Page 126: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 125

if (KeyDown){ KeyDown = OS_FALSE; TA3 = KeyRptStartDly; Shift = 0; PORT_10 = 0x0E; /* Decode the pressed key from X and Y positions */ while(((PORT_10 | 0x0F) == 0xFF) & Shift++ < 4) PORT_10 = ((PORT_10 << 1) | 0x01) & 0x0F; Code = PORT_10; } else {PORT_10 = Code; if (PORT_10 == Code) { TA3 = KeyRptDly;} else { KeyHot = OS_FALSE; ONSF &= 0xF7; }} KeyCode = Code; PORT_10 = 0x00; KUPIC &= 0xF7; if (KeyHot) { KeyUnRead = OS_TRUE; ONSF |= 0x08; } } /********************************************************************************/ /* LEDS DRIVER */ /********************************************************************************/ #if LedsEn > 0 void LedsInit(void)/* initialize LEDs hardware */ { PORT_3D = 0xFF;} void LedsLight(INT8U Pattern) /* Light the leds with the pettern */ { PORT_3 = Pattern;} #endif /********************************************************************************/ /* EIGHT SWITCHES DRIVER */ /********************************************************************************/ #if SwitchesEn > 0 void SwitchesInit(void)/* initialize Switches hardware */ { PORT_1D = 0x00;} INT8U SwGetStatus(void)/* Get the status of the Switches */ { INT8U Pattern; Pattern = ~PORT_1; return Pattern; } #endif

Page 127: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 126

/********************************************************************************/ /* LCD DISPLAY DRIVER */ /********************************************************************************/ #if LcdDspEn > 0 void LcdDspInit(void) /* initialize LCD Display hardware */ { PORT_0D = 0xFF; PORT_5D = 0xFF; PORT_0 = 0x00; PORT_5 = 0x00; LcdSendCmd(LcdCmd_Cfg); LcdSendCmd(LcdCmd_Inc); LcdSendCmd(LcdCmd_Set); LcdSendCmd(LcdCmd_Cls); } /* Move cursor to specified position */ void LcdDspMoveTo(INT8U Row, INT8U Col) { switch(Row) {case 0 : LcdSendCmd(0x80 | Col); break; case 1 : LcdSendCmd(0xC0 | Col); break; } } /* Display the String in Row, Col position with Chars Nb length */ void LcdDspChars(INT8U Row, INT8U Col, char* String, INT8U CharsNb) { LcdDspMoveTo(Row, Col); while(CharsNb-- > 0) LcdSendData(*String++);} /* Send command to LCD routine */ void LcdSendCmd(INT8U Cmd) { PORT_5 &= 0xDF; /* Select Write Mode rw = 0 */ PORT_5 &= 0xBF; /* Select command register rs = 0 */ PORT_0 = Cmd; /* Put data on Port 0 */ /* Clock Cmd into LCD... */ PORT_5 |= 0x80; /* Enable = 1 */ asm("nop"); PORT_5 &= 0x7F; /* Enable = 0 */ OSTimeDly(1); } /* Give time to LCD to get the command */ /* Send displayed char to LCD routine */ void LcdSendData(INT8U DATA) { INT16U Cnt; PORT_5 &= 0xDF; /* Select Write Mode rw = 0 */ PORT_5 |= 0x20; /* Select DATA register rs = 1 */ PORT_0 = DATA; /* Put data on Port 0 */ /* Clock DATA into LCD... */ PORT_5 |= 0x80; /* Enable = 1 */ asm("nop"); PORT_5 &= 0x7F; /* Enable = 0 */ for(Cnt = 0; Cnt < 40; Cnt++) dummy(); /* Give short delay to LCD to display the char */ } #endif /********************************************************************************/

Page 128: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 127

/* ADC DRIVER */ /********************************************************************************/ #if ADCEn > 0 OS_EVENT *ADCSem; /* Semaphore to prevent multiaccess to adc in same time */ void ADCInit(void)/* initialize the ADC Hardware */ { INT8U err; ADCSem = OSSemCreate(1); /* Create the ADC Semaphore */ OSSemPend(ADCSem, 0, &err); /* wait until semaphore released */ ADCON0 = 0x80; /* Cannel 0, one-shoot mode, Software trigger */ ADCON1 = 0x20; /* 8-bits conversion, vref connected */ #if TenBitAdcEn > 0 ADCON1 = 0x28; /* 10-bits conversion if selected */ #endif ADCON2 = 0x06; /* Port 2 selected */ OSSemPost(ADCSem);/* release the semaphore */ } /* Pickup the Analog value of the specified channel */ INT16U ADCGetValue(INT8U Channel) { INT16U ADCValue = 0; INT8U err; OSSemPend(ADCSem, 0, &err); /* ask for exclusive access to ADC */ ADCON0 &= (Channel | 0xF8); /* Select the channel */ ADCON0 |= 0x40; /* Start the conversion */ while(ADCON0 & 0xBF != 0xFF);/* wait until conversion done */ /* Depend of the selected channel pickup the ADC value frome the right register */ switch (Channel) { case 0 : ADCValue = AD0H; ADCValue <<= 8; ADCValue |= AD0L; break; case 1 : ADCValue = AD1H; ADCValue <<= 8; ADCValue |= AD1L; break; case 2 : ADCValue = AD2H; ADCValue <<= 8; ADCValue |= AD2L; break; case 3 : ADCValue = AD3H; ADCValue <<= 8; ADCValue |= AD3L; break; case 4 : ADCValue = AD4H; ADCValue <<= 8; ADCValue |= AD4L; break; case 5 : ADCValue = AD5H; ADCValue <<= 8; ADCValue |= AD5L; break; case 6 : ADCValue = AD6H; ADCValue <<= 8; ADCValue |= AD6L;

Page 129: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 128

break; case 7 : ADCValue = AD7H; ADCValue <<= 8; ADCValue |= AD7L; break; default : ADCValue = AD0H; ADCValue <<= 8; ADCValue |= AD0L; break; } OSSemPost(ADCSem); /* release the semaphore */ return ADCValue; } #endif /********************************************************************************/ /* INTERRUPT SWITCHES DRIVER */ /********************************************************************************/ #if SwitchesIntEn > 0 void (*SW1_CallBack)(void) = (void*)0; void (*SW2_CallBack)(void) = (void*)0; void (*SW3_CallBack)(void) = (void*)0; /* initialize the interrupts hardwares by giving the callback function of each interrupt */ void SwIntInit(void(*SW1_CallBck)(void), void(*SW2_CallBck)(void), void(*SW3_CallBck)(void)) { INT0IC = 0x06; INT1IC = 0x06; ADIC = 0x06; SW1_CallBack = SW1_CallBck; SW2_CallBack = SW2_CallBck; SW3_CallBack = SW3_CallBck; } #endif void INT_0_CALLBACK(void)/* Switch 1 interrupt */ { if ((void*)(SW1_CallBack) != (void*)0) SW1_CallBack(); } void INT_1_CALLBACK(void)/* Switch 2 interrupt */ { if ((void*)(SW2_CallBack) != (void*)0) SW2_CallBack(); } void ADtrg_CALLBACK(void)/* Switch 3 interrupt */ { if ((void*)(SW3_CallBack) != (void*)0) SW3_CallBack(); } /********************************************************************************/ /* SOUND DRIVER */ /********************************************************************************/ #if SoundEn > 0 void SoundInit(void) /* Initialize Timer A1 used to ring the buzzer */ { TA1MR = 0x00; TA1 = 0x0E9F; TABSR|= 0x02; } /* Produce sound with SoundPeriod period for Dly clock ticks */ void Sound(INT16U SoundPeriod, INT16U Dly)

Page 130: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 129

{ TA1 = SoundPeriod; /* load the period */ TA1MR |= 0x04; /* Start the Timer */ OSTimeDly(Dly); /* Wait for Dly clock ticks */ TA1MR &= 0xFB; } /* Stop the timer */ #endif

uCOS_II_MSA0654_DRIVERS-ASM.ASM

;'*******************************************************************************; ;'FILE: uCOS-II-MSA0654-DRIVERS-ASM.ASM ;'CREATED : 18/08/07 ;'CREATED BY : Khaled Sobaihi ;'LAST MODIFICATION : 30/08/07 ;'*******************************************************************************; MODULE uCOS_II_MSA0654_DRIVERS ;'******************************************************************************** RSEG CSTACK RSEG ISTACK EXTERN OSIntNesting ; declared as INT8U,8-bit long RSEG CODE(1) EXTERN TIMER_A2_CALLBACK EXTERN TIMER_A3_CALLBACK EXTERN KEYPAD_CALLBACK EXTERN OSView_TxISRHandler EXTERN OSView_RxISRHandler EXTERN INT_0_CALLBACK EXTERN INT_1_CALLBACK EXTERN ADtrg_CALLBACK EXTERN OSView_TxISR EXTERN OSView_RxISR PUBLIC DUMMY ;’******************************************************************************** .EVEN DUMMY: REIT ;'******************************************************************************** .EVEN TIMER_A2_ISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current CPU context registers INC.B OSIntNesting ;'OSIntNesting++ JSR TIMER_A2_CALLBACK ;'Call the interrupt callback function DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore the CPU context registers REIT ;'******************************************************************************** .EVEN TIMER_A3_ISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current task registers INC.B OSIntNesting ;'OSIntNesting++ JSR TIMER_A3_CALLBACK ;'Call the TIMER Callback function DEC.B OSIntNesting ;'OSIntNesting--

Page 131: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 130

POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers of the new Task Stack REIT ;'******************************************************************************** .EVEN KEYPAD_ISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current task registers INC.B OSIntNesting ;'OSIntNesting++ JSR KEYPAD_CALLBACK ;'Call the Keypad Callback function DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers REIT ;'******************************************************************************** .EVEN INT_0_ISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current tasks registers INC.B OSIntNesting ;'OSIntNesting++ JSR INT_0_CALLBACK ;'Call the INT0 Callback function DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers REIT ;'******************************************************************************** .EVEN INT_1_ISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current tasks registers INC.B OSIntNesting ;'OSIntNesting++ JSR INT_1_CALLBACK ;'INT_1_CALLBACK() DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers REIT ;'******************************************************************************** .EVEN ADtrg_ISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current tasks registers INC.B OSIntNesting ;'OSIntNesting++ JSR ADtrg_CALLBACK ;'AD Trigger interrupt callback DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers REIT ;'******************************************************************************** .EVEN TxISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current task registers INC.B OSIntNesting ;'OSIntNesting++ JSR OSView_TxISRHandler ;'OSView_RxISRHandler() DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers REIT ;'******************************************************************************** .EVEN RxISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ;'Save current task registers INC.B OSIntNesting ;'OSIntNesting++ JSR OSView_RxISRHandler ;'OSView_RxISRHandler() DEC.B OSIntNesting ;'OSIntNesting-- POPM R0,R1,R2,R3,A0,A1,SB,FB ;'Restore registers

Page 132: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix II

Page 131

REIT ;'******************************************************************************** ;'* INTERRUPT VECTOR TABLE ;'******************************************************************************** .COMMON INTVEC .ORG 13*4 .LWORD KEYPAD_ISR .ORG 14*4 .LWORD ADtrg_ISR .ORG 23*4 .LWORD TIMER_A2_ISR .ORG 24*4 .LWORD TIMER_A3_ISR .ORG 29*4 .LWORD INT_0_ISR .ORG 30*4 .LWORD INT_1_ISR .ORG 17*4 .LWORD TxISR .ORG 18*4 .LWORD RxISR .END

Page 133: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 132

Appendix III µC/OS-II M16C Port Listing

Programs

os_cpu.h #ifdef OS_CPU_GLOBALS #define OS_CPU_EXT #else #define OS_CPU_EXT extern #endif /* ********************************************************************************** * DATA TYPES * (Compiler Specific) ********************************************************************************** */ #define OS_TASK_TMR_PRIO 0 typedef unsigned char BOOLEAN; typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ typedef signed char INT8S; /* Signed 8 bit quantity */ typedef unsigned int INT16U; /* Unsigned 16 bit quantity */ typedef signed int INT16S; /* Signed 16 bit quantity */ typedef unsigned long INT32U; /* Unsigned 32 bit quantity */ typedef signed long INT32S; /* Signed 32 bit quantity */ typedef float FP32; /* Single precision floating point */ typedef double FP64; /* Double precision floating point */ typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */ typedef INT16U OS_CPU_SR; /* Type of CPU status register */ #define OS_CRITICAL_METHOD 1

Page 134: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 133

#if OS_CRITICAL_METHOD == 1 #define OS_ENTER_CRITICAL() asm("FCLR I") /* Disable interrupts */ #define OS_EXIT_CRITICAL() asm("FSET I") /* Enable interrupts */ #endif /* ********************************************************************************** * RENESAS M16C FAMILY MISCELLANEOUS ********************************************************************************** */ #define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory */ #define OS_TASK_SW() asm("INT #0") /* Mapped to the software interrupt 0 */ /* ********************************************************************************** * PROTOTYPES ********************************************************************************** */ void OSCtxSw (void); void OSIntCtxSw (void); void OSStartHighRdy (void); void OSTickISR (void);

os_cpu_a.asm ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;' PUBLIC FUNCTIONS ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RSEG CSTACK RSEG ISTACK EXTERN OSTCBCur ; 'Current Running Task TCB, 32-bit long EXTERN OSTCBHighRdy ;'Highest Priority Task TCB ready to run,32-bitlong EXTERN OSPrioCur ;'Current running Task Priority, 8-bit long EXTERN OSPrioHighRdy ;'Priority of the Highest Priority Task ready to run, ;’8-bit long EXTERN OSIntNesting ; 'Interrupts nesting variable, 8-bit long EXTERN OSRunning ; 'Is µC/OS-II Running?, 8-bit long RSEG CODE EXTERN OSIntExit ; 'Exit from interrupt, called when finish with the ISR EXTERN OSTimeTick ; 'Function called every Clock Tick of the uCOS-II EXTERN OSTaskSwHook ; 'User hook function, Called at each context switch

Page 135: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 134

;' Tx/Rx UART Callback functions, declared in OSVIEW Module (Optional) EXTERN OSView_TxISR EXTERN OSView_RxISR EXTERN DUMMY ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PUBLIC OSStartHighRdy ; 'Start the OS, Run the Highest Priority Task PUBLIC OSCtxSw ; 'Task Level Context Switch routine PUBLIC OSIntCtxSw ; 'Interrupt Level Context Switch routine ;';;;;;;;;;;;;;;;; Run the highest priority task ready to run ;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;; void OSStartHighRdy(void) ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .EVEN OSStartHighRdy: JSR OSTaskSwHook ; 'Call OSTaskSwHook() FCLR U ; 'Software interrupt knowledge ; 'Point on the stack pointer of the highest priority ready task... MOV.W OSTCBHighRdy, A0 ; 'ISP = OSTCBHighRdy->OSTCBStkPtr LDC [A0], ISP MOV.B #01H, OSRunning ; 'OSRunning = TRUE ; 'Load the highest priority ready task registers into CPU registers POPM R0,R1,R2,R3,A0,A1,SB,FB REIT ; 'Launch the highest priority ready task ;';;;;;;;;;;;;;;;;;;; Performs the task level context switch ;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;; void OSCtxSw(void) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .EVEN OSCtxSw: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ; 'Push the CPU Regs onto current task stack MOV.W OSTCBCur, A0 ; 'OSTCBCur->OSTCBStkPtr = SP STC ISP, [A0] JSR OSTaskSwHook ; 'Call OSTaskSwHook() MOV.W OSTCBHighRdy, OSTCBCur ; 'OSTCBCur = OSTCBHighRdy MOV.W OSPrioHighRdy, OSPrioCur; 'OSPrioCur = OSPrioHighRdy MOV.W OSTCBHighRdy, A0 ; 'SP = OSTCBHighRdy->OSTCBStkPtr LDC [A0], ISP ; 'Restore all processor registers from the new tasks stack POPM R0,R1,R2,R3,A0,A1,SB,FB REIT ; 'Launch the highest priority ready task ;';;;;;;;;;;;;;;; Performs the interrupt level context switch ;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;; void OSIntCtxSw(void) ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .EVEN OSIntCtxSw: JSR OSTaskSwHook ; 'Call OSTaskSwHook() MOV.W OSTCBHighRdy, OSTCBCur ; 'OSTCBCur = OSTCBHighRdy MOV.W OSPrioHighRdy, OSPrioCur; 'OSPrioCur = OSPrioHighRdy MOV.W OSTCBHighRdy, A0 ; 'SP = OSTCBHighRdy->OSTCBStkPtr LDC [A0], ISP

Page 136: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 135

; 'Restore all processor registers from the new tasks stack POPM R0,R1,R2,R3,A0,A1,SB,FB REIT ; 'Launch the highest priority ready task ;';;;;;;;;;;;;;;;;;;;;;;;;; Clock Ticks Generator Timer ;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;; void OSTickISR (void) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .EVEN OSTickISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB ; 'Save current task registers INC.B OSIntNesting ; 'OSIntNesting++ ;' Make sure that this ISR does not interrupt another ISR... CMP.B #1,OSIntNesting ; 'if (OSIntNesting == 1) JNE OSTickISR1 ; 'If yes assign the highest priority ready task stack pointer to the current TCB stack pointer MOV.W OSTCBCur, A0 ; 'OSTCBCur->OSTCBStkPtr = SP STC ISP, [A0] OSTickISR1: JSR OSTimeTick ; 'Call OSTimeTick() JSR OSIntExit ; 'Call OSIntExit() to achieve the context switch POPM R0,R1,R2,R3,A0,A1,SB,FB ; 'Restore registers from the new tasks stack REIT ; 'Launch the highest priority ready task ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;; INTERRUPT VECTOR TABLE ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .COMMON INTVEC ;'Context Switch Interrupt Vector ORG 0 .LWORD OSCtxSw ;'Clock Ticks Generator TIMER A0 ORG 21*4 .LWORD OSTickISR ;'Tx UART Interrupt vector, Used by OSVIEW Module (Optional) .ORG 17*4 .LWORD OSView_TxISR ;'Rx UART Interrupt vector, Used by OSVIEW Module (Optional) .ORG 18*4 .LWORD OSView_RxISR ;’TIMER A2 Vector .ORG 22*4 .LWORD DUMMY .END

Page 137: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 136

os_cpu_c.c #define OS_CPU_GLOBALS #include <ucos_ii.h> #if OS_VIEW_MODULE > 0 #include <os_viewc.h> #include <os_view.h> #endif /********************************************************************************* * LOCAL VARIABLES *********************************************************************************/ #if OS_TMR_EN > 0 static INT16U OSTmrCtr; #endif /********************************************************************************* * OS INITIALIZATION HOOK * (BEGINNING) * Description: This function is called by OSInit() at the beginning of OSInit(). * Arguments : none * Note(s) : 1) Interrupts should be disabled during this call. *********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203 void OSInitHookBegin (void) { #if OS_TMR_EN > 0 OSTmrCtr = 0; #endif } #endif /********************************************************************************* * OS INITIALIZATION HOOK * (END) * Description: This function is called by OSInit() at the end of OSInit(). * Arguments : none * Note(s) : 1) Interrupts should be disabled during this call. *********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203 void OSInitHookEnd (void) { } #endif /******************************************************************************** * TASK CREATION HOOK * Description: This function is called when a task is created. * Arguments : ptcb is a pointer to the task control block of the task being created.* Note(s) : 1) Interrupts are disabled during this call. *********************************************************************************/

Page 138: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 137

#if OS_CPU_HOOKS_EN > 0 void OSTaskCreateHook (OS_TCB *ptcb) { #if OS_VIEW_MODULE > 0 OSView_TaskCreateHook(ptcb); #else (void)ptcb; /* Prevent compiler warning */ #endif } #endif /********************************************************************************** * TASK DELETION HOOK * Description: This function is called when a task is deleted. * Arguments : ptcb is a pointer to the task control block of the task being deleted. * * Note(s) : 1) Interrupts are disabled during this call. *********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 void OSTaskDelHook (OS_TCB *ptcb) { ptcb = ptcb; /* Prevent compiler warning */ } #endif /********************************************************************************** IDLE TASK HOOK * Description: This function is called by the idle task. This hook has been added to allow you to do such things as STOP the CPU to conserve power. * * Arguments : none * * Note(s) : 1) Interrupts are enabled during this call. ********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 && OS_VERSION >= 251 void OSTaskIdleHook (void) { } #endif /********************************************************************************* * STATISTIC TASK HOOK * Description: This function is called every second by uC/OS-II's statistics task. This allows your application to add functionality to the statistics task. * Arguments : none *********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 void OSTaskStatHook (void) { } #endif

Page 139: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 138

/******************************************************************************** * INITIALIZE A TASK'S STACK ********************************************************************************/ OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { INT16U *pstk16; INT16U flag; flag = 0x0040; pstk16 = (INT16U *)ptos; pstk16--; /* Simulate ISR entry */ *pstk16-- = (flag & 0x00FF) /*... The lowest byte of the FLAG register */ | (((INT32U)task >> 8) & 0x00000F00) /*..The highest nibble of the PC register*/ | ((flag << 4) & 0xF000);/* ... The highest nibble of the FLAG register */ *pstk16-- = (((INT32U)task ) & 0x0000FFFF);/*..The lowest bytes of the PC register*/ /* Save registers onto stack frame */ *pstk16-- = (INT16U)0xFBFB; /* ... FB register */ *pstk16-- = (INT16U)0x3B3B; /* ... SB register */ *pstk16-- = (INT16U)0xA1A1; /* ... A1 register */ *pstk16-- = (INT16U)0xA0A0; /* ... A0 register */ *pstk16-- = (INT16U)0x3333; /* ... R3 register */ *pstk16-- = (INT32U)pdata >> 16L; /* ... Pass argument in R2 register */ *pstk16-- = (INT32U)pdata & 0x0000FFFFL; /*..Pass argument in R1 register */ *pstk16 = (INT16U)0x0000; /*..R0 register */ return ((OS_STK *)pstk16); } /********************************************************************************* * TASK SWITCH HOOK * * Description: This function is called when a task switch is performed. This allows you to perform other * operations during a context switch. * * Arguments : none * * Note(s) : 1) Interrupts are disabled during this call. * 2) It is assumed that the global pointer 'OSTCBHighRdy' points to the TCB of the task that * will be 'switched in' (i.e. the highest priority task) and, 'OSTCBCur' points to the * task being switched out (i.e. the preempted task). *********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 void OSTaskSwHook (void) { #if OS_VIEW_MODULE > 0 OSView_TaskSwHook(); #endif } #endif

Page 140: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Appendix III

Page 139

/********************************************************************************* * OSTCBInit() HOOK * Description: This function is called by OS_TCBInit() after setting up most of the TCB. * Arguments : ptcb is a pointer to the TCB of the task being created. * Note(s) : 1) Interrupts may or may not be ENABLED during this call. ********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203 void OSTCBInitHook (OS_TCB *ptcb) { (void)ptcb; /* Prevent Compiler warning */ } #endif /********************************************************************************* * TICK HOOK * Description: This function is called every tick. * Arguments : none * Note(s) : 1) Interrupts may or may not be ENABLED during this call. *********************************************************************************/ #if OS_CPU_HOOKS_EN > 0 void OSTimeTickHook (void) { #if OS_VIEW_MODULE > 0 OSView_TickHook(); #endif #if OS_TMR_EN > 0 OSTmrCtr++; if (OSTmrCtr >= (OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC)) { OSTmrCtr = 0; OSTmrSignal(); } #endif } #endif

Page 141: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Table of Contents

TABLE OF CONTENTS

ABSTRACT ......................................................................................................................................................................................................... 1 ACKNOWLEDGEMENTS .......................................................................................................................................................................... 2 INTRODUCTION .......................................................................................................................................................................................... 3

CHAPTER I ............................................................................................................................................................................... 4 REAL TIME OPERATING SYSTEM (RTOS) ....................................................................................................................... 4

INTRODUCTION .......................................................................................................................................................................................... 4 1. THE OPERATING SYSTEM .................................................................................................................................................................... 4 2. REAL TIME OPERATING SYSTEM ....................................................................................................................................................... 5

2.1. Multitasking .................................................................................................................................................................................. 7 2.2. Non-Preemptive Kernel ........................................................................................................................................................... 7 2.3. Preemptive Kernel ...................................................................................................................................................................... 9

3. CONTEXT SWITCH .............................................................................................................................................................................. 10 4. RTOS VS INFINITE LOOP .................................................................................................................................................................. 12 CONCLUSION ................................................................................................................................................................................................... 14

CHAPTER II ........................................................................................................................................................................... 15 µC/OS-II REAL TIME OPERATING SYSTEM ................................................................................................................. 15

INTRODUCTION .............................................................................................................................................................................................. 15 1. CONTEXT SWITCH IN µC/OS-II ....................................................................................................................................................... 15

1.1. Task Level Context Switch .................................................................................................................................................... 16 1.2. Interrupt Level Context Switch .......................................................................................................................................... 17 1.3. µC/OS-II Tasks States ............................................................................................................................................................. 17 1.4. Task Control Blocks (OS_TCBs) .......................................................................................................................................... 19 1.5. Ready list ...................................................................................................................................................................................... 21

2. TASK SCHEDULING ............................................................................................................................................................................. 24 2.1. OSSched() Context Switch function .................................................................................................................................. 24 2.2. OSIntExit() Context Switch function ................................................................................................................................ 25

3. INTERRUPTS UNDER µC/OS-II ....................................................................................................................................................... 26 4. CLOCK TICKS ....................................................................................................................................................................................... 27 5. TASK MANAGEMENT .......................................................................................................................................................................... 28

5.1. Creating µC/OS-II Task .......................................................................................................................................................... 29 6. INTER-TASK COMMUNICATION & SYNCHRONIZATION ................................................................................................................. 31

6.1. Semaphores ................................................................................................................................................................................. 33 6.2. Mutual Exclusion Semaphores ........................................................................................................................................... 37 6.3. Message Mailboxes .................................................................................................................................................................. 39 6.4. Messages Queues ...................................................................................................................................................................... 42 6.5. Event Flags (µC/OS-II V2.51 and higher) ...................................................................................................................... 45

7. TIME MANAGEMENT .......................................................................................................................................................................... 48 8. TIMERS MANAGEMENT...................................................................................................................................................................... 49 9. MEMORY MANAGEMENT ................................................................................................................................................................... 51 CONCLUSION ............................................................................................................................................................................................. 54

CHAPTER III ......................................................................................................................................................................... 55

Page 142: Porting the µC-OS-II Real Time Operating System to the M16C Microcontrollers

Table of Contents

PORTING µC/OS-II TO THE M16C MICROCONTROLLERS ...................................................................................... 55 INTRODUCTION ........................................................................................................................................................................................ 55 1. COMPILER SPECIFIC DATA TYPES AND MACROS, <OS_CPU.H> ............................................................................................... 57 2. STACK INITIALIZATION AND USER HOOK FUNCTION, OS_CPU_C.C ........................................................................................ 58 3. ASSEMBLY LANGUAGE FUNCTIONS FILE, OS_CPU_A.ASM ........................................................................................................ 60

3.1. OSStartHighRdy() .................................................................................................................................................................... 60 3.2. OSCtxSw(), ................................................................................................................................................................................... 61 3.3. OSIntCtxSw(), ............................................................................................................................................................................. 62 3.4. OSTickISR(), ................................................................................................................................................................................ 63

CONCLUSION ............................................................................................................................................................................................. 65 CHAPTER IV .......................................................................................................................................................................... 66 µC/OS-II DEMONSTRATION EXAMPLE......................................................................................................................... 66

INTRODUCTION .............................................................................................................................................................................................. 66 1. µC/OS-II PROJECT TEMPLATE ........................................................................................................................................................ 67 2. µC/OS-VIEW MODULE ..................................................................................................................................................................... 74 3. µC/OS-II DEMONSTRATION EXAMPLE .......................................................................................................................................... 78 4. MSA0654 DEVELOPMENT BOARD ................................................................................................................................................ 82 5. TEST AND DEMONSTRATION ............................................................................................................................................................ 84

5.1. Normal Running of the Application ................................................................................................................................. 86 5.2. When Keypad’s button pressed .......................................................................................................................................... 88 5.3. When the Messages memory is full ................................................................................................................................... 89 5.4. In Case of emergency .............................................................................................................................................................. 90

CONCLUSION ............................................................................................................................................................................................. 94 CONCLUSION ........................................................................................................................................................................ 95

REFERENCES .................................................................................................................................................................................................. 97 APPENDIX I ........................................................................................................................................................................... 98 PROGRAMS LISTINGS OF THE PROJECT APPLICATION .......................................................................................... 98 APPENDIX II ...................................................................................................................................................................... 120 MSA0654 DEVELOPMENT BOARD DRIVERS ........................................................................................................... 120 APPENDIX III ..................................................................................................................................................................... 132 µC/OS-II M16C PORT LISTING PROGRAMS ............................................................................................................. 132