0% found this document useful (0 votes)
147 views150 pages

ELT048 - Embedded Operational Systems: Rodrigo Maximiano Antunes de Almeida @rmaalmeida Universidade Federal de Itajubá

Perifericos

Uploaded by

nelson_herrera
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
147 views150 pages

ELT048 - Embedded Operational Systems: Rodrigo Maximiano Antunes de Almeida @rmaalmeida Universidade Federal de Itajubá

Perifericos

Uploaded by

nelson_herrera
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 150

ELT048 – Embedded

Operational Systems
Rodrigo Maximiano Antunes de
Almeida
[email protected]
@rmaalmeida
Universidade Federal de Itajubá
Creative Commons License

The work ”Embedded System Design: From Electronics to Microkernel


Development” of Rodrigo Maximiano Antunes de Almeida was licensed
with Creative Commons 3.0 – Attribution – Non Commercial – Share Alike
license.

Additional permission can be given by the author through direct contact


using the e-mail: [email protected]
Schedule
Schedule

• 01) Intro / Pointers / • 08) Drivers


Buffers Controller
• 02) void and • 09) Callbacks
function pointers • 10) Preemptive
• 03) Tasks Kernel, scheduler
• 04) Cooperative • 11) Real time
kernel • 12) Mutex &
• 05) Timing Semaphores
requirements • 13) Message
• 06) Drivers Passing
U
UNN II F
FEE II
FreeRTOS
Material
copyright
All slides are copyright trough CC BY-SA 3.0
Adapted from original work by Sergio Prado – Embedded Labworks
Added info from Mastering the FreeRTOS™ Real Time Kernel – Richard Barry
LPCXpresso

• Integrated development environment for


NXP DSPs and microcontrollers.
 https://www.nxp.com/support/developer-resour
ces/software-development-tools/developer-res
ources-/lpc-microcontroller-utilities/lpcxpr
esso-ide-v8.2.2:LPCXPRESSO
 Remember to register
• Based on Eclipse.
• Support for Windows, Linux and Mac OS
operating systems.
U
UNN II F
FEE II
LPCXpresso(cont.)

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

• In the context of software development, a


system is designed to receive a stimulus
(or event), which can be internal or
external, perform the processing and
produce an output.
• Some systems work with events that
have time restrictions, that is, they have
a deadline or time limit for the stimulus
to be processed and generate the
corresponding output.
•U
UNN II F
These FEE II
types of systems are called "Real
Real time systems (cont.)

• Therefore, a real-time system needs to


ensure that all events are met within
their respective time constraints.
• That's why a real-time system is related
to determinism, not execution time!
• There are basically two types of real-time
systems, classified according to the
tolerance to time constraints, and the
consequences in not respecting these
restrictions.
U
UNN II F
FEE II
The real time kernel
• A real-time kernel is software that manages CPU
time and resources, and is based on the concept
of tasks and priorities.
• All system features are divided into tasks (tasks
or threads).
• The kernel decides when a task should be run
based on the task priority.
• It is the responsibility of the developer to divide
the system into tasks and set the priorities
according to the real-time characteristics of each
one.
U
UNN II F
FEE II
Multitask

• In a multitasking system, we get the


impression that all tasks are running at
the same time.

TAREFA 1

TAREFA 2

TAREFA 3

t1 t2 Tempo tn

U
UNN II F
FEE II
Multitask

• But because the processor can only


execute one task at a time (considering a
CPU with only one core), a switch
between tasks is performed:

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

• While a task is running, it has a certain


context (stack, CPU registers, etc).
• By changing the running task, the kernel
saves the context of the task to be
suspended and retrieves the context of
the next task to be executed.
• The control of the context of each of the
tasks is carried out through a structure
called TCB (Task Control Block).
U
UNN II F
FEE II
Context switch
Context switch
Context switch
Context switch
Context switch
Scheduler

• The task scheduler takes action during


context changes.
• It is the part of the kernel that is
responsible for deciding the next task to
be performed at any given time.
• The algorithm responsible for deciding
what the next task to perform is called a
scheduling policy.

U
UNN II F
FEE II
Other kernel
responsabilities

• In addition to managing CPU usage, a


real-time kernel typically has other
responsibilities, including:
 Manage communication between tasks.
 Manage communication between interrupts
and tasks.
 Manage access to application resources
