S emb t13-freertos
-
Upload
joao-moreira -
Category
Technology
-
view
825 -
download
3
Transcript of S emb t13-freertos
RMR©2012
Maths is not everything
Embedded Systems7 - Real-Time Operating System (FreeRTOS)
IntroductionTask ManagementQueue Management
Interrupts and SynchronizationResource ManagementMemory ManagementAditional Features
RMR©2012
Maths is not everything
FreeRTOS
Introduction
RMR©2012
Maths is not everything
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 real-time OSSimple but yet very powerful
3
RMR©2012
Maths is not everything
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 ManualAPI functions and configuration options
Online documentationwww.freertos.org
4
RMR©2012
Maths is not everything
FreeRTOS History
FreeRTOSV4.0.0
FreeRTOSV3.0.0
FreeRTOSV2.0.0
FreeRTOSV1.0.1
FreeRTOSV5.0.0
FreeRTOS V1.0.1+
Scalability +
New Task APIs
API Changes+
Directory Names Changed
+Changes in Kernel
FreeRTOS V3.0.0+
Co-routinesAPI Changes
FreeRTOSV6.0.0
2009Backward Comp.
MPU Support
5
RMR©2012
Maths is not everything
FreeRTOS Features
ArchitectureSource codePortableScalablePreemptive and co-operative schedulingMultitaskingServicesInterrupt managementAdvanced features
6
Application
Task 1 Task 2 Task 3 Task 4
RMR©2012
Maths is not everything
Architecture
7
include
Portable
main kernel source directory
CommonDir xRMR©2012
Maths is not everything
Source Code Organization
8
FreeRTOS
Source
kernel header files directory
Compiler xCompiler yMemMang
Demo
kernel port
compiler x portcompiler x portmalloc/free implementation
Dir y
demo app common directorydemo app of port xdemo app of port y
RMR©2012
Maths is not everything
Source Code Organization
9 port.c and portmacro.h files within the HAL
RMR©2012
Maths is not everything
Portability
Highly portable C
24 architectures supported
Assembly is kept minimum.
Ports are freely available in source code.
Other contributions do exist.
10
RMR©2012
Maths is not everything
Scalable
Use the service you only need:
FreeRTOSConfig.hVery few services / Complete services availableA group of #defines determines scalability.
Minimum footprint = 4 KB
11
RMR©2012
Maths is not everything
FreeRTOSConfig.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...
12
RMR©2012
Maths is not everything
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
A hybrid of both scheduling can be used in your application.
Application = Tasks
Or Application = Tasks + Co-routines
Tasks have higher priority than Co-routines13
RMR©2012
Maths is not everything
Tasks vs Co-routines
Tasksno restrictions
totally prioritized
each task maintains its own stack
re-entrance has to be tackled carefully when used with preemption
14
Co-routinesAPI calls have restrictions
totally prioritized among co-routines but interruptible by tasks when using hybrid mode
stack shared among them
re-entrance managed by cooperation (less problematic)
cooperation only between co-routines
RMR©2012
Maths is not everything
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
15
RMR©2012
Maths is not everything
Services
Queues
SemaphoresBinary and counting
Mutexes With priority inheritance
Support recursion
16
RMR©2012
Maths is not everything
Interrupts
An interrupt can suspend a task execution.
Interrupt mechanism is port dependent.Nesting
Scheduling after interrupts
Preemptive or cooperative scheduler
17
RMR©2012
Maths is not everything
Advanced Features
Execution tracing
Run time statistics collection
Memory management
Memory protection support
Stack overflow protection18
RMR©2012
Maths is not everything
Conventions: variables’ names
19
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”
RMR©2012
Maths is not everything
Conventions: functions’ names
20
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
RMR©2012
Maths is not everything
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
FreeRTOS can’t be used in any comparisons without the authors’ permission.
21
RMR©2012
Maths is not everything
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)
Compliant with:
FDA510(k) Class III medical device standards
EN6230422
RMR©2012
Maths is not everything
FreeRTOS
Kernel Structure - Tasks
RMR©2012
Maths is not everything
Task States
Runningtask is executing owning the CPU
Readytask may run but is waiting for the CPU to be available,
Blockedtask is delayed (timing / event) or is waiting for another task (synchronization)
Suspendedtask may enter this state only through specific calls
there is no associated timeout
not considered in scheduling
24
RMR©2012
Maths is not everything
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.
25
Task prototypevoid ATaskFunction(void *pvParameters);
RMR©2012
Maths is not everything
Task skeleton
26
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 );}
RMR©2012
Maths is not everything
Typical Application
27
RMR©2012
Maths is not everything
Task creation
28
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.
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.
RMR©2012
Maths is not everything
Task creation
29
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.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
Maths is not everything
Example application that uses RTOS
30
v
v
v
v
RMR©2012
Maths is not everything
Example application: execution sequence (ARM)
31
RMR©2012
Maths is not everything
Task Control Block
32
0
1
2
3
n-1
pxReadyTaskList
currentTask
NULL
NULL
NULL
NULL
NULL
0
1
2
3
n-1
pxReadyTaskList
currentTask
NULL
Task1
NULL
NULL
NULL
RMR©2012
Maths is not everything
Example application: Data-structures maintained by RTOS
Initially
33
After TaskCreate(1)
0
1
2
3
n-1
pxReadyTaskList
currentTask
NULL
Task1
Task2
NULL
NULL
0
1
2
3
n-1
Task2
pxReadyTaskList
currentTask
idle
Task1
NULL
NULL
NULL
RMR©2012
Maths is not everything
Example application: Data-structures maintained by RTOS
After TaskCreate(2)
34
After TaskStartSchedule ()
RMR©2012
Maths is not everything
Ready and Blocked Lists
35
RMR©2012
Maths is not everything
Hypothetical DelayedTaskList
36
RMR©2012
Maths is not everything
Tasks within tasks
37
void vTask1( void *pvParameters ){const char *pcTaskName = "Task 1 is running\r\n";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. */
}
}
}
RMR©2012
Maths is not everything
Using the task parameter
38
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.*/
}}
}
/* 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 running\r\n”;static const char *pcTextForTask2 = “Task 2 is running\t\n”;
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( ;; );}
RMR©2012
Maths is not everything
Block State: the delay primitive
39
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
C o n s t a n t s c o n f i g T I C K _ R A T E _ M S a n d configTICK_RATE_HZ usable to get desired time delay
RMR©2012
Maths is not everything
Blocking: delaying a task for a period of time
40
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 );
}}
RMR©2012
Maths is not everything
Block State: the exact delay primitive
41
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
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
Maths is not everything
Blocking: delaying a task for the same EXACT period of time
42
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 ) ); }}
RMR©2012
Maths is not everything
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
Tasks may have the same priority. In this case the scheduler will execute them in a round-robin fashion.
43
RMR©2012
Maths is not everything
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.
44
RMR©2012
Maths is not everything
Scheduler and Priorities
There is no automatic management of prioritiesa 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.
45
RMR©2012
Maths is not everything
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
46
RMR©2012
Maths is not everything
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)
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.
47
RMR©2012
Maths is not everything
Idle Task Hook
Common uses for the Idle task hook include: Executing low priority, background or continuous processing.
Measuring the amount of spare processing capacitymeasuring 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
48
/* Declare a variable that will be incremented by the hook function. */unsigned long ulIdleCycleCount = 0UL;
/* Idle hook functions MUST be called vApplicationIdleHook(), take no parameters, and return void. */void vApplicationIdleHook( void ){
/* This hook function does nothing but increment a counter. */ ulIdleCycleCount++;}
RMR©2012
Maths is not everything
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
provide the implementation of the hook function using:
49
void vApplicationTickHook( void );
RMR©2012
Maths is not everything
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
50
RMR©2012
Maths is not everything
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.
The scheduler will always select the highest priority Ready state task to enter the Running state.
51
RMR©2012
Maths is not everything
Task Management API
52
RMR©2012
Maths is not everything
FreeRTOS
Queues
RMR©2012
Maths is not everything
FreeRTOS Queues
Queue is a communication mechanism between tasks or between tasks and interrupt handlers
Tasks: xQueueSend, xQueueReceive, uxQueueMessagesWaitingISRs: xQueueSendFromISR, xQueueReceiveFromISR
Tasks can wait for items to enter/exit the queue while allowing other tasks to execute
54
RMR©2012
Maths is not everything
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.
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 pointers55
RMR©2012
Maths is not everything
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.
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.
56
RMR©2012
Maths is not everything
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.
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.
57
RMR©2012
Maths is not everything
Queue read/write: example
58
RMR©2012
Maths is not everything
Queue read/write: example
58
RMR©2012
Maths is not everything
Queue read/write: example
58
RMR©2012
Maths is not everything
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
59
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.
xQueueHandle MyQueue;int main( void ){ ... MyQueue = xQueueCreate( 20, sizeof( unsigned char ) ); ...}
RMR©2012
Maths is not everything
Queue System Calls: send to queue
60
portBASE_TYPE xQueueSendToFront (xQueueHandle xQueue,const void* pvItemToQueue,portTickType xTicksToWait);
portBASE_TYPE xQueueSendToBack ( xQueueHandle xQueue,const void* pvItemToQueue,portTickType xTicksToWait);
RMR©2012
Maths is not everything
Queue System Calls: send to queue
60
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()
RMR©2012
Maths is not everything
Queue System Calls: send to queue
60
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.
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.
RMR©2012
Maths is not everything
Queue System Calls: send to queue
60
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.
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.
pdPASS will only be returned if data was successfully sent to the queue.
RMR©2012
Maths is not everything
Queue System Calls: send to queue
60
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.
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.
pdPASS will only be returned if data was successfully sent to the queue.
errQUEUE_FULL will be returned if data could not be written to the queue because the queue was already full.
RMR©2012
Maths is not everything
Queue System Calls: receive from queue
61
portBASE_TYPE xQueueReceive (xQueueHandle xQueue,void *pvBuffer,portTickType xTicksToWait);
portBASE_TYPE xQueuePeek ( xQueueHandle xQueue,void *pvBuffer,portTickType xTicksToWait);
RMR©2012
Maths is not everything
Queue System Calls: receive from queue
61
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
RMR©2012
Maths is not everything
Queue System Calls: receive from queue
61
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 queuepvBuffer - A pointer to the memory into which the received data will be copied.
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.
RMR©2012
Maths is not everything
Queue System Calls: receive from queue
61
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 queuepvBuffer - A pointer to the memory into which the received data will be copied.
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.
pdPASS will only be returned if data was successfully read from the queue.
RMR©2012
Maths is not everything
Queue System Calls: receive from queue
61
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 queuepvBuffer - A pointer to the memory into which the received data will be copied.
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.
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.
RMR©2012
Maths is not everything
Queue System Calls: auxiliary
uxQueueMessagesWaiting() is used to query the number of items that are currently in a queue
62
unsigned portBASE_TYPE uxQueueMessagesWaiting ( xQueueHandle xQueue);
Never call uxQueueMessagesWaiting() from an interrupt service routine. The interrupt safe uxQueueMessagesWaitingFromISR() should be used in its place
RMR©2012
Maths is not everything
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().
63
RMR©2012
Maths is not everything
Queue System Calls: examples
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 willnow be running the tasks. Otherwise, it is likely that there was insufficient heap memory available for the idle task to be created*/for( ;; );
}
RMR©2012
Maths is not everything
Queue System Calls: examples
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.\r\n" );
}/* 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(); }}
RMR©2012
Maths is not everything
Queue System Calls: examples
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!\r\n" );}/* 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.\r\n" ); } }}
RMR©2012
Maths is not everything
Queue System Calls: examples
67
RMR©2012
Maths is not everything
Queues: transferring compound types
68
RMR©2012
Maths is not everything
Queues: transferring compound types
69
/* 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 */
};
RMR©2012
Maths is not everything
Queues: transferring compound types
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 willnow be running the tasks. Otherwise, it is likely that there was insufficient heap memory available for the idle task to be created*/for( ;; );
}
RMR©2012
Maths is not everything
Queues: transferring compound types
71
static void vSenderTask( void *pvParameters ){portBASE_TYPE xStatus;const portTickType xTicksToWait = 100 / portTICK_RATE_MS;
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.\r\n" );
}/* 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(); }}
RMR©2012
Maths is not everything
Queues: transferring compound types
72
static void vReceiverTask( void *pvParameters ){xData xReceivedStructure;portBASE_TYPE xStatus;const portTickType xTicksToWait = 100 / portTICK_RATE_MS;
for( ;; ) {
/* always expects the queue to have 3 items */if( uxQueueMessagesWaiting( xQueue ) != 3 ) { vPrintString( "Queue should have been full!\r\n" );}/* 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.\r\n" ); } }}
RMR©2012
Maths is not everything
Queues: transferring compound types
73
RMR©2012
Maths is not everything
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
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.
74
RMR©2012
Maths is not everything
Queue Management API
75
RMR©2012
Maths is not everything
FreeRTOS
Interrupts and Synchronization
RMR©2012
Maths is not everything
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?
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? 77
RMR©2012
Maths is not everything
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
a context switch to the “handler” task should occur
when it has a higher priority than the interrupted task which is preempted
78
RMR©2012
Maths is not everything
Interrupt Handling
79
RMR©2012
Maths is not everything
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
80
RMR©2012
Maths is not everything
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
80
RMR©2012
Maths is not everything
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
80
RMR©2012
Maths is not everything
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
80
RMR©2012
Maths is not everything
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
80
RMR©2012
Maths is not everything
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
80
RMR©2012
Maths is not everything
Synchronization mechanisms
F r e e R T O S h a s t h e f o l l o w i n g synchronization mechanisms:
Binary Semaphores
Counting Semaphores
Queues
These mechanisms can be used in the communication/synchronization between tasks and between interrupt handlers and tasks.
81
RMR©2012
Maths is not everything
Binary Semaphores
Binary Semaphore Creation & Manipulation
82
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);
RMR©2012
Maths is not everything
Binary Semaphores
Binary Semaphore Creation & Manipulation
82
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.
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.
RMR©2012
Maths is not everything
Binary Semaphores
Binary Semaphore Creation & Manipulation
82
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.
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.
pdPASS will only be returned if the call was successful.
RMR©2012
Maths is not everything
Binary Semaphores
Binary Semaphore Creation & Manipulation
82
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.
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.
pdPASS will only be returned if the call was successful.
pdFALSE will be returned if the semaphore was not available.
RMR©2012
Maths is not everything
Forcing a context switch in the IH
To force the context switching from an ISR one should call:
The flag parameter should use the value returned in the pxHigherPriorityTaskWoken variable used by the primitives “FromISR” called in the ISR.
83
void portEND_SWITCHING_ISR(portBASE_TYPE flag); // ARM port
void portSWITCH_CONTEXT(); // DOS port
RMR©2012
Maths is not everything
Communication between an IH and the Handler Task: example
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( ;; ); }
RMR©2012
Maths is not everything
Communication between an IH and the Handler Task: example
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.\r\n\r\n\r\n" );}
}
RMR©2012
Maths is not everything
Communication between an IH and the Handler Task: example
86
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.\r\n" );
}}
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); }}
RMR©2012
Maths is not everything
Communication between an IH and the Handler Task: example
87
RMR©2012
Maths is not everything
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:
Handling events.
Managing the access to resources
88
RMR©2012
Maths is not everything
Handling events with Counting Semaphores
89
RMR©2012
Maths is not everything
Handling events with Counting Semaphores
89
RMR©2012
Maths is not everything
Handling events with Counting Semaphores
89
RMR©2012
Maths is not everything
Handling events with Counting Semaphores
89
RMR©2012
Maths is not everything
Handling events with Counting Semaphores
89
RMR©2012
Maths is not everything
Handling events with Counting Semaphores
89
RMR©2012
Maths is not everything
Handling events with Counting Semaphores
89
RMR©2012
Maths is not everything
Counting Semaphores: creation and manipulation
90
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);
RMR©2012
Maths is not everything
Counting Semaphores: creation and manipulation
90
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.
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.
RMR©2012
Maths is not everything
Counting Semaphores: example of use in an IH
91
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( ;; ); }
RMR©2012
Maths is not everything
Counting Semaphores: example of use in an IH
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); }}
RMR©2012
Maths is not everything
Semaphore Management API
93
RMR©2012
Maths is not everything
Synchronizing IH and Tasks with Queues
Semaphores are used to communicate events.Queues are used to both communicate events and transfer data.
xQueueSendToFrontFromISR(), xQueueSendToBackFromISR() and xQueueReceiveFromISR() are versions of xQueueSendToFront(), xQueueSendToBack() and xQueueReceive() respectively that are safe to use within an interrupt service routine.
94
RMR©2012
Maths is not everything
Queue System Calls (from ISR): send to queue
95
portBASE_TYPE xQueueSendToFrontFromISR (xQueueHandle xQueue,const void* pvItemToQueue,portBASE_TYPE *pxHigherPriorityTaskWoken);
portBASE_TYPE xQueueSendToBackFromISR ( xQueueHandle xQueue,const void* pvItemToQueue,portBASE_TYPE *pxHigherPriorityTaskWoken);
RMR©2012
Maths is not everything
Queue System Calls (from ISR): send to queue
95
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()
RMR©2012
Maths is not everything
Queue System Calls (from ISR): send to queue
95
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.
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.
RMR©2012
Maths is not everything
Queue System Calls (from ISR): send to queue
95
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.
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.
pdPASS will only be returned if data was successfully sent to the queue.
RMR©2012
Maths is not everything
Queue System Calls (from ISR): send to queue
95
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.
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.
pdPASS will only be returned if data was successfully sent to the queue.
errQUEUE_FULL will be returned if data could not be written to the queue because the queue was already full.
RMR©2012
Maths is not everything
Queue System Calls: (from ISR) receive from queue
96
portBASE_TYPE xQueueReceiveFromISR (xQueueHandle xQueue,void *pvBuffer,portBASE_TYPE *pxHigherPriorityTaskWoken);
RMR©2012
Maths is not everything
Queue System Calls: (from ISR) receive from queue
96
portBASE_TYPE xQueueReceiveFromISR (xQueueHandle xQueue,void *pvBuffer,portBASE_TYPE *pxHigherPriorityTaskWoken);
pvBuffer - A pointer to the memory into which the received data will be copied.
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.
RMR©2012
Maths is not everything
Queue System Calls: (from ISR) receive from queue
96
portBASE_TYPE xQueueReceiveFromISR (xQueueHandle xQueue,void *pvBuffer,portBASE_TYPE *pxHigherPriorityTaskWoken);
pvBuffer - A pointer to the memory into which the received data will be copied.
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.
pdPASS will only be returned if data was successfully read from the queue.
RMR©2012
Maths is not everything
Queue System Calls: (from ISR) receive from queue
96
portBASE_TYPE xQueueReceiveFromISR (xQueueHandle xQueue,void *pvBuffer,portBASE_TYPE *pxHigherPriorityTaskWoken);
pvBuffer - A pointer to the memory into which the received data will be copied.
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.
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.
RMR©2012
Maths is not everything
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.
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.
97
RMR©2012
Maths is not everything
Interrupt Nesting: example
98
! •! 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.
RMR©2012
Maths is not everything
Interrupt Nesting: example
98
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.
RMR©2012
Maths is not everything
Interrupt Nesting: example
98
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.
RMR©2012
Maths is not everything
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.
Note: In some µP, high-priority relates with high int. numbers, while in others (e.g. ARM) higher int. numbers means lower priority.
99
RMR©2012
Maths is not everything
FreeRTOS
Resource Management
RMR©2012
Maths is not everything
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 variablesPeripheralsCode
101
RMR©2012
Maths is not everything
Accessing global variables
Let’s assume the following C code: GlobalC += 10;
In assembly we got:1. LOAD R, [#1234]2. SUM R, 103. 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
When a global variable is accessed by more than 1 task, to assure its consistency, one needs to control the access to it!
102
RMR©2012
Maths is not everything
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.
Task A continues from the point at which it was pre-empted and completes outputting the remaining characters – “orld”.
The LCD will now be displaying the corrupted string “Hello wAbort, Retry, Fail?orld”.
103
RMR©2012
Maths is not everything
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.
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;}
RMR©2012
Maths is not everything
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.
105
/* 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 : lReturn = lVar1 + 20; lState = 0;
break;
}}
RMR©2012
Maths is not everything
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
but the best is to assure (if possible) that resources are not shared and each resource is only accessed from a single task.
106
RMR©2012
Maths is not everything
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()
107
/* 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();
It’s a “brute force” method as: disables all the int. up to configMAX_SYSCALL_INTERRUPT_PRIORITY
preemption results from an interrupt, so taskENTER_CRITICAL() assures that the task stays in Running until taskEXIT_CRITICAL()
RMR©2012
Maths is not everything
Mutual Exclusion by implementing Critical Sections: example
Critical sections must be kept very short otherwise they will adversely affect interrupt response times.
108
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();
}
RMR©2012
Maths is not everything
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.
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();
}
RMR©2012
Maths is not everything
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.)
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.
110
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
111
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
111
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
111
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
111
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
111
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
111
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
111
RMR©2012
Maths is not everything
Mutual Exclusion through the use of Mutexes: examples
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.
RMR©2012
Maths is not everything
Mutex Semaphores
MUTEX Creation & Manipulation
112
xSemaphoreHandle vSemaphoreCreateMutex(void);portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
RMR©2012
Maths is not everything
Mutex Semaphores
MUTEX Creation & Manipulation
112
xSemaphoreHandle vSemaphoreCreateMutex(void);portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
When to use
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.
RMR©2012
Maths is not everything
Mutex Semaphores
MUTEX Creation & Manipulation
112
xSemaphoreHandle vSemaphoreCreateMutex(void);portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
When to use
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.
non-NULL will only be returned if the MUTEX was created successfully.
RMR©2012
Maths is not everything
Mutex Semaphores
MUTEX Creation & Manipulation
112
xSemaphoreHandle vSemaphoreCreateMutex(void);portBASE_TYPE xSemaphoreTake (xSemaphoreHandle xSemaphore,
portTickType xTicksToWait);portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore);
When to use
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.
non-NULL will only be returned if the MUTEX was created successfully.
NULL will be returned if the MUTEX could not be created.
RMR©2012
Maths is not everything
Using a Mutex
113
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 );}
RMR©2012
Maths is not everything
Using a Mutex
114
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 ) );
}
}
RMR©2012
Maths is not everything
Using a Mutex
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 ******************************************\r\n", 1, NULL ); xTaskCreate( prvPrintTask, "Print2", 1000, "Task 2 ------------------------------------------\r\n", 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( ;; );}
RMR©2012
Maths is not everything
Using a Mutex
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
RMR©2012
Maths is not everything
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
117
RMR©2012
Maths is not everything
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
117
An interesting REAL example of priority inversion
http://www.motherboardpoint.com/really-happened-mars-priority-inversion-and-mars-pathfinder-t169706.html
RMR©2012
Maths is not everything
Priority Inheritance
FreeRTOS mutexes automatically provide a basic ‘priority inheritance’ mechanism.
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
118
assumes a task w
ill only hold a single mutex at any one tim
e.
RMR©2012
Maths is not everything
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.
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.119
RMR©2012
Maths is not everything
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
May ease the access to a resource from an interrupt handler
120
Task 1
Task 2
MSG2
MSG1
Gatekeeper
Resource
Queue
RMR©2012
Maths is not everything
Gatekeepers
121
RMR©2012
Maths is not everything
The Gatekeeper itself
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. */
} }
RMR©2012
Maths is not everything
The tasks that generate messages for the gatekeeper
123
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 ) );
}
}
RMR©2012
Maths is not everything
Interrupt Hook using also the gatekeeper
124
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;
}
} 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()’.
RMR©2012
Maths is not everything
creating everything in the Main
125
static char *pcStringsToPrint[] = { "Task 1 ****************************************************\r\n", "Task 2 ----------------------------------------------------\r\n", "Message printed from the tick hook interrupt ##############\r\n"};/*-----------------------------------------------------------*//* 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();
}}
RMR©2012
Maths is not everything
FreeRTOS
Memory Management
RMR©2012
Maths is not everything
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.
They can suffer from memory fragmentation.
127
RMR©2012
Maths is not everything
Managing memory dynamically
Different embedded systems have varying RAM allocation and timing requirementsFreeRTOS 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().
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.
128
RMR©2012
Maths is not everything
Managing memory dynamically
FreeRTOS prov ides 4 d i f ferent 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.
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.
129
RMR©2012
Maths is not everything
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
130
Heap_1 is always deterministic.
Useful in: any application that never deletes a task, queue or semaphore.
RMR©2012
Maths is not everything
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 a lso uses a s imple array d imensioned 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.
131
Heap_2 can suffer from fragmentation
Heap_2 is not deterministic.
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);
RMR©2012
Maths is not everything
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
132
void *pvPortMalloc( size_t xWantedSize ){void *pvReturn; vTaskSuspendAll(); { pvReturn = malloc( xWantedSize ); } xTaskResumeAll(); return pvReturn;}void vPortFree( void *pv ){ if( pv != NULL ) { vTaskSuspendAll(); {
free( pv ); } xTaskResumeAll(); }}
Useful in: applications that repeatedly allocate and free memory buffers of different size.
RMR©2012
Maths is not everything
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
133
RMR©2012
Maths is not everything
Stack Overflow - WaterMark
134
unsigned portBASE_TYPEuxTaskGetStackHighWaterMark(xTaskHandle xTask);
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'.
RMR©2012
Maths is not everything
Stack Overflow - WaterMark
134
unsigned portBASE_TYPEuxTaskGetStackHighWaterMark(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
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.
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'.
RMR©2012
Maths is not everything
Stack Overflow : Run Time Checking
FreeRTOS includes two optional run time stack checking mechanisms.
controlled by the configCHECK_FOR_ST ACK_OVERFLOW c o m p i l e t i m e c o n fi g u r a t i o n c o n s t a n t w i t h i n 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.
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.
RMR©2012
Maths is not everything
Stack Overflow : Run Time Checking
Run Time Stack Checking - Method 1configCHECK_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 2configCHECK_FOR_STACK_OVERFLOW= 2.
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 overflows136
RMR©2012
Maths is not everything
FreeRTOS
Additional Features
RMR©2012
Maths is not everything
Software Timer
A Software Timer provides a mechanism for calling back a function when a certain time passes.
The timer's callback function is executed when the timer's period expires.
It doesn’t execute a timer callback functions from an interrupt context.
It does not consume any processing time unless a timer has actually expired,
It is not part of the kernel and it does not add any processing overhead to the tick interrupt, and does not walk any link list structures while interrupts are disabled.
138
RMR©2012
Maths is not everything
Software Timer
Basically, the FreeRTOS software timer implementation acts like a task using the resources provided by the FreeRTOS.Timer callback functions execute in the context of the timer service task.
Essential that timer callback functions never attempt to block. For example, a timer callback function must not call vTaskDelay(), vTaskDelayUntil(), or specify a non zero block time when accessing a queue or a semaphore.
APIs that communicate with the timer task through queues.
139
RMR©2012
Maths is not everything
Software Timer
140
RMR©2012
Maths is not everything
Software Timer Creation
141
xTimerHandle xTimerCreate(const signed char *pcTimerName, portTickType xTimerPeriod, unsigned portBASE_TYPE uxAutoReload, void * pvTimerID, tmrTIMER_CALLBACK pxCallbackFunction);
portBASE_TYPE xTimerDelete( xTimerHandle xTimer, portTickType xBlockTime);
RMR©2012
Maths is not everything
Software Timer Handling
142
portBASE_TYPE xTimerStart(xTimerHandle xTimer, portTickType xBlockTime);
portBASE_TYPE xTimerReset(xTimerHandle xTimer, portTickType xBlockTime);
portBASE_TYPE xTimerStop(xTimerHandle xTimer, portTickType xBlockTime);
RMR©2012
Maths is not everything
Software Timer - types
One-shotexecutes the callback function just once although it can be re-armed manually
Auto-reloadupon executing the callback function it an automatic rearming is performed thus executing the callback function periodically
143
RMR©2012
Maths is not everything
Enabling Software Timers
To enable this functionality, add timers.c to the project and configure the following options in FreeRTOSConfig.h:configUSE_TIMERS: “1” to enable the timer.
configTIMER_TASK_PRIORITY : t i m e r t a s k priority.
configTIMER_QUEUE_LENGTH: timer task queue size.
configTIMER_TASK_STACK_DEPTH: timer task stack size.
144