freertos线程安全的printf方式
FreeRTOS 提供了一个非常有用的API函数 vTaskSuspendAll() 和 xTaskResumeAll() 来实现对任务调度器的全局锁定和解锁。这样就可以在多个任务或中断中安全地使用 printf() 函数。

以下是一个使用 vTaskSuspendAll() 和 xTaskResumeAll() 来实现 printf() 线程安全的例子:

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdio.h>
 
void safe_printf(const char* format, ...)
{
    TickType_t xTicksToWait = pdMS_TO_TICKS(100); // 等待全局解锁的时间
    portBASE_TYPE xYieldRequired;
    va_list args;
 
    // 全局锁定调度器,防止中断
    vTaskSuspendAll();
 
    // 使用可变参数列表
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
 
    // 如果需要,切换到其他任务
    xYieldRequired = pdFALSE;
 
    // 全局解锁调度器,允许中断
    if (xTaskResumeAll() != pdFALSE) {
        xYieldRequired = pdTRUE;
    }
 
    // 如果需要,切换到其他任务
    portEND_SWITCHING_ISR(xYieldRequired);
}
 
void task1(void *pvParameters)
{
    for( ;; )
    {
        safe_printf("Task 1 is running\n");
        vTaskDelay(1000);
    }
}
 
void task2(void *pvParameters)
{
    for( ;; )
    {
        safe_printf("Task 2 is running\n");
        vTaskDelay(1000);
    }
}
 
int main(void)
{
    xTaskCreate(task1, "Task 1", 1000, NULL, 1, NULL);
    xTaskCreate(task2, "Task 2", 1000, NULL, 2, NULL);
 
    vTaskStartScheduler();
 
    return 0;
}

在这个例子中,safe_printf() 函数通过调用 vTaskSuspendAll() 来全局锁定调度器,确保在 printf() 执行期间不会发生任务切换。在 printf() 执行完毕后,通过调用 xTaskResumeAll() 来尝试全局解锁调度器。如果解锁操作成功,它会返回一个非零值,表示需要进行一次任务切换;否则,如果解锁失败(可能是因为有其他任务正在执行 xTaskResumeAll()),它会返回零,表示不需要进行切换。

请注意,这个方法在打印大量数据或在打印期间频繁进行任务切换时可能会导致性能下降。在这种情况下,可以考虑使用其他硬件支持的串行数据输出方法,例如使用 DMA 和双缓冲区机制,或者使用专门设计的硬件串行接口(如 USB CDC)。

更多推荐