(hardware, data structures, etc).
 Manage memory usage.
 Provide other features like timers, tracing, etc.

U
UNN II F
FEE II
The FreeRTOS

• Created around the year 2000 by Richard


Barry, and today maintained by the
company Real Time Engineers Ltd.
• RTOS open source most widely used in the
world.
• It's simple, small and extremely portable.
• Project website, with lots of documentation
available: http://www.freertos.org/
• Source code can be downloaded at:
 http://sourceforge.net/projects/freertos/files/
U
UNN II F
FEE II
The FreeRTOS

• Designed to be small, simple and easy to


use.
• Written in C, extremely portable.
• Supports more than 30 different
architectures.
• In a typical configuration, the FreeRTOS
kernel can occupy from 4KB to 9KB of
code (ROM / flash) and around 200 bytes
of data (RAM).

U
UNN II F
FEE II
The FreeRTOS

• Kernel can work preemptively or


collaboratively.
• Mutex with priority inheritance support.
• Trace capabilities and stack overflow
detection.
• No restriction on the number of tasks
that can be created, or the number of
priorities that can be used.

U
UNN II F
FEE II
The FreeRTOS

• Several demonstration projects and


applications to facilitate learning.
• Open source, without royalty and with
free forum available.
• Open and free development tools.
• Great community of users.
• Support and commercial license if
necessary

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

• 1. The path to the core FreeRTOS header


files, which is always
FreeRTOS/Source/include.
• 2. The path to the source files that are
specific to the FreeRTOS port in use. As
described above, this is
FreeRTOS/Source/portable/[compiler]/
[architecture]
• 3. A path to the FreeRTOSConfig.h header
file.

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

• Commercial version of FreeRTOS provided


by WITTENSTEIN High Integrity Systems.
http://www.openrtos.com/rtos/openrtos/
• Pretty much the same source code, but
without any reference to the GPL.
• Freedom to change the kernel without
having to release the fonts.
• Access to technical support and
development.
• Legal protection and guarantee.
U
UNN II F
FEE II
SafeRTOS

• Commercial and modified version of


FreeRTOS, designed for critical
applications.
http://www.openrtos.com/rtos/safertos/
• All the advantages of the commercial
version (support, warranty, etc).
• Certified for industrial and medical
applications.

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

• Dynamic memory allocation


 malloc()
 calloc()
 free()
• Heap x Stack
 Tasks individual stacks

U
UNN II F
FEE II
FreeRTOS memory

• Port function instead original ones


 pvPortMaloc()
 pvPortFree()
• 5 models
 heap1.c
 heap2.c
 heap3.c
 heap4.c
 heap5.c

U
UNN II F
FEE II
Heap1.c

U
UNN II F
FEE II
Heap2.c

U
UNN II F
FEE II
Heap3.c

• Calls regular (compiler) malloc/free


 configTOTAL_HEAP_SIZE setting has no effect
 Makes malloc/free thread safe

U
UNN II F
FEE II
Heap4.c

U
UNN II F
FEE II
Heap5.c

• Same as heap4.c but multispace suport

U
UNN II F
FEE II
How much heap do I need?

• Guess at first, measure latter:


 size_t xPortGetFreeHeapSize( void );
 size_t xPortGetMinimumEverFreeHeapSize( void );
• Who uses memory?
 Any kernel object:
• Tasks, queus, semaphores and event groups.
• How to know it a problem arrises?
 configUSE_MALLOC_FAILED_HOOK is 1?
 void vApplicationMallocFailedHook( void );

U
UNN II F
FEE II
Tasks
Tasks

• Each task behaves like an isolated


program:
 It has an entry point.
 It is usually implemented with an infinite
loop.
 Usually never returns. If a task finishes, it is
the responsibility of the developer to remove
it from the kernel task list.
void ATaskFunction(void *pvParameters);
• Prototype of a task:

U
UNN II F
FEE II
Task Prototype

void ATaskFunction(void *pvParameters)


{
for(;;)
{
/* task code */
}
}
Creating a task

#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);

/* start the scheduler */


vTaskStartScheduler();

/* should never reach here! */


for(;;);
}
Removing a task

• Before returning, a task must remove


itself from the kernel task list with the
vTaskDelete () function.
• To use this function, enable the
INCLUDE_vTaskDelete option in the
FreeRTOSConfig.h file.

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

• The scheduler can work preemptively or


