Real-Time Operating Systems (RTOS) Basics
Real-Time Operating Systems (RTOS) Basics
Applications can be divided into different tasks (modules) with or without the use of an RTOS. For
example, one task can be responsible for reading the keyboard, another for checking temperature, a
third for printing messages on a LCD screen, and so on. With an RTOS you not only get the tool to
create tasks, but also tools to communicate between the tasks, and tools to ensure that tasks that are
time critical are executed in time. Since the interface between the different tasks becomes very clean
when using an RTOS you will save time both in development and in maintenance of the application.
How does an RTOS work?
The core of an RTOS is known as the kernel. An API is provided to allow access to the kernel for the
creation of tasks, among other things. A task is like a function that has its own stack, and a Task Control
Block (TCB). In addition to the stack, which is private to a task, each task control block holds information
about the state of that task.
The kernel also contains a scheduler. The scheduler is responsible for executing tasks in accordance
with a scheduling mechanism. The main difference among schedulers is how they distribute execution
time among the various tasks they are managing.
The heart beat of the kernel is the system tick, often called just systick. For every system tick the kernel
will check to determine if a task switch needs to be performed. The system tick is often implemented
using one of the hardware timers within the microcontroller.
Page 2
Using a timer in this way, may not be the optimum solution for all applications. If the application is
energy-sensitive, you might not want the system tick handler to run each time a timer times out.
For example, you might have an application that goes into sleep mode to save power. The device wakes
up only if an external event occurs. If the triggering event is an external interrupt, the system tick
handler can be called from within that external interrupt. This way, the application will run the RTOS only
when something actually happens. The rest of the time, the application can remain in sleep mode.
Closely associated with the system tick are software timers. Software timers ticks once for each system
tick, and is a convenient way to setup for example delays in an RTOS. For this to work properly you
should call the system tick handler with regular intervals.
There will be further discussion about scheduling algorithms and system ticks later in this article.
Think modules
Perhaps the best way to get started with an RTOS application is to think about how the application can
be divided into distinct modules. For example, a somewhat simple engine input control application could
be divided into the following modules:
Engine temperature
Oil pressure
RPM
User input
These modules can then be set up as tasks, or can be divided into sub-modules. For example:
Engine temperature
- Read engine temperature
- Update LCD with current temperature
Oil pressure
- Read current oil pressure
- Conduct emergency engine shutdown
RPM
- Read RPM
- Update LCD with current RPM
User input
- Get gas pedal angle
- Get current gear
This division into sub-modules can continue until each module is suitable to be handled by a single task.
RTOS components
Lets see what features an RTOS has to offer and how each of these features might come in handy in
different applications.
Tasks
Tasks are like functions, each with its own stack and task control block (TCB). Unlike most functions,
however, a task is almost always an infinite loop. That is, once it has been created, it will never exit.
void TaskSendData( void )
{
while (1)
{
// Send the data
}
}
Page 3
A task is always in one of several states. A task can be ready to be executed, that is, in the READY
state. Or the task may be suspended (pending), that is, the task is waiting for something to happen
before it goes into the READY state. This is called the WAITING state.
Here is a short description of the states we will use in our RTOS.
State
Description
DORMANT
READY
RUNNING
WAITING
This task is waiting for something. This could be an event, a message or maybe
for the RTOS clock to reach a specific value (delayed).
Note: Different RTOSes may have different names for each of these states.
Scheduler
There are two major types of scheduler from which you can choose:
1. Time-sharing:
The most common time-sharing algorithm is called Round-Robin. With round-robin scheduling, the
scheduler has a list of the tasks that make up the system and it uses this list to check for the next task
that is ready to execute. If a task is READY, that task will execute. Associated with each task is its timeslice. This time-slice is the maximum time a task can execute for each round the scheduler makes.
Page 4
Page 5
Now then, lets see what we can do with the components we have so far. Imagine that we need to
create an engine control application and we have a microcontroller and a LCD screen. We would also
like to have the current time displayed on the LCD. We will reuse the engine control example, where we
divided this application into different modules and tasks. (We will ignore the User input to make the
example clearer.)
For this exercise, we will create the following tasks:
1.
2.
3.
Page 6
For this system, we will need a semaphore to control writes to the LCD. We need this because we have
two different tasks that are writing to the LCD and if one of these tasks was interrupted by the other, the
output would most likely be corrupted.
To summarize, we need the following signaling mechanisms.
System ticks:
We now have a system that will update the LCD with motor commands and show the current time on the
LCD. There is, however, one very important thing missing and that is the RTOS tick function. As stated
earlier, the kernel needs to gain control over the system every now and then to be able to, for example,
switch tasks according to the RTOS scheduler. This is normally done by calling a tick-handling API
function driven by one of the microcontroller timers. Without system ticks, nothing will happen in the
system.
Connection to the microcontroller hardware:
The last piece in the puzzle is to connect all this to the hardware. In our case we will assume that we
have access to a firmware library. We assume that we have the following firmware API functions:
Page 7
Now we only have to put the pieces together to see if our system will work as intended. The easiest way
to get started is, if available, to use a BSP from the RTOS vendor. For IAR PowerPac and C/OS-II
there are numerous different BSPs for various devices.
To build your application without a BSP you have to:
Assuming that we have added all of the files and have set the build options according to the RTOS
specifications, we are ready to start creating tasks in our application. But before we can create our
tasks, we need to define their Task Control Blocks (TCB), and specify their stacks.
The code snippets below are examples for how task stacks can be defined for the IAR PowerPac RTOS
and for Micrium's C/OS-II RTOS:
IAR PowerPac
Micrium C/OS-II
static
OS_STK StackEngineCTRL[128];
static
OS_STK StackOilLCD[128];
static
OS_STK StackTempLCD[128];
static
OS_STK StackOilRead[128];
static
OS_STK StackTempRead[128];
An example of a definition of the TCB (Task Control Block) for IAR PowerPac is seen here:
OS_TASK
OS_TASK
OS_TASK
OS_TASK
OS_TASK
TCBEngineCTRL;
TCBOilLCD;
TCBTempLCD;
TCBOilRead;
TCBTempRead;
Page 8
For the Micrium C/OS-II RTOS, these TCBs are created by the kernel.
Now, what remains is the main function. In our main(), we will start by disabling interrupts to make sure
we are not interrupted during the setup of our system. After this we will initialize the hardware, both for
the RTOS and for our application and BSP.
Then we will create our tasks, events, semaphores and mailboxes/queues.
When all of this is done we can start our RTOS and let the scheduler take care of the problem of making
sure that the correct task is executed at the right time.
Below are the pieces we have in our main function, both for IAR PowerPac and for C/OS-II.
IAR PowerPac
int main(void) {
BSP_IntDisAll();
OS_InitHW();
BSP_Init();
OSTaskCreate(...);
// create at least one task before starting the RTOS
// create your events, mailboxes and semaphores
...
OSStart();
// Start your RTOS
}
Micrium C/OS-II
int main(void) {
BSP_IntDisAll();
// initially disable interrupts
OSInit();
// initialize OS
BSP_Init();
// initialize Hardware for OS and the board
OS_CPU_SysTickInit();
OSTaskCreateExt(...);
// create at least one task before starting the RTOS
// create your events, mailboxes and semaphores
...
OSStart();
// Start your RTOS
Summary
This article explains the concepts of kernel, tasks, messaging (like events and mailboxes), scheduler
and the main pieces to put an RTOS application together. To do this in a real application will require
more details, for example, how to create mailboxes and events.
If you are a novice RTOS user, the best way to get started is to bring up one of the many examples that
usually come with different RTOSes. In this article I have used IAR PowerPac and Micriums C/OS-II
as examples when showing code snippets. Both IAR Systems and Micrium provide many example
applications and have BSPs for many different boards and devices.
I would also recommend obtaining a copy of the MicroC/OS-II book from Micrium. This is an excellent
way to learn what constitutes an RTOS and how to use it.