Tasks: ParameterToTasks
Learn to add dynamic parameter to Tasks in FreeRTOS with the ESP32 and ESP-IDF
Notes
Check the previous tutorial on Creating a Basic Task
Check this tutorial on Pointer to void
Import your project from one of these configured builds
Why do we need to add “Parameters to Tasks”
In many cases, we would need to change the usage of a function with the Parameter inputs to a function.
It would be highly inefficient to copy an existing function and make just a slight modification to it.
Using the xTaskCreate
API
1
2
3
4
5
6
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask)
We learnt the usage of this API is the previous tutorial
However, since we just needed to create Basic Tasks we did not use many parameters, mainly the void *pvParameters
and the TaskHandle_t *pxCreatedTask
parameters.
In this tutorial we will learn how to use the void *pvParameters
parameter pertaining to the xTaskCreate() API
Code Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const char *pcTask1 = "Task1\n"
const char *pcTask2 = "Task2\n"
void vPrintFunction(void *parameter);
void app_main()
{
xTaskCreate(vPrintFunction, "Print100", 2048, (void *) pcTask1, 1, NULL);
xTaskCreate(vPrintFunction, "Print200", 2048, (void *) pcTask2, 1, NULL);
}
void vPrintFunction(void *parameter)
{
char *pcTaskName;
pcTaskName = (char *) parameter;
while(1)
{
printf("Parameter: %s", pcTaskName);
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
Check the entire code here
Output
1
2
3
4
5
6
Parameter: Task 1
Parameter: Task 2
Parameter: Task 2
Parameter: Task 1
Parameter: Task 1
Parameter: Task 2
The output on your terminal would be something akin to this. As you can see Task2 runs after Task1 after the first iteration.
Why does this happen?
This is because both Task1 and Task2 have the same priority to run. When the program finishes one iteration, during the second function iteration both functions have equal priority to run.
This might cause either Task1 or Task2 to be chosen, i.e both functions have an equal probability to be executed.
We shall talk about task priorities and how they function more in later tutorials too
Sending a struct as a parameter
While the above example does use only const char *
supplied as a void *parameter
we can very well supply a struct
to our program.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct Data_t
{
uint32_t ucData;
char id;
} GenericData_t;
void genericStructPrint(void *xStruct)
{
GenericData_t * data = (GenericData_t *) xStruct;
printf("ucData: %" PRIu32 "\n", data->ucData);
printf("id: %c\n", data->id);
}
int main()
{
GenericData_t data1 = {100, 'a'};
GenericData_t data2 = {200, 'z'};
genericStructPrint((void *) &data1);
genericStructPrint((void *) &data2);
return 0;
}
As you can see from the above example the void *
is a very efficient way to send generic data to a function. As opposed to function overloading practiced in C++ this method can be used where code flexibility is of the utmost importance.
Conclusion
From the above example you can see, we slightly modified the output/working of a function by supplying two different arguments to the same function.
In this way we reduce redundant code and improve readability.
In the next tutorial, we shall task more about delaying tasks and delaying tasks until a certain time. These functions would help us give more control over Task based timing actions and creating asynchronous tasks.
Meanwhile, have fun and keep learning!
Comments