Introduction To FreeRTOS
Introduction To FreeRTOS
Introduction To FreeRTOS
Reference Books
Port-dependent definitions
Coding Conventions
Naming conventions
For example, a pointer
to a short will have the
prefix ps;a pointer to
void will have the prefix
pv;an unsigned short
will have the prefix us
Topics covered:
How to implement tasks.
How to create one or more instances of a task.
How to use the task parameter.
How to change the priority of a task that has already been
created.
How to delete a task.
How to implement periodic processing.
When the idle task will execute and how it can be used.
1.1 Tasks in FreeRTOS
With FreeRTOS, application can be structured as a set of
autonomous tasks
Each task executes within its own context (e.g., stack) with
no coincidental dependency on other tasks
The scheduler is responsible for starting, stop, swapping in,
and swapping out tasks
1.2 Tasks Functions
Tasks are implemented as C functions
The prototype of a task function must return void and take a void
pointer parameter
A task will typically execute indefinitely in an infinite loop: must never
terminate by attempting to return to its caller
If required, a task can delete itself prior to reaching the function end
A single task function definition can be used to create any number of
tasks
Each created task is a separate execution instance
Each created task has its own stack
Each created task has its own copy of any automatic variables
defined within the task itself
1.2 Tasks Functions
The structure of a typical task function:
pvTaskCode
is a pointer to the function that implement the task
pcName
is a descriptive name for the task, not used by FreeRTOS
usStackDepth
specifies the number of words the stack of this task can hold
pvParameters
is the parameter that can be passed to the task function (pointer to a
complex data structure)
uxPriority
is the priority of the task (0 is the lowest priority,
configMAX_PRIORITIES-1 is the highest priority)
pxCreatedTask
is the handler of the generated task, which can be used to reference
the task within API calls
Returned value
There are two possible return values:
1. pdTRUE: when the task was created successfully
2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: the task
could not be created because there was insufficient heap memory available
for FreeRTOS to allocate enough RAM to hold the task data structures and
stack
A Simple Printing Application
Example
void vTask1( void *pvParameters )
{
const char *pcTaskName = "Task 1 is running\r\n";
volatile unsigned long ul;
/* 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. Later examples will replace this crude loop with
a proper delay/sleep function. */
}
}
}
void vTask2( void *pvParameters )
{
const char *pcTaskName = "Task 2 is running\r\n";
volatile unsigned long ul;
for( ;; )
{
vPrintString( pcTaskName );
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ ) {}
}
}
The Output Produced
1.5 Expanding the Not Running State
The Blocked state : A task that is waiting for an event
Tasks can enter the Blocked state to wait for two different
types of event:
Temporal (time related) events where a delay period expiring or
an absolute time being reached
For example, wait for 10 ms to pass
Synchronization events where the events originate from another
task or interrupt
For example, wait for data to arrive on a queue
It is possible for a task to block on a synchronization event
with a timeout
For example, wait for a maximum of 10 ms for data to arrive on a
queue
Expanding the Not Running State
The Suspended state: 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 through a call to the vTaskResume()or
xTaskResumeFromISR()API functions
Expanding the Not Running State
The Ready State: tasks that are in the Not Running but are
not Blocked or Suspended
Tasks are able to run, and therefore ‘ready’ to run, but not
currently in the Running state
Task State Transitions
Example: Using the Blocked state
to create a delay
The tasks in the previous examples generate delay using a
null loop – polling a counter until it reaches a fixed value
Waste processor cycles
An efficient method is to create a delay with a call to the
vTaskDelay() API function
vTaskDelay() places the calling task into the Blocked state for a
fixed number of tick interrupts
The task in the Blocked state will not use any processing time at all
An alternative method is to use the vTaskDelayUntil()
API function
vTaskDelay() Prototype
xTicksToDelay
is the number of tick interrupts that the calling task should remain in
the Blocked state before being transitioned back into the Ready state.
The idle task is created automatically when the scheduler is started to ensure there
is always at least one task that is able to run (at least one task in the Ready state)
vTaskDelayUntil() Prototype
void vTaskDelayUntil ( portTickType * pxPreviousWakeTime,
portTickType xTicksToDelay );
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.
xTicksToDelay
vTaskDelayUntil()is normally being used to implement a task
that executes periodically; the frequency being set by this value which is
specified in ‘ticks’
Improved Printing Application
Example Using vTaskDelayUntil()
1.6 Task Priority
Returned value
The priority currently assigned to the task being queried.
Experiment with Priorities
The Output Produced
1.7 Idle Task and Idle Task Hook
Functions
The idle task is executed when no application tasks are in
Running and Ready state
The idle task is automatically created by the scheduler when
vTaskStartScheduler()is called
The idle task has the lowest possible priority (priority 0) to
ensure it never prevents a higher priority application task
Application specific functionality can be directly added into
the idle task via an idle hook (or call-back) function
An idle hook function is automatically called per iteration of the idle
task loop
Can be utilized to execute low priority, background or continuous
processing
1.9 Delete a Task
A task can use the vTaskDelete()API function to delete
itself or any other task
The memory allocated by the kernel to a deleted task is
freed by the idle task
vTaskDelete()prototype:
pxTaskToDelete
The handle of the task that is to be deleted
A task can delete itself by passing NULL in place of a valid task handle
2 Queue Management
Topics covered:
How to create a queue.
How a queue manages the data it contains.
How to send data to a queue.
How to receive data from a queue.
What it means to block on a queue.
The effect task priorities have when writing to and reading
from a queue.
2.1 Characteristics of a Queue
Queue is used for inter-communication among autonomous
tasks
A queue can hold a finite number of fixed size data items
Normally, it is First In First Out (FIFO) buffers
Writing data to a queue causes a byte for byte copy of data
Reading data from a queue will remove the data
Any number of tasks can write (read) to (from) the same
queue
Blocking on Queue Reads
A non-NULL value being returned indicates that the mutex was created
successfully. The returned value should be stored as the handle to the
created mutex.
4.4 Priority Inversion & Priority
Inheritance
With a mutex, it is possible that a task with a higher priority
has to wait for a task with a lower priority which hold the
mutex to give up the control of the mutex
A higher priority task being delayed by a lower priority task
in this manner is called ‘priority inversion’.
Priority inheritance works by temporarily raising the priority
of the mutex holder to that of the highest priority task that is
attempting to obtain the same mutex
The priority of the mutex holder is automatically reset back
to its original value when it gives the mutex back
Priority inheritance does not ‘fix’ priority inversion, it merely
lessens its impact.
4.5 Deadlock
Deadlock occurs when two tasks are both waiting for a
resource that is held by the other, e.g.,