2025最新超详细FreeRTOS入门教程:第五章 FreeRTOS信号量
2025最新超详细FreeRTOS入门教程:第五章 FreeRTOS信号量
摘要
在前一章中,我们学习了 消息队列(Queue),它主要用于任务之间传递数据。但在某些场景中,我们并不需要传递数据,只需要实现一种 同步机制(比如“有资源时允许访问”,“某事件发生后允许继续执行”),这时使用 信号量(Semaphore) 更加合适。
FreeRTOS 的信号量基于 队列机制 实现,是实现 任务同步、事件触发、共享资源访问保护 的重要工具。本章将带你全面理解 FreeRTOS 信号量的概念、类型、API 使用、常见应用场景以及和消息队列的区别。
文章目录
- 2025最新超详细FreeRTOS入门教程:第五章 FreeRTOS信号量
- 摘要
- 一、信号量的基本概念
- 功能
- 二、信号量的类型
- 三、API 使用
- 1. 创建信号量
- 2. 获取信号量(Take)
- 3. 释放信号量(Give)
- 四、二值信号量的应用
- 示例:中断通知任务
- 五、计数型信号量的应用
- 示例:任务统计事件次数
- 六、互斥信号量的应用
- 示例:串口输出保护
- 七、信号量与队列的区别
- 八、信号量状态监控
- 九、常见问题与解决方法
- 十、经验总结
- 十一、总结
一、信号量的基本概念
📌 定义:信号量(Semaphore)是一种用于 多任务同步与互斥 的机制,它本质上是一个特殊的消息队列,容量为 1 或更大,但存储的不是数据,而是 一个计数值。
功能
- 任务间同步:一个任务通知另一个任务继续执行
- 中断与任务同步:ISR 中发信号,任务收到后继续执行
- 资源管理:实现类似操作系统的 P/V 操作,保证共享资源不会被同时访问
二、信号量的类型
FreeRTOS 提供了三种主要信号量:
类型 | 特点 | 典型应用 |
---|---|---|
二值信号量 | 值为 0 或 1,类似事件触发 | 中断事件通知 |
计数型信号量 | 可累加,允许多个事件存储 | 事件计数、资源池 |
互斥信号量 | 特殊的二值信号量,带优先级继承机制 | 防止共享资源竞争 |
三、API 使用
1. 创建信号量
SemaphoreHandle_t xSemaphoreCreateBinary(void); // 二值信号量
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount); // 计数信号量
SemaphoreHandle_t xSemaphoreCreateMutex(void); // 互斥信号量
2. 获取信号量(Take)
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait
);
xTicksToWait
:阻塞等待时间- 返回值
pdPASS
表示获取成功
3. 释放信号量(Give)
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
ISR 中使用:
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken
);
四、二值信号量的应用
示例:中断通知任务
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"SemaphoreHandle_t xBinarySemaphore;void vTaskHandler(void *pvParameters)
{for(;;){if(xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdPASS){printf("任务被中断唤醒!\n");}}
}void EXTI0_IRQHandler(void) // 按键中断
{BaseType_t xHigherPriorityTaskWoken = pdFALSE;xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}int main(void)
{HAL_Init();SystemClock_Config();xBinarySemaphore = xSemaphoreCreateBinary();xTaskCreate(vTaskHandler, "Handler", 128, NULL, 2, NULL);vTaskStartScheduler();while(1) {}
}
运行结果
- 当按键触发中断时,任务
Handler
被唤醒,执行相应逻辑
五、计数型信号量的应用
示例:任务统计事件次数
SemaphoreHandle_t xCountingSemaphore;void vTaskConsumer(void *pvParameters)
{for(;;){if(xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdPASS){printf("处理一个事件\n");}}
}void EXTI0_IRQHandler(void)
{BaseType_t xHigherPriorityTaskWoken = pdFALSE;xSemaphoreGiveFromISR(xCountingSemaphore, &xHigherPriorityTaskWoken);portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}int main(void)
{HAL_Init();SystemClock_Config();xCountingSemaphore = xSemaphoreCreateCounting(10, 0);xTaskCreate(vTaskConsumer, "Consumer", 128, NULL, 2, NULL);vTaskStartScheduler();
}
- 每次按键触发中断,信号量计数值 +1
- 消费任务依次取出,处理事件
六、互斥信号量的应用
互斥信号量(Mutex)用于 资源保护,保证同一时刻只有一个任务访问共享资源。
其区别在于 支持优先级继承:当低优先级任务占有资源时,高优先级任务会暂时提升低优先级任务的优先级,避免“优先级反转”。
示例:串口输出保护
SemaphoreHandle_t xMutex;void vTaskA(void *pvParameters)
{for(;;){xSemaphoreTake(xMutex, portMAX_DELAY);printf("Task A 输出...\n");vTaskDelay(500);xSemaphoreGive(xMutex);vTaskDelay(1000);}
}void vTaskB(void *pvParameters)
{for(;;){xSemaphoreTake(xMutex, portMAX_DELAY);printf("Task B 输出...\n");vTaskDelay(500);xSemaphoreGive(xMutex);vTaskDelay(1000);}
}
七、信号量与队列的区别
特性 | 队列 | 信号量 |
---|---|---|
传递数据 | ✅ | ❌ |
同步机制 | 次要 | ✅ |
ISR 使用 | 支持 | 支持 |
内部实现 | FIFO 缓冲区 | 基于队列封装 |
典型应用 | 生产者-消费者 | 事件触发、资源互斥 |
八、信号量状态监控
FreeRTOS 提供 uxSemaphoreGetCount()
来查询信号量计数值:
UBaseType_t uxSemaphoreGetCount(SemaphoreHandle_t xSemaphore);
示例:
printf("当前信号量数量: %d\n", uxSemaphoreGetCount(xCountingSemaphore));
九、常见问题与解决方法
问题 | 原因 | 解决方法 |
---|---|---|
任务无法被唤醒 | 信号量未 Give | 确认 ISR 或任务正确释放信号量 |
死锁 | 任务未释放信号量 | 确保 xSemaphoreGive 在所有逻辑路径中调用 |
优先级反转 | 使用普通二值信号量 | 使用 Mutex |
中断中报错 | 使用了非 ISR API | 替换为 xSemaphoreGiveFromISR |
十、经验总结
📌 开发建议
- 二值信号量 适合任务同步,特别是中断与任务之间的触发关系
- 计数信号量 适合事件计数,避免事件丢失
- 互斥信号量 适合资源保护,优先级继承机制能有效防止优先级反转
- 尽量避免在高频中断中频繁使用信号量,可能导致系统负担过重
十一、总结
通过本章学习,你已经掌握:
- FreeRTOS 提供的三类信号量(Binary、Counting、Mutex)
- 信号量的 API 使用与应用场景
- 任务同步与共享资源保护的方法
- 信号量与队列的区别
信号量是 RTOS 最常用的同步机制,后续我们会进一步学习 互斥量、事件组 等更复杂的同步方法。
🔗 FreeRTOS专栏👉 下一章:2025最新超详细FreeRTOS入门教程:第六章 FreeRTOS互斥量 ——进一步探讨多任务环境下的资源保护与并发访问。