In this tutorial, we will see what is a task and its different states. Later we will see how to create a FreeRTOS tasks and finally we will write a simple example to see how the task works and task switching happens.
What is a Task?
Task is a small piece of program which competes with other tasks to get the CPU resources.
Every task has an entry point and runs continuously within an infinite loop. A freeRTOS task will not have an exit point but can be deleted once its job is done.
In the FreeRTOS a task can be in one of four different states viz., Running, Ready, Blocked and Suspended as shown in below image.
1.Running: The task which is executing currently is said to be in running state. It owns the CPU.
2.Ready: The task which is neither suspended nor blocked but still not executing will be in ready state. It's not in running state because either a high priority or equal priority task is executing.
3.Blocked: A task will go in blocked state whenever it is waiting for an event to happen. The event can be completing a delay period or availability of a resource. The blocked tasks are not available for scheduling.
4.Suspended: When vTaskSuspend() is called, the task goes in suspended state. It can be resumed by calling xTaskResume(). The suspended tasks are also not available for scheduling.
Creating a Task
Every task has a distinct set of parameters which needs to be provided while creating a task. The parameters include unique task name, priority, stack memory, a task routine and optional input parameters to task routine.
Below is the prototype of xTaskCreate() function.
pvTaskCode: Pointer to the task entry function (just the name of the function that implements the task, see the example below).
pcName: A descriptive name for the task. This is mainly used to facilitate debugging, but can also be used to obtain a task handle. The maximum length of a task's name is set using the configMAX_TASK_NAME_LEN parameter in FreeRTOSConfig.h.
usStackDepth: The number of words (not bytes!) to allocate for use as the task's stack. For example, if the stack is 16-bits wide and usStackDepth is 100, then 200 bytes will be allocated for use as the task's stack. As another example, if the stack is 32-bits wide and usStackDepth is 400 then 1600 bytes will be allocated for use as the task's stack. The stack depth multiplied by the stack width must not exceed the maximum value that can be contained in a variable of type size_t.
pvParameters: A value that will be passed into the created task as the task's parameter. If pvParameters is set to the address of a variable then the variable must still exist when the created task executes - so it is not valid to pass the address of a stack variable.
uxPriority: The priority at which the created task will execute. Systems that include MPU support can optionally create a task in a privileged (system) mode by setting bit portPRIVILEGE_BIT in uxPrriority. For example, to create a privileged task at priority 2 set uxPriority to ( 2 | portPRIVILEGE_BIT ).
pxCreatedTask: Used to pass a handle to the created task out of the xTaskCreate() function. pxCreatedTask is optional and can be set to NULL. Returns: If the task was created successfully then pdPASS is returned. Otherwise, errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned.
Here we will discuss some of the most frequently used APIs related to tasks.
xTaskCreate(): This interface is used to create a new Task, if the task is successfully created then it returns pdPass(1) or else errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(-1). Check this link for more details.
vTaskDelay(): This function is used to delay/block the task for specified delay time(ticks). INCLUDE_vTaskDelay needs to be set to 1 in FreeRtosConfig.h file for using this function. Check this link for more details.
In this example, we are going to create 3-Tasks with different priorities.
Each task will run for some time and then goes to blocked state allowing the other tasks to run. Once the waiting time is elapsed the scheduler will bring the task to ready state and eventually run the state if its priority is higher compared to the currently running task.
- The controller starts the execution from setup function. The Serial port is initialized at 9600 baud rate and setup message is printed.
- Later 3-Tasks(Task1, Task2 and Idle) are created in order with priorities 2,1,0. At the end of the Setup function, scheduler/kernel takes the control.
- There are 3-tasks in the Ready state and since Task2 has the highest priority it will run first and goes to block state for 150ms.
- Now 2-tasks are available for scheduler and it chooses Task1 as it is the available higher priority task.
- Now Task1 runs for some time and then goes to blocked state for 100ms.
- CPU is left out with the Idle task and it starts running.
- Task2 will be in blocked state for 150ms and task1 will 100ms. So task1 will come out of blocked state first.
- After Task1 waiting time is elapsed, it comes to the Ready state. Since it has got higher priority compared to the Idle task, it will preempt Idle task and starts running. It again goes to blocked state for 100ms.
- Now CPU is left out with IDLE task and it starts running it.
- After Task2 waiting time is elapsed, it comes to the Ready state. Since it has got higher priority compared to the Idle task, it will preempt Idle task and starts running. It again goes to blocked state for 150ms.
- Same thing continues.
Download the complete project and libraries from here.
Have an opinion, suggestion , question or feedback about the article let it out here!