ELT048 - Embedded Operational Systems: Rodrigo Maximiano Antunes de Almeida @rmaalmeida Universidade Federal de Itajubá
ELT048 - Embedded Operational Systems: Rodrigo Maximiano Antunes de Almeida @rmaalmeida Universidade Federal de Itajubá
Operational Systems
Rodrigo Maximiano Antunes de
Almeida
[email protected]
@rmaalmeida
Universidade Federal de Itajubá
Creative Commons License
U
UNN II F
FEE II
Development board
U
UNN II F
FEE II
LPCXpresso + baseboard
U
UNN II F
FEE II
FreeRTOS
Real time systems
TAREFA 1
TAREFA 2
TAREFA 3
t1 t2 Tempo tn
U
UNN II F
FEE II
Multitask
TAREFA 1
TAREFA 2
TAREFA 3
t1 t2 Tempo tn
U
UNN II F
FEE II
Multitask (cont.)
• This switch or task change can happen in
different situations:
A task may block waiting for a resource (eg serial
port) to be available or an event to occur (eg
receive a packet from the USB interface).
A task can sleep (block) for a while.
A task can be unintentionally suspended by the
kernel. In this case, we call the kernel
preemptive.
• This change of tasks is also called context
switching.
U
UNN II F
FEE II
Context switch
U
UNN II F
FEE II
Other kernel
responsabilities
U
UNN II F
FEE II
The FreeRTOS
U
UNN II F
FEE II
The FreeRTOS
U
UNN II F
FEE II
The FreeRTOS
U
UNN II F
FEE II
The FreeRTOS file structure
FreeRTOS
│ │
│ ├─Source Directory containing the FreeRTOS source files
│ │
│ └─Demo Directory containing pre-configured and port
│ specific FreeRTOS demo projects
│
FreeRTOS-Plus
│
├─Source Directory containing source code for some
│ FreeRTOS+ ecosystem components
│
└─Demo Directory containing demo projects for
FreeRTOS+ ecosystem component
The FreeRTOS files
• tasks.c
provides the basic task manipulation
• list.c
Implements a list function to store other resources
• queue.c
provides both queue and semaphore services. queue.c is nearly always
required.
• timers.c
provides software timer functionality.
• event_groups.c
provides event group functionality.
• croutine.c
croutine.c implements the FreeRTOS co-routine functionality. Co-routines
were intended for use on very small microcontrollers, are rarely used now,
and are therefore not maintained to the same level as other FreeRTOS
features.
U
UNN II F
FEE II
The FreeRTOS files
FreeRTOS
│
└─Source
│
├─tasks.c FreeRTOS source file - always required
├─list.c FreeRTOS source file - always required
├─queue.c FreeRTOS source file - nearly always required
├─timers.c FreeRTOS source file - optional
├─event_groups.c FreeRTOS source file - optional
└─croutine.c FreeRTOS source file - optional
Ports
• FreeRTOS is ported to
26 manufactures
Over 70 architectures
• Portability is constraind by two files
port.c
portmacro.h
U
UNN II F
FEE II
Manufactures
• Altera • NXP
• Atmel • Renesas
• Cadence • RISC-V
• Cortus • Silicon Labs
• Cypress • Spansion (ex Fujitsu)
• Energy Micro (see Silicon Labs)
• ST Microelectronics
• Freescale
• Synopsys ARC
• Imagination/MIPS
• Texas Instruments
• Infineon
• Xilinx
• Luminary Micro
• x86 (real mode)
• Microchip
• NEC • x86 / Windows Simulator
• Microsemi (formally Actel) • Unsupported and contributed
ports
U
UNN II F
FEE II
FreeRTOS portable file structure
FreeRTOS
│
└─Source
│
└─portable Directory containing all port specific source files
│
├─MemMang Directory containing the 5 alternative heap allocation source files
│
├─[compiler 1] Directory containing port files specific to compiler 1
│ │
│ ├─[architecture 1] Contains files for the compiler 1 architecture 1 port
│ ├─[architecture 2] Contains files for the compiler 1 architecture 2 port
│ └─[architecture 3] Contains files for the compiler 1 architecture 3 port
│
└─[compiler 2] Directory containing port files specific to compiler 2
│
├─[architecture 1] Contains files for the compiler 2 architecture 1 port
├─[architecture 2] Contains files for the compiler 2 architecture 2 port
└─[etc.]
Includes
U
UNN II F
FEE II
FreeRTOS config.h
#define configUSE_PREEMPTION 1
#define configCPU_CLOCK_HZ 58982400
#define configTICK_RATE_HZ 250
#define configMAX_PRIORITIES 5
#define configMINIMAL_STACK_SIZE 128
#define configTOTAL_HEAP_SIZE 10240
#define configMAX_TASK_NAME_LEN 16
#define configUSE_MUTEXES 0
...
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#define INCLUDE_xTaskGetIdleTaskHandle 0
...
//http://www.freertos.org/a00110.html
Licensing
• FreeRTOS is licensed by Real Time Engineers Ltd.
under a modified version of the GPL.
• It has open source code, no need to pay royalties
and can be used freely in commercial
applications.
• You do not need to release the sources of your
application, as long as you do not create any
functionality already provided by FreeRTOS.
• You need to indicate that you use FreeRTOS (a
link to the site is sufficient).
• Any change to the kernel needs to be released in
open-source fashion.
U
UNN II F
FEE II
OpenRTOS
U
UNN II F
FEE II
Memory
management
Heap models
Memory
• Memory can be allocated using the standard C library
malloc() and free() functions, but they may not be suitable,
or appropriate, for one or more of the following reasons:
They are not always available on small embedded systems.
Their implementation can be relatively large, taking up valuable
code space.
They are rarely thread-safe
They are not deterministic; the amount of time taken to execute
the functions will differ from call to call.
They can suffer from fragmentation.
They can complicate the linker configuration.
They can be the source of difficult to debug errors if the heap
space is allowed to grow into memory used by other variables
U
UNN II F
FEE II
Memory
U
UNN II F
FEE II
FreeRTOS memory
U
UNN II F
FEE II
Heap1.c
U
UNN II F
FEE II
Heap2.c
U
UNN II F
FEE II
Heap3.c
U
UNN II F
FEE II
Heap4.c
U
UNN II F
FEE II
Heap5.c
U
UNN II F
FEE II
How much heap do I need?
U
UNN II F
FEE II
Tasks
Tasks
U
UNN II F
FEE II
Task Prototype
#include "task.h"
/* create a new task and add it to the list
of tasks that are ready to run */
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode,
const char *const pcName,
unsigned short usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pvCreatedTask );
Scheduler start
#include "FreeRTOS.h"
#include "task.h"
int main(void) {
[...]
/* create task1 */
xTaskCreate(task1, (signed char *)"Task1",
configMINIMAL_STACK_SIZE+120,
(void *)NULL, 1, NULL);
U
UNN II F
FEE II
Removing a task
#include "task.h"
/* remove a task from the RTOS kernel
management */
void vTaskDelete(TaskHandle_t xTask);
Removing a task
#include "task.h"
void ATaskFunction(void *pvParameters)
{
for(;;)
{
/* task code */
}
vTaskDelete(NULL);
}
Scheduler
U
UNN II F
FEE II
Tick interrupt and
preemption
KERNEL
TAREFA 1
TAREFA 2
t1 t2 Tempo tn
U
UNN II F
FEE II
Events
U
UNN II F
FEE II
Event-oriented tasks
U
UNN II F
FEE II
Time functions
#include "task.h"
// Suspend ourselves.
vTaskSuspend( NULL );
U
UNN II F
FEE II
Task state diagram
U
UNN II F
FEE II
• Fazer um Soft WatchDog
U
UNN II F
FEE II
Exercise
U
UNN II F
FEE II
Exercise
Run FreeRTOS_simple_task_lcd_example.zip
FreeRTOS_simple_task_lcd_exam
ple.zip
U
UNN II F
FEE II
Queue
As a silent Q.
Prob Var global
Queue management
U
UNN II F
FEE II
Example
QUEUE
3
TAREFA 1 TAREFA 2
Send
Example
QUEUE
8 3
TAREFA 1 TAREFA 2
Send
Example
QUEUE
8 3
TAREFA 1 TAREFA 2
Receive
Example
TAREFA 1 TAREFA 2
QUEUE
• A queue does not belong to any specific task.
• Several tasks and interrupts can share the same
queue, both for reading and for writing.
• Each queue stores a finite set of items (queue
lenght).
• Each item in the queue can have a fixed size of
bytes (item size).
• Both "queue length" and "item size" are defined
at the time the queue is created (FreeRTOS
allocates a space in the heap to hold the
queue).
U
UNN II F
FEE II
Create and remove task to
QUEUE
#include "queue.h"
/* delete a queue */
void vQueueDelete(QueueHandle_t xQueue);
#include "queue.h"
/* receive an item from a queue */
BaseType_t xQueueReceive(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait);
/* receive an item from a queue without removing
the item from the queue */
BaseType_t xQueuePeek(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait);
Writing on the QUEUE
• When typing in the queue, a task can enter
the Blocked state if the queue is full, waiting
for a space in the queue to save the new item.
• A task can set a write timeout (time that will
be in the Blocked state expecting a space in
the queue to save the new item).
• A task writing an item in queue is
automatically put into Ready state when:
The element is written in the queue successfully.
Writing timeout expires.
U
UNN II F
FEE II
Writing on the QUEUE
• It is possible to have more than one task writing
items in the queue.
• If the queue is full, all the tasks that write in the
queue are placed in the Blocked state, waiting for a
space in the queue.
• When a space becomes free in the queue, only the
highest priority task is placed in the ready state so
that it can write to the queue.
• If there is more than one task with the same priority
waiting for a free space to write to the queue, the
task waiting for the longest time will be passed to
the Ready state.
U
UNN II F
FEE II
Writing on the QUEUE
#include "queue.h"
/* post an item to the front of a queue */
BaseType_t xQueueSendToFront(
QueueHandle_t xQueue,
const void* pvItemToQueue,
TickType_t xTicksToWait);
/* post an item to the back of a queue */
BaseType_t xQueueSendToBack(
QueueHandle_t xQueue,
const void* pvItemToQueue,
TickType_t xTicksToWait);
#include "queue.h"
/* post an item on a queue same as xQueueSendToBack() */
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void* pvItemToQueue,
TickType_t xTicksToWait);
/* A version of xQueueSendToBack() that will write to the
queue even if the queue is full, overwriting data that
is already held in the queue. */
BaseType_t xQueueOverwrite(
QueueHandle_t xQueue,
const void* pvItemToQueue);
• From ISR
• Gateway
U
UNN II F
FEE II
Before Q
U
UNN II F
FEE II
Synchronizati
on
mechanisms
Synchronization mechanisms
SEMÁFORO BINÁRIO
ISR TASK
HANDLER
U
UNN II F
FEE II
Binary semaphores
SEMÁFORO BINÁRIO
ISR TASK
Take HANDLER
U
UNN II F
FEE II
Binary semaphores
SEMÁFORO BINÁRIO
ISR TASK
Give Take HANDLER
U
UNN II F
FEE II
Binary semaphores
SEMÁFORO BINÁRIO
ISR TASK
Take HANDLER
U
UNN II F
FEE II
Creating a Binary semaphore
#include "semphr.h"
/* create a binary semaphore */
SemaphoreHandle_t xSemaphoreCreateBinary(void);
/* delete a semaphore */
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);
Geting a Binary semaphores
#include "semphr.h"
/* obtain a semaphore */
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait);
/* obtain a semaphore from an ISR */
BaseType_t xSemaphoreTakeFromISR(
SemaphoreHandle_t xSemaphore,
signed BaseType_t
*pxHigherPriorityTaskWoken);
Giving a Binary semaphores
#include "semphr.h"
/* release a semaphore */
BaseType_t xSemaphoreGive(
SemaphoreHandle_t xSemaphore);
U
UNN II F
FEE II
Global variables accesses
• Imagine the following code in C:
counter + = 10;
• Now imagine your correspondent in a
"generic" assembly:
LOAD R, [# 1234]
SUM R, 10
STORE R, [# 1234]
• The above C operation is not atomic because
it needs more than one CPU instruction to
complete. What is the consequence of this?
U
UNN II F
FEE II
Global variables accesses
• Imagine the following situation:
Task A loads the value of the variable into the register (first
assembly instruction).
Task A is interrupted by task B.
Task B changes the value of the global variable, and then
sleeps.
Task A returns from the point where it stopped, and
executes the other two statements in assembly, but using
an old value of the global variable, since it was changed by
task B.
• In this case, since the global variable can be accessed
by more than one task at the same time, it is
necessary to control access to this variable to
guarantee its consistency
U
UNN II F
FEE II
Peripheral acesses
• Consider a scenario where two tasks write on the
LCD:
Task A starts typing "Enter your password on the display:".
While writing on the display, task A is interrupted by task
B, and can only write "Enter your pas".
Task B writes "Communication error" in the display.
Task A continues where you left off and writes “sword on
the display".
At the end, the display shows the following message:
"Enter your pasCommunicationErrorsword in the display"!
• Concurrent access to any hardware resource for
more than one application task must be managed
correctly.
U
UNN II F
FEE II
Thread safe functions
U
UNN II F
FEE II
Example
// Esta implementação NÃO ...
// é threadsafe LOCK()
struct Date d; getDate();
void getDate() UNLOCK();
{ ...
d.day = getDay();
d.month = getMon(); LOCK();
d.year = getYear(); d.day = getDay();
} d.month = getMon();
d.year = getYear();
UNLOCK();
U
UNN II F
FEE II
Critical sessions and mutual
exclusion
U
UNN II F
FEE II
Disabling interrupts
• It's a "rough" method of controlling access to
resources!
• Disables all interrupts until the priority interrupt
is set at the
configMAX_SYSCALL_INTERRUPT_PRIORITY
constant.
Since the preemption can only occur in an interrupt,
the task called taskENTER_CRITICAL () will continue
in the Running state until it exits the critical region.
• Critical regions should remain very small
because they negatively impact application
response times.
U
UNN II F
FEE II
Pause the scheduler
• Critical sessions that are accessed only by
tasks, not interrupts, can be protected by
pausing the scheduler.
• When pausing the scheduler, interrupts are still
enabled, but there will be no context switch,
and other tasks will not be performed while the
scheduler is paused:
...
vTaskSuspendAll ();
tx_serial (buf, size);
vTaskResumeAll ();
…
U
UNN II F
FEE II
Pause the scheduler
U
UNN II F
FEE II
Mutex (mutual exclusion)
• The mutex is a special type of semaphore binary used
to protect access to a resource shared by more than
one task.
• A mutex is basically a token associated with a given
resource.
• For a task to access the resource, it must first take the
token (take).
• If the resource is busy, it must wait for the resource to
be released in order to use it.
• When you have finished using the feature, it should be
given (give).
• This mechanism is totally dependent on developer
discipline!
U
UNN II F
FEE II
Mutex (mutual exclusion)
U
UNN II F
FEE II
Mutex (mutual exclusion)
U
UNN II F
FEE II
Mutex (mutual exclusion)
U
UNN II F
FEE II
Mutex (mutual exclusion)
U
UNN II F
FEE II
Mutex (mutual exclusion)
U
UNN II F
FEE II
Mutex (mutual exclusion)
U
UNN II F
FEE II
Mutex (mutual exclusion)
U
UNN II F
FEE II
How the mutex Works?
• Hardware
Plataform dependente
• Software
Dekker's algorithm
Peterson's algorithm
Lamport's bakery algorithm
Szymanski's algorithm
Taubenfeld's black-white bakery algorithm
Maekawa's algorithm
U
UNN II F
FEE II
Peterson`s Algorithm
U
UNN II F
FEE II
FreeRTOS implementation
U
UNN II F
FEE II
Mutex (mutual exclusion)
xSemaphoreHandle vSemaphoreCreateMutex(void);
portBASE_TYPE xSemaphoreTake(
xSemaphoreHandle xSemaphore,
portTickType xBlockTime);
portBASE_TYPE xSemaphoreGive(
xSemaphoreHandle xSemaphore);
U
UNN II F
FEE II
When use each
mechanism
• If you are sharing a resource between a task
and an interrupt, your only option is to use the
functions that disable interrupts.
• If you are sharing a resource between tasks
only, the default protection mechanism should
be MUTEX.
• In some very specific cases, you may need to
pause the scheduler:
When you need to execute code from beginning to
end without being interrupted.
In libraries that are not thread safe (see function
malloc()).
U
UNN II F
FEE II
Mutex x Binary semaphore
U
UNN II F
FEE II
Mutex x Binary semaphore
• “Strictly speaking, a mutex is locking mechanism used
to synchronize access to a resource. Only one task (can be
a thread or process based on OS abstraction) can acquire
the mutex. It means there is ownership associated with
mutex, and only the owner can release the lock (mutex).”
• “Semaphore is signaling mechanism (“I am done, you
can carry on” kind of signal). For example, if you are
listening songs (assume it as one task) on your mobile
and at the same time your friend calls you, an interrupt is
triggered upon which an interrupt service routine (ISR)
signals the call processing task to wakeup.”
• http://www.geeksforgeeks.org/mutex-vs-semaphore/
U
UNN II F
FEE II
Exercise
FreeRTOS_simple_task_lcd_exam
ple.zip
U
UNN II F
FEE II
SoftTimer
• It runs a software timer. When a match is
done a call-back is called:
void ATimerCallback( TimerHandle_t
xTimer );
U
UNN II F
FEE II
U
UNN II F
FEE II
U
UNN II F
FEE II
Event Groups
U
UNN II F
FEE II
static void vEventBitSettingTask( void *pvParameters ){
const TickType_t xDelay200ms = pdMS_TO_TICKS( 200UL ), xDontBlock
= 0;
for( ;; )
{
/* Delay for a short while before starting the next loop. */
vTaskDelay( xDelay200ms );
/* Print out a message to say event bit 0 is about to be set
by the task,
then set event bit 0. */
vPrintString( "Bit setting task -\t about to set bit
0.\r\n" );
xEventGroupSetBits( xEventGroup, mainFIRST_TASK_BIT );
/* Delay for a short while before setting the other bit. */
vTaskDelay( xDelay200ms );
/* Print out a message to say event bit 1 is about to be set
by the task,
then set event bit 1. */
vPrintString( "Bit setting task -\t about to set bit
1.\r\n" );
xEventGroupSetBits( xEventGroup, mainSECOND_TASK_BIT );
}
}
static void vEventBitReadingTask( void *pvParameters ){
EventBits_t xEventGroupValue;
const EventBits_t xBitsToWaitFor = ( mainFIRST_TASK_BIT |
mainSECOND_TASK_BIT |
mainISR_BIT );
for( ;; ) {
/* Block to wait for event bits to become set within the event group. */
xEventGroupValue = xEventGroupWaitBits(
/* The event group to read. */ xEventGroup,
/* Bits to test. */ xBitsToWaitFor,
/* Clear bits on exit if the unblock condition is met. */ pdTRUE,
/* Don't wait for all bits. This parameter is set to pdTRUE for the
second execution. */ pdFALSE,
/* Don't time out. */ portMAX_DELAY );
/* Print a message for each bit that was set. */
if( ( xEventGroupValue & mainFIRST_TASK_BIT ) != 0 ){
vPrintString( "Bit reading task -\t Event bit 0 was set\r\n" );
}
if( ( xEventGroupValue & mainSECOND_TASK_BIT ) != 0 ){
vPrintString( "Bit reading task -\t Event bit 1 was set\r\n" );
}
if( ( xEventGroupValue & mainISR_BIT ) != 0 ){
vPrintString( "Bit reading task -\t Event bit 2 was set\r\n" );
}
}
}
Task
Notification
U
UNN II F
FEE II
U
UNN II F
FEE II
Limitations of Task
Notifications
U
UNN II F
FEE II
• BaseType_t
xTaskNotifyGive( TaskHandle_t
xTaskToNotify );
• uint32_t ulTaskNotifyTake( BaseType_t
xClearCountOnExit, TickType_t
xTicksToWait );
U
UNN II F
FEE II
/* The rate at which the periodic task generates software interrupts. */
const TickType_t xInterruptFrequency = pdMS_TO_TICKS( 500UL );
static void vHandlerTask( void *pvParameters ){
/* xMaxExpectedBlockTime is set to be a little longer than the maximum
expected time
between events. */
const TickType_t xMaxExpectedBlockTime = xInterruptFrequency +
pdMS_TO_TICKS( 10 );
uint32_t ulEventsToProcess;
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; ) {
/* Wait to receive a notification sent directly to this task from the
interrupt service routine. */
ulEventsToProcess = ulTaskNotifyTake( pdTRUE,
xMaxExpectedBlockTime );
if( ulEventsToProcess != 0 ){
while( ulEventsToProcess > 0 ){
vPrintString( "Handler task - Processing event.\r\n" );
ulEventsToProcess--;
}
}else{
}
}
}
static uint32_t ulExampleInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken;
/* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE
as it will get set to pdTRUE inside the interrupt safe API function if a
context switch is required. */
xHigherPriorityTaskWoken = pdFALSE;
/* Send a notification directly to the task to which interrupt processing
is being deferred. */
vTaskNotifyGiveFromISR( /* The handle of the task to which the
notification is being sent. The handle was saved when the task was created. */
xHandlerTask,
/* xHigherPriorityTaskWoken is used in the usual
way. */
&xHigherPriorityTaskWoken );
/* Pass the xHigherPriorityTaskWoken value into portYIELD_FROM_ISR(). If
xHigherPriorityTaskWoken was set to pdTRUE inside vTaskNotifyGiveFromISR()
then calling portYIELD_FROM_ISR() will request a context switch. If
xHigherPriorityTaskWoken is still pdFALSE then calling portYIELD_FROM_ISR()
will have no effect. The implementation of portYIELD_FROM_ISR() used by the
Windows port includes a return statement, which is why this function does not
explicitly return a value. */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
U
UNN II F
FEE II