collaboratively depending on the setting of
the configUSE_PREEMPTION option in the
FreeRTOS configuration file.
• In preemptive mode, the kernel will always
execute the highest priority task ready for
execution.
• In collaborative mode, tasks are not
interrupted by the kernel during execution
(but can still be interrupted by an interrupt).
U
UNN II F
FEE II
Time Slice

• In a preemptive kernel, each task has a


slice of time to execute, usually called a
time slice.
• At the end of this time slice, it will be
stopped, and the scheduler will select the
next task to run.
• To stop the running task and switch
context to a new task, the kernel uses a
system interrupt.
U
UNN II F
FEE II
Tick Interrupt

• This interrupt is called tick interrupt


(system tick or cock tick).
• It is a periodic interrupt whose frequency
is defined in configTICK_RATE_HZ in the
file FreeRTOSConfig.h.
• For example, if it is set to 100 (Hz), it
means that the time slice will be 10ms.

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

• In an RTOS, if we create tasks that


monopolize the CPU, they should always
have the lowest priority, because if they
have a higher priority, lower priority
tasks will never be performed.
• Therefore, whenever possible, the tasks
should be event-oriented, that is, they
must wait for an event to perform the
processing.

U
UNN II F
FEE II
Event-oriented tasks

void ATaskFunction(void *pvParameters)


{
for(;;)
{
wait_event();
process_event();
}
}
Blocked state

• A task waiting for an event is in the


Blocked or Locked state.
• A task may be blocked waiting for two
types of events:
 Temporal events: event generated by the
kernel itself. Ex: delay routines.
 Synchronization events: event caused by
another task or interrupt. Ex: communication
mechanisms such as queues and traffic
lights.

U
UNN II F
FEE II
Time functions

#include "task.h"

/* delay a task for a given number of ticks */


void vTaskDelay(
const TickType_t xTicksToDelay );

/* delay a task until a specified time */


void vTaskDelayUntil(
TickType_t *pxPreviousWakeTime,
const TickType_t xTimeIncrement);
Delay example

/* do something every 500ms */


void taskLed(void *pvParameters)
{
for (;;) {
do_something();
vTaskDelay(500/portTICK_PERIOD_MS);
}
}
Suspended state

• Tasks in the Suspended state are not


staggered (executed) by the kernel.
• The only way for a task to enter the
Suspended state is through the call to
vTaskSuspend().
• The only way for a task to exit the
Suspended state is by calling vTaskResume
().
• In order to control task execution state
one must get the task handle on its
U
UNN II F
FEE II
creation.
Suspended state

void vAFunction( void ){


TaskHandle_t xHandle;

// Create a task, storing the handle.


xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL,
tskIDLE_PRIORITY, &xHandle );

// Use the handle to suspend the created task.


vTaskSuspend( xHandle );
// The created task will not run during this period,
// unless another task calls vTaskResume( xHandle ).

// Suspend ourselves.
vTaskSuspend( NULL );

// We cannot get here unless another task calls


// vTaskResume with our handle as the parameter.
}
Ready state

• Tasks that are not in the Blocked or


Suspended states are in Ready state.
• These tasks are waiting in the queue,
ready to be selected and executed by the
scheduler.

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

Creating and managing tasks with FreeRTOS


Exercise

• Download and execute:


 FreeRTOS_simple_task_example.zip
• Create a new main file with 3 tasks:
 Blink the green LEDS (1s)
 Blink the red LEDS (0.5s)
 Test the joystic input
• If left/right was entered one should
suspend/unsuspend green leds task
• If up/down was entered one should
suspend/unsuspend red leds task

U
UNN II F
FEE II
Exercise

Run FreeRTOS_simple_task_lcd_example.zip
FreeRTOS_simple_task_lcd_exam
ple.zip

• It does implement 2 tasks


 One prints the trimmer value (through ADC)
 One prints the lux value (thorugh I2C)
• Change the period for each tasks to 50
mS
 What happens to LCD?

U
UNN II F
FEE II
Queue

As a silent Q.
Prob Var global
Queue management

• An application with FreeRTOS is


structured through a set of tasks that run
independently.
• These tasks are likely to need to
communicate with each other.
• Queue is a mechanism of communication
(exchange of messages) between tasks
or between a task and an interruption.

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"

/* create a new queue instance */


QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );

