Implementing Task Management and Binary Semaphores in FreeRTOS for STM32
Task Execution Mechanism
When a task is successfully created, it's placed in the ready queue. Once the scheduler starts, it selects the highest-priority task from this queue for execution. If multiple tasks share the same priority, they execute in the order they were created.
During execution, a task transitions to the running state and is removed from the ready queue. If the task encounters functions like vTaskDelay, it moves to the blocked queue, allowing the scheduler to execute other tasks.
When the blocking condition is resolved, the task returns to the ready queue. If this task has high priority, it can preempt the currently executing task and immediately gain CPU control.
Stack Overflow Detection
Stack overflow detection serves debugging purposes and should be disabled in production code. Before implementing detection functions, modify the appropriate macro definitions. The second stack checking method is typically employed.
void vApplicationStackOverflowHook(TaskHandle_t taskHandle, char *taskName)
{
printf("Task: %s -> Stack overflow detected\r\n", taskName);
printf("Remaining stack space: %d\r\n",
(int)uxTaskGetStackHighWaterMark(taskHandle));
// Halt execution when stack overflow occurs
while(1);
}
This hook function performs checks during context switching, introducing slight latency. Detection only occurs when context switches happen, so immediate overflow identification isn't guaranteed. Despite this limitation, the mechanism remains valuable for identifying coding errors and enhancing application reliability.
Binary Semaphore Implementation for Task Synchronization
Task synchronization enables one task to wait for another's completion before proceeding. With binary semaphores, ownership determines execution rights. For instance, Task B requires acquiring a semaphore before execution, waiting until Task A releases it.
Implementation Steps
- Include the semaphore header:
#include "semphr.h"
- Declare a binary semaphore handle:
SemaphoreHandle_t syncSemaphore;
- Create the binary semaphore in main function:
syncSemaphore = xSemaphoreCreateBinary();
- Release the semaphore using:
xSemaphoreGive(syncSemaphore);
- Acquire the semaphore with timeout handling:
if(xSemaphoreTake(syncSemaphore, 1000) == pdTRUE)
{
printf("Button pressed, semaphore acquired\r\n");
}
Complete Implementation Example
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "adc.h"
#include "kqm.h"
#include "dht11.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
SemaphoreHandle_t syncSemaphore;
TaskHandle_t sensorTaskHandle;
void sensorTask(void *param)
{
while(1)
{
taskENTER_CRITICAL();
Dht11ReadData();
taskEXIT_CRITICAL();
KQMDealData();
ADC_DataFliter();
vTaskDelay(1000);
}
}
TaskHandle_t ledTaskHandle;
void ledTask(void *param)
{
uint8_t counter = 0;
while(1)
{
if(xSemaphoreTake(syncSemaphore, 1000) == pdTRUE)
{
printf("Button pressed, semaphore acquired\r\n");
}
else
{
printf("Semaphore acquisition timeout\r\n");
}
counter++;
printf("counter=%d\r\n", counter);
Led_Toggle(1);
vTaskDelay(200);
}
}
TaskHandle_t keyTaskHandle;
void keyTask(void *param)
{
while(1)
{
switch(key_getvalue())
{
case 1: vTaskSuspend(ledTaskHandle); break;
case 2: vTaskResume(ledTaskHandle); break;
case 3: xSemaphoreGive(syncSemaphore); break;
case 4: break;
}
vTaskDelay(10);
}
}
int main()
{
Led_Init();
Key_init();
Usart1_Config();
ADC_Config();
Kqm_U4Config();
DHT11_Mode();
BaseType_t result = pdPASS;
syncSemaphore = xSemaphoreCreateBinary();
result = xTaskCreate(sensorTask, "SensorTask", 200, NULL, 1, &sensorTaskHandle);
result = xTaskCreate(ledTask, "LEDToggle", 100, NULL, 3, &ledTaskHandle);
if(result == pdPASS)
{
printf("LEDToggle task created successfully\r\n");
}
result = xTaskCreate(keyTask, "KeyTask", 100, NULL, 2, &keyTaskHandle);
vTaskStartScheduler();
while(1);
}