||This article duplicates, in whole or part, the scope of other articles, specifically, MicroC/OS-II. (September 24, 2012)|
||This article appears to be written like an advertisement. (September 2012)|
MicroC/OS-III (commonly termed µC/OS-III or uC/OS-III), is the acronym for Micro-Controller Operating Systems Version 3. It is a low-cost priority-based pre-emptive real-time multitasking operating system kernel for microprocessors, written mainly in the C programming language. It was introduced in the year 2009 that is intended for use in embedded systems. It is a scalable, ROMable, pre-emptive kernel that manages a number of tasks. It allows for unlimited tasks, semaphores, mutexes, event flags, message queues, timers and memory partitions. The user allocates all kernel objects at run time.
- μC/OS-III allows integration with other software packages such as μC/TCP-IP, μC/GUI, μC/File System, μC/USB, μC/CAN, μC/Modbus, μC/Bluetooth, for obtaining greater scalability and performance.
- It provides features to allow stack growth of tasks to be monitored. While task size is not limited, they need to have a minimum size based on the CPU used.
- It allows multiple tasks to run at the same priority level. When equal priority tasks are ready to run, μC/OS-III runs each for a user- specified time. Each task can define its own time quanta and give up its time slice if it does not require the full-time quanta.
- The way the software structure is built allows easy porting to many other architectures.
- Tasks Stacks feature allows using separate stacks with different sizes for each task thus permitting better footprint management.
- The kernel can manage interrupts with up to 255 levels deep.
- Signaling a task without a semaphore is possible.
- It is Run-Time configurable.
Probably the most important goal of μC/OS-III was to make it compatible from an application’s standpoint of view. A μC/OS port might need to be modified to work with μC/OS-III but at least, the application code should require only minor changes. Also, because μC/OS-III is based on the same core as μC/OS II, it is just as reliable. This is especially useful when you have resource limited products.
The kernel is the part of a multitasking system responsible for the management of tasks (that is, for managing the CPU's time) and communication between tasks. The fundamental service provided by the kernel is context switching. The use of a real-time kernel will generally simplify the design of systems by allowing the application to be divided into multiple tasks managed by the kernel. A kernel will add overhead to your system because it requires extra ROM (code space), additional RAM for the kernel data structures but most importantly, each task requires its own stack space which has a tendency to eat up RAM quite quickly. A kernel will also consume CPU time (typically between 2 and 5%). Single chip microcontrollers are generally not able to run a real-time kernel because they have very little RAM. A kernel can allow you to make better use of your CPU by providing you with indispensable services such as semaphore management, mailboxes, queues, time delays, etc. Once you design a system using a real-time kernel, you will not want to go back to a foreground/background system.
The scheduler, also called the dispatcher, is the part of the kernel responsible for determining which task will run next. Most real-time kernels are priority based. Each task is assigned a priority based on its importance. The priority for each task is application specific. In a priority-based kernel, control of the CPU will always be given to the highest priority task ready-to-run. When the highest-priority task gets the CPU, however, is determined by the type of kernel used. There are two types of priority-based kernels: non-preemptive and preemptive.
A task is a simple program that thinks it has the CPU all to itself. On a single CPU, only one task executes at any given time. μC/OS-III supports multitasking and allows the application to have any number of tasks. The maximum number of tasks is actually only limited by the amount of memory (both code and data space) available to the processor. A task can be implemented as a run-to-completion task in which the task deletes itself when it is finished or more typically as an infinite loop, waiting for events to occur and processing those events. A task needs to be created. When creating a task, it is necessary to specify the address of an OS_TCB to be used by the task, the priority of the task, and an area in RAM for the task’s stack. A task can also perform computations (CPU bound task), or manage one or more I/O (Input/Output) devices.
μC/OS-III creates up to five internal tasks: the idle task, tick task, ISR handler task, statistics task, and timer task. The idle and tick tasks are always created while statistics and timer tasks are optional.
A priority is assigned to each task. The more important the task, the higher the priority given to it.
Static Priorities Task priorities are said to be static when the priority of each task does not change during the application's execution. Each task is thus given a fixed priority at compile time. All the tasks and their timing constraints are known at compile time in a system where priorities are static.
Dynamic Priorities Task priorities are said to be dynamic if the priority of tasks can be changed during the application's execution; each task can change its priority at run-time. This is a desirable feature to have in a real-time kernel to avoid priority inversions.
Priority inversion is a problem in real-time systems and occurs mostly when you use a real-time kernel. If Task 1 has a higher priority than Task 2 which in turn has a higher priority than Task 3. Task 1 and Task 2 are both waiting for an event to occur and thus, Task 3 is executed. At some point, Task 3 acquires a semaphore that it needs before it can access a shared resource. Task 3 performs some operations on the acquired resource until it gets preempted by the high priority task, Task 1. Task 1 executes for a while until it also wants to access the resource. Because Task 3 owns the resource, Task 1 will have to wait until Task 3 releases the semaphore. As Task 1 tries to get the semaphore, the kernel notices that the semaphore is already owned and thus, Task 1 gets suspended and Task 3 is resumed. Task 3 continues execution until it gets preempted by Task 2 because the event that Task 2 was waiting for occurred. Task 2 handles the event and when it’s done, Task 2 relinquishes the CPU back to Task 3. Task 3 finishes working with the resource and thus, releases the semaphore.
At this point, the kernel knows that a higher priority task is waiting for the semaphore and, a context switch is done to resume Task 1. Now, Task 1 has the semaphore and can thus access the shared resource. The priority of Task 1 has been virtually reduced to that of Task 3’s because it was waiting for the resource that Task 3 owned. The situation got aggravated when Task 2 preempted Task 3 which further delayed the execution of Task 1. A multitasking kernel should allow task priorities to change dynamically to help prevent priority inversions. However, it takes some time to change a task’s priority.
A port involves three aspects: CPU, OS and board specific (BSP) code. μC/OS-III port consists of writing or changing the contents of three kernel specific files - OS_CPU.H, OS_CPU_A.ASM and OS_CPU_C.C. It is necessary to write or change the content of three CPU specific files: CPU.H, CPU_A.ASM and CPU_C.C. Finally create or change a Board Support Package (BSP) for the evaluation board or target board being used. A μC/OS-III port is similar to a μC/OS-II port. Some ports available are:
- AT91SAMxxxxx (ARM7/9)
- PowerPC 430