FreeRTOS Notes


https://www.freertos.org/FreeRTOS-quick-start-guide.html
https://www.freertos.org/Documentation/RTOS_book.html


  • FreeRTOS is solely owned, developed and maintained by Real Time Engineers Ltd.
  • FreeRTOS is a real-time kernel (or real-time scheduler) on top of which embedded applications can be built to meet their hard real-time requirements.
  • Product to market using FreeRTOS without even talking to Real Time Engineers ltd.
  • each thread of execution is called a ‘task’.
Why Use a Real-time Kernel?
  • Abstracting away timing information : application code to be simpler, and the overall code size to be smaller.
  • Maintainability/Extensibility : Interdependencies between modules, and allows the software to evolve in a controlled and predictable way.
  • Modularity, Team development, Easier testing, Code reuse.
  • Improved efficiency : software to be completely event-driven, so no processing time is wasted by polling for events that have not occurred. Code executes only when there is something that must be done.
  • Idle time : Idle Task executes whenever there are no application tasks wishing to execute.
  • Power Management : Power consumption can be decreased significantly by placing the processor into a low power state each time the Idle task runs. 
FreeRTOS has the following standard features:
  • Pre-emptive or co-operative operation
  • Very flexible task priority assignment
  • Flexible, fast and light weight task notification mechanism
  • Queues, Binary semaphores, Counting semaphores, Mutexes, Recursive Mutexes
  • Software timers, Event groups, Tick hook functions, Idle hook functions
  • Stack overflow checking,  Trace recording, Task run-time statistics gathering
Chapter 1 The FreeRTOS Distribution
  • FreeRTOS can be built with approximately twenty different compilers, and can run on more than thirty different processor architectures
  • FreeRTOS is supplied as a set of C source files.
  • FreeRTOS is configured by a header file called FreeRTOSConfig.h.
  • A demo application is provided for every FreeRTOS port, and every demo application contains a FreeRTOSConfig.h file.
The Top Directories in the FreeRTOS Distribution

FreeRTOS
│ │
│ ├─Source FreeRTOS source files
│ │
│ └─Demo pre-configured and port specific FreeRTOS demo projects
FreeRTOS-Plus
├─Source source code for some FreeRTOS+ ecosystem components
└─Demo demo projects for FreeRTOS+ ecosystem components

Core FreeRTOS source files within the FreeRTOS directory tree
  • The core FreeRTOS source code is contained in just two C files that are common to all the FreeRTOS ports. These are called tasks.c, and list.c. 
  • queue.c provides both queue and semaphore services.
  • timers.c provides software timer functionality. if event groups are actually going to be used.
  • croutine.c croutine.c implements the FreeRTOS co-routine functionality. It need only be included in the build if co-routines are actually going to be used.
FreeRTOS Source Files Specific to a Port:
  • FreeRTOS port are contained within the FreeRTOS/Source/portable directory.
  • If you are running FreeRTOS on a processor with architecture ‘architecture’ using compiler ‘compiler’ then, in addition to the core FreeRTOS source files, you must also build the files located in FreeRTOS/Source/portable/[compiler]/[architecture] directory.
  • Projects that use a FreeRTOS version older than V9.0.0 must include a heap memory manager.
Port specific source files within the FreeRTOS directory tree:
Include Paths:
FreeRTOS requires three directories to be included in the compiler’s include path.
  • core FreeRTOS header files, FreeRTOS/Source/include.
  • The path to the source files that are specific to the FreeRTOS port in use.FreeRTOS/Source/portable/[compiler]/[architecture].
  • A path to the FreeRTOSConfig.h header file.
A note to Linux users: FreeRTOS is developed and tested on a Windows host. Occasionally
this results in build errors when demo projects are built on a Linux host.

Every demo project includes a file called main.c. This contains the main() function, from where
all the demo application tasks are created.

Creating a FreeRTOS Project
new projects are created by adapting one of these existing demo projects; this will allow the project to have the correct files included, the correct interrupt handlers installed, and the correct compiler options set.
  • Open the supplied demo project and ensure that it builds and executes as expected.
  • Remove the source files that define the demo tasks. Any file that is located within the Demo/Common directory can be removed from the project.
  • Delete all the function calls within main(), except prvSetupHardware() and vTaskStartScheduler().
  • Check the project still builds.
Following these steps will create a project that includes the correct FreeRTOS source files, but
does not define any functionality.
Data Types and Coding Style Guide:

TickType_t is data type used to hold the tick count value, and to specify times.
  • FreeRTOS configures a periodic interrupt called the tick interrupt.
  • The number of tick interrupts that have occurred since the FreeRTOS application started is called the tick count. The tick count is used as a measure of time.
  • The time between two tick interrupts is called the tick period.
  • TickType_t can be either an unsigned 16-bit type, or an unsigned 32-bit type, depending on the setting of configUSE_16_BIT_TICKS within FreeRTOSConfig.h.
BaseType_t is always defined as the most efficient data type for the architecture. Typically, this is a 32-bit type on a 32-bit architecture, a 16-bit type on a 16-bit architecture, and an 8-bit type on an 8-bit architecture.

