FreeRTOS 二值信号量的使用与示例

FreeRTOS 二值信号量的使用与示例

一、FreeRTOS 二值信号量的核心概念

二值信号量是 FreeRTOS 中最简单的信号量类型,仅包含0(空) 和1(满) 两个状态:

  • 当信号量为「满」时,xSemaphoreTake() 可获取信号量,信号量变为「空」;

  • 当信号量为「空」时,xSemaphoreGive() 可释放信号量,信号量变为「满」;

  • 常用于同步场景(任务间 / 中断与任务),也可用于简单的互斥(但推荐用互斥信号量做互斥,因二值信号量无优先级继承)。

二、二值信号量的代码实现(基于 STM32+FreeRTOS)

以下示例包含两个核心场景:中断与任务同步(按键中断通知任务处理)、任务间同步。

1. 头文件与全局句柄定义

#include "stm32f10x.h"

#include "FreeRTOS.h"

#include "semphr.h"

// 二值信号量句柄(按键中断同步用)

SemaphoreHandle_t xBinarySem_Key;

// 二值信号量句柄(任务间同步用)

SemaphoreHandle_t xBinarySem_Task;

// 按键中断初始化(PA0 外部中断0)

void Key_EXTI_Init(void);

// 任务函数声明

void vTask_Key_Process(void *pvParameters);

void vTask_Producer(void *pvParameters);

2. 信号量创建与任务启动(main 函数)

int main(void)

{

   // 1. 硬件初始化(串口、按键中断等)

   USART_Init();

   Key_EXTI_Init();

  

   // 2. 创建二值信号量(动态创建,初始为空)

   xBinarySem_Key = xSemaphoreCreateBinary();

   xBinarySem_Task = xSemaphoreCreateBinary();

  

   // 检查信号量创建是否成功

   if((xBinarySem_Key != NULL) && (xBinarySem_Task != NULL))

   {

       // 3. 创建任务

       // 按键处理任务(优先级2)

       xTaskCreate(vTask_Key_Process, "KeyProcess", 128, NULL, 2, NULL);

       // 生产者任务(优先级1)

       xTaskCreate(vTask_Producer, "Producer", 128, NULL, 1, NULL);

      

       // 4. 启动FreeRTOS调度器

       vTaskStartScheduler();

   }

  

   // 若调度器启动失败(如内存不足),进入死循环

   while(1);

}

3. 按键中断服务函数(发送信号量)

中断中必须使用xSemaphoreGiveFromISR()(而非xSemaphoreGive()),避免中断上下文冲突:

void EXTI0_IRQHandler(void)

{

   BaseType_t xHigherPriorityTaskWoken = pdFALSE; // 标记是否需要切换任务

  

   // 确认中断触发(消抖简化版)

   if(EXTI_GetITStatus(EXTI_Line0) != RESET)

   {

       // 向二值信号量发送信号(中断中)

       xSemaphoreGiveFromISR(xBinarySem_Key, &xHigherPriorityTaskWoken);

      

       // 清除中断标志位

       EXTI_ClearITPendingBit(EXTI_Line0);

      

       // 若高优先级任务就绪,触发上下文切换

       portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

   }

}

4. 任务实现

(1)按键处理任务(接收信号量)
void vTask_Key_Process(void *pvParameters)

{

   for(;;)

   {

       // 等待信号量:阻塞时间portMAX_DELAY(永久等待)

       if(xSemaphoreTake(xBinarySem_Key, portMAX_DELAY) == pdTRUE)

       {

           // 信号量获取成功,处理按键逻辑

           printf("按键中断触发,任务开始处理...rn");

           // 模拟按键处理(如控制LED、读取数据等)

           for(int i=0; i<3; i++)

           {

               printf("按键处理中...%drn", i+1);

               vTaskDelay(pdMS_TO_TICKS(200)); // 延时200ms

           }

           printf("按键处理完成rn");

       }

   }

}
(2)生产者任务(任务间同步,发送信号量)
void vTask_Producer(void *pvParameters)

{

   for(;;)

   {

       // 模拟生产数据(每3秒生产一次)

       vTaskDelay(pdMS_TO_TICKS(3000));

       printf("生产者任务:数据生产完成,通知消费任务rn");

      

       // 向任务间同步的二值信号量发送信号(任务中用xSemaphoreGive)

       xSemaphoreGive(xBinarySem_Task);

      

       // 可选:消费任务可单独创建,此处简化为在当前任务演示接收

       if(xSemaphoreTake(xBinarySem_Task, pdMS_TO_TICKS(100)) == pdTRUE)

       {

           printf("消费任务(简化):接收数据,开始处理rn");

       }

   }

}

三、二值信号量的核心用途与举例

二值信号量的核心价值是 「同步」(让一个执行流等待另一个执行流的触发),而非复杂的资源管理,以下是典型场景:

1. 中断与任务同步(最常用)

  • 场景:中断处理应尽量简短(避免抢占 CPU),仅做「信号通知」,复杂逻辑交给任务处理。

  • 例子:

    • 按键中断:中断仅标记 “按键被按下”,任务负责消抖、执行按键逻辑(如控制电机、打印日志);

    • 串口接收中断:中断仅缓存 1 字节数据,任务负责拼接数据、解析协议;

    • 定时器中断:中断仅触发 “定时到”,任务负责执行周期性任务(如传感器采集)。

  • 优势:减少中断占用时间,避免嵌套中断风险,任务级逻辑更易维护。

2. 任务间同步

  • 场景:一个任务等待另一个任务完成某操作后再执行。

  • 例子:

    • 生产消费模型(简化版):生产者任务完成数据采集后,发送信号量通知消费者任务处理数据;

    • 初始化同步:系统启动时,任务 A 完成硬件初始化后发送信号量,任务 B(依赖硬件)才开始运行;

    • 步骤化任务:任务 1 完成 “数据采集”→发信号量→任务 2 执行 “数据处理”→发信号量→任务 3 执行 “数据上传”。

3. 简单的资源互斥(不推荐,仅临时使用)

  • 场景:两个任务共享一个独占资源(如单串口、单传感器)。

  • 例子:任务 A 和任务 B 都需要打印日志到串口,通过二值信号量保证同一时间只有一个任务访问串口。

  • 缺点:无优先级继承机制,可能导致 “优先级反转”(低优先级任务占用资源,高优先级任务等待);推荐用互斥信号量(xSemaphoreCreateMutex())做互斥。

四、关键注意事项

  1. API 使用场景:
  • 任务中:xSemaphoreTake() / xSemaphoreGive();

  • 中断中:xSemaphoreTakeFromISR()(极少用) / xSemaphoreGiveFromISR()。

  1. 阻塞时间:xSemaphoreTake()的第二个参数为阻塞时间(tick 数),portMAX_DELAY表示永久等待,0表示不等待。

  2. 信号量创建:xSemaphoreCreateBinary()创建的信号量初始为空,需先Give才能Take;新版 FreeRTOS 也可使用xSemaphoreCreateBinaryStatic()(静态创建,避免内存分配失败)。

五、运行效果

  • 按键按下时,中断触发→发送信号量→按键处理任务执行,打印 “按键处理中”;

  • 每 3 秒,生产者任务发送信号量→模拟消费任务处理数据,打印 “接收数据,开始处理”;

  • 任务间无冲突,中断处理耗时极短,符合嵌入式实时性要求。

posted @ 2025-12-04 14:38  Afangdong  阅读(633)  评论(0)    收藏  举报