Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Figure 3.1 shows the µC/OS-II architecture and its relationship with the hardware. When you use µC/OS-II in an application, you are responsible for providing the Application Software and the µC/OS-II Configuration sections. This book and CD contain all the source code for the Processor-Independent Code section as well as the Processor-Specific Code section for the Intel 80x86, real mode, large model. If you intend to use µC/OS-II on a different processor, you need to either obtain a copy of a port for the processor you intend to use or write one yourself if the desired processor port is not available. Check the official µC/OS-II Web site at www.micrium.com for a list of available ports.

Critical Sections, OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()

µC/OS-II, like all real-time kernels, needs to disable interrupts in order to access critical sections of code and to re-enable interrupts when done. This allows µC/OS-II to protect critical code from being entered simultaneously from either multiple tasks or ISRs. The interrupt disable time is one of the most important specifications that a real-time kernel vendor can provide because it affects the responsiveness of your system to real-time events. µC/OS-II tries to keep the interrupt disable time to a minimum, but with µC/OS-II, interrupt disable time is largely dependent on the processor architecture and the quality of the code generated by the compiler.

...

Alternatively, the task can delete itself upon completion as shown in Listing 3.3. Note that the task code is not actually deleted; µC/OS-II simply doesn’t know about the task anymore, so the task code will not run. Also, if the task calls OSTaskDel(), the task never returns.

...

The maximum number of tasks (OS_MAX_TASKS) that an application can have is specified in OS_CFG.H and determines the number of OS_TCBs allocated for your application. You can reduce the amount of RAM needed by setting OS_MAX_TASKS to the actual number of tasks needed in your application. All OS_TCBs are placed in OSTCBTbl[]. Note that µC/OS-II allocates OS_N_SYS_TASKS (see uCOS_II.H) extra OS_TCBs for internal use. Currently, one is used for the idle task, and another is used for the statistic task (if OS_TASK_STAT_EN in OS_CFG.H is set to 1). When µC/OS-II is initialized, all OS_TCBs in the table are linked in a singly-linked list of free OS_TCBs, as shown in Figure 3.3. When a task is created, the OS_TCB pointed to by OSTCBFreeList is assigned to the task, and OSTCBFreeList is adjusted to point to the next OS_TCB in the chain. When a task is deleted, its OS_TCB is returned to the list of free OS_TCBs.

An OS_TCB is initialized by the function OS_TCBInit() (see Listing 3.6) when a task is created. OS_TCBInit() is called by either OSTaskCreate() or OSTaskCreateExt() (see Chapter 4, Task Management). OS_TCBInit() receives seven arguments:

prio

is the task priority,

ptos

is a pointer to the top of stack once the stack frame has been built by OSTaskStkInit() (will be described in Chapter 13, Porting µC/OS-II) and is stored in the .OSTCBStkPtr field of the OS_TCB.

pbos

is a pointer to the stack bottom and is stored in the .OSTCBStkBottom field of the OS_TCB.

id

is the task identifier and is saved in the .OSTCBId field.

stk_size

is the total size of the stack and is saved in the .OSTCBStkSize field of the OS_TCB.

pext

is the value to place in the .OSTCBExtPtr field of the OS_TCB.

opt

is the OS_TCB options and is saved in the .OSTCBOpt field.

Ready List

Each task is assigned a unique priority level between 0 and OS_LOWEST_PRIO, inclusive (see OS_CFG.H). Task priority OS_LOWEST_PRIO is always assigned to the idle task when µC/OS-II is initialized. Note that OS_MAX_TASKS and OS_LOWEST_PRIO are unrelated. You can have only 10 tasks in an application while still having 32 priority levels (if you set OS_LOWEST_PRIO to 31).

...

The code in Listing 3.7 is used to place a task in the ready list. prio is the task’s priority.

As you can see from Figure 3.4, the lower three bits of the task’s priority are used to determine the bit position in OSRdyTbl[] , and the next three most significant bits are used to determine the index into OSRdyTbl[] . Note that OSMapTbl[] (see OS_CORE.C ) is in ROM and is used to equate an index from 0 to 7 to a bit mask, as shown in Table 3.1.

Table 3.1 Contents of OSMapTbl[].

Index

Bit Mask (Binary)

0

00000001

1

00000010

2

00000100

3

00001000

4

00010000

5

00100000

6

01000000

7

10000000

A task is removed from the ready list by reversing the process using the code in Listing 3.8.

This code clears the ready bit of the task in OSRdyTbl[] and clears the bit in OSRdyGrp only if all tasks in a group are not ready to run; that is, all bits in OSRdyTbl[prio >> 3] are 0. Another table lookup is performed, rather than scanning through the table starting with OSRdyTbl[0] , to find the highest priority task ready to run. OSUnMapTbl[256] is a priority resolution table (see OS_CORE.C ). Eight bits represent when tasks are ready in a group. The least significant bit has the highest priority. Using this byte to index OSUnMapTbl[] returns the bit position of the highest priority bit set — a number between 0 and 7. Determining the priority of the highest priority task ready to run is accomplished with the code in Listing 3.9.

