S emb t13-freertos

1,694 views
1,493 views

Published on

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,694
On SlideShare
0
From Embeds
0
Number of Embeds
13
Actions
Shares
0
Downloads
166
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

S emb t13-freertos

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

×