Real-Time operating system
A real-time operating system (RTOS) is an operating system (OS)
intended to serve real-time application requests.
A key characteristic of a RTOS is the
level of its consistency concerning the amount of time it takes to
accept and complete an application's task; the variability is jitter.
A hard real-time operating system has
less jitter than a soft real-time operating system.
The chief design goal is not high throughput, but rather a guarantee of
a soft or hard performance category.
A RTOS that can usually or generally meet a deadline is a soft
real-time OS, but if it can meet a deadline deterministically it is a
hard real-time OS.
A real-time OS has an advanced algorithm for scheduling.
Scheduler flexibility enables a wider, computer-system orchestration of
process priorities, but a real-time OS is more frequently dedicated to
a narrow set of applications.
Key factors in a real-time OS are
minimal interrupt latency and minimal thread switching latency,
but a real-time OS is valued more for how quickly or how predictably it
can respond than for the amount of work it can perform in a given
period of time.
Why use
an RTOS ?
The use of a RTOS can simplify
the design of what would otherwise be a complex software application.
Structured framework for embedded applications
Hardware interface layer
Easy expansion of system software
Hardware independent
Housekeeping
Process scheduling
CPU resource management
Task communication (semaphores and message queues)
Focus on Application Development
Leave basic system management to the
RTOS kernel
Avoid re-writing resource management code that already exists
Reduce porting and testing overheads
ARM Cortex is RTOS friendly due to CMSIS Libraries
Witch RTOS ?
Performance
Predictable behaviour
Low latency
High number of interrupt levels
Ease of Use
Flexible API and implementation
Tool-chain integration
Scheduling options
Multitasking, Pre-emptive, Round Robin
System Friendly
Consumes small amount of system resource
Proven kernel
Low cost
Popular RTOS'es
Some popular real-time operating systems:
VxWorks - ( WinRiver )
Integrity - ( Green Hills )
FreeRTOS ( Freertos.org )
uC/OS ( Micrium Technologies Corporation )
CMX-RTX ( CMX Systems )
embOS ( Segger )
These are not real-time operating systems:
Linux
Android
Windows
SCHEDULER
Each
of us will certainly realize that it is difficult to make many things
at once, hence it becomes necessary to do one thing at a time,
following a certain criterion, which aims not to lose sight of any,
this criterion is the scheduling algorithm .
In multitasking operating
systems the scheduler is a
fundamental component capable of temporarily interrupting a process to
advance another, thus creating a change of context (Context Switch).
The scheduling policy is the algorithm
used by the scheduler to decide which task to execute at any point in
time.
Preemptive
The preemptive scheduler is one of the most used in the RTOS systems.
In most cases we use a
fixed priority scheduler a preemptive scheduler that ensures that at
any time the processor performs the task with highest priority
(previously fixed by the programmer) among all tasks at that time are
in the READY state.
Its operation is quite simple in fact, this type of scheduler is governed by a timer, the duration of which is called the quantum of default tick;
passed one of tick, a selection is made between the processes in the
READY state and the more priority will be executed (RUN) for at least
of the one tick.
Preemptive multitasking involves the use of an interrupt mechanism
which suspends the currently executing process and invokes a scheduler
to determine which process should execute next. Therefore all processes
will get some amount of CPU time at any given time.
t1, t2, t3 are the tasks
Cooperative
This scheduling algorithm is obtained by removing the timer from preemptive (see above).
Now the task, in RUN, must be brought up to the end of execution.
The cooperative scheduling algorithms are designed so that each running
process to remain so until voluntarily invoke the services the
operating system.
The major limitation of this approach is that the Real-Time performance is not guaranteed.
IRQ - interrupt request
SVC - system service call
PendSV - Pending SVC handler
SysTick - Sys tick Handler
taskYield() - inform the scheduler that a switch to another task should occur now
vTaskDelay() - place a task in Blocked state for a fixed number of sys-tick
Round-Robin
The term
round-robin is used in many contexts to refer to a system in which
different participants in an activity alternate in a circular fashion,
see below.
In the FreeRTOS, where more than one task exists at the highest priority, tasks are executed in round robin fashion.
TASK
Each
executing program is a task under control of the RTOS.
Process or task we mean
a sequence of instructions which, in the absence of other activities,
is performed by the MCU in a continuous manner to completion.
The states in which lives a task
inside scheduler are mainly three:
Running
it is currently using the processor.
Only one task can be in RUN mode at the moment.
Ready
able to execute but a different task of equal or higher priority is
already in the Running state.
Blocked
currently waiting for either a
temporal (indicated by SysTick when) or external event (indicated by
semaphore or queue).
Suspended
not available for scheduling. Tasks
will only enter or exit the suspended state when explicitly commanded
to do so through the API calls of the RTOS (Task_Suspend() and
Task_Resume() respectively).
Each
task has its own stack area and its priority (which can be changed).
To free the RAM memory it is
recommended to delete the task when it is no longer in use. Function for delete the task is:
xTaskDelete(task handler or NULL
when we would like to delete current task);
Context Switching
As
a task executes it utilizes the MCU registers and accesses RAM and ROM
just as any other program.
These resources together (the
processor registers, stack, etc.) comprise the task execution context.
The process of saving the
context of a task being suspended and restoring the context of a task
being resumed is called context switching.
Task Priority numbers should be
chosen.
For
example, if your application has 3 user tasks that must all be at
different priorities then use priorities:
3 (highest)
2 and 1 (lowest )
0 the idle task
Implementing a Task
A task should have the following structure:
void
vTaskName( void *pvParameters )
{
for( ;; )
{
-- Task application code here. --
}
}
The parameters can be used to pass any
information into the task.
Task functions should never
return so are typically implemented as a continuous loop.
Tasks are created by calling xTaskCreate() and deleted by calling
xTaskDelete().
Exe2 - Example of FreeRTOS on STM32F4-Discovery
This example was tested on
STM32F4-Discovery and ATOLLIC TrueSTUDIO for STMicroelectronics
STM32
v.2.2.0
Core resources used:
System
timer (SysTick) – generate system time (time slice)
Two stack pointers: MSP (Main Stack Pointer), PSP (Process Stack Pointer)
Interrupt vectors used:
SVC
– system service call (like SWI in ARM7)
PendSV – pended system call
(switching context)
SysTick – System Timer
(those 3 vectors should be removed from stm32f4xx_it.c file because are used by FreeRTOS)
Source file structure is below
Structure of
the code
Include files:
FreeRTOS.h
– main header file for basic functionality of FreeRTOS
Task.h – if tasks will be used in
the application (99% cases)
Semphr.h – if semaphores/queues
will be used in the application
/* FreeRTOS includes */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
Remember that It is necessary declare the Task, for example see below.
/* Task functions declarations */
static
void vLEDTask( void
*pvParameters );
static
void vSWITCHTask( void
*pvParameters );
static
void vMEMSTask(void
*pvParameters);
static
void vBALANCETask(void
*pvParameters);
At the beginning of the main()
function all hardware configuration should be performed (clock, GPIO
configuration).
In our example it is prvSetupHardware()
function.
/*
initialize hardware... */
prvSetupHardware();
Next step is creation of
the basic components of the application:
tasks
– piece of the code executed periodically (ie. LCD_control,
GPIO_control)
semaphores – used to synchronization
among the tasks and between the task and interrupt
queues – used to transfer data
between the tasks
Last point in main()
function is starting the scheduler:
vTaskStartScheduler()
Code of the task should be
put in never ending loop, like:
void
vTaskName( void *pvParameters )
{
for( ;; )
{
-- Task application code here. --
}
}
Between specified tasks
IDLE task is run (if it is possible according to task priority scheme)
TASKs
Task is a C function void vTaskName(void *pvParameters)
which can be used to run any number of separate instances by the
function xTaskCreate().
In our example we have created 4 different tasks for LED control
passing as its argument number of the LED to be controlled and delay
parameter.
NVIC Configuration
FreeRTOS kernel and its irq procedures (PendSV, SysTick) have lowest possible interrupt priority (255) set in FreeRTOSConfig.h (configKERNEL_INTERRUPT_PRIORITY)
There is a group of interrupts
which can cooperate with FreeRTOS API by calling its functions. Maximum
level for those peripherals (based on the position in vector table) is
set in configMAX_SYSCALL_INTERRUPT_PRIORITY
It is possible to use nested interrupts.
The Non-RTOS IRQ are used in normal way for set the semaphore, see below - this code is into: stm32f4xx_it.c
/* User button handler */
void EXTI0_IRQHandler(void)
{
xHigherPriorityTaskWoken = pdFALSE;
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
/* Set binary semaphore */
xSemaphoreGiveFromISR(xSemaphoreSW,&xHigherPriorityTaskWoken);
/* Clear the Wakeup Button EXTI line pending bit */
EXTI_ClearITPendingBit(EXTI_Line0);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
}
Interrupt Processing
There are dedicated API functions to be executed within IRQ procedures.
All of those functions has FromISR suffix in its names. Below there is a list of most important ones:
xSemaphoreGiveFromISR(semaphore, *hp_task )
xQueueSendFromISR(...,*hp_task)
xQueueSendToBackFromISR(...,*hp_task)
xQueueSendToFrontFromISR(...,*hp_task)
xQueueReceiveFromISR(...,*hp_task)
All of those function have their equivalents in non-irq environment.
The only difference for the programmer is additional argument *hp_task.
It is pointer to the variable which is used to indicate whether
operation on queue or semaphore within IRQ cause unblocking task with
higher priority than currently running. If this parameter is pdTRUE,
context switch should be requested by kernel before the interrupt exits.
IRQ - interrupt request
SVC - system service call
PendSV - Pending SVC handler
SysTick - Sys tick Handler
Stack overflow protection
Check how much stack is used by task – stack ‘high water mark’.
There is a function:
uxTaskGetStackHighWaterMark(xTaskHandle xTask);
After call it with task handler as argument in return the minimum amount of remaining stack for xTask is presented.
Stack overflow hook function is a function called by the kernel at detected stack overflow.
It should be implemented by the user. Its declaration should look like:
vApplicationStackOverflowHook(xTaskHandle *pxTask, signed char *pcName);
Optional runtime stack check mechanisms (configured in FreeRTOSConfig.h)
Method1 (configCHECK_FOR_STACK_OVERFLOW set to 1)
quick to run, can miss some stack overflow event
Method2 (configCHECK_FOR_STACK_OVERFLOW set to 2)
slower one but more precise in stack overflow events
EXAMPLE File - how to get it and how to use it
Create a new workspace in Atollic IDE and IMPORT archive project:
Ex2_–_FreeRTOS_on_STM32F4-Discovery_–_solution.zip
For get this file (Ref.Cod.RefRef.Cod..Cod. STM32F4-Discovery-EXE2) go here.
1) DownLoad the example
2) Request password to us for unzip itRef.Cod.
3) After unzip you find a file named:
Ex2_-_FreeRTOS_on_STM32F4-Discovery_-_solution_v2.zip
do not unzip it but import it in Atollic, see explanation here that are for EXE1 but the procedure is the same.
What does this program
After the reset the all 4 LEDs: LD3..6 are blinking with different speed (MEMS task and BALANCE task are suspended)
When pressing User button,
application starts to work as balance detector (LEDs are blinking if
the board is not in flat position – based on data coming from MEMS –
U5. (LED tasks: LD3, LD4, LD5, LD6 are suspended)
Pressing User button once again will switch the application to its initial state (4LEDs blinking with different speed).
vLEDTask() Task
void vLEDTask( void *pvParameters )
{
volatile int *LED;
LED = (int *) pvParameters;
for( ;; )
{
STM_EVAL_LEDToggle((Led_TypeDef)LED[0]);
vTaskDelay(LED[1]/portTICK_RATE_MS);
}
}
Create and define the Task priorities, see below.
/* Start the tasks defined within this file/specific to this demo. */
xTaskCreate( vLEDTask, ( signed portCHAR * ) "LED3",
configMINIMAL_STACK_SIZE, (void *)LEDS[0],tskIDLE_PRIORITY,
&xLED_Tasks[0] );
xTaskCreate( vLEDTask, ( signed portCHAR * ) "LED4",
configMINIMAL_STACK_SIZE, (void *)LEDS[1],tskIDLE_PRIORITY,
&xLED_Tasks[1] );
xTaskCreate( vLEDTask, ( signed portCHAR * ) "LED5",
configMINIMAL_STACK_SIZE, (void *)LEDS[2],tskIDLE_PRIORITY,
&xLED_Tasks[2] );
xTaskCreate( vLEDTask, ( signed portCHAR * ) "LED6",
configMINIMAL_STACK_SIZE, (void *)LEDS[3],tskIDLE_PRIORITY,
&xLED_Tasks[3] );
xTaskCreate( vSWITCHTask, ( signed portCHAR * ) "SWITCH", configMINIMAL_STACK_SIZE, NULL,tskIDLE_PRIORITY, NULL );
xTaskCreate( vMEMSTask, ( signed portCHAR * ) "MEMS", configMINIMAL_STACK_SIZE, NULL,tskIDLE_PRIORITY, &xMEMS_Task );
xTaskCreate( vBALANCETask, ( signed portCHAR * ) "BALANCE",
configMINIMAL_STACK_SIZE, NULL,tskIDLE_PRIORITY, &xBALANCE_Task );
Inside the ATOLLIC IDE, press on the Console button and you must see something similar to image below.
Now we suggest you to study this example for get familiarity to FreeRTOS.