Copia Di Freertos - v1.7 - Cmsis Os API
Copia Di Freertos - v1.7 - Cmsis Os API
CMSIS_OS API
T.O.M.A.S – Technically Oriented Microcontroller Application Services
v1.7
• FreeRTOS
Agenda
• Operating system: what is … ?
• Basic features
• CMSIS_OS API vs FreeRTOS API
• FreeRTOS and STM32CubeMX
• Configuration
• Memory allocation
• Scheduler
• Tasks
• Intertask communication
• Queues (messages, mail)
• Semaphores (binary, counting)
• Signals
• Resources management
• Mutexes
• Software Timers
• Advanced topics (hooks, stack overflow protection, gatekeeper task)
• Debugging
• Low power support (tickless modes)
• Footprint
Operating System
what is … ?
What is Task?
START
• It is C function:
• It should be run within infinite loop, like:
for(;;)
Ready Running
{
/* Task code */
}
Suspended
What is scheduler?
• The scheduler is an algorithm determining which task to execute.
• Is select one of the task being ready to be executed (in READY state)
• There are few mechanisms controlling access to CPU for tasks (timeslice, preemption, idle)
Task2 Task2
time time
What is OS heap?
Data memory
start HEAP (for main
application)
Queue storage area
QUEUE 1
TASK B
free memory
TASK A
STACK (for main Stack of the Task A
application and IRQs)
end
TCB (Task Control
Block) ~88B
FreeRTOS
basic features
About FreeRTOS (1/2)
Interrupt vectors:
• SVC – system service call (like SWI in ARM7)
• PendSV – pended system call (switching context)
• SysTick – System Timer
Flash memory:
• 6-10kB
RAM memory:
• ~0.5kB + task stacks:
System Service Call (SVC)
• SVC – system service call / supervisor call
• It is an instruction and an exception. Once the svc instruction is executed, SVD
IRQ is triggered immediately (unless there is higher priority IRQ active)
• SVC contains an 8bit immediate value what could help to determine which OS
service is requested.
• Do not use SVC inside NMI or Hard Fault handler
Pended System Call (PendSV)
• PendSV is a priority programmable exception triggered by SW (write to the in
ICSR register @0xE000ED04)
SCB->ICSR |= (1<<28)
• It is not precise (in contrary to SVC). After set a pending bit CPU can execute a
number of instructions before the exception will start. Usually it is used like a
subroutine called i.e. by the system timer in OS
System timer
• It is necessary to trigger a context switching in regular time slots.
• In CortexM architecture 24bit downcounting SysTick is used for this purpose (it can
be changed – more details in Tickless mode section)
• System timer is triggering PendSV SW interrupt to perform context switch.
• In case we are using HAL library it is strongly recommended to change its
TimeBase timer from Systick to other timer available (i.e. TIM6)
FreeRTOS sources file structure
File / header role
Directory
croutine.c / croutine.h Co-routines functions definitions. Efficient in 8 and 16bit architecture. In 32bit architecture usage of tasks is suggested
.\Source
.\Source\include
event_groups.c / event_groups.h
.\Source
.\Source\include
heap_x.c Memory management functions (allocate and free memory segment, three different approaches in heap_1, heap_2, heap_3 and heap_4
.\Source\portable\MemMang files)
list.c / list.h List implementation used by the scheduler.
.\Source
.\Source\include
port.c / portmacro.h Low level functions supporting SysTick timer, context switch, interrupt management on low hw level – strongly depends on the platform
.\Source\portable\xxx\yyy (core and sw toolset). Mostly written in assembly. In portmacro.h file there are definitions of portTickType and portBASE_TYPE
queue.c / queue.h / semphr.h Semaphores, mutexes functions definitions
.\Source
.\Source\include
tasks.c / task.h Task functions and utilities definition
.\Source
.\Source\include
timers.c / timers.h Software timers funcitons definitions
.\Source
.\Source\include
FreeRTOS.h Configuration file which collect whole FreeRTOS sources
.\Source\include
FreeRTOSConfig.h Configuration of FreeRTOS system, system clock and irq parameters configuration
FreeRTOS sources file structure
File / header role
Directory
heap_x.c Memory management functions (allocate and free memory segment, three different approaches in heap_1, heap_2, heap_3 and heap_4
.\Source\portable\MemMang files)
• Middleware components using the CMSIS-OS API are RTOS independent, this allows an easy linking to any
third-party RTOS
• The following table lists examples of the CMSIS-RTOS APIs and the FreeRTOS APIs used to implement
them
• Most of the functions returns osStatus value, which allows to check whether the function is
completed or there was some issue (cmsis_os.h file)
• Most of the functions returns osStatus value, below you can find return values on function
completed list (cmsis_os.h file)
osStatus value description
• Start a new project within STM32CubeMX for selected MCU (or open already prepared existing one
– important is to have printf() implementation).
• Go to System Core section -> SYS and change Timebase source (for HAL) from SysTick to other
timer i.e. TIM6
FreeRTOS in STM32CubeMX
adding FreeRTOS middleware
1
2 3
FreeRTOS configuration in STM32CubeMX
• Config parameters tab
• Kernel settings
• RTOS components settings
• Memory setup
• Mutexes tab
• Creation of mutexes
FreeRTOS
configuration
FreeRTOS
Configuration options
• Configuration options are declared in file FreeRTOSConfig.h
• Important configuration options are:
configLIBRARY_LOWEST_INTERRUPT_PRIORITY Lowest interrupt priority (0xF when using 4 cortex preemption bits)
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY Highest thread safe interrupt priority (higher priorities are lower numeric value)
Kernel settings
• Use preemption
• If enabled use pre-emptive scheduling
Priority level
Create Task2 Task2 suspended
High priority
Task2
time
time
FreeRTOS memory management
HEAP
Heap (1/6)
• FreeRTOS uses a region of memory called Heap (into the RAM) to allocate memory for tasks, queues, timers , semaphores,
mutexes and when dynamically creating variables. FreeRTOS heap is different than the system heap defined at the compiler
level.
• When FreeRTOS requires RAM instead of calling the standard malloc it calls PvPortMalloc(). When it needs to free
memory it calls PvPortFree() instead of the standard free().
• FreeRTOS offers several heap management schemes that range in complexity and features. It includes five sample memory
allocation implementations, each of which are described in the following link :
• http://www.freertos.org/a00111.html
• The total amount of available heap space is set by configTOTAL_HEAP_SIZE which is defined in FreeRTOSConfig.h.
• The xPortGetFreeHeapSize() API function returns the total amount of heap space that remains unallocated (allowing
the configTOTAL_HEAP_SIZE setting to be optimized). The total amount of heap space that remains unallocated is also
available with xFreeBytesRemaining variable for heap management schemes 2 to 5.
Heap (2/6)
• Each created task (including the idle task) requires a Task Control Block (TCB) and a stack that are allocated
in the heap.
• The TCB size in bytes depends of the options enabled in the FreeRTOSConfig.h.
• With minimum configuration the TCB size is 24 words i.e 96 bytes.
• if configUSE_TASK_NOTIFICATIONS enabled add 8 bytes (2 words)
• if configUSE_TRACE_FACILITY enabled add 8 bytes (2 words)
• if configUSE_MUTEXES enabled add 8 bytes (2 words).
• The task stack size is passed as argument when creating at task. The task stack size is defined in words of 32
bits not in bytes.
• osThreadDef(Task_A, Task_A_Function, osPriorityNormal, 0, stacksize );
• configMINIMAL_STACK_SIZE defines the minimum stack size that can be used in words. the idle task stack
size takes automatically this value
Heap (3/6)
• The necessary task stack size can be fine-tuned using the API
uxTaskGetStackHighWaterMark() as follow:
• Use an initial large stack size allowing the task to run without issue (example 4KB)
• The API uxTaskGetStackHighWaterMark() returns the minimum number of free bytes (ever encountered) in the task
stack. Monitor the return of this function within the task.
• Calculate the new stack size as the initial stack size minus the minimum stack free bytes.
• The method requires that the task has been running enough to enter the worst path (in term of stack consumption).
configTOTAL_HEAP_SIZE
Heap (4/6)
• FreeRTOS requires to allocate in the heap for each message queue:
• number of bytes = 76 + queue_storage_area.
• queue_storage_area (in bytes) = (element_size * nb_elements) + 16
• When Timers are enabled (configUSE_TIMERS enabled) , the scheduler creates automatically
the timers service task (daemon) when started. The timers service task is used to control and
monitor (internally) all timers that the user will create. The timers task parameters are set through
the fowling defines :
• configTIMER_TASK_PRIORITY : priority of the timers task
• configTIMER_TASK_STACK_DEPTH : timers task stack size (in words)
• The scheduler also creates automatically a message queue used to send commands to the timers
task (timer start, timer stop ...)
Heap (5/6)
• The number of elements of this queue (number of messages that can be hold) are configurable
through the define:
• configTIMER_QUEUE_LENGTH.
• To save heap size (i.e RAM footprint) it is recommended to disable the define
“configUSE_TIMERS” when timers are not used by the application
Heap (6/6)
• Each semaphore declared by the user application requires 88 bytes to be allocated in the heap.
• Each mutex declared by the user application requires 88 bytes to be allocated in the heap.
• To save heap size (i.e RAM footprint) it is recommended to disable the define
configUSE_MUTEXES when mutexes are not used by the application (task TCB static size being
reduced)
How to reduce RAM footprint (1/2)
• Optimize stack allocation for each task :
• uxTaskGetStackHighWaterMark(). This API returns the minimum number of free bytes (ever encountered) in the task
stack
• vApplicationStackOverflowHook(). This API is a stack overflow callback called when a stack overflow is detected
(available when activating the define configCHECK_FOR_STACK_OVERFLOW)
• If heap_1.c, heap_2.c, heap_4.c or heap_5.c are being used, and nothing in your application is ever calling
malloc() directly (as opposed to pvPortMalloc()), then ensure the linker is not allocated a heap to the C library, it
will never get used.
How to reduce RAM footprint (2/2)
• Recover and minimize the stack used by main and rationalize the number of tasks.
• If the application doesn’t use any software timers then disable the define configUSE_TIMERS.
• If the application doesn’t use any mutexe then disable the define configUSE_MUTEXES.
• configMAX_PRIORITIES defines the number of priorities available to the application tasks. Any number of
tasks can share the same priority. Each available priority consumes RAM within the RTOS kernel so this value
should not be set any higher than actually required by the application. It is recommended to declare tasks with
contiguous priority levels: 1, 2, 3, 4, etc… rather than 10, 20, 30, 40, etc. The scheduler actually allocates
statically the ready task list of size configMAX_PRIORITIES * list entry structure : so high value of
configMAX_PRIORITIES shall be avoided to reduce RAM footprints
FreeRTOS
Memory allocation
FreeRTOS
Dynamic memory management
Data memory
start HEAP (for main
application)
Queue storage area
QUEUE 1
TASK B
free memory
TASK A
STACK (for main Stack of the Task A
application and IRQs)
end
TCB (Task Control
Block) ~88B
FreeRTOS in STM32
memory management (Heap_3.c model)
Data memory
start
QUEUE 1
TASK B
free memory
TASK A
STACK (for main Stack of the Task A
application and IRQs)
end
TCB (Task Control
Block) ~88B
FreeRTOS
Dynamic memory management
• Heap_1.c
• Uses first fit algorithm to allocate memory. Simplest allocation method (deterministic), but does
not allow freeing of allocated memory => could be interesting when no memory freeing is
necessary
Heap Heap
pvPortMalloc
Allocated vPortFree
block 1
Allocated
pvPortMalloc vPortFree
block 2
Allocated
pvPortMalloc vPortFree
block 3
FreeRTOS
Dynamic memory management
• Heap_2.c
• Not recommended to new projects. Kept due to backward compatibility.
• Implements the best fit algorithm for allocation
• Allows memory free() operation but doesn’t combine adjacent free blocks
=> risk of fragmentation
Heap Heap Heap Heap
Allocated Allocated
pvPortMalloc
block 1 block 1
vPortFree Heap 4
Allocated
pvPortMalloc
block 2
vPortFree Heap 2 Heap 2
Allocated
pvPortMalloc
block 3
vPortFree Heap 3 Heap 3
FreeRTOS
Dynamic memory management
• Heap_3.c
• Implements simple wrapper for standard C library malloc() and free(); wrapper makes these
functions thread safe, but makes code increase and not deterministic
• It uses linker heap region.
• configTOTAL_HEAP_SIZE setting has no effect when this model is used.
Allocated Allocated
pvPortMalloc vPortFree
block 1 block 1
pvPortMalloc
Allocated vPortFree
block 2
Allocated Heap 2
pvPortMalloc vPortFree
block 3
FreeRTOS
Dynamic memory management
uint8_t ucHeap[configTOTAL_HEAP_SIZE];
FreeRTOS
Dynamic memory management
• Using heap_4.c : heap is organized as a linked list: for better efficiency when
dynamically allocating/Freeing memory.
• As consequence when allocating “N” bytes in the heap memory using
“pvPortMalloc” API it consumes:
• Sizeof (BlockLink_t) (structure of the heap linked list) : 8 bytes.
• Data to be allocated itself : N bytes.
• Add padding to total allocated size (N + 8) to be 8 bytes aligned :
• Example if trying to allocate 52 Bytes : it consumes from the heap : 52 + 8 = 60 bytes aligned to 8 bytes it gives 64
bytes consumed from the heap.
pvPortMalloc(N);
configTOTAL_HEAP_SIZE
FreeRTOS
Dynamic memory management
• Heap_5.c (1/2)
• Fit algorithm able to combine adjacent free memory blocks into a single block using the same
algorithms like in heap_4, but supporting different memory regions (i.e. SRAM1, SRAM2) being
not in linear memory space
• It is the only memory allocation scheme that must be explicitly initialized before any OS object
cab be created (before first call of pvPortMalloc() ).
• An example for STM32L476 device with SRAM1 and SRAM2 areas is on the next slide
FreeRTOS
Dynamic memory management
• Heap_5.c (2/2)
• An example for STM32L476 device with SRAM1 and SRAM2 areas.:
Priority level
High priority
Other IRQs vPortStartFirstTask(
SVC
PendSV
SysTick
time
STM32 priority NVIC configuration
0
254
Kernel(PendSV, SysTick)
255 configKERNEL_INTERRUPT_PRIORITY
• FreeRTOS kernel and its irq procedures (PendSV, SysTick) have lowest possible interrupt priority (255) set in
FreeRTOSConfig.h (configKERNEL_INTERRUPT_PRIORITY)
• There is a group of interrupts which can cooperate with FreeRTOS API by calling its functions. Maximum level for those
peripherals (based on the position in vector table) is set in configMAX_SYSCALL_INTERRUPT_PRIORITY
osDelay()
• The only difference for the programmer is additional argument *hp_task. It SysTick
is a pointer to the variable which is used to indicate whether operation on
queue or semaphore within IRQ causes unblocking of the task with higher Tasks
priority than currently running. If this parameter is pdTRUE, context switch A D A
(PendSV irq) should be requested by kernel before the interrupt exits. time
• When using CMSIS API, this process is automatically handled by the Example: Task A has been interrupted
library (by checking IPSR content) and is transparent for the programmer, by IRQ1. During an interrupt, Task D
i.e.: with higher priority was unblocked, thus
it will be executed once IRQ will finish
osSemaphoreRelease(semaphore)
API functions in IRQ procedures
list of the functions which could be run from IRQ procedure
osKernelSysTick() xTaskGetTickCountFromISR()
osThreadResume() xTaskResumeFromISR()
osThreadGetPriority() uxTaskPriorityGetFromISR()
osSignalSet xTaskGenericNotifyFromISR()
osMessagePut(), xQueueSendFromISR()
osMailPut()
osMessageGet(), xQueueReceiveFromISR()
osMailGet()
osMessageWaiting() uxQueueMessagesWaitingFromISR()
osMutexWait(), xSemaphoreTakeFromISR()
osSemaphoreWait()
osMutexRelease(), xSemaphoreGiveFromISR()
osSemaphoreRelease()
osTimerStart() xTimerChangePeriodFromISR()
osTimerStop() xTimerStopFromISR()
FreeRTOS – boot sequence & timing
HW dependent:
• Configure the CPU clocks
• Initialize static and global variables that contain only the value zero (bss)
• Initialize variables that contain a value other than zero
• Perform any other hardware set up required
time
FreeRTOS related *)
• Create application queues, semaphores and mutexes (~500 CPU cycles/object)
• Create application tasks (~1100 CPU cycles/task)
• Start the RTOS scheduler (~1200 CPU cycles)
The RTOS scheduler is started by calling vTaskStartScheduler(). The start up process includes configuring the tick
interrupt, creating the idle task, and then restoring the context of the first task to run
*) calculations based on ARM CortexM3 device, using ARM RVDS compiler with low optimization level (1)
Source: FreeRTOS FAQ – Memory Usage, Boot Time & Context Switch Times on www.freertos.org web page
Idle task code
• Idle task code is generated automatically when the scheduler is started
• It is portTASK_FUNCTION() function within task.c file
• It is performing the following operations (in endless loop):
• Check for deleted tasks to clean the memory
• taskYIELD() if we are not using preemption (configUSE_PREEMPTION=0)
• Get yield if there is another task waiting and we set configIDLE_SHOULD_YIELD=1
• Executes vApplicationIdleHook() if configUSE_IDLE_HOOK=1
• Perform low power entrance if configUSE_TICKLESS_IDLE!=0) -> let’s look closer on this
FreeRTOS start
step by step 1/2
• xPortStartScheduler() function (port.c file) is configuring lowest priority level for SysTick and
PendSV interrupts, then it is starting the timer that generates the tick (in CortexM architecture usually
it is SysTick), enables FPU if present (CortexM4) and starts the first task using
prvPortStartFirstTask() function
FreeRTOS start
step by step 2/2
• prvPortStartFirstTask() function (port.c file, usually written in assembler) locates the stack
and set MSP (used by the OS) to the start of the stack, then enables all interrupts. After this triggers
software interrupt SVC
• vPortSVCHandler() function (port.c file) restores the context, loads TCB (Task Control Block)
for the first task (highest priority) form ready list and starts executing this task
FreeRTOS – lists management
name Description conditions
ReadyTasksLists[0] Prioritized ready tasks lists separate for each task priority configMAX_PRIORITIES
… (up to configMAX_PRIORITIES
ReadyTasksList[configMAX_PRIORITIES] Value stored in FreeRTOSConfig.h)
TasksWaitingTermination List of tasks which have been deleted but their memory INCLUDE_vTaskDelete == 1
pools are not freed yet.
SuspendedTaskList List of tasks currently suspended INCLUDE_vTaskSuspend == 1
PendingReadyTaskList Lists of tasks that have been read while the scheduler was -
suspended
DelayedTaskList List of delayed tasks -
OverflowDelayedTaskList List of delayed tasks which have overflowed the current tick -
count
There is no dedicated list for task in Running mode (as we have only one task in this
state at the moment), but the currently run task ID is stored in variable pxCurrentTCB
API - Operations on scheduler
• Start the scheduler
osKernelStart()
• Set priorities for PendSV and SysTick IRQs (minimum possible)
• Starts kernel of the FreeRTOS (by executing SVC procedure)
• IDLE task is created automatically
(with handler or without it if INCLUDE_xTaskGetIdleTaskHandle is not defined)
• There could be another thread creation done.
• Stop the scheduler -> not implemented in STM32 (function vTaskEndScheduler() is empty)
• Two calls to pvPortMalloc()are made during task creation. First one allocates
TCB, second one allocates the task stack (it is taken from declared FreeRTOS
heap area).
• The process of saving the context of a task that is being suspended and restoring
the context of a task being resumed is called context switching.
Task Control Block (TCB)
Name Description condition
*pxTopOfStack Points to the location of the last item placed on the tasks stack.
THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT
xMPUSettings The MPU settings are defined as part of the port layer. portUSING_MPU_WRAPPERS == 1
THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT
xGenericListItem The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ).
uxTCBNumber Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been configUSE_TRACE_FACILITY == 1
deleted and then recreated.
uxTaskNumber Stores a number specifically for use by third party trace code configUSE_TRACE_FACILITY == 1
uxBasePriority The priority last assigned to the task - used by the priority inheritance mechanism configUSE_MUTEXES == 1
uxMutexesHeld configUSE_MUTEXES == 1
pxTaskTag configUSE_APPLICATION_TASK_TAG == 1
ulRunTimeCounter Stores the amount of time the task has spent in the Running state configGENERATE_RUN_TIME_STATS == 1
_reent xNewLib_reent Allocate a Newlib reent structure that is specific to this task. configUSE_NEWLIB_REENTRANT == 1
Note Newlib support has been included by popular demand, but is not used by the FreeRTOS maintainers themselves.
FreeRTOS is not responsible for resulting newlib operation. User must be familiar with newlib and must provide
system-wide implementations of the necessary stubs.
Task Control Block (TCB)
Main fields within TCB (task.c file)
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //Points to the location of the last item placed on the tasks stack
…
ListItem_t xStateListItem; //The list that the state list item of a task is reference from denotes
//the state of that task (Ready, Blocked, Suspended )
ListItem_t xEventListItem; //Used to reference a task from an event list
UBaseType_t uxPriority; //The priority of the task. 0 is the lowest priority
StackType_t *pxStack; //Points to the start of the stack
char pcTaskName[ configMAX_TASK_NAME_LEN ];//Descriptive name given to the task when created.
…
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; //The priority last assigned to the task – for priority inheritance
UBaseType_t uxMutexesHeld;
#endif
…
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
…
} tskTCB;
Task function example
void FirstTask(void const * argument)
{
/* task initialization */ Run once at first run of
each task instance
for(;;)
{ Run when task instance
/* Task code */ is in RUN mode
}
osThreadResume
• Task is waiting for either a temporal or an external event Event
• Suspended Blocked
• Task not available for scheduling, but still being kept in
memory
Suspended
Task states – CMSIS_OS
Tasks states are stored within osThreadState enum (cmsis_os.h file)
State name value comment
osThreadRunning 0 RUNNING
osThreadReady 1 READY
osThreadBlocked 2 BLOCKED
osThreadSuspended 3 SUSPEND
osThreadDeleted 4 Task has been deleted, but its TCB has not yet been freed
osThreadError 0x7FFFFFFF Error code
Task priorities
• Each task is assigned a priority from [tskIDLE_PRIORITY] (defined in task.h) to
[MAX_PRIORITIES – 1] (defined in FreeRTOSConfig.h)
• The order of execution of tasks depends on this priority
• The scheduler activates the task that has the highest priority of all tasks in the
READY state.
• Task with higher priority can preempt running task if configUSE_PREEMPTION (in
FreeRTOSConfig.h) is set to 1
• Task priorities can be changed during work of the application
osThreadYield()
PendSV Tasks priorities:
osDelay()
Idle 0
SysTick A,B,C 1
D 2
Tasks A B C A D A
time
A -> RUN A -> READY B -> READY C -> READY A -> READY D -> BLOCKED
B,C = READY B -> RUN C -> RUN A -> RUN D -> RUN A -> RUN
D = BLOCKED C = READY A = READY B = READY B,C = READY B,C = READY
D = BLOCKED D = BLOCKED D = BLOCKED
NVIC priority COOPERATIVE
IRQ
SVC
osThreadYield()
Tasks priorities:
PendSV Idle 0
A,B,C 1
osDelay()
SysTick
Tasks A B C C
time
A = RUN A -> READY B -> BLOCKED B -> BLOCKED
B,C = READY B -> RUN C -> RUN C -> RUN
C = READY A = READY A = READY
Context switching
list.h
Tasks are grouped within lists at List_t objects (list.h file)
Field name comment
listFIRST_LIST_INTEGRITY_CHECK_VALUE known test value – not used
ListItem_t * Used to walk through the list. Points to the last item returned by a call to
listGET_OWNER_OF_NEXT_ENTRY ()
MiniListItem_t List item that contains the maximum possible item value meaning it is
always at the end of the list and is therefore used as a marker.
listSECOND_LIST_INTEGRITY_CHECK_VALUE known test value – not used
Context switching
list.h
Tasks are grouped within lists at ListItem_t objects (list.h file)
Field name comment
listFIRST_LIST_INTEGRITY_CHECK_VALUE known test value – not used
TickType_t The value being listed. In most cases this is used to sort the list in descending
order.
ListItem_t * Pointer to the next ListItem_t in the list.
Void * Pointer to the object (normally a TCB) that contains the list item. There is
therefore a two way link between the object containing the list item and the list
item itself.
Void * Pointer to the list in which this list item is placed (if any).
TickType_t The value being listed. In most cases this is used to sort the list in
descending order.
ListItem_t * Pointer to the next ListItem_t in the list.
Remarks:
• Under these test conditions the context switch time is not dependent on whether a different task was selected to run or the same task was
selected to continue running.
• The ARM Cortex-M port performs all task context switches in the PendSV interrupt. The quoted time does not include interrupt entry time.
• The quoted time includes a short section of C code. It has been determined that 12 CPU cycles could have been saved by providing the entire
implementation in assembly code. It is considered that the benefit of maintaining a short section of generic C code (for reasons of maintenance,
support, robustness, automatic inclusion of features such as tracing, etc.) outweighs the benefit of removing 12 CPU cycles from the context
switch time.
• The Cortex-M CPU registers that are not automatically saved on interrupt entry can be saved with a single assembly instruction, then restored
again with a further single assembly instruction. These two instructions on their own consume 12 CPU cycles.
*) source: FreeRTOS FAQ – Memory Usage, Boot Time & Context Switch Times on www.freertos.org web page
FreeRTOS – context switch time (2/2)
• Context switch time can be much longer in CortexM4 and CortexM7 based devices with Floating
Point Unit due to necessity of stacking FPU registers (additional 17 32bit registers: S0-S15 and
FPSCR).
• Rest of FPU registers (S16-S31) should be handled by software
• Within PendSV handler there is a check done whether floating point unit instruction has been used
and based on this informaiton those registers are stacked/unstacked from/for current task or not:
/* Is the task using the FPU context?
If so, push high vfp registers. */
tst r14, #0x10
it eq
vstmdbeq r0!, {s16-s31}
• And then on PendSV exit after the task switch:
/* Is the task using the FPU context?
If so, pop the high vfp registers too. */
tst r14, #0x10
it eq
vldmiaeq r0!, {s16-s31}
• More information can be found in Application note 298 from ARM.
Context switching time
STM32CubeMX modifications
Within STM32CubeMX, pinout tab:
• Configure PB6, PB7 as GPIO_Output
• Configure PD0 as EVENTOUT
Re-generate the code and within the code please add some modifications:
1. To set both pins (PB6, PB7), please use GPIOB->ODR |= 0xC0;
2. To reset PB6, you can used GPIOB->ODR &= 0xFFBF;
3. To reset PB7, you can used GPIOB->ODR &= 0xFF7F;
4. To generate 1 sys clk long pulse on PD0 use sev (assembly code)
Put above lines in various places in the code to measure time intervals (on the next slide instruction 1)
has been placed within SysTick_Handler() in stm32L4xx_it.c, instruction 2 and 3 in empty for(;;)
loop within Task1 and Task2 accordingly (main.c file). Instruction 4 has been placed within
xPortPendSVHandler() function (port.c file) just before its jump to user task (line BX LR).
PendSV interrupt
Task 1 Task 2
• Both 32bit registers are visible as R13 register of the Core and only one can be
used at one time.
• Dual stack architecture is used for OS:
• MSP – OS kernel and exception handlers
• PSP – application tasks
Tasks API
• Create Task example
&os_thread_def_Task1
Return value:
• Task1Handle = NULL -> error (i.e. lack of heap memory to allocate the stack)
• Task1Handle != NULL -> task ID for reference by other functions
Tasks API
• Task handle definition:
/* Private variables ------------------------------------------------*/
osThreadId Task1Handle;
• Create task
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
• Delete task
osStatus osThreadTerminate (osThreadId thread_id)
• Get task ID
osThreadId osThreadGetId (void)
Tasks API
• Yield task
osStatus osThreadYield(void)
• Check if task is suspended
osStatus osThreadIsSuspended(osThreadId thread_id)
• Resume task
osStatus osThreadResume (osThreadId thread_id)
• To Generate Code 2
3. Select Generate Code button
Tasks lab
analysis of the code generated by STM32CubeMX
• Any component in FreeRTOS need to have handle, very similar to STM32CubeMX
/* Private variables ---------------------------------------------------------*/
osThreadId Task1Handle;
osThreadId Task2Handle;
*) if it ends, it means that we are out of declared heap size and there was not enough memory space to create a new task
Tasks lab
• If both Delays are processed the FreeRTOS is in idle state
Priority level
osDelay osDelay Delay ends osDelay osDelay
High priority PendSV PendSV PendSV PendSV PendSV
Task1 Task1
Running Running Running Running Running Running
Task2 Task2
Task1
Ready Task2
Ready Ready Ready Ready Ready
Task2
Task1 Task1
Blocked Blocked Blocked Blocked Blocked Blocked
Task2 Task2 Task1
Task1 Task1
Running Running Running Running Task2 Running Running
Task2 Task1
Task1 Task1 Task1
Ready Task2
Ready Ready Task2 Ready Ready Ready
Task2 Task2
• If higher priority task is not running we can print text from this task
/* StartTask2 function */
void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{ Helps not spam
printf("Task 2\n");
HAL_Delay(50); terminal
}
/* USER CODE END StartTask2 */
}
Tasks lab
• What happen if Task1 not call osDelay() ?
Priority level
osDelay Delay end osDelay Delay end osDelay
High priority PendSV PendSV PendSV PendSV PendSV
Task1 Task1
Running Running Running Runnning Task2 Running Running
Task2 Task1
Task1
Ready Task2
Ready Ready Task2 Ready Ready Ready
Task2 Task2
Task1 Task1
Blocked Blocked Blocked Blocked Blocked Blocked
Priority level
High priority
Task1
Low priority
time
/* StartTask2 function */
void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{
printf("Task 2\n");
HAL_Delay(200);
}
/* USER CODE END StartTask2 */
}
Include vTaskDelayUntil
osDelay() and osDelayUntil()
• Enable vTaskDelayUntil in Include parameters
• Regenerate project, modify tasks to:
void StartTask1(void const * argument)
{
For osDelayUntil function we
/* USER CODE BEGIN 5 */ need mark wakeup time
uint32_t wakeuptime;
wakeuptime=osKernelSysTick();
/* Infinite loop */
for(;;)
{
printf("Task 1\n");
HAL_Delay(1000);
osDelayUntil(wakeuptime,2000); Function will be
} executed every 2s
/* USER CODE END 5 */
time
priority
6 Task 1 Task 1 Task 1, Task2 Task 1
5 Task 2
4 Task 2 Task 2
Task 2
Task 1 PendSV PendSV PendSV
IDLE
Task 2
osThreadTerminate
time
Creating and deleting tasks lab
• Example how to create tasks
• Comment Task2 creation part in main.c
/* definition and creation of Task2 */
// osThreadDef(Task2, StartTask2, osPriorityNormal, 0, 128);
// Task2Handle = osThreadCreate(osThread(Task2), NULL);
• Modify Task1 to create task2
void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
printf("Create task2");
osThreadDef(Task2, StartTask2, osPriorityNormal, 0, 128);
Task2Handle = osThreadCreate(osThread(Task2), NULL);
osDelay(1000);
}
/* USER CODE END 5 */ Task 2 creation
}
Tasks_lab
Include vTaskDelete
Creating and deleting tasks lab
• Example how to delete tasks
• Modify Task2 to delete himself:
/* StartTask2 function */
void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{
printf("Delete Task2\n");
osThreadTerminate(Task2Handle); Delete Task
}
/* USER CODE END StartTask2 */
}
Include vTaskDelete
osThreadTerminate API
step by step
• osThreadTerminate() calls vTaskDelete() (cmsis_os.c file)
• The only argument specifies the ID of the task to be deleted. NULL means that the
calling task will be deleted.
Memory allocated by the task code is not automatically freed and should be freed
before the task is deleted, TCB and its original stack are freed by IDLE Task.
If the task has finished its job earlier…
• osThreadYield() – move the task from Run to Ready state. Next task with the
same priority will be executed.
SysTick SysTick SysTick SysTick SysTick
Priority level PendSV PendSV PendSV PendSV PendSV
osTaskYield osTaskYield
High priority PendSV PendSV
Task1 Task1
Runing Runing Runing Runing Task2 Runing Runing
Task2 Task1
Task1
Ready Task2
Ready Ready Task2 Ready Ready Ready
Task2 Task2
Task3 Task1 Task1
Blocked Blocked Blocked Blocked Blocked Blocked
• It makes sense if we have few tasks on the same priority otherwise yielded task will
be executed again
priority
Tick time
time
osThreadYield API
step by step
• Semaphores are used to communication between the tasks without specifying the ID of the thread
who can accept it. It allows counting multiple events and can be accepted by many threads.
• Direct to task notifications are used to precise communication between the tasks. It is necessary
to specify within signal thread id.
• Mutexes are used to guard the shared resources. It must be taken and released always in that
order by each task that uses the shared resource.
• Event Groups are used to synchronize task with multiple events (OR-ed together). There could be
8 or 24 bit value used here (depends on configUSE_16_BIT_TICKS settings) – not implemented in
CMSIS_OS API
FreeRTOS
Queues
Queues (1/2)
• Queues are pipes to transfer data between tasks in RTOS
• By default queue is behaving as FIFO (First In - First Out); can be redefined to
perform as LIFO (Last In - First Out) structure by using xQueueSendToFront()
function (not available in current CMSIS-RTOS API).
• All data send by queue must be of the same type, declared during queue
creation phase. It can be simple variable or structure.
• Within CMSIS-RTOS API there are two types of queues:
• Message where one can send only integer type data or a pointer
• Mail where one can send memory blocks
Queues (2/2)
• Length of queue is declared during creation phase and is defined as a number of
items which will be send via queue.
• Operations within queues are performed in critical sections (blocking interrupts
by programming BASEPRI register for the time of operation on queue.
• Tasks can block on queue sending or receiving data with a timeout or infinitely.
• If multiple tasks are blocked waiting for receiving/Sending data from/To a queue
then only the task with the highest priority will be unblocked when a data/space
is available. If both tasks have equal priority the task that has been waiting the
longest will be unblocked.
Queue structure management
queue.c
Name Description condition
*pcHead Points to the beginning of the queue storage area
*pcTail Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the
queue items, this is used as a marker
*pcReadFrom Points to the last place that a queued item was read from when the structure is used as a queue Use of a union is an exception to the coding standard to
ensure two mutually exclusive structure members don't
appear simultaneously (wasting RAM)
uxRecursiveCallCount Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is Use of a union is an exception to the coding standard to
used as a mutex ensure two mutually exclusive structure members don't
appear simultaneously (wasting RAM)
xTasksWaitingToSend List of tasks that are blocked waiting to post onto this queue. Stored in priority order
xTasksWaitingToReceive List of tasks that are blocked waiting to read from this queue. Stored in priority order
uxLength The length of the queue defined as the number of items it will hold, not the number of bytes.
uxItemSize The size of each items that the queue will hold.
xRxLock Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set
to queueUNLOCKED when the queue is not locked
xTxLock Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to
queueUNLOCKED when the queue is not locked.
uxQueueNumber configUSE_TRACE_FACILITY == 1
ucQueueType configUSE_TRACE_FACILITY == 1
*pxQueueSetContainer configUSE_QUEUE_SETS == 1
Queue
Sender Task Receiver Task
Message 1
osMessagePut
Queue handle
Queue
• Read an item from a Queue without removing the item from it:
osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec)
5-7
4
printf redirection to USART2
• The following code should be included into main.c file to redirect printf
output stream to UART2
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
• Queue item type initialization, length definition and create of queue and memory
allocation
/* Create the queue(s) */ Queue item definition
/* definition and creation of Queue1 */
osMessageQDef(Queue1, 256, uint8_t);
Queue1Handle = osMessageCreate(osMessageQ(Queue1), NULL);
Queue size
Queue lab
code processing
• Sender1 task
void StartSender1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;) Put value ‘1’ into queue
{
printf("Task1\n"); Item to send
osMessagePut(Queue1Handle,0x1,200);
printf("Task1 delay\n"); Timeout for send
osDelay(1000);
} Queue handle
/* USER CODE END 5 */
}
Queue lab
code processing
• Receiver task
/* StartReceiver function */
void StartReceiver(void const * argument)
{
/* USER CODE BEGIN StartReceiver */
osEvent retvalue;
/* Infinite loop */
for(;;)
{
Get item from queue How long we wait on
printf("Task2\n"); data in queue
retvalue=osMessageGet(Queue1Handle,4000);
Queue handle
printf(“%d \n",retvalue.value.p); It will block task
}
/* USER CODE END StartReceiver */
}
Queue Blocking
Sender Task
Message 1
Receiver
Receiver Task
Task
osMessageGet
Sender Task
Blocked
Receiver Task
Queue Blocking
• After calling osMessagePut()
• If there is no free space in queue the Sender task is blocked for settable time then it will
continue (without sending the data)
• If there is free space in queue the Sender task will continue just after data send
Receiver Sender1
PendSV PendSV
PendSV
SysTick SysTick
Receiver Sender1 Receiver Sender1
osMessageGet osMessagePut osMessageGet osMessagePut
Blocked
Two senders lab
• Let’s create two sending tasks: Sender1, Sender2 and one Receiver task with the
same priorities.
priority
Receiver
osMessageGet
Blocked
PendSV
Queue empty
Sender1
osMessagePut
PendSV
SysTick
Sender2
osMessagePut
PendSV
• What would happened if will be more tasks?
SysTick
Receiver Sender1 Sender2
Receiver
osMessageGet
PendSV
SysTick
Sender1
osMessagePut
PendSV
SysTick
Sender2
osMessagePut
PendSV
SysTick
Sender1
osMessagePut
PendSV
SysTick
Sender2
osMessagePut
Blocked
PendSV
Queue full
• Because tasks have same priority, receiver will get data from queue after both task put data into queue
Multiple senders, one receiver
Two senders lab
• Two sending tasks
• They are same no change necessary
/* StartReceiver function */
void StartReceiver(void const * argument)
{
/* USER CODE BEGIN StartReceiver */
osEvent retvalue;
/* Infinite loop */
for(;;)
{
retvalue=osMessageGet(Queue1Handle,4000);
printf("Receiver\n");
printf("%d \n",retvalue.value.p);
}
/* USER CODE END StartReceiver */
}
Receiver with higher priority lab
• Senders have same priority
• Receiver have higher priority than senders
• Please verify whether behavior is inline with expectations
2x
priority
Receiver
osMessageGet
Blocked
PendSV
Receiver
Queue empty
Sender1
osMessagePut
PendSV
SysTick
Sender1
Receiver
osMessageGet
Blocked PendSV
SysTick
Sender2
Queue empty
Sender2
osMessagePut
PendSV
SysTick
Receiver
osMessageGet
Blocked
PendSV
SysTick
Queue empty
Sender1
osMessagePut
PendSV
SysTick
Receiver
osMessageGet
PendSV
Blocked
SysTick
Queue empty
Sender2
osMessagePut
• Receiver is now unblocked every time when sender tasks put data into queue
PendSV
Receiver with higher priority lab
Single sender, two receivers
• Message from the queue is taken by the task with higher priority
• In case of equal priorities currently executed or first executed task will
get the message. It is not deterministic.
Queue items lab
• Queues allow to define type (different variables or structures) which the queue use.
• Within Queue1 Item size put a structure called Data
• Regenerate project
Queue items lab
• Create new structure type for data
/* Define the structure type that will be passed on the queue. */
typedef struct
{
uint16_t Value;
uint8_t Source;
} Data;
typedef struct {
osStatus status; ///< status code: event or error information
union {
uint32_t v; ///< message as 32-bit value
void *p; ///< message or mail as void pointer
int32_t signals; ///< signal flags
} value; ///< event value
union {
osMailQId mail_id; ///< mail id obtained by \ref osMailCreate
osMessageQId message_id; ///< message id obtained by \ref osMessageCreate
} def; ///< event definition
} osEvent;
• Allocate a memory block from a mail and set memory block to zero
void * osMailCAlloc (osMailQId queue_id, uint32_t millisec)
osMessageWaiting() uxQueueMessagesWaiting(queue_handler)
uxQueueMessagesWaitingFromISR(queue_handler)
- xQueueSendToBack(queue_handle,*to_queue,block_time)
xQueueSendToBackFromISR(queue_handle,*to_queue,block_time)
- xQueueSendToFront(queue_handle,*to_queue,block_time)
xQueueSendToFrontFromISR(queue_handle,*to_queue,block_time)
osMessagePeek() xQueuePeek(queue_handle,*to_queue,block_time)
osMailCAlloc() osMailAlloc(),
osMailPut() xQueueSendFromISR()
xQueueSend()
osMailGet() xQueueReceiveFromISR()
xQueueReceive()
osMailFree() osPoolFree()
FreeRTOS
Semaphores
Semaphores
• Semaphores are used to synchronize tasks with other events in the system (especially IRQs)
• Waiting for semaphore is equal to wait() procedure, task is in blocked state not taking CPU time
• Semaphore should be created before usage
• In FreeRTOS implementation semaphores are based on queue mechanism
• In fact those are queues with length 1 and data size 0
• There are following types of semaphores in FreeRTOS:
• Binary – simple on/off mechanism
• Counting – counts multiple give and multiple take
• Mutex – Mutual Exclusion type semaphores (explained later on)
• Recursive (in CMSIS FreeRTOS used only for Mutexes)
• Turn on semaphore = give a semaphore can be done from other task or from interrupt subroutine (function
osSemaphoreRelease() )
• Turn off semaphore = take a semaphore can be done from the task (function osSemaphoreWait() )
Semaphores: binary vs counting
A C blocked A C blocked
0 0
IRQ1 D blocked IRQ1 D blocked
osSemaphoreRelease osSemaphoreRelease
A C blocked A give C blocked
1 2
IRQ1 D blocked IRQ1 D blocked
give give
osSemaphoreRelease osSemaphoreRelease
osSemaphoreWait osSemaphoreWait
A C Run/ready A take C Run/ready
0 1
IRQ1 D blocked IRQ1 D blocked
A C Run/ready
0
IRQ1 D Run/ready
take
osSemaphoreWait
Binary Counting
Binary Semaphore
time
Task1 Task2
Task2
Blocked
osSemaphoreWait
Task1 Task2
Blocked
osSemaphoreRelease osSemaphoreWait
Task1 Task2
Blocked
osSemaphoreWait
Task1 Task2
osSemaphoreRelease
Task1 Task2
osSemaphoreWait
• Semaphore creation
Binary Semaphore
osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count)
• Semaphore release
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)
4
Binary Semaphore lab
• Task1 is synchronized with Task2
• Both tasks have the same priorities
• Task1 is waiting for semaphore (with 4sec delay)
• Task2 is releasing the semaphore
priority
Task1 Task2
PendSV PendSV
PendSV
SysTick SysTick
Blocked
printf redirection to USART2
• The following code should be included into main.c file to redirect printf
output stream to UART2
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
• Semaphore creation
/* Create the semaphores(s) */
/* definition and creation of myBinarySem01 */
osSemaphoreDef(myBinarySem01);
myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);
Binary Semaphore lab
code processing
• Semaphore release usage
• If tasks/interrupt is done the semaphore is released
void StartTask1(void const * argument)
{
• Semaphore can be released from interrupt (if interrupt priority is below – higher
number in CortexM cores - configured
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY)
• Semaphore release
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)
Counting Semaphore
Task1 Task2 Task3
osSemaphoreRelease
PendSV PendSV
PendSV
SysTick SysTick
Blocked
1 2 1
0 0
printf redirection to USART2
• The following code should be included into main.c file to redirect printf
output stream to UART2
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
• If Task notification is used in place of a message queue then the receiving task (waiting for the notification) is set to the blocked state.
However The sending task (sending the notification) cannot wait in the Blocked state for a send to complete if the send cannot complete
immediately
configUSE_TASK_NOTIFICATIONS
Signals
• Signals are used to trigger execution states between the threads and from IRQ to
thread.
• Each thread has up to 31 assigned signal flags.
• The maximum number of signal flags is defined in cmsis_os.h
(osFeature_Signals). It is set to 8. It is not possible to configure signals from
STM32CubeMX.
• Main functions:
• osSignalWait() - wait for one or more signal flags for running thread
• Mutexes
• Kind of binary semaphore shared between tasks
• Require set configUSE_MUTEXES at 1 in FreeRTOSConfig.h
Critical sections
• Critical section mechanism allows to block all the interrupts during sensitive/atomic
operation execution (like operations on queues)
• To enter into critical section portENTER_CRITICAL() should be used
• To exit from critical section portEXIT_CRITICAL() should be used
void function(void)
{ Storing current BASEPRI value (current level of interrupt masking) and
portENTER_CRITICAL(); programming BASEPRI with MASK value *)
It means masking of all interrupts below this value
..sensitive code
execution
restoring BASEPRI value and masking of all interrupts below this value
portEXIT_CRITICAL();
}
• When used for mutual exclusion the mutex acts like a token that is used to guard a
resource.
• When a task wishes to access the resource it must first obtain ('take') the token.
• When it has finished with the resource it must 'give' the token back - allowing
other tasks the opportunity to access the same resource.
• In case of recursive mutex it should be given as many times as it was
successfully taken (like counting semaphores) to release it for another task.
Mutex 2/2
• Mutexes use the same access API functions as semaphores – this permits a block
time to be specified.
• The block time indicates the maximum number of 'ticks' that a task should enter the
Blocked state when attempting to 'take' a mutex if the mutex is not available
immediately.
• Unlike binary semaphores however - mutexes employ priority inheritance. This
means that if a high priority task is blocked while attempting to obtain a mutex
(token) that is currently held by a lower priority task, then the priority of the task
holding the token is temporarily raised to that of the blocked task.
• Mutex Management functions cannot be called from interrupt service routines
(ISR).
• A task must not be deleted while it is controlling a Mutex. Otherwise, the Mutex
resource will be locked out to all other tasks
Mutex, Semaphore – threats 1/3
Priority inversion
• This is the situation where a higher priority task is waiting for a lower priority task
to give a control of the mutex and low priority task is not able to execute.
Mutex, Semaphore – threats 2/3
Priority inheritance
• It is temporary raise of the priority of the mutex holder to that of the highest
priority task that is attempting to obtain the same mutex. The low priority task that
holds the mutex inherits the priority of the task waiting for the mutex. The priority
of the mutex holder is reset automatically to its original value when it gives the
mutex back.
• It is a mechanism that minimizes the negative effects of priority inversion
• It is complicating system timing analysis and it is not a good practice to rely on it
for correct system operation
Mutex, Semaphore – threats 3/3
Deadlock (Deadly Embrace)
• It occurs when two tasks cannot work because they are both waiting for a resource
held by each other
• The best way to avoid deadlock is to consider them at design time and design the
system to be sure that the deadlock cannot occur.
Mutex
• Used to guard access to limited recourses
• Works very similar as semaphores
Task1 Task1
Resource Resource
osMutexWait
Resours Resours
Task2
Task2 e Task2
Task2 e
Blocked
osMutexWait Blocked
osMutexWait
Task1 Task1
Resource Resource
osMutexRelease
Resours Resours
Task2 e Task2 e
Blocked
osMutexWait osMutexRelease
configUSE_MUTEXES
Mutex
• Mutex creation
• Mutex release
osStatus osMutexRelease (osMutexId mutex_id)
Task1 Task2
PendSV
SysTick
Only one task can have mutex Only one task can have mutex
printf redirection to USART2
• The following code should be included into main.c file to redirect printf
output stream to UART2
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
osRecursiveMutexWait() xSemaphoreTakeRecursive()
FreeRTOS
Software Timers
Software Timers (1/3)
• Software timer is one of standard component of every RTOS
• FreeRTOS “software” Timers allows to execute a callback at a set of time (timer
period). Timer callback functions execute in the context of the timer service task.
• It is therefore essential that timer callback functions never attempt to block. For
example, a timer callback function must not call vTaskDelay(), vTaskDelayUntil(),
or specify a non zero block time when accessing a queue or a semaphore.
Software Timers (2/3)
• It is not precise, intended to handle periodic actions and delay generation
• Can be conditionally used to extend number of hardware timers in STM32
• One Pulse (execute its callback only once with an option of manual re-trigger)
Task1 Software
Timer
osTimerStart Callback
• The timers service task is used to control and monitor (internally) all timers that the user will create.
• The timers task parameters are set through the fowling defines (in FreeRTOSConfig.h):
• configTIMER_TASK_PRIORITY : priority of the timers task
• configTIMER_TASK_STACK_DEPTH : timers task stack size (in words)
• The scheduler also creates automatically a message queue used to send commands to the timers
task (timer start, timer stop ...) .
• The number of elements of this queue (number of messages that can be hold) are configurable
through the define:
• configTIMER_QUEUE_LENGTH.
configUSE_TIMERS
Software Timers
configuration
• Software timer is one of standard component of every RTOS
Config field description
(default value)
configUSE_TIMERS 1 – includes software timers functionality and automatically creates
(0 – disabled) timer service task on scheduler start
0 – disabled, no timer service task
configTIMER_TASK_PRIORITY Priority for timer service task from the range between IDLE task
() priority and configMAX_PRIORITIES-1
configTIMER_QUEUE_LENGTH This sets the maximum number of unprocessed commands that the
() timer command queue can hold at any one time.
configTIMER_TASK_STACK_DEPTH Sets the size of the stack (in words, not bytes) allocated to the timer
() service task.
configUSE_TIMERS
Software Timers
• Software timer creation
void vApplicationIdleHook(void)
void vApplicationIdleHook(void) {
{ while(1)
tick_IDLE++; {
} tick_IDLE++;
}
}
Correct Wrong
configUSE_IDLE_HOOK
Idle Hook
• If the scheduler cannot run any task it goes into idle mode
• Idle hook is callback from idle mode
• Within this task is possible to put power saving function
• It is necessary to enable it within Config parameters (part of
FreeRTOSConfig.h configuration file)
configUSE_IDLE_HOOK
Idle Hook
• Idle hook callback in freertos.c created by STM32CubeMX
/* USER CODE END FunctionPrototypes */
/* Hook prototypes */
void vApplicationIdleHook(void);
• Use only the interrupt safe FreeRTOS functions (with suffix FromISR() ).
Memory management models - monitoring
Malloc Failed Hook Function
• Memory allocation schemes implemented by heap_1.c, heap_2.c, heap_3.c, and heap_4 and
heap_5.c can optionally include malloc() failure hook (or callback) function that can be configured
to get called on pvPortMalloc() returning NULL.
• Defining malloc() failure hook will help to identify problems caused by lack of heap memory;
especially when call to pvPortMalloc() fails within an API function.
• Malloc failed hook will only get called if configUSE_MALLOC_FAILED_HOOK is set to 1 in
FreeRTOSConfig.h. When it is set, an application must provide hook function with the following
prototype:
void vApplicationMallocFailedHook( void )
configUSE_MALLOC_FAILED_HOOK
Malloc Failed Hook
• This callback is called if the memory allocation process fails (pvPortMalloc() returns NULL)
• Helps to react on malloc problems, when function return is not handled
• It is necessary to enable it within Config parameters (part of FreeRTOSConfig.h configuration file)
configUSE_MALLOC_FAILED_HOOK
Malloc Failed Hook
• Malloc Failed hook callback skeleton is present in freertos.c created by STM32CubeMX
/* Hook prototypes */
void vApplicationMallocFailedHook(void);
• Do impossible memory allocation within one of our tasks /* Private variables -------------*/
osThreadId Task1Handle;
void StartTask1(void const * argument) osPoolId PoolHandle;
{
/* USER CODE BEGIN 5 */
osPoolDef(Memory,0x10000000,uint8_t);
/* Infinite loop */
for(;;) Impossible memory allocation
{
PoolHandle = osPoolCreate(osPool(Memory));
osDelay(5000);
}
/* USER CODE END 5 */
}
Stack overflow protection
check of stack ‘high watermark
• During task creation, its stack memory space is filled with 0xA5 data
• During run time we can check how much stack is used by task – stack ‘high water mark’
• To turn on this mechanism, some additional configuration of FreeRTOS is required (FreeRTOSConfig.h file or
STM32CubeMX FreeRTOS configuration window):
• configUSE_TRACE_FACILITY should be defined to 1
• INCLUDE_uxTaskGetStackHighWaterMark should be defined to 1
• There is a dedicated function to perform this operation:
uxTaskGetStackHighWaterMark(xTaskHandle xTask);
• After call it with task handle as an argument returns the minimum amount of remaining stack for xTask is
presented (NULL means task which is currently in RUN mode).
• Additional configuration within FreeRTOSConfig.h is required
Stack overflow protection
runtime stack check mechanism
• Two options are available (to be configured within Config parameters (FreeRTOSConfig.h file):
• Option 1
• Option 2
Stack overflow protection
stack overflow hook implementation
• Stack overflow hook function is a function called by the kernel at detected stack overflow
• It should be implemented by the user. Its declaration should look like:
• vApplicationStackOverflowHook(xTaskHandle *pxTask, signed char *pcName);
• Its skeleton is generated by STM32CubeMX in freertos.c file
/* Hook prototypes */
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName);
• After selection of i.e. FreeRTOS Task List there will be an additional window present, then after run and pause of
the code, the list of task till be displayed
FreeRTOS
low power modes
FreeRTOS and low power modes
Tickless idle mode operation
• Kernel can stop tick interrupt and place MCU in low power mode, on exit from this mode tick counter
is updated
• Enabled when setting configUSE_TICKLESS_IDLE as 1
• The kernel will call a macro (tasks.c) portSUPPRESS_TICKS_AND_SLEEP() when the Idle task is
the only task able to run (and no other task is scheduled to exit from blocked state after n* ticks)
• FreeRTOS implementation of portSUPRESS_TICKS_AND_SLEEP for cortexM3/M4 enters MCU in
sleep low power mode
• Wakeup from sleep mode can be from a system interrupt/event
• User implementation can be done by setting configUSE_TICKLESS_IDLE above 1 (to avoid usage
of kernel macros)
• Lowest power consumption can be achieved by replacing default SysTick by LowPower timers
(LPTIM or RTC) as tick timer
• Execute portSUPPRESS_TICKS_AND_SLEEP() with expected idle time and enter into low power
mode
Low power mode
• Wakeup from low power mode and resume all tasks (start scheduler)
Perform low power entrance
configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING