FreeRTOS 使用方式与典型场景:两个可运行示例
使用方式与典型场景:两个可运行示例
示例一:生产者与消费者任务配合队列
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#define QUEUE_LEN 8
static QueueHandle_t xQueue;
static void vProducer(void *pvParameters)
{
uint16_t value = 0;
for (;;) {
value++;
/* 队列满则最多等 100ms,超时就丢一帧并计数 */
if (xQueueSend(xQueue, &value, pdMS_TO_TICKS(100)) != pdPASS) {
/* 这里记一次丢帧 */
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
static void vConsumer(void *pvParameters)
{
uint16_t value;
for (;;) {
/* portMAX_DELAY:一直阻塞,直到有数据 */
if (xQueueReceive(xQueue, &value, portMAX_DELAY) == pdPASS) {
/* 处理 value */
}
}
}
void app_create_tasks(void)
{
xQueue = xQueueCreate(QUEUE_LEN, sizeof(uint16_t));
configASSERT(xQueue != NULL);
xTaskCreate(vProducer, "prod", 256, NULL, 2, NULL);
xTaskCreate(vConsumer, "cons", 256, NULL, 3, NULL);
}
要点:xTaskCreate 的栈深度参数以字为单位,256 在 32 位内核上等于 1024 字节;消费者优先级更高,数据一到就能被调度。
示例二:中断服务程序里使用 FromISR API
static QueueHandle_t xUartQueue;
void USART1_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint8_t byte;
if (/* 接收寄存器非空 */) {
byte = /* 读数据寄存器 */;
xQueueSendFromISR(xUartQueue, &byte, &xHigherPriorityTaskWoken);
}
/* 清除中断标志 */
/* 若唤醒了一个更高优先级任务,退出中断后立刻切换过去 */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
三条铁律:中断里只能用带 FromISR 后缀的 API;xHigherPriorityTaskWoken 必须先初始化为 pdFALSE 再取地址传入;最后要用 portYIELD_FROM_ISR(较早的端口里是 portEND_SWITCHING_ISR)把这个标志用掉,否则被唤醒的高优先级任务要等到下一个 tick 才会运行。此外,中断的抢占优先级数值必须大于等于 configMAX_SYSCALL_INTERRUPT_PRIORITY,超出这个范围的中断不能调用任何 FreeRTOS API。
适合与不适合的场景。
- 适合:需要多个并发逻辑、有硬实时响应要求、MCU 有足够 RAM 给每个任务留一份栈的场合;需要 tickless 低功耗的电池设备;需要跨厂商平台保持代码一致的场合。
- 不适合:RAM 极紧且逻辑简单的低端 MCU(几 KB 内存里塞任务栈很勉强);纯顺序执行、没有并发需求的场景;要求微秒级确定性且完全不能接受动态分配的场合——这类要么走全静态方案,要么考虑经过认证的更严格内核。

