任务间为什么要通信

多任务系统里,任务之间必须交换数据和同步。直接用全局变量会有竞态:一个任务在读、另一个在写,读到半更新的数据就崩了。FreeRTOS 提供了一套原语:消息队列传数据,信号量做同步,互斥量保护共享资源。

能传值就别传指针。队列传值自带拷贝,安全;传指针得自己管生命周期。
消息队列与信号量配图:中央发光队列方块横排,周围圆形信号量锁图标以光线连接
队列 + 信号量 + 互斥量

这篇把 FreeRTOS 四种原语讲透。

消息队列 Queue

队列是任务间传数据的主力:

// 创建:长度 10,每项 4 字节(一个 int)
QueueHandle_t q = xQueueCreate(10, sizeof(int));

// 发送(中断里用 FromISR 版本)
int v = 42;
xQueueSend(q, &v, portMAX_DELAY);

// 接收
int r;
xQueueReceive(q, &r, pdMS_TO_TICKS(100));
队列要点
  • 拷贝语义发送时把数据拷进队列,不共享指针。
  • FIFO先进先出。
  • 满/空阻塞发送满了等,接收空了等。

二值信号量

二值信号量只有 0/1 两个状态,用来做"中断通知任务":

// ISR 里 give,任务里 take
SemaphoreHandle_t s = xSemaphoreCreateBinary();

// 中断:GPIO 按下
void EXTI_IRQHandler() {
  BaseType_t hpw = pdFALSE;
  xSemaphoreGiveFromISR(s, &hpw);
  portYIELD_FROM_ISR(hpw);
}

// 任务:等信号量
void vTask(void *p) {
  while (1) {
    xSemaphoreTake(s, portMAX_DELAY);
    // 处理按键
  }
}

计数信号量

计数信号量维护一个计数,典型用法是资源池:

// 比如 3 个缓冲区,任务拿一个用一个
SemaphoreHandle_t pool = xSemaphoreCreateCounting(3, 3);
xSemaphoreTake(pool, portMAX_DELAY);  // 申请一个
// ...用缓冲区...
xSemaphoreGive(pool);                 // 归还