/* delete a queue */
void vQueueDelete(QueueHandle_t xQueue);

/* reset a queue to its original empty state */


BaseType_t xQueueReset(QueueHandle_t xQueue);
Using a QUEUE

• Typically, queues are used as FIFO (First


In First Out), where data is written at the
end of the queue and read at the
beginning.
• But it is also possible to write at the
beginning of the queue.
• Writing in queue means copying the data
byte to byte! For this reason, if the queue
element is too large, it is best to work
with pointers.
U
UNN II F
FEE II
Readigh through internet

• When reading from the queue, a task enters


the Blocked state waiting for a queue item.
• A task can set a read timeout (time that will
be in the Blocked state expecting an item in
the queue).
• A task waiting for an item in the queue is
automatically put into the Ready state
when:
 An item is written in the queue.
 Read timeout expires.
U
UNN II F
FEE II
Readigh through internet

• Queues can have more than one task


waiting to read an item.
• In this case, if an item is written in the
queue, only the highest priority task is
passed to the Ready state.
• If there is more than one task with the
same priority waiting for an item in the
queue, the task waiting for the longest
time will be passed to the Ready state.
U
UNN II F
FEE II
Readigh through internet

#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

Using the same LCD


Exercise

• Create a third process


 This process will get a message from the
queue and print on LCD.
 Every process that needs to print data will
send the info thorough the queue to the third
process

U
UNN II F
FEE II
Synchronizati
on
mechanisms
Synchronization mechanisms

• An interrupt is able to defer work to a


task through synchronization
mechanisms.
• FreeRTOS has some synchronization
mechanisms, among them:
 Binary Semaphores.
 Semaphores Counters (Counting
Semaphores).
 Queues.
• These synchronization mechanisms can
U N
N II F
Ube E
E II
used
F both for communication between
Binary semaphores

• A Binary Semaphore is a synchronization


mechanism provided by FreeRTOS.
• It can be used to wake up (unlock) a task
when a certain interruption occurs,
synchronizing the interrupt with the task.
• In this way, only the essential is executed
in the interrupt, the rest of the work is
deferred to the task corresponding to the
interrupt treatment.
U
UNN II F
FEE II
Binary semaphores

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);

/* release a semaphore from an ISR */


BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
signed BaseType_t
*pxHigherPriorityTaskWoken);
Resources
management
Resources management

• In a multitasking system, there is the


possibility of a task be interrupted while
accessing a resource, leaving this
resource in an inconsistent state, and
may cause problems if another task tries
to access this same resource.
• Examples:
 Access to global variables.
 Access to peripherals.
 Code Sharing.

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

• In a multitasking environment, if a given


function can be called by more than one
thread (task), this function must be
thread safe.
• This is common when we share code
between different tasks.
• A thread-safe function protects access to
resources shared by tasks, ensuring
concurrent access with security.

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

• Critical Sessions are code regions that access


a shared resource, and should not be
accessed concurrently by more than one
task.
• Access to a resource that is shared between
tasks, or between a task and an interruption,
must be managed with the mutual exclusion
technique, to ensure consistency in access.
• The idea is to ensure that only one task has
exclusive access to the resource at any given
time.
U
UNN II F
FEE II
Disabling interrupts

• In FreeRTOS, you can use the functions


taskENTER_CRITICAL () and
taskEXIT_CRITICAL () to protect a critical
section:
 ...
 taskENTER_CRITICAL ()
 tx_serial (buf, size);
 taskEXIT_CRITICAL ();
…

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

• Use with care because summarizing the


scheduler's execution can be a time-
consuming task.
• In addition, FreeRTOS API functions can
not be used while the scheduler is
stopped

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

• Are the two the same thing?


• “There is an ambiguity between binary
semaphore and mutex. We might have
come across that a mutex is binary
semaphore. But they are not! The
purpose of mutex and semaphore are
different. May be, due to similarity in
their implementation a mutex would
be referred as 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

• It does implement 2 tasks


 One prints the trimmer value (through ADC)
 One prints the lux value (thorugh I2C)
• Create a mutex to protect the LCD
acesses
 Without the need to create a queue and a 3rd
task to serve as gateway.

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

• Sending an event or data to an ISR


• Enabling more than one receiving task
• Buffering multiple data items
• Broadcasting to more than one task
• Waiting in the blocked state for a send to
complete

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

You might also like