Function Names  are prefixed with both the type they return, and the file they are defined within.
  • vTaskPrioritySet() returns a void and is defined within task.c
  • xQueueReceive() returns a variable of type BaseType_t and is defined within queue.c
  • pvTimerGetTimerID() returns a pointer to void and is defined within timers.c
Macro Names 

Common macro definitions:
Chapter 2 skipped ;)
Chapter 3 Task Management
this chapter are fundamental to understanding how to use FreeRTOS, and how FreeRTOS applications behave.

Task Functions : Tasks are implemented as C functions. The only thing special about them is their prototype, which must return void and take a void pointer parameter. 
  • Each task is a small program in its own right. It has an entry point, will normally run forever
    within an infinite loop, and will not exit.
  • FreeRTOS tasks must not be allowed to return from their implementing function in any way they must not contain a ‘return’ statement and must not be allowed to execute past the end of the function.

Top Level Task States:
  • An application can consist of many tasks. If the processor running the application contains a
    single core, then only one task can be executing at any given time. This implies that a task
    can exist in one of two states, Running and Not Running.
  • When a task is in the Running state the processor is executing the task’s code. When a task
    is in the Not Running state, the task is dormant, its status having been saved ready for it to
    resume execution the next time the scheduler decides it should enter the Running state.
  • The FreeRTOS scheduler is the only entity that can switch a task in and out.
Creating Tasks: The xTaskCreate() API Function
FreeRTOS V9.0.0 also includes the xTaskCreateStatic() function, which allocates the memory required to create a task statically at compile time:

  • Tasks are created using the FreeRTOS xTaskCreate() API function.
  • pvTaskCode parameter is simply a pointer to the function that implements the task (in effect, just the name of the function).
  • pcName A descriptive name for the task. this is not used by FreeRTOS in any way. It is included purely as a debugging aid. Identifying a task by a human readable name is much simpler than attempting to identify it by its handle.
  • usStackDepth Each task has its own unique stack that is allocated by the kernel to the task when the task is created. The usStackDepth value tells the kernel how large to make the stack.
  • pvParameters Task functions accept a parameter of type pointer to void ( void* ). The value assigned to pvParameters is the value passed into the task.
  • uxPriority Defines the priority at which the task will execute. Priorities can be assigned from 0, which is the lowest priority, to (configMAX_PRIORITIES – 1), which is the highest priority.
  • pxCreatedTask pxCreatedTask can be used to pass out a handle to the task being created. This handle can then be used to reference the task in API calls that, for example, change the task priority or delete the task. If your application has no use for the task handle, then pxCreatedTask can be set to NULL.
 Returned value 
  • pdPASS This indicates that the task has been created successfully. 
  • pdFAIL This indicates that the task has not been created because there is insufficient heap memory available for FreeRTOS to allocate enough RAM to hold the task data structures and stack.
Creating tasks


/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();

Output:


Task Priorities

  • The priority can be changed after the scheduler has been started by using the vTaskPrioritySet() API function.
  • Priorities are defined in configMAX_PRIORITIES compile time configuration constant within FreeRTOSConfig.h.
  • Therefore, the range of available priorities is 0 to (configMAX_PRIORITIES – 1).
  • The FreeRTOS scheduler will always ensure that the highest priority task that is able to run is the task selected to enter the Running state. Where more than one task of the same priority is able to run, the scheduler will transition each task into and out of the Running state, in turn.
Time Measurement and the Tick Interrupt

  • Scheduling Algorithms, describes an optional feature called ‘time slicing’.To be able to select the next task to run, the scheduler itself must execute at the end of each time slice 1 . 
  • A periodic interrupt, called the ‘tick interrupt’, is used for this purpose.
  • configTICK_RATE_HZ compile time configuration constant within FreeRTOSConfig.h. 
  • configTICK_RATE_HZ is set to 100 (Hz), then the time slice will be 10 milliseconds. The time between two tick interrupts is called the ‘tick period’. One time slice equals one tick period.
  • The optimal value for configTICK_RATE_HZ is dependent on the application being developed, although a value of 100 is typical.

FreeRTOS API calls always specify time in multiples of tick periods, which are often referred to
simply as ‘ticks’. The pdMS_TO_TICKS() macro converts a time specified in milliseconds into
a time specified in ticks.

TickType_t xTimeInTicks = pdMS_TO_TICKS( 200 );

Expanding the ‘Not Running’ State

  • To make the tasks useful they must be re-written to be event-driven. An event-driven task has work (processing) to perform only after the occurrence of the event that triggers it, and is not able to enter the Running state before that event has occurred.
  • A task that is waiting for an event is said to be in the ‘Blocked’ state, which is a sub-state of the Not Running state.
Temporal (time-related) events—the event being either a delay period expiring, or an absolute time being reached. For example, a task may enter the Blocked state to waitfor 10 milliseconds to pass.

Synchronization events—where the events originate from another task or interrupt. For example, a task may enter the Blocked state to wait for data to arrive on a queue. Synchronization events cover a broad range of event types.

