This article aims to learn and use synchronization and mutex in FreeRTOS.
Following the descriptions in this article, you should be able to run the experiment and apply the knowledge.
Experimental conditions: Basic knowledge of C language and an integrated development environment installed, such as Keil uVision5.
Concepts of Synchronization and Mutex
Synchronization is used to control the execution order and timing of tasks, for example, waiting for other tasks to complete a certain operation.
Mutex is used to protect shared resources, ensuring that only one task can modify or access these resources at a time.
Mechanisms for Implementing Synchronization and Mutex
-
Task Notification: Used for waiting and waking between tasks, can pass data and state. -
Queue: Used for passing data, tasks and ISRs can both enqueue data and read from it. -
Semaphore: Used to maintain the number of resources, both producers and consumers can use it. -
Mutex: Used to protect access to shared resources, ensuring that only one task can modify the resource at a time.
These concepts will be further studied in future articles.
Based on the above concepts, I conducted an experiment on my own hardware board. In this experiment, I designed two tasks: PrintTask, which is responsible for printing some information, and CalcTask, which performs calculations. When the calculation task is completed, it passes the result to the PrintTask for display.
Code Snippet for PrintTask
void PrintTask(void *params){
struct TaskPrintInfo *pInfo = params;
OLED_Init();
while (1) {
// vTaskDelay(3000); // Enter block
while (g_calc_end == 0); // Wait for calculation task to complete
/* Print information */
if (g_LCDCanUse) {
g_LCDCanUse = 0;
OLED_ShowString(pInfo->x,pInfo->y,"Sum:",16);
OLED_ShowNum((pInfo->x + 8 * 6),pInfo->y,g_sum,10,16);
OLED_ShowString(pInfo->x,pInfo->y + 16,"Time(ms): ",16);
OLED_ShowNum((pInfo->x + 8 * 10),pInfo->y + 16,g_time / 1000000,4,16);
g_LCDCanUse = 1;
}
OLED_Refresh();
vTaskDelete(NULL);
}
}
xTaskCreate(PrintTask, "task1", 128, &g_Task1Info, osPriorityNormal, NULL);
static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"};
Code Snippet for CalcTask
void CalcTask(void *params){
uint32_t i = 0;
OLED_ShowString(0, 0, "Waiting",16);
OLED_Refresh();
g_time = system_get_ns();
for (i = 0; i < 10000000; i++) {
g_sum += i;
}
g_calc_end = 1; // Calculation complete flag
g_time = system_get_ns() - g_time;
vTaskDelete(NULL);
}
xTaskCreate(CalcTask, "task2", 128, &g_Task2Info, osPriorityNormal, NULL);
static struct TaskPrintInfo g_Task2Info = {0, 16, "Task2"};
Experiment Results
After downloading the code to the board, you can see the calculation results displayed on the first line, and the time taken to calculate the numbers displayed on the second line. The PrintTask preempts CPU resources even when the CalcTask has not finished calculating, causing the calculation to be slightly slower. Here, based on the calculation results, I added a simple delay vTaskDelay(3000); // Enter block
to make the PrintTask block at the beginning and not preempt CPU resources, which speeds up the calculation a bit. Of course, this is just an example with certain flaws, used for learning purposes.
End of this article!

