Contenu connexe
Similaire à S emb t13-freertos (20)
Plus de João Moreira (11)
S emb t13-freertos
- 1. Maths is not everything
Embedded Systems
7 - Real-Time Operating System (FreeRTOS)
RMR©2012
Introduction
Task Management
Queue Management
Interrupts and Synchronization
Resource Management
Memory Management
Aditional Features
- 3. At the Beginning
Written by Richard Barry & FreeRTOS
Team
Huge number of users all over the world
6000 downloads per month
in March 2010, it came on the top of the
market study made by www.embedded.com.
A good starting point to experience realtime OS
Maths is not everything
RMR©2012
3
Simple but yet very powerful
- 4. FreeRTOS in Literature
An e-book is written to explain the internals.
“Using the FreeRTOS Real Time Kernel - a Practical Guide”
4 editions are available:
Standard edition
Microchip edition
Generic Cortex M3 edition
LPC17xx edition
FreeRTOS Reference Manual
API functions and configuration options
Maths is not everything
Online documentation
RMR©2012
4
www.freertos.org
- 8. Source Code Organization
FreeRTOS
main kernel source directory
Source
include
kernel header files directory
Portable
kernel port
Compiler x
Compiler y
RMR©2012
8
compiler x port
MemMang
Maths is not everything
compiler x port
malloc/free implementation
Demo
Common
demo app common directory
Dir x
demo app of port x
demo app of port y
Dir y
- 10. Portability
Highly portable C
24 architectures supported
Assembly is kept minimum.
Ports are freely available in
source code.
Other contributions do exist.
Maths is not everything
RMR©2012
10
- 11. Scalable
Use the service you only need:
FreeRTOSConfig.h
Very few services / Complete services available
A group of #defines determines scalability.
Minimum footprint = 4 KB
Maths is not everything
RMR©2012
11
- 12. FreeRTOSConfig.h
Maths is not everything
RMR©2012
12
#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
...
- 13. Preemptive and Cooperative Scheduling
Preemptive scheduling:
Fully preemptive
Always runs the highest priority task that is ready to run
Comparable with other preemptive kernels
Used in conjunction with tasks
Cooperative scheduling:
Context switch occurs if:
A task/co-routine blocks
Or a task/co-routine yields the CPU
Used in conjunction with tasks/co-routines
Maths is not everything
A hybrid of both scheduling can be used in your
application.
Application = Tasks
RMR©2012
13
Or Application = Tasks + Co-routines
Tasks have higher priority than Co-routines
- 14. Tasks vs Co-routines
Tasks
Co-routines
no restrictions
API calls have restrictions
totally prioritized
totally prioritized among coroutines but interruptible by
tasks when using hybrid mode
each task maintains its
own stack
re-entrance has to be
tackled carefully when
used with preemption
stack shared among them
re-entrance managed by
cooperation (less
problematic)
Maths is not everything
RMR©2012
14
cooperation only between coroutines
- 15. Multitasking
No software restriction on:
# of tasks that can be created
# of priorities that can be used
Priority assignment
More than one task can be assigned the same
priority.
RR with time slice = 1 RTOS tick
Maths is not everything
RMR©2012
15
- 17. Interrupts
An interrupt can suspend a task
execution.
Interrupt mechanism is port dependent.
Nesting
Scheduling after interrupts
Preemptive or cooperative scheduler
Maths is not everything
RMR©2012
17
- 19. Conventions: variables’ names
In FreeRTOS a prefix is used in the name
of the variables indicating its type
Chars start with a “c”
Shorts start with an “s”
Longs start with an “l”
other types start with an “x” (e.g. structures)
unsigned vars start with an “u”
pointers start with a “p”
Maths is not everything
RMR©2012
19
- 20. Conventions: functions’ names
private functions start with “prv”
API functions’ return are pre-fixed with the same
convention as variables
API functions start with the name of the source
archive where they were defined
e.g. xTaskCreate is defined in Task.c
Maths is not everything
RMR©2012
20
- 21. Licensing
Modified GPL (Real Time Engineers Ltd.)
Only FreeRTOS is GPL.
Open-source code, no royalties involved
May be used freely in commercial applications
Any modifications to the kernel need to be made
available as open-source code
Any app source code can be maintained as private
provided that no new functionalities at the kernel level
is involved
Maths is not everything
RMR©2012
21
FreeRTOS can’t be used in any comparisons without
the authors’ permission.
- 22. Other FreeRTOS Variants
OpenRTOS (High Integrity Systems)
= FreeRTOS + Commercial License
Tailored BSP, middle ware, applications …
no need to publish any kernel modification as open-source
Training, technical support, guaranties, ...
Platform preparation
SafeRTOS
= FreeRTOS + Commercial + IEC61508 SIL3 Certification
(critical apps)
Maths is not everything
Compliant with:
FDA510(k) Class III medical device standards
RMR©2012
22
EN62304
- 23. Maths is not everything
FreeRTOS
Kernel Structure - Tasks
RMR©2012
- 24. Task States
Running
task is executing owning the
CPU
Ready
task may run but is waiting
for the CPU to be available,
Blocked
task is delayed (timing / event)
or is waiting for another task
(synchronization)
Suspended
task may enter this state only
through specific calls
Maths is not everything
RMR©2012
24
there is no associated
timeout
not considered in scheduling
- 25. Global characteristic of a task
Every task behaves as an isolated
sequential program
has a single entry point
implemented usually as an infinite loop
normally, it never returns. If eventually it ends, it’s up
to the programmer to remove it (kill) from the
kernel’s list.
Task prototype
Maths is not everything
RMR©2012
25
void ATaskFunction(void *pvParameters);
- 26. Task skeleton
void ATaskFunction( void *pvParameters )
{
/* Variables can be declared just as per a normal function. Each instance of a task
created using this function will have its own copy of the iVariableExample variable.
This would not be true if the variable was declared static – in which case only one
copy of the variable would exist and this copy would be shared by each created
instance of the task. */
int iVariableExample = 0;
/* A task will normally be implemented as in infinite loop. */
for( ;; )
{
/* The code to implement the task functionality will go here. */
}
/* Should the task implementation ever break out of the above loop
then the task must be deleted before reaching the end of this function.
The NULL parameter passed to the vTaskDelete() function indicates that
the task to be deleted is the calling (this) task. */
vTaskDelete( NULL );
Maths is not everything
RMR©2012
26
}
- 28. Task creation
portBASE_TYPE xTaskCreate(
pdTASK_CODE pvTaskCode,
const char * const pcName,
unsigned short usStackDepth,
void *pvParameters,
unsigned portBASE_TYPE uxPriority,
xTaskHandle *pvCreatedTask
);
pvTaskCode: a pointer to the function (actually just the name) where the task is implemented.
pcName: name given to the task. This is useless to FreeRTOS but is intended for debugging purposes (human
readable) only.
usStackDepth: length of the stack (each task has its own stack) for this task in words. Should be tailored to
task needs.
Maths is not everything
RMR©2012
28
pvParameters: a pointer to arguments given to the task. A good practice consists in creating a dedicated
structure, instantiate and fill it then give its pointer to the task.
uxPriority: priority given to the task, a number between 0 and MAX_PRIORITIES – 1.
pxCreatedTask: a pointer to an identifier that allows to handle the task. If the task does not have to be
handled in the future, this can be leaved NULL.
- 29. Task creation
portBASE_TYPE xTaskCreate(
pdTASK_CODE pvTaskCode,
const char * const pcName,
unsigned short usStackDepth,
void *pvParameters,
unsigned portBASE_TYPE uxPriority,
xTaskHandle *pvCreatedTask
);
Returned Value - there are two possible return values:
1. pdTrue: indicates that the task has been launched successfully.
Maths is not everything
2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: indicates that the task
could not be created because there was insufficient heap memory available for FreeRTOS
to allocate RAM to hold the task data structures and stack.
RMR©2012
29
- 33. Example application: Data-structures maintained by RTOS
Initially
After TaskCreate(1)
currentTask
currentTask
Maths is not everything
RMR©2012
33
0
NULL
1
NULL
1
Task1
2
NULL
2
NULL
3
NULL
3
NULL
NULL
n-1
NULL
pxReadyTaskList
NULL
n-1
pxReadyTaskList
0
- 34. Example application: Data-structures maintained by RTOS
After TaskCreate(2)
currentTask
After TaskStartSchedule ()
currentTask
Task2
Maths is not everything
RMR©2012
34
0
idle
1
Task1
1
Task1
2
Task2
2
NULL
3
NULL
3
NULL
NULL
n-1
NULL
pxReadyTaskList
NULL
n-1
pxReadyTaskList
0
- 37. Tasks within tasks
void vTask1( void *pvParameters )
{
const char *pcTaskName = "Task 1 is runningrn";
volatile unsigned long ul;
/* If this task is executing then the scheduler must already have
been started. Create the other task before entering the infinite
loop.*/
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
for( ;; )
{
/* Print out the name of this task. */
vPrintString( pcTaskName );
/* Delay for a period. */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
/* This loop is just a very crude delay implementation. There
is nothing to do in here. There is a need for proper delay/
sleep function. */
}
Maths is not everything
}
RMR©2012
37
}
- 38. Using the task parameter
void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
volatile unsigned long ul;
/* The string to print out is passed in via the parameter. Cast to a char ptr.*/
pcTaskName = ( char * ) pvParameters;
/* As per most tasks, this task is implemented in an infinite loop. */
for( ;; ) {
/* Print out the name of this task. */
vPrintString( pcTaskName );
/* Delay for a period. */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
/* This loop is just a very crude delay implementation. There is nothing to
do in here.*/
}
}
}
Maths is not everything
RMR©2012
38
/* Define the strings to be passed in as the task parameters. These are defined
const and not on the stack to ensure they remain valid when the tasks are
executing. */
static const char *pcTextForTask1 = “Task 1 is runningrn”;
static const char *pcTextForTask2 = “Task 2 is runningtn”;
int main( void )
{
/* Create one of the two tasks. */
xTaskCreate( vTaskFunction, "Task 1", 1000, (void*)pcTextForTask1, 1, NULL );
/* Create the other instance of the same task. */
xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL );
/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();
for( ;; );
}
- 39. Block State: the delay primitive
void vTaskDelay(portTickType xTicksToDelay);
Delays for xTicksToDelay kernel ticks, while allowing
other tasks to execute
portTickType is unsigned char
Argument is number of kernel ticks to delay
Upon completion of the delay task is returned to ready
state, resumes execution when possible
Maths is not everything
RMR©2012
39
Constants configTICK_RATE_MS and
configTICK_RATE_HZ usable to get desired time delay
- 40. Blocking: delaying a task for a period of time
void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
/* The string to print out is passed in via the parameter */
pcTaskName = ( char * ) pvParameters;
/* As per most tasks, this task is implemented in an infinite loop. */
for( ;; ) {
/* Print out the name of this task. */
vPrintString( pcTaskName );
/* Delay for a period. Call to vTaskDelay() places the task into the Blocked
state until the delay period has expired. The delay period is specified in
'ticks', but the constant portTICK_RATE_MS can be used to convert this to a
value in milliseconds*/
vTaskDelay( 250 / portTICK_RATE_MS );
}
}
Maths is not everything
RMR©2012
40
- 41. Block State: the exact delay primitive
void vTaskDelayUntil(portTickType *pxPreviousWakeTime,
portTickType xTimeIncrement);
vTaskDelay() specifies the number of ticks between the call
and the same task once again transitioning out of the Block
state. But this amount of time is relative to the time at
which vTaskDelay() was called.
Delays for xTimeIncrement from last time called
Used for cyclic tasks, such as e.g. keypad scanning
First argument is variable with last time woken up
Second argument is number of ticks between wake-ups
Maths is not everything
The parameters specify the exact tick count value at which the calling task
should be moved from the Blocked state into the Ready state. The time at
which the calling task is unblocked is absolute, rather than relative to when the
function was called (as is the case with vTaskDelay())
RMR©2012
41
- 42. Blocking: delaying a task for the same EXACT period of time
void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
portTickType xLastWakeTime;
/* The string to print out is passed in via the parameter. Cast this to a char
pointer. */
pcTaskName = ( char * ) pvParameters;
/* The xLastWakeTime variable needs to be initialized with the current tick
count. Note that this is the only time the variable is written to explicitly.
After this xLastWakeTime is updated automatically internally within
vTaskDelayUntil(). */
xLastWakeTime = xTaskGetTickCount();
/* As per most tasks, this task is implemented in an infinite loop. */
for( ;; ) {
/* Print out the name of this task. */
vPrintString( pcTaskName );
/* This task should execute exactly every 250 milliseconds. As per
the vTaskDelay() function, time is measured in ticks, and the
portTICK_RATE_MS constant is used to convert milliseconds into ticks.
xLastWakeTime is automatically updated within vTaskDelayUntil() so is not
explicitly updated by the task. */
vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );
Maths is not everything
RMR©2012
42
}
}
- 43. Priorities
Priorities range from 0 (lowest priority)
up to (configMAX_PRIORITIES – 1)
defined in FreeRTOSConfig.h.
the higher the MAX_PRIORITIES parameter is the
higher the RAM requirements will be.
Tasks will have priorities according to
their real-time characteristics
The scheduler will guarantee that the ready task with
the highest priority will be the one to be executed
Maths is not everything
RMR©2012
43
Tasks may have the same priority. In this case the
scheduler will execute them in a round-robin fashion.
- 44. Scheduler and Priorities
Task scheduling decides which “Ready” task has to be run
at a given time.
FreeRTOS uses priorities for this purpose. Priority is the only element the
scheduler takes into account to decide which task has to be switched in.
Every clock tick makes the scheduler to decide which task has to be waken up.
Maths is not everything
RMR©2012
44
- 45. Scheduler and Priorities
There is no automatic management of priorities
a task always keeps the same priority unless the programmer change it explicitly.
A low value means a low priority: priority 0 is the minimal priority a task could have
and this level should be strictly reserved for the idle task.
Task management allows an implementation of Rate Monotonic:
tasks with higher frequencies are given an higher priority whereas low frequencies
tasks deserve a low priority. Eventbased or continuous tasks are preempted by
periodic tasks.
Maths is not everything
RMR©2012
45
- 46. Equally-priority tasks
Tasks created with an equal priority are treated equally by
the scheduler
If two of them are ready to run, the scheduler shares running time among all of
them
This configures a Round Robin implementation where quantum is the time
between each clock tick.
This value is available in TICK_RATE_HZ constant, in FreeRTOSConfig.h
Maths is not everything
RMR©2012
46
- 47. The idle task
When all tasks are either blocked or
suspended the
CPU needs to be executing
something
The Idle Task is then executed!
this task is automatically created when the scheduler is started ➱
vTaskStartScheduler().
this task has priority 0 (lowest possible).
Normally, the idle task does nothing (actually is when idle task
runs that the kernel does some housekeeping like purging
deleted tasks stuff)
Maths is not everything
RMR©2012
47
It is however possible to add application specific functionality
directly into the idle task through the use of an idle hook (or
call-back) function – a function that is automatically called by
the idle task once per iteration of the idle task loop.
- 48. Idle Task Hook
Common uses for the Idle task hook include:
Executing low priority, background or continuous processing.
Measuring the amount of spare processing capacity
measuring the amount of processing time allocated to the idle task
provides a clear indication of how much processing time is spare
Placing the processor into a low power mode
To run it ➱ configUSE_IDLE_HOOK must be set to 1 within FreeRTOSConfig.h
/* Declare a variable that will be incremented by the hook function. */
unsigned long ulIdleCycleCount = 0UL;
Maths is not everything
/* Idle hook functions MUST be called vApplicationIdleHook(), take no parameters,
and return void. */
void vApplicationIdleHook( void )
{
RMR©2012
48
/* This hook function does nothing but increment a counter. */
ulIdleCycleCount++;
}
- 49. Tick Interrupt Hook
It is possible to implement a callback function to
be called at every tick interrupt:
this function can be used to run a periodic routine like the one
needed to reset the watchdog timer;
as this routine runs in interrupt time, its processing effort should
be kept at a minimum, using short code and only a moderate
amount of stack space and not call any FreeRTOS API functions
whose name does not end with ‘FromISR()’.
To run it ➱ configUSE_TICK_HOOK must be set to 1 within
FreeRTOSConfig.h
Maths is not everything
RMR©2012
49
provide the implementation of the hook function using:
void vApplicationTickHook( void );
- 50. Suspended State
Tasks in the Suspended state are not
scheduled by the kernel
The only way to enter the Suspended State is by
invoking the vTaskSuspended() primitive
The only way to exit the Suspended State is by
invoking the vTaskResume() primitive
Maths is not everything
RMR©2012
50
- 51. Scheduler review
Each application comprises one or more tasks.
Each task is assigned a priority.
Each task can exist in one of several states.
(Running, Ready, Blocked, Suspended).
Only one task can exist in the Running state at any
one.
Maths is not everything
RMR©2012
51
The scheduler will always select the highest
priority Ready state task to enter the Running
state.
- 54. FreeRTOS Queues
Queue is a communication mechanism between tasks or between
tasks and interrupt handlers
Tasks: xQueueSend, xQueueReceive, uxQueueMessagesWaiting
ISRs: xQueueSendFromISR, xQueueReceiveFromISR
Tasks can wait for items to enter/exit the queue while allowing
other tasks to execute
Maths is not everything
RMR©2012
54
- 55. Queue’s characteristics
A queue doesn’t belong to a given task.
Many tasks and int. handlers may share the same queue, either
for reading or writing.
Each queue stores a finite number of data items (queue length).
Every item has a fixed sized (item size).
Both "queue length" and "item size" are set when the queue is
created.
FreeRTOS allocates heap space for storing the queue.
Queues hold data in order, First In First Out (FIFO)
Writing to a queue causes a byte for byte copy of the data to be
stored in the queue.
Maths is not everything
RMR©2012
55
Reading from a queue causes the copy of the data to be removed
from the queue.
for this reason, if the element size is too big is better to work with
pointers
- 56. Queue’s characteristics: reading from a queue
When a task attempts to read from a queue it may enter into
the Blocked state waiting for an item in there.
A task may define a reading timeout ➟ time it is kept in the
Blocked state waiting for data, should the queue is empty.
A task in the Blocked state waiting for data to become
available is automatically moved into the Ready state when:
An item is written into the queue.
The reading timeout expires.
Queues can have more than one task blocked on it waiting for
data, as queues can have multiple readers.
Maths is not everything
RMR©2012
56
only one task (the one waiting for data with the highest priority) will
be unblocked when data becomes available.
blocked tasks with equal priority ➟ the task that has been waiting
for data the longest will be unblocked.
- 57. Queue’s characteristics: writing to a queue
When a task attempts to write to a queue it may enter into
the Blocked state if the queue is full.
A task may define a writing timeout ➟ time it is kept in the
Blocked state waiting for space in the queue, should the queue
is full.
A task trying to write an item into a queue is automatically
moved into the Ready state when:
The item is successfully written into the queue.
The writing timeout expires.
Queues can have multiple writers; so, more than one task can
be blocked on it waiting for sending data.
Maths is not everything
RMR©2012
57
only one task (the one waiting for queue space with the highest
priority) will be unblocked when space becomes available.
blocked tasks with equal priority ➟ the task that has been waiting
for space the longest will be unblocked.
- 61. Queue Creation
Creates a queue for uxQueueLength items of size uxItemSize bytes per
item
Handle should be global variables if ISRs and tasks need to access it
After creation, check to see if not null before use
xQueueHandle xQueueCreate (unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize);
uxQueueLength - The maximum number of items that the queue being created can hold at any one time.
uxItemSize - The size in bytes of each data item that can be stored in the queue.
Return Value - If NULL is returned then the queue could not be created because there was insufficient heap
memory available for FreeRTOS to allocate the queue data structures and storage area.
A non-NULL value being returned indicates that the queue was created successfully. The returned value
should be stored as the handle to the created queue.
Maths is not everything
RMR©2012
59
xQueueHandle MyQueue;
int main( void )
{
...
MyQueue = xQueueCreate( 20, sizeof( unsigned char ) );
...
}
- 62. Queue System Calls: send to queue
portBASE_TYPE xQueueSendToFront (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
portBASE_TYPE xQueueSendToBack (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
Maths is not everything
RMR©2012
60
- 63. Queue System Calls: send to queue
portBASE_TYPE xQueueSendToFront (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
portBASE_TYPE xQueueSendToBack (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
xQueueSend() is equivalent to and exactly the same as xQueueSendToBack()
Maths is not everything
RMR©2012
60
- 64. Queue System Calls: send to queue
portBASE_TYPE xQueueSendToFront (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
portBASE_TYPE xQueueSendToBack (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
xQueueSend() is equivalent to and exactly the same as xQueueSendToBack()
pvItemToQueue - A pointer to the data that will be copied into the queue.
Maths is not everything
RMR©2012
60
xTicksToWait - The maximum amount of time the task should remain in the Blocked state,
should the queue already be full.
Both xQueueSendToFront() and xQueueSendToBack() will return immediately if xTicksToWait
is 0 and the queue is already full. Setting xTicksToWait to portMAX_DELAY will cause the task
to wait indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
- 65. Queue System Calls: send to queue
pdPASS will only be
portBASE_TYPE xQueueSendToFront (
returned if data was
successfully sent to the
xQueueHandle xQueue,
queue.
const void* pvItemToQueue,
portTickType xTicksToWait);
portBASE_TYPE xQueueSendToBack (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
xQueueSend() is equivalent to and exactly the same as xQueueSendToBack()
pvItemToQueue - A pointer to the data that will be copied into the queue.
Maths is not everything
RMR©2012
60
xTicksToWait - The maximum amount of time the task should remain in the Blocked state,
should the queue already be full.
Both xQueueSendToFront() and xQueueSendToBack() will return immediately if xTicksToWait
is 0 and the queue is already full. Setting xTicksToWait to portMAX_DELAY will cause the task
to wait indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
- 66. Queue System Calls: send to queue
pdPASS will only be
portBASE_TYPE xQueueSendToFront (
returned if data was
successfully sent to the
xQueueHandle xQueue,
queue.
const void* pvItemToQueue,
portTickType xTicksToWait);
portBASE_TYPE xQueueSendToBack (
xQueueHandle xQueue,
const void* pvItemToQueue,
portTickType xTicksToWait);
errQUEUE_FULL will
be returned if data
could not be written to
the queue because the
queue was already full.
xQueueSend() is equivalent to and exactly the same as xQueueSendToBack()
pvItemToQueue - A pointer to the data that will be copied into the queue.
Maths is not everything
RMR©2012
60
xTicksToWait - The maximum amount of time the task should remain in the Blocked state,
should the queue already be full.
Both xQueueSendToFront() and xQueueSendToBack() will return immediately if xTicksToWait
is 0 and the queue is already full. Setting xTicksToWait to portMAX_DELAY will cause the task
to wait indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
- 67. Queue System Calls: receive from queue
portBASE_TYPE xQueueReceive (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
portBASE_TYPE xQueuePeek (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
Maths is not everything
RMR©2012
61
- 68. Queue System Calls: receive from queue
portBASE_TYPE xQueueReceive (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
portBASE_TYPE xQueuePeek (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
xQueuePeek() is used to receive an item from a queue without the item being removed from
the queue
Maths is not everything
RMR©2012
61
- 69. Queue System Calls: receive from queue
portBASE_TYPE xQueueReceive (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
portBASE_TYPE xQueuePeek (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
xQueuePeek() is used to receive an item from a queue without the item being removed from
the queue
pvBuffer - A pointer to the memory into which the received data will be copied.
Maths is not everything
RMR©2012
61
xTicksToWait - The maximum amount of time the task should remain in the Blocked state,
should the queue already be empty.
Both xQueueReceive() and xQueuePeek() will return immediately if xTicksToWait is 0 and the
queue is already empty. Setting xTicksToWait to portMAX_DELAY will cause the task to wait
indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
- 70. Queue System Calls: receive from queue
pdPASS will only be
portBASE_TYPE xQueueReceive (
returned if data was
successfully read from
xQueueHandle xQueue,
the queue.
void *pvBuffer,
portTickType xTicksToWait);
portBASE_TYPE xQueuePeek (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
xQueuePeek() is used to receive an item from a queue without the item being removed from
the queue
pvBuffer - A pointer to the memory into which the received data will be copied.
Maths is not everything
RMR©2012
61
xTicksToWait - The maximum amount of time the task should remain in the Blocked state,
should the queue already be empty.
Both xQueueReceive() and xQueuePeek() will return immediately if xTicksToWait is 0 and the
queue is already empty. Setting xTicksToWait to portMAX_DELAY will cause the task to wait
indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
- 71. Queue System Calls: receive from queue
pdPASS will only be
portBASE_TYPE xQueueReceive (
returned if data was
successfully read from
xQueueHandle xQueue,
the queue.
void *pvBuffer,
portTickType xTicksToWait);
portBASE_TYPE xQueuePeek (
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait);
errQUEUE_EMPTY
will be returned if data
could not be read from
the queue because the
queue was already
empty.
xQueuePeek() is used to receive an item from a queue without the item being removed from
the queue
pvBuffer - A pointer to the memory into which the received data will be copied.
Maths is not everything
RMR©2012
61
xTicksToWait - The maximum amount of time the task should remain in the Blocked state,
should the queue already be empty.
Both xQueueReceive() and xQueuePeek() will return immediately if xTicksToWait is 0 and the
queue is already empty. Setting xTicksToWait to portMAX_DELAY will cause the task to wait
indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
- 72. Queue System Calls: auxiliary
unsigned portBASE_TYPE uxQueueMessagesWaiting (
xQueueHandle xQueue);
uxQueueMessagesWaiting() is used to query the number of items
that are currently in a queue
Never call uxQueueMessagesWaiting() from an interrupt service
routine. The interrupt safe uxQueueMessagesWaitingFromISR()
should be used in its place
Maths is not everything
RMR©2012
62
- 73. Queue System Calls in Int. Handlers
Never use the previous SysCalls within
Int. Handlers!
In these handlers use the SysCalls ending
with“FromISR()”.
Examples:
XQueueSendToFrontFromISR().
XQueueSendToBackFromISR().
XQueueReceiveFromISR().
Maths is not everything
RMR©2012
63
- 74. Queue System Calls: examples
Maths is not everything
RMR©2012
64
/* To store the reference to the queue that is accessed by all three tasks. */
xQueueHandle xQueue;
int main( void )
{
/* queue is to hold a max of 5 values, each of type long. */
xQueue = xQueueCreate( 5, sizeof( long ) );
if( xQueue != NULL )
{
/* Create 2 instances of the task that will send to the queue. Task
par. is used to pass the value that the task will write to the queue;
only one task will continuously write 100 to the queue while the
other task will continuously write 200. Both tasks created w/ prio 1 */
xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );
xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );
/* Create the task that will read from the queue. The task is created
with priority 2, so above the priority of the sender tasks */
xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
}else {
/* The queue could not be created. */
}
/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. Otherwise, it is likely that there was
insufficient heap memory available for the idle task to be created*/
for( ;; );
}
- 75. Queue System Calls: examples
Maths is not everything
RMR©2012
65
static void vSenderTask( void *pvParameters )
{
long lValueToSend;
portBASE_TYPE xStatus;
/* queue was created to hold ‘long’ values, so cast to the required type */
lValueToSend = ( long ) *pvParameters;
for( ;; )
{
/* Send the value to the queue. The queue was created before the
scheduler was started. The 2nd parameter -> address of the data to be
sent. The 3rd parameter -> time the task should wait for space in the
queue. In this case a block time is not specified because the queue
should never contain more than one item and therefore never be full */
xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );
if( xStatus != pdPASS )
{
/* The send operation could not complete because the queue was
full - must be an error as the queue should never have more than
one item! */
vPrintString( "Could not send to the queue.rn" );
}
/* Allow the other sender task to execute. taskYIELD() informs the
scheduler that a switch should occur now rather than keeping this task
in the Running state until the end of the current time slice */
taskYIELD();
}
}
- 76. Queue System Calls: examples
Maths is not everything
RMR©2012
66
static void vReceiverTask( void *pvParameters )
{
long lReceivedValue;
portBASE_TYPE xStatus;
const portTickType xTicksToWait = 100 / portTICK_RATE_MS;
for( ;; ) {
/* call should always find the queue empty as this task will
immediately remove any data that is written to the queue */
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
vPrintString( "Queue should have been empty!rn" );
}
/* Receive data from the queue. 2nd par. -> the buffer that receives
data in queue. Buffer is an address of a var. with the required size
to hold the data. 3rd parameter -> time the task should wait for data
to be available, should the queue already be empty.*/
xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );
if( xStatus == pdPASS )
{/* Data successfully received from queue, printout the value */
vPrintStringAndNumber( "Received = ", lReceivedValue );
} else {
/* Data was not received from the queue even after waiting for 100ms.
Must be an error as the sending tasks are free running and will be
continuously writing to the queue. */
vPrintString( "Could not receive from the queue.rn" );
}
}
}
- 79. Queues: transferring compound types
/* Define the structure type that will be passed on the queue.
*/
typedef struct
{
unsigned char ucValue;
unsigned char ucSource;
} xData;
/* Declare two variables of type xData that will be passed on
the queue. */
static const xData xStructsToSend [ 2 ] =
{
{ 100, mainSENDER_1 }, /* Used by Sender1 */
{ 200, mainSENDER_2 } /* Used by Sender2 */
};
Maths is not everything
RMR©2012
69
- 80. Queues: transferring compound types
Maths is not everything
RMR©2012
70
/* To store the reference to the queue that is accessed by all three tasks. */
xQueueHandle xQueue;
int main( void )
{
/* queue is to hold a max of 3 values, each of structure of type xData */
xQueue = xQueueCreate( 3, sizeof( xData ) );
if( xQueue != NULL )
{
/* Create 2 instances of the task that will send to the queue. Task
par. is used to pass the struct that the task will write to the queue;
only one task will send xStructsToSend[0] to the queue while the
other task will xStructsToSend[1]. Both tasks created w/ prio 2 */
xTaskCreate(vSenderTask,"Sender1",1000,&(xStructsToSend[0],2,NULL );
xTaskCreate(vSenderTask,"Sender2",1000,&(xStructsToSend[1],2,NULL);
/* Create the task that will read from the queue. The task is created
with priority 1, so below the priority of the sender tasks */
xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
}else {
/* The queue could not be created. */
}
/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. Otherwise, it is likely that there was
insufficient heap memory available for the idle task to be created*/
for( ;; );
}
- 81. Queues: transferring compound types
static void vSenderTask( void *pvParameters )
{
portBASE_TYPE xStatus;
const portTickType xTicksToWait = 100 / portTICK_RATE_MS;
Maths is not everything
RMR©2012
71
}
for( ;; )
{
/* Send to the queue. The queue was created before the scheduler was
started. 2nd parameter -> address of the structure being sent. The 3rd
parameter -> time the task should wait for space in the queue. A block
time is specified as the sending tasks have a higher priority than the
receiving task so the queue is expected to become full. The receiving
task will remove items from the queue when both sending tasks are
Blocked */
xStatus = xQueueSendToBack( xQueue, pvParameters, xTicksToWait );
if( xStatus != pdPASS )
{
/* The send operation could not complete even after waiting for
100ms - must be an error as the receiving task should make space
in the queue as soon as both sending tasks are in the Blocked
state */
vPrintString( "Could not send to the queue.rn" );
}
/* Allow the other sender task to execute. taskYIELD() informs the
scheduler that a switch should occur now rather than keeping this task
in the Running state until the end of the current time slice */
taskYIELD();
}
- 82. Queues: transferring compound types
static void vReceiverTask( void *pvParameters )
{
xData xReceivedStructure;
portBASE_TYPE xStatus;
const portTickType xTicksToWait = 100 / portTICK_RATE_MS;
Maths is not everything
RMR©2012
72
}
for( ;; )
{
/* always expects the queue to have 3 items */
if( uxQueueMessagesWaiting( xQueue ) != 3 ) {
vPrintString( "Queue should have been full!rn" );
}
/* Receive data from the queue. 2nd par. -> the buffer (address of a var)
with the required size to hold the data. 3rd parameter -> time the task
should wait for data to be available when queue is empty. It is not needed
as this task will only run when the queue is full.*/
xStatus = xQueueReceive( xQueue, &xReceivedStructure, 0 );
if( xStatus == pdPASS ) {
/* Data successfully received from queue, printout the value */
if( xReceivedStructure.ucSource == mainSENDER_1) {
vPrintStringAndNumber( "From Sender 1 = ",
xReceivedStructure.ucValue );
} else {
vPrintStringAndNumber( "From Sender 2 = ",
xReceivedStructure.ucValue );
}
} else {
/* Nothing was received from the queue. Must be an error as this tasks
should only runs when the queue is full */
vPrintString( "Could not receive from the queue.rn" );
}
}
- 84. Queues: handling big data
When the data being stored is large it is
preferable to use the queue to transfer pointers
rather than copy the data itself into and out of
the queue, byte by byte. However ...
The owner of the RAM being pointed to must be clearly defined
only the sending task should access the memory until a
pointer to the memory has been queued,
only the receiving task should access the memory after the
pointer has been received from the queue.
The RAM being pointed to remains valid
Maths is not everything
RMR©2012
74
If the memory being pointed to was allocated dynamically
then exactly one task should be responsible for freeing it. No
tasks should attempt to access the memory after it has been
freed.
A pointer should never be used to access data that has been
allocated on a task stack.
- 86. Maths is not everything
FreeRTOS
Interrupts and Synchronization
RMR©2012
- 87. Interrupt Handling
Embedded real-time systems have to take
actions in response to external events
a packet from a communication interface (event)
might require passing to a TCP/IP stack for processing
(action)
Usually, events are handled through
interrupts inside an ISR.
but how much processing should be done inside the
ISR?
Maths is not everything
RMR©2012
77
how can these events be communicated from a ISR to
the application tasks and this code can be structured
to best accommodate processing of potentially
asynchronous occurrences?
- 88. Interrupt Handling
Interrupt handling is critical for the app
performance ➟ the ISR should be short
and execute fast.
In FreeRTOS, an ISR should:
Acknowledge the interrupt
Collect data from the event
Defer the “hard work” to a “handler” task
Maths is not everything
RMR©2012
78
a context switch to the “handler” task
should occur
when it has a higher priority than the interrupted task
which is preempted
- 90. Communication between an IH and the Handler Task
An IH can defer the event heavy processing to a
Handler task through a synchronization
mechanism.
A simple way to do it is through a binary semaphore
Maths is not everything
RMR©2012
80
- 91. Communication between an IH and the Handler Task
An IH can defer the event heavy processing to a
Handler task through a synchronization
mechanism.
A simple way to do it is through a binary semaphore
Maths is not everything
RMR©2012
80
- 92. Communication between an IH and the Handler Task
An IH can defer the event heavy processing to a
Handler task through a synchronization
mechanism.
A simple way to do it is through a binary semaphore
Maths is not everything
RMR©2012
80
- 93. Communication between an IH and the Handler Task
An IH can defer the event heavy processing to a
Handler task through a synchronization
mechanism.
A simple way to do it is through a binary semaphore
Maths is not everything
RMR©2012
80
- 94. Communication between an IH and the Handler Task
An IH can defer the event heavy processing to a
Handler task through a synchronization
mechanism.
A simple way to do it is through a binary semaphore
Maths is not everything
RMR©2012
80
- 95. Communication between an IH and the Handler Task
An IH can defer the event heavy processing to a
Handler task through a synchronization
mechanism.
A simple way to do it is through a binary semaphore
Maths is not everything
RMR©2012
80
- 96. Synchronization mechanisms
FreeRTOS has the following
synchronization mechanisms:
Binary Semaphores
Counting Semaphores
Queues
Maths is not everything
RMR©2012
81
These mechanisms can be used in the
communication/synchronization between
tasks and between interrupt handlers and
tasks.
- 97. Binary Semaphores
Binary Semaphore Creation & Manipulation
void vSemaphoreCreateBinary (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreGiveFromISR (
xSemaphoreHandle xSemaphore,
signed portBASE_TYPE *pxHigherPriorityTaskWoken);
Maths is not everything
RMR©2012
82
- 98. Binary Semaphores
Binary Semaphore Creation & Manipulation
void vSemaphoreCreateBinary (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreGiveFromISR (
xSemaphoreHandle xSemaphore,
signed portBASE_TYPE *pxHigherPriorityTaskWoken);
xSemaphore - referenced by a variable and must be explicitly created before being used.
Maths is not everything
RMR©2012
82
xTicksToWait - The maximum amount of time the task should wait for the semaphore if it is
not already available. If xTicksToWait is 0 then xSemaphoreTake() will return immediately if the
semaphore is not available. Setting xTicksToWait to portMAX_DELAY will cause the task to
wait indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
pxHigherPriorityTaskWoken - If xSemaphoreGiveFromISR() sets this value to pdTRUE then
a context switch should be performed before the interrupt is exited. This will ensure the
interrupt returns directly to the highest priority Ready state task.
- 99. Binary Semaphores
Binary Semaphore Creation & Manipulation
void vSemaphoreCreateBinary (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreGiveFromISR (
pdPASS will only be xSemaphoreHandle xSemaphore,
returned if the call signed portBASE_TYPE *pxHigherPriorityTaskWoken);
was successful.
xSemaphore - referenced by a variable and must be explicitly created before being used.
Maths is not everything
RMR©2012
82
xTicksToWait - The maximum amount of time the task should wait for the semaphore if it is
not already available. If xTicksToWait is 0 then xSemaphoreTake() will return immediately if the
semaphore is not available. Setting xTicksToWait to portMAX_DELAY will cause the task to
wait indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
pxHigherPriorityTaskWoken - If xSemaphoreGiveFromISR() sets this value to pdTRUE then
a context switch should be performed before the interrupt is exited. This will ensure the
interrupt returns directly to the highest priority Ready state task.
- 100. Binary Semaphores
Binary Semaphore Creation & Manipulation
void vSemaphoreCreateBinary (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreGiveFromISR ( pdFALSE will be returned if
the semaphore was not
pdPASS will only be xSemaphoreHandle xSemaphore, available.
returned if the call signed portBASE_TYPE *pxHigherPriorityTaskWoken);
was successful.
xSemaphore - referenced by a variable and must be explicitly created before being used.
Maths is not everything
RMR©2012
82
xTicksToWait - The maximum amount of time the task should wait for the semaphore if it is
not already available. If xTicksToWait is 0 then xSemaphoreTake() will return immediately if the
semaphore is not available. Setting xTicksToWait to portMAX_DELAY will cause the task to
wait indefinitely (without timing out) provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h.
pxHigherPriorityTaskWoken - If xSemaphoreGiveFromISR() sets this value to pdTRUE then
a context switch should be performed before the interrupt is exited. This will ensure the
interrupt returns directly to the highest priority Ready state task.
- 101. Forcing a context switch in the IH
To force the context switching from an
ISR one should call:
void portEND_SWITCHING_ISR(portBASE_TYPE flag); // ARM port
void portSWITCH_CONTEXT(); // DOS port
The flag parameter should use the value
returned in the pxHigherPriorityTaskWoken
variable used by the primitives “FromISR”
called in the ISR.
Maths is not everything
RMR©2012
83
- 102. Communication between an IH and the Handler Task: example
Maths is not everything
RMR©2012
84
int main( void )
{
/*In this example a binary semaphore is created. */
vSemaphoreCreateBinary( xBinarySemaphore );
/* Install the interrupt handler. DOS emulation case*/
_dos_setvect( 0x82, vExampleInterruptHandler );
if( xBinarySemaphore != NULL ) /* Check if it was created successfully */
{
/* This is the task that will be synchronized with the interrupt. The
handler task is created with a high priority to ensure it runs
immediately after the interrupt exits */
xTaskCreate( vHandlerTask, "Handler", 1000, NULL, 3, NULL );
/* Create the task that will periodically generate a software
interrupt. This is created with a priority below the handler task to
ensure it will get pre-empted each time the handler task exits the
Blocked state */
xTaskCreate( vPeriodicTask, "Periodic", 1000, NULL, 1, NULL );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
}
/* If main() does reach here then it is likely that there was insufficient
heap memory available for the idle task or for the semaphore (data
structures) to be created with success*/
for( ;; );
}
- 103. Communication between an IH and the Handler Task: example
Maths is not everything
RMR©2012
85
static void vPeriodicTask( void *pvParameters )
{
for( ;; ) {
/* This task is just used to 'simulate' an interrupt by
generating a software interrupt every 500ms. */
vTaskDelay( 500 / portTICK_RATE_MS );
/* Generate the interrupt, printing a message both before and
after so the sequence of execution is evident from the output
produced when the example is executed. */
vPrintString( "Periodic task - About to generate an interrupt.r
n" );
__asm{ int 0x82 } /* generates the interrupt. */
vPrintString( "Periodic task - Interrupt generated.rnrnr
n" );
}
}
- 104. Communication between an IH and the Handler Task: example
static void vHandlerTask( void *pvParameters )
{
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; ) {
/* Use the semaphore to wait for an event. The task blocks indefinitely so
the call will only return once the semaphore has been successfully taken.
There is therefore no need to check the function return value. */
xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );
/* To get here the event must have occurred. Process the event. In this case
processing is simply a matter of printing out a message. */
vPrintString( "Handler task - Processing event.rn" );
}
}
Maths is not everything
RMR©2012
86
static void __interrupt __far vExampleInterruptHandler( void )
{
static portBASE_TYPE xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
/* 'Give' the semaphore to unblock the task. */
xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );
if( xHigherPriorityTaskWoken == pdTRUE ) {
/* Giving the semaphore unblocked a task, and the priority of the unblocked task
is higher than the currently running task - force a context switch to ensure that
the interrupt returns directly to the unblocked (higher priority) task.
NOTE: The actual macro to use (context switch) from an ISR is dependent on the
port. This is the correct macro for the Open Watcom DOS port. Other ports may
require different syntax */
portSWITCH_CONTEXT(); // e.g.portEND_SWITCHING_ISR (xHigherPriorityTaskWoken);
}
}
- 106. Counting Semaphores
Binary semaphores are useful for low interrupt
rates.
However, when this rate increases,
failing to attend events is very likely to
happen.
a binary semaphore can latch at most one interrupt event; any
subsequent events occurring before the latched event has
been processed would be lost.
In such cases, counting semaphores can be
used instead of binary semaphores. Counting
semaphores can be used for:
Maths is not everything
RMR©2012
88
Handling events.
Managing the access to resources
- 114. Counting Semaphores: creation and manipulation
xSemaphoreHandle xSemaphoreCreateCounting (unsigned
portBASE_TYPE uxMaxCount,
unsigned portBASE_TYPE uxInitialCount);
portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore,
portTickType xBlockTime);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreGiveFromISR (
xSemaphoreHandle xSemaphore,
signed portBASE_TYPE *pxHigherPriorityTaskWoken);
Maths is not everything
RMR©2012
90
- 115. Counting Semaphores: creation and manipulation
xSemaphoreHandle xSemaphoreCreateCounting (unsigned
portBASE_TYPE uxMaxCount,
unsigned portBASE_TYPE uxInitialCount);
portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore,
portTickType xBlockTime);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
portBASE_TYPE xSemaphoreGiveFromISR (
xSemaphoreHandle xSemaphore,
signed portBASE_TYPE *pxHigherPriorityTaskWoken);
uxMaxCount - The maximum value the semaphore will count to. When the semaphore is
used to count or latch events uxMaxCount is the maximum number of events that can be
latched. When the semaphore is used to manage access to a collection of resources
uxMaxCount should be set to the total number of resources that are available.
Maths is not everything
RMR©2012
90
uxInitialCount - The initial count value of the semaphore after it has been created. When the
semaphore is used to count or latch events uxInitialCount should be set to 0 – as presumably
when the semaphore is created no events have yet occurred. When the semaphore is used to
manage access to a collection of resources uxInitialCount should be set to equal uxMaxCount
– as presumably when the semaphore is created all the resources are available.
- 116. Counting Semaphores: example of use in an IH
int main( void )
{
/* In this example a counting semaphore is created. The semaphore is created to have a
maximum count value of 10, and an initial count value of 0 */
xCountingSemaphore = xSemaphoreCreateCounting( 10, 0 );
/* Install the interrupt handler. DOS emulation case*/
_dos_setvect( 0x82, vExampleInterruptHandler );
if( xBinarySemaphore != NULL ) /* Check if it was created successfully */
{
/* This is the task that will be synchronized with the interrupt. The
handler task is created with a high priority to ensure it runs
immediately after the interrupt exits */
xTaskCreate( vHandlerTask, "Handler", 1000, NULL, 3, NULL );
/* Create the task that will periodically generate a software
interrupt. This is created with a priority below the handler task to
ensure it will get pre-empted each time the handler task exits the
Blocked state */
xTaskCreate( vPeriodicTask, "Periodic", 1000, NULL, 1, NULL );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
}
/* If main() does reach here then it is likely that there was insufficient
heap memory available for the idle task or for the semaphore (data
structures) to be created with success*/
for( ;; );
Maths is not everything
RMR©2012
91
}
- 117. Counting Semaphores: example of use in an IH
Maths is not everything
RMR©2012
92
static void __interrupt __far vExampleInterruptHandler( void )
{
static portBASE_TYPE xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
/* 'Give' the semaphore multiple times. The first will unblock the handler
task, the following 'gives' are to demonstrate that the semaphore latches
the events to allow the handler task to process them in turn without any
events getting lost. This simulates multiple interrupts being taken by the
processor, even though in this case the events are simulated within a
single interrupt occurrence.*/
xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );
xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );
xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );
if( xHigherPriorityTaskWoken == pdTRUE ) {
/* Giving the semaphore unblocked a task, and the priority of the unblocked
task is higher than the currently running task - force a context switch to
ensure that the interrupt returns directly to the unblocked (higher
priority) task.
NOTE: The actual macro to use (context switch) from an ISR is dependent on
the port. This is the correct macro for the Open Watcom DOS port. Other
ports may require different syntax */
portSWITCH_CONTEXT(); // e.g.portEND_SWITCHING_ISR
(xHigherPriorityTaskWoken);
}
}
- 119. Synchronizing IH and Tasks with Queues
Semaphores are used to communicate
events.
Queues are used to both communicate
events and transfer data.
Maths is not everything
RMR©2012
94
xQueueSendToFrontFromISR(), xQueueSendToBackFromISR()
and xQueueReceiveFromISR() are versions of
xQueueSendToFront(), xQueueSendToBack() and
xQueueReceive() respectively that are safe to use within an
interrupt service routine.
- 120. Queue System Calls (from ISR): send to queue
portBASE_TYPE xQueueSendToFrontFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
portBASE_TYPE xQueueSendToBackFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
Maths is not everything
RMR©2012
95
- 121. Queue System Calls (from ISR): send to queue
portBASE_TYPE xQueueSendToFrontFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
portBASE_TYPE xQueueSendToBackFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
xQueueSendFromISR() is equivalent to xQueueSendToBackFromISR()
Maths is not everything
RMR©2012
95
- 122. Queue System Calls (from ISR): send to queue
portBASE_TYPE xQueueSendToFrontFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
portBASE_TYPE xQueueSendToBackFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
xQueueSendFromISR() is equivalent to xQueueSendToBackFromISR()
pvItemToQueue - A pointer to the data that will be copied into the queue.
Maths is not everything
RMR©2012
95
pxHigherPriorityTaskWoken - If calling the API function causes a task to leave the Blocked
state, and the unblocked task has a priority higher than the task that was interrupted, then the
API function will internally set *pxHigherPriorityTaskWoken to pdTRUE.
If xQueueSendToFrontFromISR() or xQueueSendToBackFromISR() sets this value to pdTRUE
then a context switch should be performed before the interrupt is exited. This ensure the
interrupt returns directly to the highest priority Ready state task.
- 123. Queue System Calls (from ISR): send to queue
portBASE_TYPE xQueueSendToFrontFromISR (
pdPASS will only be
returned if data was
xQueueHandle xQueue,
successfully sent to the
const void* pvItemToQueue,
queue.
portBASE_TYPE *pxHigherPriorityTaskWoken);
portBASE_TYPE xQueueSendToBackFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
xQueueSendFromISR() is equivalent to xQueueSendToBackFromISR()
pvItemToQueue - A pointer to the data that will be copied into the queue.
Maths is not everything
RMR©2012
95
pxHigherPriorityTaskWoken - If calling the API function causes a task to leave the Blocked
state, and the unblocked task has a priority higher than the task that was interrupted, then the
API function will internally set *pxHigherPriorityTaskWoken to pdTRUE.
If xQueueSendToFrontFromISR() or xQueueSendToBackFromISR() sets this value to pdTRUE
then a context switch should be performed before the interrupt is exited. This ensure the
interrupt returns directly to the highest priority Ready state task.
- 124. Queue System Calls (from ISR): send to queue
portBASE_TYPE xQueueSendToFrontFromISR (
pdPASS will only be
returned if data was
xQueueHandle xQueue,
successfully sent to the
const void* pvItemToQueue,
queue.
portBASE_TYPE *pxHigherPriorityTaskWoken);
errQUEUE_FULL will
be returned if data
could not be written to
the queue because the
queue was already full.
portBASE_TYPE xQueueSendToBackFromISR (
xQueueHandle xQueue,
const void* pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken);
xQueueSendFromISR() is equivalent to xQueueSendToBackFromISR()
pvItemToQueue - A pointer to the data that will be copied into the queue.
Maths is not everything
RMR©2012
95
pxHigherPriorityTaskWoken - If calling the API function causes a task to leave the Blocked
state, and the unblocked task has a priority higher than the task that was interrupted, then the
API function will internally set *pxHigherPriorityTaskWoken to pdTRUE.
If xQueueSendToFrontFromISR() or xQueueSendToBackFromISR() sets this value to pdTRUE
then a context switch should be performed before the interrupt is exited. This ensure the
interrupt returns directly to the highest priority Ready state task.
- 125. Queue System Calls: (from ISR) receive from queue
portBASE_TYPE xQueueReceiveFromISR (
xQueueHandle xQueue,
void *pvBuffer,
portBASE_TYPE *pxHigherPriorityTaskWoken);
Maths is not everything
RMR©2012
96
- 126. Queue System Calls: (from ISR) receive from queue
portBASE_TYPE xQueueReceiveFromISR (
xQueueHandle xQueue,
void *pvBuffer,
portBASE_TYPE *pxHigherPriorityTaskWoken);
pvBuffer - A pointer to the memory into which the received data will be copied.
Maths is not everything
RMR©2012
96
pxHigherPriorityTaskWoken - If calling the API function causes a task to leave the Blocked
state, and the unblocked task has a priority higher than the task that was interrupted, then the
API function will internally set *pxHigherPriorityTaskWoken to pdTRUE.
If xQueueSendToFrontFromISR() or xQueueSendToBackFromISR() sets this value to pdTRUE
then a context switch should be performed before the interrupt is exited. This ensure the
interrupt returns directly to the highest priority Ready state task.
- 127. Queue System Calls: (from ISR) receive from queue
pdPASS will only be
returned if data was
successfully read from
the queue.
portBASE_TYPE xQueueReceiveFromISR (
xQueueHandle xQueue,
void *pvBuffer,
portBASE_TYPE *pxHigherPriorityTaskWoken);
pvBuffer - A pointer to the memory into which the received data will be copied.
Maths is not everything
RMR©2012
96
pxHigherPriorityTaskWoken - If calling the API function causes a task to leave the Blocked
state, and the unblocked task has a priority higher than the task that was interrupted, then the
API function will internally set *pxHigherPriorityTaskWoken to pdTRUE.
If xQueueSendToFrontFromISR() or xQueueSendToBackFromISR() sets this value to pdTRUE
then a context switch should be performed before the interrupt is exited. This ensure the
interrupt returns directly to the highest priority Ready state task.
- 128. Queue System Calls: (from ISR) receive from queue
pdPASS will only be
returned if data was
successfully read from
the queue.
errQUEUE_EMPTY
will be returned if data
could not be read from
the queue because the
queue was already
empty.
portBASE_TYPE xQueueReceiveFromISR (
xQueueHandle xQueue,
void *pvBuffer,
portBASE_TYPE *pxHigherPriorityTaskWoken);
pvBuffer - A pointer to the memory into which the received data will be copied.
Maths is not everything
RMR©2012
96
pxHigherPriorityTaskWoken - If calling the API function causes a task to leave the Blocked
state, and the unblocked task has a priority higher than the task that was interrupted, then the
API function will internally set *pxHigherPriorityTaskWoken to pdTRUE.
If xQueueSendToFrontFromISR() or xQueueSendToBackFromISR() sets this value to pdTRUE
then a context switch should be performed before the interrupt is exited. This ensure the
interrupt returns directly to the highest priority Ready state task.
- 129. Interrupt Nesting
FreeRTOS ports allow interrupts to nest. These
ports require one or both of the following constants
to be defined within FreeRTOSConfig.h
configKERNEL_INTERRUPT_PRIORITY - Sets the interrupt
priority used by the tick interrupt (SysTick), normally configured with
the least possible priority.
configMAX_SYSCALL_INTERRUPT_PRIORITY - Sets the
highest interrupt priority from which interrupt safe FreeRTOS API
functions can be called.
when executing a critical section the kernel disables every
interrupt of equal or lower priority than the one defined by
this constant. This means that FreeRTOS doesn’t disable all
the interrupts even inside critical sections.
Maths is not everything
RMR©2012
97
If configMAX_SYSCALL_INTERRUPT_PRIORITY constant is not used
in the port, then any interrupt that uses the interrupt safe FreeRTOS API
functions must also execute at SysTick priority.
- 130. Interrupt Nesting: example
! •!
Interrupts w/ prio. 1 to 3 - prevented from executing while in a critical section, but they
can use the interrupt safe FreeRTOS API functions.
! •!
Interrupts w/ prio. ≥ 4 - not affected by critical sections (kernel cannot prevent these
ISR), within the limitations of the microcontroller itself. Applications requiring very strict
timing accuracy (e.g.motor control) would use a priority above 3 to ensure the scheduler
does not introduce jitter into the interrupts response time.
Maths is not everything
RMR©2012
98
- 131. Interrupt Nesting: example
as long as
they don’t use
FreeRTOS API
! •!
Interrupts w/ prio. 1 to 3 - prevented from executing while in a critical section, but they
can use the interrupt safe FreeRTOS API functions.
! •!
Interrupts w/ prio. ≥ 4 - not affected by critical sections (kernel cannot prevent these
ISR), within the limitations of the microcontroller itself. Applications requiring very strict
timing accuracy (e.g.motor control) would use a priority above 3 to ensure the scheduler
does not introduce jitter into the interrupts response time.
Maths is not everything
RMR©2012
98
- 132. Interrupt Nesting: example
cannot use
FreeRTOS API
as long as
they don’t use
FreeRTOS API
! •!
Interrupts w/ prio. 1 to 3 - prevented from executing while in a critical section, but they
can use the interrupt safe FreeRTOS API functions.
! •!
Interrupts w/ prio. ≥ 4 - not affected by critical sections (kernel cannot prevent these
ISR), within the limitations of the microcontroller itself. Applications requiring very strict
timing accuracy (e.g.motor control) would use a priority above 3 to ensure the scheduler
does not introduce jitter into the interrupts response time.
Maths is not everything
RMR©2012
98
- 133. Interrupt handlers: good practices
configKERNEL_INTERRUPT_PRIORITY must
be set with the lowest possible priority.
All ISR using the safe FreeRTOS API need to be
initialized with a priority level ≤
configMAX_SYSCALL_INTERRUPT_PRIORITY.
ISR extremely critical may have a higher priority
than
configMAX_SYSCALL_INTERRUPT_PRIORITY
but cannot use any FreeRTOS ISR primitive.
Maths is not everything
RMR©2012
99
Note: In some µP, high-priority relates with
high int. numbers, while in others (e.g.
ARM) higher int. numbers means lower
priority.
- 134. Maths is not everything
FreeRTOS
Resource Management
RMR©2012
- 135. Sharing resources in a multitask environment
In a multitasking system there is the
potential risk of a task to be interrupted
while accessing a resource without
completing it.
the task may leave the resource in an inconsistent
state which could result in data corruption if other
task or interrupt tries to access the same resource.
Examples of shared resources:
Global variables
Maths is not everything
Peripherals
Code
RMR©2012
101
- 136. Accessing global variables
Let’s assume the following C code:
GlobalC += 10;
In assembly we got:
1. LOAD R, [#1234]
2. SUM R, 10
3. STORE R, [#1234]
The C statement is not atomic. What are the
consequences? Suppose that:
Task A exec. Inst. 1 ➟ A preempted by B ➟ Task B changes
GlobalC and sleeps afterwards ➟ Task A resumes and exec. 2
and 3, using an old value of GlobalC
Maths is not everything
RMR©2012
102
When a global variable is accessed by more than 1 task, to
assure its consistency, one needs to control the access to
it!
- 137. Accessing peripherals
Consider the following scenario where two
tasks attempt to write to an LCD:
Task A executes and starts to write the string “Hello
world” to the LCD.
Task A is preempted by Task B after outputting just
the beginning of the string – “Hello w”.
Task B writes “Abort, Retry, Fail?” to the LCD before
entering the Blocked state.
Maths is not everything
RMR©2012
103
Task A continues from the point at which it was preempted and completes outputting the remaining
characters – “orld”.
The LCD will now be displaying the corrupted string
“Hello wAbort, Retry, Fail?orld”.
- 138. Function Reentrancy
A function is reentrant
when it’s safe to call the function from more than one task,
or from interrupts.
Each task maintains its own stack and a fresh set of
register values. If a function does not access any data other
than data that is allocated to the stack or held in a register
then the function is reentrant.
Maths is not everything
RMR©2012
104
/* A parameter is passed into the function. This will either be passed on the stack
or in a CPU register. Either way is safe as each task maintains its own stack and its
own set of register values. */
long lAddOneHundered( long lVar1 )
{
/* This function scope variable will also be allocated to the stack or a register,
depending on compiler and optimization level. Each task or interrupt that calls this
function will have its own copyof lVar2. */
long lVar2;
lVar2 = lVar1 + 100;
/* Most likely the return value will be placed in a CPU register, although it too
could be placed on the stack. */
return lVar2;
}
- 139. Function Reentrancy
A function not reentrant
when works with global variables (or static).
Even though each task works with its own stack and a
fresh set of register values, the function does access data
that is shared among all the tasks.
Maths is not everything
/* In this case lVar1 is a global variable so every task that calls the function will
be accessing the same single copy of the variable. */
long lVar1;
long lNonsenseFunction( void )
{
/* This variable is static so is not allocated on the stack. Each task that calls the
function will be accessing the same single copy of the variable. */
static long lState = 0;
long lReturn;
switch( lState )
{
case 0 : lReturn = lVar1 + 10;
lState = 1;
break;
case 1 :
RMR©2012
105
}
}
lReturn = lVar1 + 20;
lState = 0;
break;
- 140. Mutual Exclusion
Accessing resources shared between tasks
or between tasks and interrupts needs to be
managed using a ‘mutual exclusion’ technique.
to ensure that once a task starts accessing a shared
resource the same task has exclusive access until the
resource has been returned to a consistent state.
FreeRTOS provides several features for
implementing mutual exclusion
Maths is not everything
RMR©2012
106
but the best is to assure (if possible) that resources are not
shared and each resource is only accessed from a single
task.
- 141. Mutual Exclusion by implementing Critical Sections
Basic critical sections are regions of code that
are surrounded by calls to the macros:
taskENTER_CRITICAL()
taskEXIT_CRITICAL()
/* Ensure access to the PORTA register cannot be interrupted -> critical section */
taskENTER_CRITICAL();
/* A switch to another task cannot occur between the call to taskENTER_CRITICAL() and
to taskEXIT_CRITICAL(). Interrupts may still execute on FreeRTOS ports that allow
interrupt nesting, but only interrupts whose priority is above the value assigned to
the configMAX_SYSCALL_INTERRUPT_PRIORITY constant */
PORTA |= 0x01;
/* We have finished accessing PORTA so can safely leave the critical section */
taskEXIT_CRITICAL();
Maths is not everything
It’s a “brute force” method as:
disables all the int. up to configMAX_SYSCALL_INTERRUPT_PRIORITY
RMR©2012
107
preemption results from an interrupt, so taskENTER_CRITICAL()
assures that the task stays in Running until taskEXIT_CRITICAL()
- 142. Mutual Exclusion by implementing Critical Sections: example
void vPrintString( const portCHAR *pcString )
{
/* Write the string to stdout, using a critical section as a crude method
of mutual exclusion. */
taskENTER_CRITICAL();
{
printf( "%s", pcString );
fflush( stdout );
}
taskEXIT_CRITICAL();
}
Maths is not everything
RMR©2012
108
Critical sections must be kept very short
otherwise they will adversely affect
interrupt response times.
- 143. implementing Critical Sections by pausing the scheduler
Critical sections implemented by suspending
the scheduler only protects a region of code
from access by other tasks because interrupts
remain enabled
A critical section that is too long to be implemented by
disabling interrupts can instead be implemented by suspending
the scheduler
‘un-suspending’ the scheduler may be a relatively lengthy
operation.
Maths is not everything
RMR©2012
109
void vPrintString( const portCHAR *pcString )
{
/* Write the string to stdout, suspending the scheduler as a method of mutual
exclusion. */
vTaskSuspendScheduler();
{
printf( "%s", pcString );
fflush( stdout );
}
xTaskResumeScheduler();
}
- 144. Mutual Exclusion through the use of Mutexes
Mutex is a special type of binary semaphore used to
control accesses to a shared resource.
A mutex can be conceptually thought of as a token that
is associated with the resource being shared.
For a task to legitimately access the resource it must
first successfully ‘take’ the token (be the token holder).
When the token holder has finished with the resource
it must ‘give’ the token back (different from BinSem.)
Maths is not everything
RMR©2012
110
Only when the token has been returned can another
task successfully take the token and then safely access
the same shared resource.
A task is not permitted to access the shared resource
unless it holds the token.
- 152. Mutual Exclusion through the use of Mutexes: examples
Maths is not everything
RMR©2012
111
The mechanism works purely through the discipline of the application writer. There
is no reason why a task cannot access the resource at any time, but each task
“agrees” not to unless they are first able to become the mutex holder.
- 153. Mutex Semaphores
MUTEX Creation & Manipulation
xSemaphoreHandle vSemaphoreCreateMutex(void);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
Maths is not everything
RMR©2012
112
- 154. Mutex Semaphores
MUTEX Creation & Manipulation
xSemaphoreHandle vSemaphoreCreateMutex(void);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
When to use
Maths is not everything
RMR©2012
112
When sharing a resource between a task and an ISR, the only option is to use a
critical section, disabling interrupts.
When sharing a resource between tasks the standard mechanism should be the
MUTEX.
- 155. Mutex Semaphores
MUTEX Creation & Manipulation
xSemaphoreHandle vSemaphoreCreateMutex(void);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
non-NULL will only be
returned if the MUTEX
was created successfully.
When to use
Maths is not everything
RMR©2012
112
When sharing a resource between a task and an ISR, the only option is to use a
critical section, disabling interrupts.
When sharing a resource between tasks the standard mechanism should be the
MUTEX.
- 156. Mutex Semaphores
MUTEX Creation & Manipulation
xSemaphoreHandle vSemaphoreCreateMutex(void);
portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);
portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
non-NULL will only be
returned if the MUTEX
was created successfully.
NULL will be returned if the
MUTEX could not be created.
When to use
Maths is not everything
RMR©2012
112
When sharing a resource between a task and an ISR, the only option is to use a
critical section, disabling interrupts.
When sharing a resource between tasks the standard mechanism should be the
MUTEX.
- 157. Using a Mutex
static void prvNewPrintString( const portCHAR *pcString )
{
/* The mutex already exists by the time this task first executes.
Attempt to take the mutex, blocking indefinitely to wait for the mutex
if it is not available straight away. */
/* The call to xSemaphoreTake() will only return when the mutex has
been successfully obtained so there is no need to check the function
return value. If any other delay period was used then the code must
check that xSemaphoreTake() returns pdTRUE before accessing the shared
resource (which in this case is standard out). */
xSemaphoreTake( xMutex, portMAX_DELAY );
{
/* The following line will only execute once the mutex has been
successfully obtained. Standard out can be accessed freely now as
only one task can have the mutex at any one time. */
printf( "%s", pcString );
fflush( stdout );
}
/* The mutex MUST be given back! */
xSemaphoreGive( xMutex );
Maths is not everything
RMR©2012
113
}
- 158. Using a Mutex
static void prvPrintTask( void *pvParameters )
{
char *pcStringToPrint;
/* Two instances of this task are created so the string the task will
send to prvNewPrintString() is passed into the task using the task
parameter */
pcStringToPrint = ( char * ) pvParameters;
for( ;; ) {
/* Print out the string using the newly defined function. */
prvNewPrintString( pcStringToPrint );
/* Wait a pseudo random time. Note that rand() is not necessarily
reentrant, but in this case it does not really matter as the code
does not care what value is returned. In a more secure application
a version of rand() that is known to be reentrant should be used,
or calls to rand() should be protected using a critical section.
*/
vTaskDelay( ( rand() & 0x1FF ) );
Maths is not everything
}
}
RMR©2012
114
- 159. Using a Mutex
Maths is not everything
RMR©2012
115
int main( void )
{
/* Before a semaphore is used it must be explicitly created */
xMutex = xSemaphoreCreateMutex();
/* Tasks will use a pseudo random delay -> seed the random number generator
*/
srand( 567 );
/* Check if the semaphore was created successfully before creating the
tasks*/
if( xMutex != NULL )
{
/* Create two instances of the tasks that write to stdout. The string they
write is passed in as the task parameter. Tasks are created at different
priorities so some preemption will occur */
xTaskCreate( prvPrintTask, "Print1", 1000,
"Task 1 ******************************************rn", 1, NULL );
xTaskCreate( prvPrintTask, "Print2", 1000,
"Task 2 ------------------------------------------rn", 2, NULL );
/* Start the scheduler to execute the created tasks */
vTaskStartScheduler();
}
/* If all is well then main() will never reach here as the scheduler will
now be
running the tasks. */
for( ;; );
}
- 160. Using a Mutex
Maths is not everything
RMR©2012
116
The possible sequence of execution depicted shows the
higher priority Task 2 having to wait for the lower priority
Task 1 to give up control of the mutex
- 161. Mutex Problems: Priority Inversion
A higher prio. task being delayed by a lower prio.task due to a
mutual exclusion issue is called ‘priority inversion’.
If a medium priority task started to execute while the high
priority task was waiting for the semaphore the result could
even be worse:
a high priority task waiting for a low priority task without the low
priority task even being able to execute
Maths is not everything
RMR©2012
117
- 162. Mutex Problems: Priority Inversion
A higher prio. task being delayed by a lower prio.task due to a
mutual exclusion issue is called ‘priority inversion’.
If a medium priority task started to execute while the high
priority task was waiting for the semaphore the result could
even be worse:
a high priority task waiting for a low priority task without the low
priority task even being able to execute
An interesting REAL example of priority inversion
Maths is not everything
RMR©2012
117
http://www.motherboardpoint.com/really-happened-mars-priority-inversion-and-marspathfinder-t169706.html
- 163. Priority Inheritance
it temporarily raises the mutex holder priority to that of the highest
priority task attempting to obtain the same mutex. ➟ LP task holding
the mutex ‘inherits’ the priority of the HP waiting task.
binary semaphores don’t have this feature
Maths is not everything
RMR©2012
118
assumes a task will only hold a single mutex at any one time.
FreeRTOS mutexes automatically provide a basic
‘priority inheritance’ mechanism.
- 164. Mutex Problems: deadlock
when two tasks cannot proceed because they are
both waiting for a resource that is held by the
other. Example:
Task A executes and successfully takes mutex X.
Task A is pre-empted by Task B.
Task B successfully takes mutex Y before attempting to also take
mutex X – but mutex X is held by Task A so is not available to
Task B. Task B opts to enter the Blocked state to wait for mutex
X to be released.
Task A continues executing. It attempts to take mutex Y – but
mutex Y is held by Task B so is not available to Task A. Task A opts
to enter the Blocked state to wait for mutex Y to be released.
Maths is not everything
RMR©2012
119
Task A is waiting for a mutex held by Task B, and Task B is waiting
for a mutex held by Task A.
Deadlocks are design errors of an application.
- 165. Gatekeepers
Gatekeeper tasks provide a clean method
of mutual exclusion without the worry of
priority inversion or deadlock.
A gatekeeper is a task that has sole ownership of a
resource.
Any other task needing to access the resource can
only do so indirectly by using the services of the
gatekeeper
Maths is not everything
RMR©2012
120
May ease the access to a resource from an interrupt
handler
- 167. The Gatekeeper itself
Maths is not everything
RMR©2012
122
static void prvStdioGatekeeperTask( void *pvParameters )
{
char *pcMessageToPrint;
/* This is the only task that is allowed to write to the terminal output.
Any other task wanting to write a string to the output does not access the
terminal directly, but instead sends the string to this task. As only this
task accesses standard out there are no mutual exclusion or serialization
issues to consider within the implementation of the task itself. */
for( ;; ) {
/* Wait for a message to arrive. An indefinite block time is specified so
there is no need to check the return value – the function will only return
when a message has been successfully received. */
xQueueReceive( xPrintQueue, &pcMessageToPrint, portMAX_DELAY );
/* Output the received string. */
printf( "%s", pcMessageToPrint );
fflush( stdout );
/* Now simply go back to wait for the next message. */
}
}
- 168. The tasks that generate messages for the gatekeeper
static void prvPrintTask( void *pvParameters )
{
int iIndexToString;
/* Two instances of this task are created. The task parameter is used to
pass an index into an array of strings into the task */
iIndexToString = ( int ) pvParameters;
for( ;; ) {
/* Print out the string, not directly but instead by passing a pointer
to the string to the gatekeeper task via a queue. The queue is created
before the scheduler is started. A block time is not specified because
there should always be space in the queue. */
xQueueSendToBack( xPrintQueue, &( pcStringsToPrint[ iIndexToString ] ),
0 );
/* Wait a pseudo random time. Note that rand() is not necessarily
reentrant, but in this case it does not really matter as the code does
not care what value is returned. In a more secure application a version
of rand() that is known to be reentrant should be used - or calls to
rand() should be protected using a critical section. */
vTaskDelay( ( rand() & 0x1FF ) );
Maths is not everything
RMR©2012
123
}
}
- 169. Interrupt Hook using also the gatekeeper
void vApplicationTickHook( void )
{
static int iCount = 0;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
/* Print out a message every 200 ticks. The message is not written out
directly, but sent to the gatekeeper task. */
iCount++;
if( iCount >= 200 )
{
/* In this case the last parameter (xHigherPriorityTaskWoken) is not
actually used but must still be supplied. */
xQueueSendToFrontFromISR( xPrintQueue, &( pcStringsToPrint[ 2 ] ),
&xHigherPriorityTaskWoken );
/* Reset the count ready to print out the string again in 200 ticks
time. */
iCount = 0;
}
Maths is not everything
RMR©2012
124
}
void vApplicationTickHook( void );
!
Tick hook functions execute within the context of the tick interrupt so must
be kept very short, use only a moderate amount of stack space, and not call
any FreeRTOS API functions whose name does not end with ‘FromISR()’.
- 170. creating everything in the Main
Maths is not everything
RMR©2012
125
static char *pcStringsToPrint[] = {
"Task 1 ****************************************************rn",
"Task 2 ----------------------------------------------------rn",
"Message printed from the tick hook interrupt ##############rn"
};
/*-----------------------------------------------------------*/
/* Declare a variable of type xQueueHandle. This is used to send messages from the
print tasks and the tick interrupt to the gatekeeper task. */
xQueueHandle xPrintQueue;
/*-----------------------------------------------------------*/
int main( void )
{
/* Before a queue is used it must be explicitly created. The queue is created to
hold a maximum of 5 character pointers. */
xPrintQueue = xQueueCreate( 5, sizeof( char * ) );
/* The tasks are going to use a pseudo random delay, seed the random number
generator. */
srand( 567 );
/* Check the queue was created successfully. */
if( xPrintQueue != NULL ){
/* Create two instances of the tasks that send messages to the gatekeeper.The
index to the string the task uses is passed to the task via the task
parameter. The tasks are created at different priorities so the higher
priority task will occasionally preempt the lower priority task. */
xTaskCreate( prvPrintTask, "Print1", 1000, ( void * ) 0, 1, NULL );
xTaskCreate( prvPrintTask, "Print2", 1000, ( void * ) 1, 2, NULL )
/* Create the gatekeeper task. This is the only task that is permitted to
directly access standard out. */
xTaskCreate( prvStdioGatekeeperTask, "Gatekeeper", 1000, NULL, 0, NULL );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
}
- 171. Maths is not everything
FreeRTOS
Memory Management
RMR©2012
- 172. Managing memory dynamically
Every time a task, queue or semaphore is
created➟kernel has to allocate RAM.
The standard malloc() and free() library
functions can be used but can also suffer from
one or more of the following problems:
They are not always available on small embedded systems.
Their implementation can be relatively large so take up
valuable code space.
They are not deterministic. The amount of time taken to
execute the functions will differ from call to call.
Maths is not everything
RMR©2012
127
They can suffer from memory fragmentation.
- 173. Managing memory dynamically
Different embedded systems have varying RAM
allocation and timing requirements
FreeRTOS treats memory allocation as part of
the portable layer (as opposed to part of the
core code base).
This enables individual applications to provide their own specific
implementation when appropriate.
When the kernel requires RAM, instead of calling malloc()
directly it instead calls pvPortMalloc().
When RAM is being freed, instead of calling free() directly the
kernel instead calls vPortFree().
Maths is not everything
RMR©2012
128
These primitives have the same prototypes as the original
ones.
It’s up to the developer to provide an implementation for the
pvPortMalloc() e vPortFree() allocation memory functions.
- 174. Managing memory dynamically
FreeRTOS provides 4 different
implementations that are available in
"FreeRTOS/Source/portable/MemMang"
heap_1.c: just allocates memory.
heap_2.c: allocates and frees memory but does not
handle any fragmentation.
heap_3.c: uses the standard C lib malloc() and free()
implementation.
Maths is not everything
RMR©2012
129
heap_4.c: only available from FreeRTOS 7.2.0 allocates and frees memory, handles fragmentation and
is more efficient than the majority of C lib
implementations.
- 175. HEAP_1
Heap_1.c implements a very basic version of pvPortMalloc() and
does not implement vPortFree().
The allocation scheme simply subdivides a simple array into
smaller blocks as calls to pvPortMalloc() are made. The array is
the FreeRTOS heap.
The total size (in bytes) of the array is set by the definition
configTOTAL_HEAP_SIZE within FreeRTOSConfig.h.
Defining a large array in this manner can make the application appear to consume
a lot of RAM – even before any of the array has actually been assigned
Heap_1 is always deterministic.
Maths is not everything
RMR©2012
130
Useful in: any application that never
deletes a task, queue or semaphore.
- 176. HEAP_2
It uses a best fit algorithm to allocate memory and unlike heap_1
it does allow memory to be freed.
Heap_2.c also uses a simple array dimensioned by
configTOTAL_HEAP_SIZE..
Again the array is statically declared so it will make the application
appear to consume a lot of RAM.
The best fit algorithm ensures pvPortMalloc() uses the free
block of memory that is closest in size to the number of bytes
requested.
Heap_2 can suffer from fragmentation
Heap_2 is not deterministic.
Maths is not everything
RMR©2012
131
Useful in: applications that repeatedly
create and delete tasks provided the
size of the stack allocated to the
created tasks does not change.
size_t xPortGetFreeHeapSize(void);
- 177. HEAP_3
Heap_3.c simply uses the standard library malloc() and free()
function but in a thread safe way.
configTOTAL_HEAP_SIZE does not affect the size of the heap
and is instead defined by the linker configuration.
There is no statically allocated buffer in compilation time
Maths is not everything
RMR©2012
132
void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn;
vTaskSuspendAll();
{
pvReturn = malloc( xWantedSize );
}
xTaskResumeAll();
return pvReturn;
}
void vPortFree( void *pv )
{
if( pv != NULL )
Useful in: applications that repeatedly
{
vTaskSuspendAll();
allocate and free memory buffers of
{
different size.
free( pv );
}
xTaskResumeAll();
}
}
- 178. The Stack
Memory region used for local variables,
registers and parameters.
Each task has its own stack
If its size is under-dimensioned a stack overflow will
occur.
Some techniques do exist for monitoring the use of
the stack and the existence of overflow situations
FreeRTOS provides several features to assist trapping
and debugging stack related issues
Maths is not everything
RMR©2012
133
- 179. Stack Overflow - WaterMark
uxTaskGetStackHighWaterMark() is used to query how
near a task has come to overflowing the stack space
allocated to it. This value is called the stack 'high water
mark'.
unsigned portBASE_TYPE
uxTaskGetStackHighWaterMark(xTaskHandle xTask);
Maths is not everything
RMR©2012
134
- 180. Stack Overflow - WaterMark
uxTaskGetStackHighWaterMark() is used to query how
near a task has come to overflowing the stack space
allocated to it. This value is called the stack 'high water
mark'.
unsigned portBASE_TYPE
uxTaskGetStackHighWaterMark(xTaskHandle xTask);
xTask - The handle of the task whose stack high water mark is being queried. A task can
query its own stack high water mark by passing NULL in place of a valid task handle
Maths is not everything
RMR©2012
134
Returned value - The amount of stack the task is actually using will grow and shrink as the
task executes and interrupts are processed. uxTaskGetStackHighWaterMark() returns the
minimum amount of remaining stack space that was available since the task started executing.
This is the amount of stack that remained unused when the stack usage was at its greatest
(deepest) value. The closer the high water mark is to 0 the closer the task has come to
overflowing its stack.
- 181. Stack Overflow : Run Time Checking
FreeRTOS includes two optional run time stack
checking mechanisms.
controlled by the configCHECK_FOR_ST ACK_OVERFLOW
compile time configuration constant within
FreeRTOSConfig.h.
Both methods will increase the time it takes to
perform a context switch.
In any of these methods the kernel will monitor the
task stacks and execute a stack overflow hook (or
callback) function upon detecting an overflow.
Maths is not everything
RMR©2012
135
void vApplicationStackOverflowHook(
xTaskHandle *pxTask, signed char *pcTaskName);
Use this function to identify and correct stack problems during development.
The goal is simplify the debug and not to recover from a stack that has
overflown.
- 182. Stack Overflow : Run Time Checking
Run Time Stack Checking - Method 1
configCHECK_FOR_STACK_OVERFLOW= 1.
Kernel will check whether the stack pointer remains within the
valid stack space after the context has been saved.
The stack overflow hook is called if the stack pointer is found to be
outside of its valid range.
This method is quick to execute but can miss stack overflows that occur
between context saves.
Run Time Stack Checking - Method 2
configCHECK_FOR_STACK_OVERFLOW= 2.
Maths is not everything
RMR©2012
136
When a task is created its stack is filled with a known pattern.
This method walks the last valid 20 bytes of the task stack space
to check that this pattern has not been overwritten.
The stack overflow hook function is called if any of the 20 bytes have
changed from their expected value.
not as quick to execute as 1 but very likely to catch all stack overflows
- 183. Maths is not everything
FreeRTOS
Additional Features
RMR©2012