The Suspended State Suspended’ is also a sub-state of Not Running. Tasks in the Suspended state are not available to the scheduler. The only way into the Suspended state is through a call to the vTaskSuspend() API function, the only way out being through a call to the vTaskResume() or xTaskResumeFromISR() API functions.

The Ready State Tasks that are in the Not Running state but are not Blocked or Suspended are said to be in the Ready state. They are able to run, and therefore ‘ready’ to run, but are not currently in the
Running state.

vTaskDelay() places the calling task into the Blocked state for a fixed number of tick interrupts.

void vTaskDelay( TickType_t xTicksToDelay );

vTaskDelay( pdMS_TO_TICKS( 100 ) ) will result in the calling task remaining in the Blocked state for 100 milliseconds.



The vTaskDelayUntil() API Function

  • The parameters to vTaskDelayUntil() specify, instead, the exact tick count value at which the calling task should be moved from the Blocked state into the Ready state.
  • vTaskDelayUntil() is the API function that should be used when a fixed execution period is required (where you want your task to execute periodically with a fixed frequency), as 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()).
void vTaskDelayUntil( TickType_t * pxPreviousWakeTime, TickType_t xTimeIncrement );

pxPreviousWakeTime : This parameter is named on the assumption that vTaskDelayUntil() is being used to implement a task that executes periodically and with a fixed frequency. In this case, pxPreviousWakeTime holds the time at which the task last left the Blocked state (was ‘woken’ up). This time is used as a reference point to calculate the time at which the task should next leave the Blocked state.

xTimeIncrement This parameter is also named on the assumption that vTaskDelayUntil() is being used to implement a task that executes periodically and with a fixed frequency—the frequency being set by the xTimeIncrement value.



The xLastWakeTime variable needs to be initialized with the current tick count. Note that this is the only time the variable is explicitly written to. After this xLastWakeTime is managed automatically by the vTaskDelayUntil() API function.

The vTask3 should execute every 250 milliseconds and The vTask4 should execute every 500ms.


The Idle Task and the Idle Task Hook

  • There must always be at least one task that can enter the Running state. To ensure this is the case, an Idle task is automatically created by the scheduler when vTaskStartScheduler() is called.
  • The idle task has the lowest possible priority (priority zero), to ensure it never prevents a higher priority application task from entering the Running state.
Idle Task Hook Functions
  • To add application specific functionality directly into the idle task through the use of an idle hook (or idle callback) function—a function that is called automatically by the idle task once per iteration of the idle task loop.
  • Placing the processor into a low power mode.
  • An Idle task hook function must never attempt to block or suspend.
  • Idle task is responsible for cleaning up kernel resources after a task has been deleted. If the idle task remains permanently in the Idle hook function, then this clean-up cannot occur.
void vApplicationIdleHook( void );

Changing the Priority of a Task
  • The vTaskPrioritySet() API Function can be used to change the priority of any task after the scheduler has been started.
  • vTaskPrioritySet() API function is available only when INCLUDE_vTaskPrioritySet is set to 1 in FreeRTOSConfig.h.
  • The uxTaskPriorityGet() API function can be used to query the priority of a task. uxTaskPriorityGet() API function is available only when INCLUDE_uxTaskPriorityGet is set to 1 in FreeRTOSConfig.h.
void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority );

UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask );

pxTask The handle of the task whose priority is being modified, A task can change its own priority by passing NULL in place of a valid task handle.

uxNewPriority The priority to which the subject task is to be set.

Deleting a Task

  • A task can use the vTaskDelete() API function to delete itself, or any other task.  vTaskDelete() API function is available only when INCLUDE_vTaskDelete is set to 1 in FreeRTOSConfig.h.
  • Deleted tasks no longer exist and cannot enter the Running state again. 
  • It is the responsibility of the idle task to free memory allocated to tasks that have since been deleted.
void vTaskDelete( TaskHandle_t pxTaskToDelete );

pxTaskToDelete The handle of the task that is to be deleted (the subject task)—see the pxCreatedTask parameter of the xTaskCreate() API function for information on obtaining handles to tasks.
A task can delete itself by passing NULL in place of a valid task handle.

Scheduling policy

Fixed Priority 
Scheduling algorithms described as ‘Fixed Priority’ do not change the priority assigned to the tasks being scheduled, but also do not prevent the tasks themselves from changing their own priority, or that of other tasks.

Pre-emptive
Pre-emptive scheduling algorithms will immediately ‘pre-empt’ the Running state task if a task that has a priority higher than the Running state task enters the Ready state. Being pre-empted means being involuntarily (without explicitly yielding or blocking) moved out of the Running state and into the Ready state to allow a different task to enter the Running state.

Time Slicing
Time slicing is used to share processing time between tasks of equal priority, even when the tasks do not explicitly yield or enter the Blocked state. Scheduling algorithms described as using ‘Time Slicing’ will select a new task to enter the Running state at the end of each time slice if there are other
Ready state tasks that have the same priority as the Running task. A time slice is equal to the time between two RTOS tick interrupts.