For example, as shown in Figure 3.5, if OSRdyGrp contains 01101000 (binary) or 0x68, then the table lookup OSUnMapTbl[OSRdyGrp] yields a value of 3, which corresponds to bit 3 in OSRdyGrp . Note that bit positions are assumed to start on the right with bit 0 being the rightmost bit. Similarly, if OSRdyTbl[3] contains 11100100 (binary) or 0xE4, then OSUnMapTbl[OSRdyTbl[3]] results in a value of 2 (bit 2). The task priority (prio) is then 26 (i.e. 3 x 8 + 2). Getting a pointer to the OS_TCB for the corresponding task is done by indexing into OSTCBPrioTbl[] using the task’s priority.

Task Scheduling

µC/OS-II always executes the highest priority task ready to run. The determination of which task has the highest priority, and thus which task will be next to run, is determined by the scheduler. Task-level scheduling is performed by OS_Sched(). ISR-level scheduling is handled by another function [OSIntExit()] described later. The code for OS_Sched() is shown in Listing 3.10. µC/OS-II task-scheduling time is constant irrespective of the number of tasks created in an application.

...

  • A Stack Pointer (SP)
  • A Program Counter (PC)
  • A Processor Status Word (PSW)
  • Four general purpose registers (R1, R2, R3 and R4)

Figure 3.7 shows the state of the variables and data structures after calling OS_TASK_SW() and after saving the context of the task to suspend.

Figure 3.8 shows the state of the variables and data structures after executing the last part of the context switch code.

The pseudo code for the context switch is shown in Listing 3.11. OSCtxSw() is generally written in assembly language because most C compilers cannot manipulate CPU registers directly from C. In Chapter 14, 80x86 Large Model Port, we will see how OSCtxSw() as well as other µC/OS-II functions look on a real processor, the Intel 80x86.

Locking and Unlocking the Scheduler

...

Figure 3.9 illustrates the flow of execution when initializing the statistic task.

The code for OSStatInit() is shown in Listing 3.16.

The code for OS_TaskStat() is shown in Listing 3.17.

Interrupts under µC/OS-II

...

The above description is further illustrated in Figure 3.10.

The code for OSIntEnter() is shown in Listing 3.19 and the code for OSIntExit() is shown in Listing 3.20. Very little needs to be said about OSIntEnter() .

You need to call OSIntCtxSw() instead of OS_TASK_SW() because the ISR has already saved the CPU registers onto the interrupted task and thus shouldn’t be saved again. Implementation details about OSIntCtxSw() are provided in Chapter 13, Porting µC/OS-II.

...

The code for OSTimeTick() is shown in Listing 3.24.

If you don’t like to make ISRs any longer than they must be, OSTimeTick() can be called at the task level as shown in Listing 3.25. To do this, create a task that has a higher priority than all your other application tasks. The tick ISR needs to signal this high-priority task by using either a semaphore or a message mailbox.

 

You obviously need to create a mailbox (contents initialized to NULL) that will be used to signal the task that a tick interrupt has occurred (Listing 3.26).

...

  • OS_TASK_STAT_EN is set to 1,
  • OS_FLAG_EN is set to 1,
  • OS_LOWEST_PRIO is set to 63, and
  • OS_MAX_TASKS is set to 62.

µC/OS-II also initializes five pools of free data structures as shown in Figure 3.12. Each of these pools is a singly linked list and allows µC/OS-II to obtain and return an element from and to a pool quickly.

After OSInit() has been called, the OS_TCB pool contains OS_MAX_TASKS entries. The OS_EVENT pool contains OS_MAX_EVENTS entries, the OS_Q pool contains OS_MAX_QS entries, the OS_FLAG_GRP pool contains OS_MAX_FLAGS entries and finally, the OS_MEM pool contains OS_MAX_MEM_PART entries. Each of the free pools are NULL pointer terminated to indicate the end. The pool is of course empty if any of the list pointers point to NULL. The size of these pools are defined by you in OS_CFG.H .

Starting µC/OS-II

You start multitasking by calling OSStart(). However, before you start µC/OS-II, you must create at least one of your application tasks as shown in Listing 3.27.

...

The code for OSStart() is shown in Listing 3.28.

Figure 3.13 shows the contents of the variables and data structures after multitasking has started. Here, I assume that the task you created has a priority of 6. Notice that OSTaskCtr indicates that three tasks have been created: OSRunning is set to TRUE, indicating that multitasking has started, OSPrioCur and OSPrioHighRdy contain the priority of your application task, and OSTCBCur and OSTCBHighRdy both point to the OS_TCB of your task.

Obtaining the Current µC/OS-II Version

You can obtain the current version of µC/OS-II from your application by calling OSVersion() (Listing 3.29). OSVersion() returns the version number multiplied by 10000. In other words, version 2.52 is returned as 25200.

To find out about the latest version of µC/OS-II and how to obtain an upgrade, you should either contact the publisher or check the official µC/OS-II Web site at http://www.micrium.com.