Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Email: skalyd@hotmail.co.uk
Supervisor Signature:
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 1
support of my supervisor, and the people who participated to make the project succeed
This project would not have been possible without the guidance and
ACKNOWLEDGEMENTS
Page 2
Introduction
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 3
CHAPTER I
time operating system (RTOS), in addition, we will make brief comparison between using 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 user interface such as a command language or a graphical user interface (GUI). [9]
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
Page 4
CHAPTER I
Multi-user: Allows two or more users to run programs at the same time. Some 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.
Page 5
Timers
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 scheduling regarding the tasks priorities during the runtime of the embedded system.
handles a distinct topic with its own deadline. In parallel, the RTOS provides 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] Timers timeouts. The third service is the Timing service; this service includes the task delay functions and can be allocated and freed in runtime. Some RTOS provides also Device I/O which provides are typical of an embedded system. [6]
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
a uniform framework for organizing and accessing the many hardware device drivers that
Page 6
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
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
CHAPTER I
The multitasking concept was born from the observation that computers spent much of misusing the power of the processors as long waiting for an I/O is unproductive time. [2] task priority.
their time waiting for slow peripheral devices to either store or retrieve data; this leads to 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 applications. One of the most important aspects of multitasking is that it allows the Multitasking is like foreground/background with multiple backgrounds. Multitasking Application programs are typically easier to design and maintain if multitasking is used. [1] maximizes the utilization of the CPU and also provides for modular construction of
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. real time kernels:
According to the manner in which the multitasking is achieved, we distinguish two types of
Called also Cooperative Multitasking, In this case the tasks cooperate with each other to another task, in addition, when the interrupt service routine (ISR) interrupts the current
Page 7
share the CPU, hence, each task runs until it decides to gives up voluntary the CPU to
The advantage of the non-preemptive kernel is the safety when using the shared variables or non re-entrant functions (non re-entrant functions dont allow to be called more than manipulating the shared variables or when returns from a non re-entrant function. corrupting the shared variables as long each task gives up the CPU only when finished
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.
CHAPTER I
one task before the first caller achieve using this function), in this case there is no risk of
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 8
CHAPTER I
Task#2 High priority Task #1 gives control of the CPU to Task #2 Task#1 Low priority Task #1 interrupted by an ISR ISR Task #1 resumed after completion of the ISR
In this kernel the highest priority ready task is immediately re-launched when an interrupt 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. ready to run is deterministic which leads to the best responsiveness time.
Time
occurs or when the current running task enters in waiting state, therefore, upon The advantage of this multitasking kernel is that the execution of the highest priority task manipulating such variables. Figure 1.3 shows the principle of this kernel.
completion of an ISR, the kernel will resume execution to the highest priority task ready to
functions, to avoid any corruption that may occur the interrupts should be disabled before
Page 9
CHAPTER I
Task#2 High priority The highest priority ready Task #2 resumed after completion of the ISR Task#1 Low priority Task #1 interrupted by an ISR ISR
Time
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 tasks context (CPU registers) into the current tasks task, each task has its own stack area in memory. When this 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 tasks stack is stored in a structure called Task Control Block (TCB) owned by each task, pointer, etc. operation performed the kernel restores the context of the highest priority task ready to
along with other information such as priority, name, next TCB pointer, previous TCB Figure 1.4 illustrates the context switch routine by taking an example of the M16C CPU stacks pointer of the high priority ready task to the stack pointer CPU register (ISP), the 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
Page 10
next step will restore the registers of this task to the CPU registers by using the POPM instruction.
/* Save all the registers into current Tasks Stack */ PUSHM R0,R1,R2,R3,A0,A1,SB,FB /* Assign the ready tasks stack pointer to stack pointer register (ISP) */ MOV.W OSTCBHighRdy, A0 LDC [A0], ISP /* Restore all register to the next Tasks Stack */ POPM R0,R1,R2,R3,A0,A1,SB,FB /* return from interrupt leads to resume task #2 */ REIT
CHAPTER I
Time
Page 11
An example is the best way to demonstrate the benefits brought by the real time operating system (RTOS) against the traditional infinite loop. Lets take an example of a data logger system; this example usually includes ADC converter, Keypad, LCD Display and RS232 Serial communication. configurations can be designed; the simplest solution is calling each function one by one achieved, executing all functions in one loop iteration will take too long time by calling functions that dont need to be called, short regular intervals by using a Timer interrupt, this timer should short enough to ensure be improved by calling more frequently the function that has the high priority. comparison when using an infinite loop and the RTOS. In the case of infinite loop each function is executed to completion, in this case many within the infinite loop, the first function gives control to the second function when 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 In the other hand, using the RTOS platform begins by creating each task that performs the that function gets called at frequency the meets its timing requirements, this method can therefore, it seems that tasks are executed in same time. Table bellow makes structural
desired function, when the RTOS starts running, the CPU switches between all tasks,
Page 12
CHAPTER I
Using RTOS
void ProcessRS232Task() { while(1){ /* ProcessRS232Task Code goes here */ Delay(); }} void ProcessADCTask() { while(1){ /* ProcessADCTask Code goes here */ Delay(); }}
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 tasks 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. trapped by the ScanKeyPad() function, this problem can be solved by using a complex Take an example if the user is typing command on the keypad, at this time an Analog input
Page 13
case 2: if (AdcReady) ProcessAdc(); State = 0; break; } TimerExpire = false; } else /* do something else */ } }
comes ready to be converted, this analog sample will be missed as long the infinite loop is
interrupt service routine for each function (Keypad ISR, Serial UART ISR, ADC ISR etc ), interrupt its optimal priority, in this case the system gets closer to an RTOS. that makes like all tasks are running in same time. CONCLUSION
CHAPTER I
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
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 The RTOS provides an excellent and reliable solution to handle events within a rigorous communication, etc. deadline, especially when there are many events and tasks to manage with timing semaphores to access to shared resources, Mailboxes and Messages Queues for inter-task constraints. In addition, the RTOS provides additional features, such as Tasks priority,
Page 14
Chapter II
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 and how it can be used to develop a multitasking application. We will then focus on
robust and reliable and suitable for use in safety critical systems common to aviation and This chapter will introduce the latest version of C/OS-II V2.83, its multitasking strategy different features provided by this RTOS such as semaphores, mailboxes, memory The kernel of the C/OS-II performs the context switching or task switch in two levels, the Context Switch in C/OS-II
first one is the task level context switch done by OSCtxSw() function, the second is the
Page 15
Chapter II
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;
users application whenever a context switch occurs, this function is useful to monitor task switching; assign its priority to the current task priority; priority ready task; Get the stack pointer of the task to resume, which is the stack pointer of the highest Reload the saved registers of this task onto the CPU registers; To resume this task, we simply call the return from interrupt instruction.
Call the user definable function OSTaskSwHook(), this function is called to inform the
Assign the TCB of the highest priority task and ready to run to the current task TCB, and
void OSCtxSw(void) { Save processor registers; Save the current tasks stack pointer into the current tasks 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 tasks stack; Execute a return from interrupt instruction; } Listing 2.1 OSCtxSw() routine Pseudo code
Page 16
Chapter II
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: pointer to CPU stack pointer; 1) Save the stacks pointer to the current task stacks pointer; 2) Call the user definable function: OSTaskSwHook(); 4) Pop the current task registers to the CPU registers; 3) Transfer the TCB of the next ready to run task to the current TCB and assign its stack 5) Execute the return from interrupt instruction which resume the current task. void OSIntCtxSw(void) { Save the current tasks stack pointer into the current tasks 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 tasks stack; Execute a return from interrupt instruction; } Listing 2.2 OSIntCtxSw() Pseudo code 1.3. C/OS-II Tasks States
almost the same as OSCtxSw() exception in that, there is no need to save the CPUs context
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
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 17
Chapter II
When the running task wait for a delay to expire or for a message by calling OSTimeDly(), at this time the task placed in ready list waiting its turn to run. task is resumed otherwise the interrupted task is resumed. the idle task OSTaskIdle(). The running task can also be preempted by an ISR when interrupts enabled, therefore, an
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, 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 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
Page 18
Chapter II
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] V2.83: The following structure describes each field in the OS_TCB data structure in case C/OS-II
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 19
Chapter II
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;
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, 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.
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, OSTCBStkBottom: This pointer points on the bottom valid stack location OSTCBStkBottom is used by OSTaskStkChk() to check the size of a tasks 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,
OSTCBPendTO: Indicates if the task is no more pending on an event (semaphore, messagebox etc...), =OS_TRUE if true (timed out);
Page 20
OSTCBStat: contains the state of the task. When equals to 0, the task is ready to run;
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;
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.
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 tasks name, length = OS_TASK_NAME_SIZE;
OSTCBPrio: Contains the task priority. A high priority task has a low OSTCBPrio;
Chapter II
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: 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
OSTCBY = priority >> 3; OSTCBBitY = OSMapTbl[priority >> 3]; OSTCBX = priority & 0x07; OSTCBBitX = OSMapTbl[priority & 0x07];
C/OS-II is a priorities based RTOS, therefore, each task is assigned a unique priority used has the lowest priority.
as identifier in many functions, the C/OS-II V2.83 allows up to 255 tasks fixed by the RAM it is preferable to fix this variable to the number of used tasks plus the idle task which
variable OS_LOWEST_PRIO = Number of tasks - 1, in order to optimize the size of the used The ready tasks are placed in the ready list consisting of two variables; OSRdyTbl and
Page 21
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).
The tasks priorities are grouped in OSRdyGrp (8 tasks per group or 16 tasks in case of 63 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
Chapter II
and 255 tasks respectively), each bit in this variable is set to 1 when at least one task that 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.
8-Bits length OSRdyGrp 7 6 5 4 3 2 1 0 [0] 7 6 Highest priority Task 4 3 2
1 9
0 8
[1] 15 14 13 12 11 10
[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 8-Bits length Task's Priority Byte X Bits Position in OSRdyTbl Y Y Y X [6] 55 54 53 52 51 50 49 48 [7] 63 62 61 60 59 58 57 56 X X Lowest priority Task
Figure 2.2 Ready to run tasks table and ready group bits map
Page 22
The following code place a task in the ready list: To remove a task from the ready list: OSRdyGrp OSRdyTbl[prio >> 3]
task from ready list or get the highest ready to run task. |= OSMapTbl[prio >> 3]; |= OSMapTbl[prio & 0x07];
This configuration allows C/OS-II to accelerate the operations of make task ready, remove
Chapter II
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) OSRdyGrp &= ~OSMapTbl[prio >> 3]; y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; prio = (y << 3) + x;
In order to get the priority of the highest priority task ready to run: 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 0, 4, 5, 4, 6, 4, 5, 4, 7, 4, 5, 4, 6, 4, 5, 4, }; const 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, OSUnMapTbl[256] = { 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0, 0, 2, 0, 1, 0, 3, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
/* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /*
0x00 0x10 0x20 0x30 0x40 0x50 0x60 0x70 0x80 0x90 0xA0 0xB0 0xC0 0xD0 0xE0 0xF0
to to to to to to to to to to to to to to to to
0x0F 0x1F 0x2F 0x3F 0x4F 0x5F 0x6F 0x7F 0x8F 0x9F 0xAF 0xBF 0xCF 0xDF 0xEF 0xFF
*/ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */
Page 23
For example, if OSRdyGrp contains 01101000 then: Y = OSUnMapTbl[OSRdyGrp] = 3; Assume that OSRdyTbl[3] = 11100100; X = = OSUnMapTbl[OSRdyTbl[3]] = 2;
Chapter II
The highest priority task ready to run (prio) would then be 26 (3 * 8 + 2). OSTCBPrioTbl[] using the task's priority.[1] 2. Task Scheduling
Getting a pointer to the OS_TCB for the corresponding task is done by indexing into The scheduler, also called the dispatcher, is the part of the kernel responsible for 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] by OSSched() function and interrupt level performed by OSIntExit(). 2.1. OSSched() Context Switch function
determining which task will run next. C/OS-II kernel is priority based. Each task is As we have seen before, C/OS-II makes the context switch in two levels; task level, done
Listing 2.3 illustrates the OSShed() code, the scheduling starts by disabling all interrupts by accept to be interrupted. The test statement tests if the scheduling enabled (OSLockNesting
calling OS_ENTER_CRITICAL() macro (1), the next code is a critical section and doesnt == 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 current running task (4), if the case no need to make an useless context switch, if not the TCBs 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 computed using the part of code (3), the next if statement tests if this task is not the which keeps track the number of the performing context switch (6), finally, the context calling OS_EXIT_CRITICAL().
Page 24
switch achieved by calling the macro OS_TASK_SW() (7) and re-enable the interrupts by
Chapter II
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) }
OSIntCtxSw() firstly because the ISR has already saved the CPU registers, secondly because OSIntExit().
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
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
called at the entry of each interrupt code, OSIntExit() verifies that OSIntNesting = 0 after a
Listing 2.4 shows the code of this function, as we can note OSIntExit() looks like OSSched() nesting variable OSIntNesting, this variable was incremented by OSIntEnter() function
except for some differences, the first one is that OSIntExit() should decrement the interrupt
OSIntCtxSw() should do some stack adjustment to remove the return address to itself and
Page 25
Chapter II
void OSIntExit (void) { OS_ENTER_CRITICAL(); if ((--OSIntNesting | OSLockNesting) == 0) { OSIntExitY = OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; OSIntCtxSw(); } } OS_EXIT_CRITICAL(); }
3.
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; Call OSIntEnter() or, increment OSIntNesting directly; Execute user code to service ISR; Call OSIntExit(); Restore all CPU registers; Execute a return from interrupt instruction; (1) (2) (3) (4) (5) (6)
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), interrupt or the highest priority ready task (depends of what OSIntExit() has performed) by
when the ISRs 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
Page 26
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 INC.B OSIntNesting JSR TIMER_A1_CALLBACK JSR OSIntExit POPM R0,R1,R2,R3,A0,A1,SB,FB REIT
Chapter II
4.
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 frequency can be between 50 and 100Hz. supply frequency), The faster the tick rate, the higher the overhead imposed on the system. serviced by this interrupt should be OSTimeTick(), the listing bellow shows the pseudo code of this ISR interrupt: ISR identifier: Depends of the microcontroller performance and desired tick resolution, the ticks
Listing 2.6 Sample of an interrupt service routine ISR in case of M16C under C/OS-II Clock Ticks
;Save current CPU context registers ;OSIntNesting++ ;Call the interrupt call-back function ;Call OSIntExit() ;Restore the CPU context registers ;Return from the interrupt
As described before the Clock Ticks Timer is serviced like any ISR, The call-back function
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.8 shows the code that services OSTimeTick() call-back function. OSTimeTick() the functionality of OSTimeTick() (1).
starts by calling a user definable function OSTimeTickHook() which can be used to extend
Page 27
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(); ptcb = OSTCBList; while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { OS_ENTER_CRITICAL(); if (ptcb->OSTCBDly != 0) { if (--ptcb->OSTCBDly == 0) { if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; } else { ptcb->OSTCBDly = 1; } } } ptcb = ptcb->OSTCBNext; OS_EXIT_CRITICAL(); } OS_ENTER_CRITICAL(); OSTime++; OS_EXIT_CRITICAL(); }
Most of the work done by OSTimeTick() basically consist of decrementing the OSTCBDly
Chapter II
field for each OS_TCB (if its 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
(5) (4)
(7) (6)
5.
infinite loop otherwise deletes itself before exiting the tasks void, listings bellow show the template of any task:
C/OS-IIs Task has the format of any C void with no return value, also it should be an
Task Management
Page 28
Chapter II
void TASK (void *pdata) { /* Task code */ while(1) /* infinite loop */ { /* Task code */ } }
/* delete task */ }
pdata argument contains the data pointer passed to the task when executed first time. 5.1. Creating C/OS-II Task
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.*/
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
Page 29
Chapter II
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. */
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 30
Chapter II
OSTaskChangePrio (INT8U oldprio, INT8U newprio) OSTaskDel (INT8U prio) OSTaskdelreq (INT8U prio) OSTaskNameSet (INT8U prio, INT8U *pname, INT8U *err) OSTaskNameGet (INT8U prio, INT8U *pname, INT8U *err) OSTaskSuspend (INT8U prio) OSTaskResume (INT8U prio)
Change the priority of a task to newprio Delete the task with priority prio Request that a task delete itself Set a name to the task with priority prio Suspend a task with priority prio Resume a suspended task with priority prio
Description
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 performs every second the computing of the percentage of CPU usage .
6.
When variables are shared among two or more tasks can allows to these tasks to interact for one task to read a value written by another task.
between each other. For example, one way to communicate information between tasks is 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 these will disable interrupts and leads to stop the multitasking. Another method can be OSSchedUnlock() respectively the multitasking can be stopped as well. 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,
Page 31
In a real time system this could lead to performance problems due to the disruption of the 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] semaphore to synchronize tasks, messages mailbox or messages queues to make the intertask 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;
Chapter II
precision of the timing. For these reasons a different technique is used for critical section a shared structure called ECB (Event Control Blocks) which can take the form of a
*/
OSEventPtr: This pointer is used when the ECB is assigned to a mailbox or a messages states respectively except that they contain a list of tasks waiting on the event instead of being a list of tasks ready-to-run. semaphore. to a message queue data.
queue. In this case, OSEventPtr points to the message when used for a mailbox or a pointer
OSEventCnt is used to hold the semaphore count when the ECB is used for a
OSEventTbl[] and OSEventGrp are similar to OSRdyTbl[ ] and OSRdyGrp used in tasks
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 objects through C/OS-IIs service calls. [1]. make sure you are accessing the proper object when you perform operations on these
Page 32
Chapter II
A semaphore is a key that your code acquires in order to continue its execution. If the someone else is using it, I am willing to wait for it!. [1]
6.1. Semaphores
semaphore is already in use, the requesting task is suspended until the semaphore is In order to use the C/OS-IIs semaphore we need to create it first using the function OSSemCreate (Assign an ECB to this semaphore) as follow:
OS_EVENT *MySemaphore;
released by its current owner. In other words, the requesting task says: "Give me the key. If
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 */
OSSemPend is called, this function will make the called task in waiting list until the prototype of this function:
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
semaphore is available or the specified timeout finished, the listing bellow show the
/* 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.
Wait for a semaphore, when a task requesting access to a semaphore the function
Page 33
Chapter II
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 */
Signal a semaphore (release a semaphore), after get access to a semaphore and we want follow:
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
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. */
Another interesting function which checks if a semaphore is available or not without this function:
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
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 */
Page 34
Chapter II
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().
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 1s 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 1 releases the LcdSem semaphore and OSSemPend terminates the waiting time of Task 2
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
Calling OSSemPost in Task 2 releases the LcdSem semaphore and OSSemPend terminates the waiting time of Task 1
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 1s 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 */ } }
Figure 2.3 Semaphore used to get exclusive access to LCD Display resource
Page 35
Chapter II
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 semaphore is represented as flag; Figure 2.4 illustrates different synchronization cases. Semaphore Semaphore
rendezvous, two tasks can synchronize their activities with each other. In this case the
OSSemPost()
OSSemPend()
TASK2
OSSemPost()
OSSemPend()
OSSemPend()
OSSemPost()
TASK1 ISR
TASK1
TASK2
Unilateral rendezvous
Bilateral rendezvous
Figure 2.4 Semaphore used for: Unilateral synchronization (Unilateral rendezvous), Bilateral synchronization (Bilateral rendezvous)
Page 36
Chapter II
void Task_1(void *pdata) { Sem = OSSemCreate(0);/* Create a semaphore with no shared resource*/ while(1) { OSSemPost(Sem); /* Send semaphores 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.
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 */ }}
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. lowest priority gets access to the semaphore, at (2) this task preempted by Task 1 (because The priority inversion problem is illustrated by Figure bellow, at (1) Task 3 that has the Task 1 has the higher priority). At (3), Task 1 requests the semaphore, but this semaphore
Page 37
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 semaphores resources, at (4) Task 2 preempts Task 3 and runs until (5), where the CPU
will be given to Task 3 instead of Task 1 (Highest priority) which still awaits for the obtains it after a long waiting.
Chapter II
semaphore to be released by Task 3. At (6) Task 3 releases this semaphore and Task 1 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. (1) (2) (3) (4)
Priority inversions problem Task 1 (Highest priority) wait for Task 2 or Task 3 (Lowest priority)
(5)
(6)
SEM
SEM
SEM
To avoid this problem, we can raise the priority of Task 3 over the others Tasks and restore this Task, therefore, the semaphore will be released more quickly and task 1 does not have using the mutual exclusion semaphore instead of the normal semaphore. to wait for a long time for the semaphore. These Operations will be done automatically by
Page 38
the original priority when releases the semaphore, this will lead to give more CPU cycles to
Task 3 resumed
SEM
Chapter II
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
OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) OSMutexPost (OS_EVENT *pevent) OSMutexAccept (OS_EVENT*pevent, INT8U *err) OSMutex_RdyAtPrio (OS_TCB *ptcb, INT8U prio)
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. Pend on mutual exclusion semaphore. Post signal to a mutual exclusion semaphore Checks the mutual exclusion semaphore is available.
6.3.
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, it, to create a mailbox the following function is requested:
Message Mailboxes
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,
numbers, structures etc), as for any ECB object, the Mailbox should be created before use
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 */
Page 39
Chapter II
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 */
To read the message sent, two functions can be used depend if we want pending on 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.*/
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
Page 40
Chapter II
Mailbox
TASK
OSMboxPost()
OSMboxPend()
TASK
ISR
OSMboxPost()
OSMboxAccept()
TASK ISR
Page 41
Chapter II
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 queue should be created using the following function: input first output) priority. As we have seen in Semaphores and Mailboxes the messages
*OSQCreate (void **start, INT16U size)
OS_EVENT
/* 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 */
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 */
To send (inject) a message at the end of the massage queue, OSQPost() is called, otherwise
Page 42
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.*/
The same situation as in mailboxes, there are two function to get message from the queue,
Chapter II
OSQPost()
Messages Queue
End of the queue
OSQPend() OSQAccept()
Front of the queue
TASK ISR
Chapter II
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 */
}}
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()
const QSize = 64;/* Messages queues 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 */
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
}}
Page 44
Chapter II
Event flags are used when a task needs to synchronize with the occurrence of multiple have occurred. This is called conjunctive synchronization (logical AND). [4] TASK TASK
TASK/ISR
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 Figure bellow shows the Disjunctive and Conjunctive synchronization using Flag Events:
Post Events
TASK
Pend on Events
Post Events
AND (ALL)
TASK
TASK/ISR
TASK
TASK
ISR
Conjunctive synchronization
ISR
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 check these Event flags states. TASK ISR
Set/Clear Event Flags OSFlagPost()
Event Flags Group 0 1 1 1 0 0 1 1
Disjunctive synchronization
task or an ISR, in the other side a task can synchronize its activities by waiting (Pend) on or
Wait Events OSFlagPend()
TASK ISR
Check Events OSFlagAccept()
Page 45
Chapter II
Before using C/OS-II Event Flags a group of flags should be created by using
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.*/
To Set/Clear some bits in the Event Flags Group, the function OSFlagPost() is called as
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. */
follow:
Page 46
Chapter II
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.*/
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.
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).
Page 47
Chapter II
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); } }
Table 2.3 Different Time Management functions TASK DELAYING FUNCTION DESCRIPTION Delay the currently running task until the number of ticks OSTimeDly(INT16U ticks) expires OSTimeDlyHMSM(INT8U Delay the currently running task until the Time specified hours, INT8U minutes, INT8U (Hours:Min:Sec:MSec) expires seconds, INT16U milli) Resume a task that has been delayed by OSTimeDly() or OSTimeDlyResume (INT8U OSTimeDlyHMSM(), or task waiting for an event with prio) timeout. This would make the task look like a timeout occurred. Get the current value of the 32-bit counter which keeps OSTimeGet (void) track of the number of clock ticks. Set the 32-bit counter which keeps track of the number of OSTimeSet (INT32U ticks) clock ticks.
C/OS-II provides a set of timing functions to delay a task a certain amount of time, the OS_TICKS_PER_SEC, the table bellow summarizes the Task delaying functions:
time resolution depends of the Clock Tick frequency fixed by the constant
Page 48
Chapter II
8.
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 a Timer we should create it by using the function described by the following listing:
Timers Management
OSTmr_Task, C/OS-IIs Timers can be settled in repeat or in one-shout mode, before using
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. */
Page 49
Chapter II
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 */
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 */
Page 50
Chapter II
9.
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: Memory Partition
Memory Block
Memory Management
typedef struct { void *OSMemAddr; void *OSMemFreeList; INT32U OSMemBlkSize; INT32U OSMemNBlks; INT32U OSMemNFree; } OS_MEM;
OSMemAddr: Points to the first block (base) of the memory partition, it contains the partition handle as well. block or to the next free memory block. OSMemBlkSize: The size of each memory blocks in the partition.
OSMemFreeList: pointer used by C/OS-II to point to either the next free memory control OSMemNBlks: Total number of memory blocks that the partition contains. OSMemNFree: Number of the available blocks from the memory partition.
Page 51
Before using a memory partition we should create a partition from an array type and assign following declaration:
Chapter II
it to the Partition Control Blocks, to achieve this OSMemCreate() is called regarding the
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. */
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 */
Page 52
Chapter II
/* 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.*/
The following listing illustrates the memory management example, the memory partition is these data are sending and no longer need to save them:
used to store the converted analogue data, and the partition blocks will be freed when
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); }}
Page 53
Chapter II
CONCLUSION
Therefore, the C/OS-II stills useless without making the next bottom layer, this layer this operation is called Porting the C/OS-II to the specific microcontroller, this will be the topic of the next chapter.
interfaces between the C/OS-II functions and the hardware of the target microcontroller,
In this chapter we have seen all the features provided by the C/OS-II, these includes Tasks
Page 54
Chapter III
Porting the C/OS-II to a given processor consists of defining the specific types length, write the context switch routines and the Time track ticks generator (Timer). produce reentrant code. 1. 2. 3. 4. 5. You must be able to disable and enable interrupts. A processor can run C/OS-II if it satisfies the following general requirements [1]:
and assembly language, especially codes that manipulate CPU registers and interrupts.
You must have a C compiler for the processor and the C compiler must be able to The processor must support interrupts and you need to provide an interrupt that The processor must support a hardware stack, and the processor must be able to store
occurs at regular intervals (typically between 10 to 100 Hz). a fair amount of data on the stack (possibly many Kbytes). CPU registers either on the stack or in memory.
The processor must have instructions to load and store the stack pointer and other
Page 55
Chapter III
C/OS-II Source files C/OS-II specific application file Processor independent files File Function File Function os_core.c Real-time kernel functions Include/exclude the ucos_ii.h C/OS-II functions prototype C/OS-II functions, keep os_mbox.c Message mailbox management only the function needed <os_cfg.h> by the application to os_q.c Message queue management optimize the output code os_sem.c Semaphore management size os_task.c Tasks management os_time.c Time management <app_cfg.h> Application configuration os_flag.c Event flag management This file includes all the os_mutex.c Mutual exclusion semaphore <includes.h> header files needed by the os_tmr.c Timer management application 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 Tasks frame stack initialization function OSTaskStkInit() os_cpu_a.asm C/OS-II assembly functions
COS-II Application
Table 3.1 shows the different files that constitute the C/OS-II RTOS, the C/OS-II source
Hardware
provided by the C/OS-II. The files to be programmed are <os_cpu.h>, <os_cpu_c.c> and
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
Page 56
Chapter III
1.
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. <OS_CPU.H> Listing 3.1 shows the implementation of this file in case of M16C microcontroller:
that represent the INT8U, INT8S, INT16U etc., it contain also the OS_ENTER_CRITICAL() and
inherently non-portable. Instead, the file <os_cpu.h> contains the processor specific type
C/OS-IIs code never makes use of Cs short, int and, long data types because they are
/***************************************************************************/ /******* DATA TYPES ******/ /******* (Compiler Specific) *******/ /***************************************************************************/ typedef typedef typedef typedef typedef typedef typedef typedef typedef unsigned unsigned signed unsigned signed unsigned signed float double char char char int int long long BOOLEAN; INT8U; /* INT8S; /* INT16U;/* INT16S;/* INT32U;/* INT32S;/* FP32; /* FP64; /*
Unsigned 8 bit quantity Signed 8 bit quantity Unsigned 16 bit quantity Signed 16 bit quantity Unsigned 32 bit quantity Signed 32 bit quantity Single precision floating point Double precision floating point
*/ */ */ */ */ */ */ */ */ */
OS_STK; /* Each stack entry is 16-bit wide 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 #define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory */ OS_TASK_SW() asm("INT #0") /* Mapped to the software interrupt 0
*/
Page 57
Chapter III
2.
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. the tasks priority. this function the start address of the task, a pointer called pdata, Tasks top-of-stack and case of M16C microcontroller.
This file contains the body of the stack frame initialization function (OSTaskStkInit()), this
shows the stack frame configuration that should be achieved by OSTaskStkInit() function in
Low memory
Stack pointer SP
OSTaskStkInit() needs the Tasks address, pdata pointer and Task top-of-stack, Figure 3.1
FLG(L)
Stack growth
Task address
The configuration of the figure 3.1 done by OSTaskStkInit(), firstly the address of the task is
Page 58
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 doesnt return and considered as infinite loop, there
is no need to push the return address of the caller, the next step pushes all the remaining 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
Chapter III
CPU registers R3, A0, A1, SB, FB and PC(L), FLG(L), PC(H) and FLH(H) are pushed at the top
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); }
which monitors all the activities of the kernel, these function are:
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
Page 59
Chapter III
calls OSView_TaskCreateHook() owned by the OS-VIEW module, OSTaskDelHook(), called when a task going to be deleted,
OSTaskCreateHook(), this function is called whenever a task is created, therefore, it OSTaskSwHook(), called when a context switch performed, allows the user to perform OSTaskStatHook(), This function is called every second by C/OS-II's statistics task, OSTaskIdleHook(), Called by the idle task, this hook has been added to allow the user to 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 Assembly Language Functions File, OS_CPU_A.ASM
other operations during a context switch, this function should calls OSView_TaskSwHook() to aware the OS-VIEW that a context switch has occurred. this allows to add new functionality to the statistics task. do such things as STOP the CPU to conserve power.
3.
function is also used to increment the C/OS-II V2.83 Timers counter. C/OS-II, these functions are: 3.1. OSStartHighRdy()
This file contains the functions written in assembly language and the interrupt table of the
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 the user that the multitasking has just starting before making OSRunning = TRUE. priority ready to run task, this function call the hook function OSTaskSwHook() to inform
Page 60
The listing below show the assembly code of this function in case of the M16C microcontroller: OS_CPU_A.ASM
Chapter III
;';;;;;;;;;;;;;;;;;; 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
3.2. OSCtxSw(),
This function performs the task level context switch, as we have seen in chapter II this task and launches this task, the listing bellow shows the pseudo code of this function:
void OSCtxSw(void) { Save processor registers; Save the current tasks stack pointer into the current tasks 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 tasks stack; Execute a return from interrupt instruction; }
Listing 3.3 Function called when C/OS-II start to run the highest priority ready to run task
routine starts by saving the context registers, loads the context of the highest priority ready
Listing 3.4 Pseudo code of the function that performs Context Switch function
Page 61
The implementation of this function in case of M16C microcontroller is shown by listing 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
Chapter III
3.5, we should note that this routine is mapped in interrupt vector number 0, thus it will be
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 function. already done by the calling interrupt, this what makes difference with the OSCtxSw()
Listing 3.5 Assembly code of the function that performs task level Context Switch function
Page 62
The listing bellow shows the assembly code of this function: OS_CPU_A.ASM
Chapter III
;';;;;;;;;;;;;; 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 decrease the reactivity of the C/OS-II application. The listing below show the pseudo code of this routine [1]:
Call OSTimeTick();
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
void OSTickISR(void) { Save processor registers; Call OSIntEnter() or increment OSIntNesting; Call OSIntExit(); Restore processor registers; Execute a return from interrupt instruction;
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
Chapter III
Page 64
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
Chapter III
;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;';;;;;;;;;;;;;;;;;;;;;;;;; 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
CONCLUSION
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
Once the porting of the COS-II to the M16C microcontrollers achieved, any real time
Page 65
Chapter IV
targets the M16C Microcontrollers, in all programs that follow the IAR Embedded Workbench for Mitsubishi V1.36 Compiler and Linker are used.
which should be the start point of our application or any C/OS-II based application that
Page 66
Chapter IV
1.
Figure bellow shows the IAR Project of the template, we can see that the projects files are C/OS-II core which contain all its features grouped by categories, these file are: Table 4.1 C/OS-II Source file Functions C/OS-II Core functions (Required) Tasks management functions (Required) Mailboxes management functions (Optional) Semaphores management functions (Optional) Mutual exclusion semaphore functions (Optional) Message queues management functions (Optional) Memory management functions (Optional) Events Flag management functions (Optional) Delay Time functions (Ex. OSTimeDly()) (Optional) Timers management functions (Optional)
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 Source file OS_CORE.C OS_TASK.C OS_MBOX.C OS_SEM.C OS_MUTEX.C OS_Q.C OS_MEM.C OS_FLAG.C OS_TIME.C OS_TMR.C
The file os_cfg.h contains the directives to include or exclude different features provided by 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, to the users hook functions. the needed functions.
the C/OS-II; these directives are useful to optimize the application code by keeping only
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.
OSIntCtxSw etc) which require to access to CPUs registers. The second file ported to the
M16C Microcontrollers is os_cpu_c.c which contains the Tasks Stack initialization and calls
Page 67
Chapter IV
The C/OS-II project need to be compiled under far memory model, therefore, this model project as shown in Figure 4.3.
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
Page 68
Chapter IV
Page 69
Chapter IV
The third group contains the embedded part of the C/OS-View software; this embedded 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_VIEW.C makes the core of C/OS-View and it is independent of the processor. all the header files used by the application and <ucos_ii.h> file.
module communicates with a Microsoft Windows application via RS232 serial port. C/OSOS_VIEWa.ASM and OS_VIEWc.C files depend of the microcontroller and contain the serial The files that belong to the application are grouped in same folder named C/OS communication interrupts declaration and UARTs registers initialization respectively. 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
Page 70
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. <includes.h> The following listing shows the previous file of the project template:
#define Chip_3062x #include <iom16c62.h>
Chapter IV
/* 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 /* CPU Clock frequency */ #define CPU_CLK_FREQ
128
16000000
Page 71
Chapter IV
/* Startup Tasks 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 72
Chapter IV
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 Tasks 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, /* Tasks Void */ (void *)0, /* No value passed to the task */ &STARTUP_STK[TASK_STK_SIZE 1],/* Top of the Tasks Stack */ STARTUP_PRIO, /* Task Priority */ STARTUP_PRIO, /* Task ID */ &STARTUP_STK[0], /* Bottom of the Tasks Stack */ TASK_STK_SIZE, /* Tasks 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 73
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
In the previous application I have used the low priority Startup Task to initialize the Clock
Chapter IV
microcontroller with C/OS-II application and a Windows application, the Windows the status of the tasks managed by the C/OS-II. The C/OS-View V1.10 allows to shows the The address of the TCB of each task; The name of each task;
application communicate with the embedded module via the rs232 port and allows to view
The status (Ready, delayed, waiting on event) of each task; The amount of stack space used and left for each task; The number of times each task has been switched to; The execution profile of each task; be processed.
The number of ticks remaining for a timeout if a task is delayed or wait on timed events; The percentage of CPU time used by each task relative to all the tasks;
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 the command structure. Pass keystrokes to your application from the Terminal window. In other words, you target specific and thus you can define those specific to your product.
can now send commands to your product from the Windows application. You determine Output ASCII strings from the target to the Terminal window. These ASCII strings are
Page 74
To use the C/OS-View terminal, these two functions are used to send or receive ASCIL character respectively: To send data:
Chapter IV
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 the interrupt level context switch, the other interrupts notify the C/OS-II that an interrupt shows the new format to write an interrupt (Ex. Serial Port data reception interrupt Tx).
;'Save the current running task registers ;'OSIntNesting++, ;'Call OSView_TxISRHandler() ;'OSIntNesting-;'Restore registers from the new task stack
causes the crash of the application when these interrupt are often triggered, therefore, I has occurred without making the context switch by incrementing OSIntNesting when enter
have kept this format only in the case of Clock Ticks Timer (OSTickISR) in order to perform to interrupt and decrements this variable when excite the interrupt, the listing bellow
OSView_TxISR: PUSHM R0,R1,R2,R3,A0,A1,SB,FB INC.B OSIntNesting JSR OSView_TxISRHandler DEC.B OSIntNesting POPM R0,R1,R2,R3,A0,A1,SB,FB REIT
Page 75
consumed by each task, we can show also how often the context switches performed to a located in <os_cfg.h>).
certain task. C/OS-View displays the Tasks 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
By using the C/OS-View application we can show the Tasks status and the CPUs cycles example the number of Clock Ticks has been fixed to 100 (OS_TICKS_PER_SEC = 100,
Chapter IV
Figure 4.4 Tasks Status and C/OS-II running information given by C/OS-View
Tasks priority
Tasks Status
C/OS-II CPU Load graph (very low compared to the idle tasks 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 */ ..
In order to make the CPU load of the Task1, Task2 and Startup Task visible on the
Chapter IV
TASK2s Activity
TASK1s Activity Startup Tasks Activity Figure 4.5 Graph of the CPU percentage consumed by each task across the time
Page 77
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 statistics proves the benefit of the C/OS-II regarding the CPU load optimization. 3. C/OS-II Demonstration Example means the rest time of the C/OS-II, in contrast, Task1 and Task2 consume only 0.63 time to add more Tasks and Codes without the risk to overload the CPU. Therefore, these and 0.65% of CPU time respectively which is very low, this means that still plenty of CPU
Chapter IV
The previous project template will be used to develop the data acquisition system feature of the C/OS-II, which is the multitasking, the whole system will be divided into in order to achieve the main functions of this system. Figure 4.6 shows the block diagram of this system: C/OS-II Inter-task communications tools to make different tasks interact with each other
described briefly at the beginning of this chapter, to get benefit of the most important
tasks, each task performs its job independently of other tasks, therefore, the system uses
Page 78
Chapter IV
Buzzer
LCD Semaphore
1 4 7 A
2 5 8 0
3 6 9 B
F E
Emergency Messages
Keypad RS232
Mails Queue
Computer
Suspend /Resume
Resume sending the stored messages when memory 90% full Stored Messages to be sent to PC
Pressure Sensor
Page 79
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.
sounds, this tasks program contains a Mailbox to receive messages from outside, and this message carries the code pattern of the desired sound.
The data acquisition system of the precedent block diagram contains the following tasks:
Chapter IV
1. Sound Alarm Task: This task drives the Buzzer to generate the warning alarm
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 cant Display. These messages will be stored on a Memory Partition with limited size, therefore, Serial Com Task. wait on the queue and should be displayed immediately. The messages posted to this task 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 4. Serial Com Task: The job of this task is to send the stored messages to the PC via
contain two text lines of 16 characters each to be displayed on the 2-Line 16-Charaters LCD this partition should be used dynamically and freed each time it is 90% full, the messages
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 when 90% of the memory is free.
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
Page 80
5. Config Menu Task: The role of this task is to manage different configuration menus of
Chapter IV
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, 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 the Menus items.
Minimum allowed Pressure, Alarmed/Disarmed gates, Time and Date). The second menu is Clock Tick and Context Switches, Time/Date. This task waits for an Event Flag to be signaled by the keypads 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 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 Message Task into its Messages Queue in order to be displayed one by one on the LCD 6. Data Capture Task: This task is the most important task in the system and the
the eight gates, these values are stamped by the Time and Date and send to 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 the emergency will more severe. 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
Page 81
the mixture of the displayed characters (Menus items and Messages) of both tasks. 4. MSA0654 Development Board
The LCD Semaphore will be used to get exclusive access to the LCD Display, This Display is
Chapter IV
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
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 development board connected with its daughter board.
peripherals (LCD Displays, LEDs, Keypad, and Buzzer etc.). Figure 4.7 shows this
The application designed in this chapter will be tested on the MITSUBISHI (MSA0654-
Page 82
Chapter IV
Daughter board
Chapter IV
Figure 4.8 shows the Applications project designed with the IAR IDE V1.36, the previous project template was used to develop this application.
5.
Applications directory
Page 84
Chapter IV
In addition of the tasks shown by the Figure 4.8, the Clock Task has been added to provide any archiving process.
Real Time Clock in order to stamp the stored messages by the time, which is a necessity in 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 Drivers and should be written in assembly language.
C/OS-II MSA0654 DRIVERS directory contains the MSA0654 board driver functions, and interrupt service routines and the interrupt vectors table needed by MSA0654 Board
Page 85
Chapter IV
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).
By using the C/OS-View kernel debugger, we can monitor the profile of the C/OS-II and each task
Figure 4.10 Graphs shows Tasks CPU load in normal operation of the application
Page 86
From the Tasks information given by Figure 4.9 and Figure 4.10 we can comment on the small amount of CPU cycles when picking up the data or sending messages; status of each task as follow:
Chapter IV
Data Capture Task: This task spend the majority of time delayed and consumes a LCD Message Task: This is the most one that loads the CPU; this is caused by LCD Displays functions that should call the dummy function to simulate a short delay to follow emergency Mailbox if a message was posted to display it on the LCD display; the LCD module responsiveness. In addition, this task checks the Message Queue and the was sent in case of emergency to light the LEDs according the message content;
LEDS Alarm Task: By lighting the LEDs one by one to show which gates are alarmed, the LCD Message Task; this task loads slightly the CPU (0.01%), in addition, this task check its Mailbox if a message Serial Com Task: Suspended until the memory is 90% full where will be resumed by
the keypads interrupt when the user presses any button. Therefore, this task spends all its time in waiting state and it doesnt load the CPU while in this state. for this message; Sound Alarm Task: This task waits for a message to be posted in its Mailbox, which therefore, this task doesnt consume too much CPU time. the C/OS-II parameters and different Tasks profiles. carries the emergency sound Alarm, this task doesnt consume any CPU time while waiting
Config Menu Task: This task waits for an Event Flags to be signaled by the means of
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 be added to the C/OS-II without overload the CPU or miss the deadline of each task. From these results we can conclude that CPU stills not full used and many other tasks can
Clock Task: This task delays it self by one second to generate the time and date,
Page 87
Chapter IV
CPU as long the LCD is not available to call its functions. The graphs of the figure bellow demonstrate this case:
When Config Menu task takes over the LCD Display, the LCD Message task loads less the
menus 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.
In this case the Event Flags which waited to be set by the Config Menus Task will be signaled by the Keypads 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
Config Menu Task goes back to wait state when exit menu Config Menu Task comes active when keypad pressed
Figure 4.11 Config Menu Task comes active to manage the menus items when keypad pressed
Page 88
Chapter IV
Messages Task, the graphs of the figure bellow shows this scenario.
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 sent by the Serial Com Task Serial Com Suspended when memory emptied Serial Com Resumed when memory full
Figure 4.12 Serial Com Task resumed to empty the memory when it is 90% full
Page 89
Chapter IV
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 Figures 4.13 shows the activities of each task involved in the Emergency state. in addition, the LCD Messages Task sends to the LCD the nature of the emergency. The
along with LEDs blinking done by the Alarm Sound and LEDs Alarm Tasks respectively,
LCD Messages and DATA Capture Tasks load more the CPU Sound Alarm Task comes active and plays the Emergency Alarm sound
Page 90
Figure 4.13 shows what is happened in the case of emergency regarding the activities of
Chapter IV
different tasks. The Sound Alarm Task which was waiting for a message starts running frequently as long the emergency state stills present.
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 CPUs cycles When there is no emergency the LCD Displays shows the temperature, pressure and the 4.2 shows the messages displayed and LEDs status for different cases. because the Emergency message will be sent and received by these tasks respectively more 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
Page 91
Chapter IV
Table 4.2 LCDs Messages displayed Messages displayed Description and LEDs status
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 92
The LCD Display can show other parameters of the C/OS-II or the application (CPU usage, parameters can be shown by selecting the View item of the Main Menu. By selecting the parameters. Table 4.3 Setup and View Menu items
Chapter IV
Amount of the free memory, number Clock ticks and Context Switches etc). These
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 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
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
CPU Usage View Menu item, Display the global CPU Load of the application
Page 93
Chapter IV
CONCLUSION
The application built in this chapter has well demonstrated the advantages of the C/OS-II advantages can be summarized as follow: no limit made by other tasks when developing a task, especially regarding the waiting time or the inertia made by other tasks; 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;
and the facilities brought by this RTOS to design a real time multitasking application, these
1. Total independence between different programmed Tasks, in other word, there is 2. Tasks can easily interact with each other or exchange information using different problem can be resolved efficiently by using the semaphore or mutual exclusion semaphore and they are manipulated with very simple functions.
3. No more worries about the resources sharing and the risk of corruption, this 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 94
Conclusion
Conclusion
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. provided by the C/OS-II (Semaphore, Mailbox, Event flags etc) to give a practical demonstration on how to use these features. In addition to the real time responsiveness of this application, I have used all the 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. demonstrated, by observing the delay between an action made on the input (Keypad, measured were very short and imperceptible by humans and were therefore, acceptable. developing board, the real time responsiveness of the C/OS-II has been well With the different Input/Output (LCD, LEDs, Switches etc) built within the MSA0654 In this project, porting the C/OS-II to the M16C microcontrollers has been achieved with
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, To achieve this work, I felt the necessity to make an attractive demonstration about what is has a Monitor called C/OS-View, made from a windows application and an embedded LEDS and the Buzzer speaker, and this in different experimental conditions The delays
microcontrollers by writing the hardware initialization of the serial communication and the Transmission/Reception interrupts for a specific microcontroller.
Page 95
happening inside the C/OS-II and the status of each application task. Luckily, the C/OS-II module built within the C/OS-II project, the embedded files should be ported to the M16C
Conclusion
The C/OS-View works along with its graphical windows, and provides the status (Ready, each task.
Wait for a delay to expire or Semaphore or Message etc) and shows graphically the activity The global CPU load of the developed application which doesnt exceed 9% demonstrates
(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 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 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. tasks. In other word, someone who uses the C/OS-II for the first time will never go back to use
Page 96
References
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 [4] C/OS-II and Event Flags, Application Note AN-1007A Jean J. Labrosse
[6] Introduction to Real-Time Operating Systems, David Kalinsky, Ph.D. Enea Embedded USA Technology, San Jose, California USA [7] C/OS-View V1.10 Users Manual, Micrim, Inc.
[5] C/OS-II and Mutual Exclusion Semaphores, Application Note AN-1002 Jean J. Labrosse
[3] C/OS-II and the Renesas M16C Processors, Application Note AN-1019 Jean J. Labrosse
[8] Open Source Real Time Operating Systems Overview T. Straumann, SSRL, Menlo Park, [9] http://www.whatis.com/ [10] C/OS-View V1.10 Users Manual, Micrim, Inc.
Page 97
Appendix I
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 98
Appendix I
/* 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 99
Appendix I
&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 100
*/ */ */ */ */
Appendix I
CM1 PRCR }
*/
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 #include <iom16c62.h> /* Define the Target Chip */ /* include the Target Chip file */
Page 101
Appendix I
/* 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 #define DATACAPTURE_PRIO #define LCDMESSAGES_PRIO #define LEDSALARM_PRIO #define SERIALCOM_PRIO #define CONFIGMENU_PRIO #define SOUNDALARM_PRIO #define CLOCK_PRIO 8 10 20 30 35 40 45 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
Page 102
Appendix I
/* 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 103
Appendix I
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 *******************/
Page 104
Appendix I
/* 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 INT8U INT8U MaxTemperature MinPressure GatesMask = 150; /* Maximum Allowed Temperature */ = 20; /* Minimum Allowed Pressure */ = 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 */ 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 */ TempEmergeLedMsg = {0xFF, OS_TRUE},/* High Temp Message sent to LEDs Task */ PressureEmergeLedMsg = {0xF0, OS_TRUE},/* Low Press Message sent to LEDs Task
Page 105
Appendix I
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 106
Appendix I
/* 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 { INT8U ItemState[3][10]; INT8U MaxItems[3]; INT8U XPos, YPos; char *MenuName[3]; char *ItemName[3][10]; } MenuMap; /* /* /* /* /* /* Menu's Map Structure */ Munu's Items States Machine */ Maximum Items of each Submenu */ X, Y Position in the Menu's Map */ SubMenus' Names */ SubMenus' Items Names */
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 107
Appendix I
"<B MAX TEMP C>", "<B MIN PRESS C>", "<B EN/DI GATE C>", "<B SET DATE C>","<B EXIT C>", "", "","","", "<B CPU USAGE C>", "<B FREE MEM C>", "<B Tick/CtxSw C>", "<B EXIT C>", "","","","",""} };
SET TIME
C>", "<B
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 108
Appendix I
/* 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 MainMenu.YPos State = 1; /* break; MainMenu.XPos MainMenu.YPos State = 1; /* break; State = 0; /* break; = 0;/* Set X and Y to "Setup" submenu Position */ = 1; Go to "Setup" submenus */ = 0;/* Set X and Y to "View" submenu Position */ = 2; Go to "View" submenus */ User slected to quit menus */
case 11 :
case 12 :
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; /* 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); }
case 21 :
Page 109
Appendix I
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; SetTime(); /* Call the function that allows the user to Set time State = 1; /* Go back the previous menu */ break; SetDate(); /* Call the function that allows the user to Set date State = 1; /* break; MainMenu.XPos MainMenu.YPos State = 1; /* break; Go back the previous menu */ = 0; /* Select to root of the Menu */ = 0; Go to the selected Munu's item */
case 23 : */
case 24 : */
case 25 :
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 110
Appendix I
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 111
Appendix I
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 112
Appendix I
} 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 113
Appendix I
} } /********* 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 114
Appendix I
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 115
Appendix I
/* 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 116
Appendix I
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 117
Appendix I
#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 118
Appendix I
} 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 119
Appendix II
Page 120
Appendix II
#define PORT_10D PD10 /* LCD Command values */ #define LcdCmd_Cfg 0x3B #define LcdCmd_Cls 0x01 #define LcdCmd_Inc 0x06 #define LcdCmd_Set 0x0C #define LcdCmd_CursorOn #define LcdCmd_CursorOff #define LcdCmd_CursorBlink #define LcdCmd_On #define LcdCmd_Off
/*Cfg: 8-bit data, 1/16 duty, 5x8 dots */ /*Clear Display, Cursor Home */ /*Set Incrument Mode */ /*Set LCD: Dsp: ON, Cursor OFF, Blink: Off */ 0x0E 0x0C 0x0D 0x0C 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 #define #define #define #define #define #define LcdDspMoveScrLeft() LcdDspMoveScrRight() LcdDspCursorOn() LcdDspCursorOff() LcdDspCursorBlink() KeyRptStartDly KeyRptDly LcdSendCmd(LcdCmd_PanLeft) LcdSendCmd(LcdCmd_PanRight) LcdSendCmd(LcdCmd_CursorOn) LcdSendCmd(LcdCmd_CursorOff) LcdSendCmd(LcdCmd_CursorBlink) 1000 400 /* x 976.56us */ /* 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 121
Appendix II
#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 122
Appendix II
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 123
Appendix II
: : : : : : : :
= = = = = = = =
case 0xE7 : KeyChar = case 0xD7 : KeyChar = case 0xB7 : KeyChar = case 0x77 : KeyChar = default : KeyChar = } } else KeyChar = 0xFF; KeyUnRead = OS_FALSE; return KeyChar;
'F'; break; 'E'; break; 'D'; break; 'C'; break; 0xFF; break;
} /* 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; CM0 |= 0x10; CM0 &= 0xF7; PRCR = 0x00; TA3 = TA3MR = TA3IC = TABSR|= 0x01; 0xC2; 0x02; 0x08; /* Enable write to Processor mode /* Xc Circuit Enabled */ */
/* Disable access to processor and clock mode */ /* /* /* /* initial reload = 976.56us */ One shoot mode timer, fc32 src =976.56us */ Priority = 2 */ 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 124
Appendix II
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 { #endif LedsLight(INT8U Pattern) /* Light the leds with the pettern */ PORT_3 = Pattern;}
/********************************************************************************/ /* 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 125
Appendix II
/********************************************************************************/ /* 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 126
Appendix II
/* 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 127
Appendix II
break; ADCValue = AD7H; ADCValue <<= 8; ADCValue |= AD7L; break; default : ADCValue = AD0H; ADCValue <<= 8; ADCValue |= AD0L; break; } OSSemPost(ADCSem); /* release the semaphore */ return ADCValue; } case 7 : #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 128
Appendix II
/* /* /* /*
load the period */ Start the Timer */ Wait for Dly clock ticks */ 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 RSEG EXTERN RSEG EXTERN EXTERN EXTERN EXTERN EXTERN EXTERN EXTERN EXTERN EXTERN EXTERN CSTACK ISTACK OSIntNesting CODE(1) TIMER_A2_CALLBACK TIMER_A3_CALLBACK KEYPAD_CALLBACK OSView_TxISRHandler OSView_RxISRHandler INT_0_CALLBACK INT_1_CALLBACK ADtrg_CALLBACK OSView_TxISR 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 129
Appendix II
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 130
Appendix II
REIT ;'******************************************************************************** ;'* INTERRUPT VECTOR TABLE ;'******************************************************************************** .COMMON .ORG .LWORD .ORG .LWORD .ORG .LWORD .ORG .LWORD .ORG .LWORD .ORG .LWORD .ORG .LWORD .ORG .LWORD .END INTVEC 13*4 KEYPAD_ISR 14*4 ADtrg_ISR 23*4 TIMER_A2_ISR 24*4 TIMER_A3_ISR 29*4 INT_0_ISR 30*4 INT_1_ISR 17*4 TxISR 18*4 RxISR
Page 131
Appendix III
/* /* /* /* /* /* /* /*
Unsigned 8 bit quantity Signed 8 bit quantity Unsigned 16 bit quantity Signed 16 bit quantity Unsigned 32 bit quantity Signed 32 bit quantity Single precision floating point Double precision floating point
*/ */ */ */ */ */ */ */ */ */
#define
OS_CRITICAL_METHOD
Page 132
Appendix III
*/ */
/* ********************************************************************************** * RENESAS M16C FAMILY MISCELLANEOUS ********************************************************************************** */ #define #define OS_STK_GROWTH OS_TASK_SW() 1 /* Stack grows from HIGH to LOW memory */ asm("INT #0") /* Mapped to the software interrupt 0 */
/* ********************************************************************************** * PROTOTYPES ********************************************************************************** */ void void void void OSCtxSw OSIntCtxSw OSStartHighRdy OSTickISR (void); (void); (void); (void);
os_cpu_a.asm
;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;' PUBLIC FUNCTIONS ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RSEG RSEG EXTERN EXTERN EXTERN EXTERN EXTERN EXTERN RSEG EXTERN EXTERN EXTERN CSTACK ISTACK OSTCBCur ; 'Current Running Task TCB, 32-bit long OSTCBHighRdy ;'Highest Priority Task TCB ready to run,32-bitlong OSPrioCur ;'Current running Task Priority, 8-bit long OSPrioHighRdy ;'Priority of the Highest Priority Task ready to run, ;8-bit long OSIntNesting ; 'Interrupts nesting variable, 8-bit long OSRunning ; 'Is C/OS-II Running?, 8-bit long CODE OSIntExit ; 'Exit from interrupt, called when finish with the ISR OSTimeTick ; 'Function called every Clock Tick of the uCOS-II OSTaskSwHook ; 'User hook function, Called at each context switch
Page 133
Appendix III
;' Tx/Rx UART Callback functions, declared in OSVIEW Module (Optional) EXTERN OSView_TxISR EXTERN OSView_RxISR EXTERN DUMMY ;';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PUBLIC PUBLIC PUBLIC OSStartHighRdy OSCtxSw OSIntCtxSw ; 'Start the OS, Run the Highest Priority Task ; 'Task Level Context Switch routine ; '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 ; MOV.W OSTCBHighRdy, OSTCBCur ; MOV.W OSPrioHighRdy, OSPrioCur; MOV.W OSTCBHighRdy, A0 ; LDC [A0], ISP
Page 134
Appendix III
; '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 JSR OSIntExit ; 'Call POPM R0,R1,R2,R3,A0,A1,SB,FB REIT
OSTimeTick() OSIntExit() to achieve the context switch ; 'Restore registers from the new tasks stack ; '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 135
Appendix III
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 136
Appendix III
#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 137
Appendix III
/******************************************************************************** * 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 138
Appendix III
/********************************************************************************* * 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 139
Table of Contents
ABSTRACT ......................................................................................................................................................................................................... 1 ACKNOWLEDGEMENTS .......................................................................................................................................................................... 2 INTRODUCTION.......................................................................................................................................................................................... 3 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 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
TABLE OF CONTENTS
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 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 Keypads button pressed ..........................................................................................................................................88 5.3. When the Messages memory is full ...................................................................................................................................89 5.4. In Case of emergency ..............................................................................................................................................................90 CONCLUSION .............................................................................................................................................................................................94 REFERENCES ..................................................................................................................................................................................................97
CONCLUSION ........................................................................................................................................................................ 95
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