当前位置: 首页 > news >正文

FreeRTOS 手动移植教程(五):信号量 —— 任务同步与中断通知的优雅解决方案

在上一篇文章中,我们学习了队列,实现了按键中断与任务之间的数据传递。但在很多场景下,我们只需要传递“某个事件发生了”的信号,而不需要附带具体数据。此时,更轻量、更直接的机制——信号量便派上了用场。本篇将详细讲解二值信号量与计数信号量,并通过实验展示任务同步与中断通知的实际用法。


一、信号量与队列的区别

队列是“有数据”的通信:发送方把数据拷贝到队列中,接收方再从队列中取出数据。而信号量更像一个“计数器”,它只负责维护一个计数值,用来表示某个事件发生了多少次,或者某个资源还有多少个可用。信号量不存储数据,因此更轻量。

常见的信号量有两类:

  • 二值信号量:计数值只能为 0 或 1,常用于任务与中断之间的同步(例如“按键按下了,请立即处理”)。
  • 计数信号量:计数值可大于 1,常用于资源管理(例如“还有 3 个缓冲区可用”)。

二、信号量的关键 API

功能API 名称说明
创建二值信号量xSemaphoreCreateBinary初始计数值为 0
创建计数信号量xSemaphoreCreateCounting需指定最大计数值和初始计数值
给出信号量(任务)xSemaphoreGive计数值加 1
给出信号量(中断)xSemaphoreGiveFromISR中断中使用的版本
获取信号量(任务)xSemaphoreTake若计数值 > 0 则减 1 并返回 pdTRUE;否则阻塞等待
获取信号量(中断)xSemaphoreTakeFromISR中断中使用的版本(极少使用)
删除信号量vSemaphoreDelete释放信号量占用的内存

注意:互斥量(Mutex)也是通过信号量 API 创建的,但它的行为更复杂,我们将在下一篇文章中专门讨论。


三、硬件准备与优先级配置

本章实验仍使用PA0 按键PC13 LED。硬件连接与前文完全相同,BSP 文件(bsp_led.cbsp_key.cbsp_exti.c)也沿用之前的实现。请确保在main()函数开头调用:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

以设置 4 位抢占优先级,与我们的 FreeRTOS 配置严格匹配。


四、二值信号量:实现中断到任务的精确同步

4.1 实验目标

每按一次按键,PA0 触发外部中断,在中断服务函数中给出一个二值信号量;一个专门的任务阻塞等待该信号量,获取后翻转一次 LED。这样即可实现“按一下、闪一下”的精确同步,比用队列传递空消息更加简洁。

4.2 中断服务函数

stm32f10x_it.c中实现EXTI0_IRQHandler,使用xSemaphoreGiveFromISR

#include"stm32f10x_it.h"#include"FreeRTOS.h"#include"semphr.h"/* 外部变量:在 main.c 中定义的信号量句柄 */externSemaphoreHandle_t xBinarySemaphore;voidEXTI0_IRQHandler(void){BaseType_t xHigherPriorityTaskWoken=pdFALSE;if(EXTI_GetITStatus(EXTI_Line0)!=RESET){/* 给出二值信号量 */xSemaphoreGiveFromISR(xBinarySemaphore,&xHigherPriorityTaskWoken);EXTI_ClearITPendingBit(EXTI_Line0);}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}

4.3 任务代码与 main 函数

#include"stm32f10x.h"#include"FreeRTOS.h"#include"task.h"#include"semphr.h"#include"bsp_led.h"#include"bsp_exti.h"/* 二值信号量句柄 */SemaphoreHandle_t xBinarySemaphore=NULL;/* LED 控制任务:等待信号量 */voidvLedTask(void*pvParameters){while(1){/* 阻塞直到信号量可用(无限等待) */if(xSemaphoreTake(xBinarySemaphore,portMAX_DELAY)==pdTRUE){LED3_Toggle();// 翻转一次 LED}}}intmain(void){/* 必须首先设置优先级分组 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);LED_InitAll();// 初始化 LED (PC13)EXTI0_Init();// 初始化 PA0 按键中断/* 创建二值信号量,初始计数值为 0 */xBinarySemaphore=xSemaphoreCreateBinary();if(xBinarySemaphore==NULL){while(1);// 创建失败则停止}xTaskCreate(vLedTask,"Led",128,NULL,1,NULL);vTaskStartScheduler();while(1);}

4.4 实验现象

上电后 LED 保持熄灭。每按一次 PA0 按键,LED 翻转一次状态(灭→亮或亮→灭)。不按键时,LED 任务因获取不到信号量而一直阻塞,完全不占用 CPU。这种“中断通知任务”的模式是实际项目中最常用的同步手段之一。


五、计数信号量:管理有限资源

5.1 使用场景

设想系统中有 3 个相同的通信缓冲区,任务使用一个缓冲区发送数据,用完后归还。计数信号量可以记录当前还有多少个缓冲区可用:初始计数值为 3,获取一个减少 1,归还一个增加 1。当计数值为 0 时,尝试获取的任务将被阻塞,直到有任务归还资源。

本章我们用按键事件来模拟资源的申请与释放,直观展示计数信号量的作用。

5.2 代码实现

实验中仍通过按键中断发送队列消息(记录按键事件),由一个任务接收队列消息后尝试获取计数信号量。若获取成功,LED 翻转一次(模拟使用资源),延时一小段时间后归还信号量;若获取失败,说明暂时无可用资源,直接忽略。

#include"stm32f10x.h"#include"FreeRTOS.h"#include"task.h"#include"queue.h"#include"semphr.h"#include"bsp_led.h"#include"bsp_exti.h"/* 队列句柄 */QueueHandle_t xKeyQueue=NULL;/* 计数信号量句柄 */SemaphoreHandle_t xCountingSemaphore=NULL;/* 按键处理任务 */voidvKeyTask(void*pvParameters){uint8_tkey_val;while(1){/* 等待按键事件 */if(xQueueReceive(xKeyQueue,&key_val,portMAX_DELAY)==pdTRUE){/* 尝试获取一个资源(非阻塞) */if(xSemaphoreTake(xCountingSemaphore,0)==pdTRUE){LED3_Toggle();// 模拟使用资源vTaskDelay(pdMS_TO_TICKS(300));// 模拟占用 300msxSemaphoreGive(xCountingSemaphore);// 归还资源}else{/* 无可用资源,本实验中不做处理 */}}}}/* 中断服务函数:按键按下时发送队列消息 */voidEXTI0_IRQHandler(void){BaseType_t xHigherPriorityTaskWoken=pdFALSE;if(EXTI_GetITStatus(EXTI_Line0)!=RESET){uint8_tevent=1;xQueueSendFromISR(xKeyQueue,&event,&xHigherPriorityTaskWoken);EXTI_ClearITPendingBit(EXTI_Line0);}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}intmain(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);LED_InitAll();EXTI0_Init();/* 创建队列,用于接收按键事件 */xKeyQueue=xQueueCreate(5,sizeof(uint8_t));if(xKeyQueue==NULL)while(1);/* 创建计数信号量:最大计数值 3,初始计数值 3 */xCountingSemaphore=xSemaphoreCreateCounting(3,3);if(xCountingSemaphore==NULL)while(1);xTaskCreate(vKeyTask,"KeyTask",128,NULL,1,NULL);vTaskStartScheduler();while(1);}

5.3 实验现象与解释

  • 上电后,LED 初始熄灭。
  • 快速连续按下按键,LED 会翻转最多 3 次(因为只有 3 个资源可用)。
  • 继续按键,由于资源已被用尽(计数值为 0),xSemaphoreTake(sem, 0)返回pdFALSE,LED 不再翻转。
  • 等待约 300ms 后,第一个资源被释放,此时再按键又可以翻转一次。

这个实验清楚地展示了计数信号量对有限资源的保护作用。在实际项目中,可以将“翻转 LED”替换为操作实际外设(如 DMA 通道、串口发送缓冲等),实现安全的多任务资源共享。


六、信号量使用注意事项

  1. 二值信号量不能用于互斥
    二值信号量可能导致优先级反转,且不提供优先级继承。如果是为保护一个共享变量,请使用下一篇文章将要介绍的互斥量(Mutex)

  2. 中断中必须使用 FromISR 版本
    在中断服务函数中给出信号量时,必须使用xSemaphoreGiveFromISR,并使用portYIELD_FROM_ISR触发可能需要的上下文切换。

  3. 初始计数值的意义
    二值信号量创建后计数值为 0,适合“等待事件发生”的模式。如果希望任务一开始就能运行一次,可在创建后立即调用xSemaphoreGive

  4. 计数信号量的最大值
    创建计数信号量时指定的最大值是一个“软上限”,用来检测编程错误。如果你在计数值达到最大值时再次调用xSemaphoreGive,操作会返回pdFAIL(在调试中可借此发现资源归还次数过多的 bug)。

  5. 启用计数信号量的宏
    要使用计数信号量,必须在FreeRTOSConfig.h中将configUSE_COUNTING_SEMAPHORES设置为 1,这在第一篇的配置文件中已经完成。


七、总结

本篇介绍了 FreeRTOS 中最常用的两种信号量:

  • 二值信号量:轻量的事件标志,是中断通知任务的首选方式;
  • 计数信号量:管理有限资源的计数器,确保系统资源不会被超额分配。

信号量与队列互为补充,是构建复杂 RTOS 应用的基础构件。在下一篇文章中,我们将直面多任务共享资源的挑战,学习互斥量以及它如何通过优先级继承机制优雅地解决优先级反转问题。


下一篇:FreeRTOS 互斥量 —— 保护共享资源与优先级继承。

http://www.gsyq.cn/news/1456904.html

相关文章:

  • Harness Hooks机制:实现Agent行为实时干预与校验
  • 虚幻引擎5时代,从Cascade迁移到Niagara的完整避坑指南(含转换插件实战)
  • Debian 11 Bullseye 上手体验:从内核 5.10 到 LibreOffice 7.0,新版本带来了哪些惊喜?
  • 告别“权限不足”:手把手教你用CobaltStrike的Bypass UAC模块搞定Windows提权
  • 2026年重庆保姆推荐榜单:住家保姆/白班保姆/半天保姆/全天保姆/照顾老人与小孩保姆公司深度解析与优质服务之选 - 品牌企业推荐师(官方)
  • Blender-Curve
  • 告别网盘限速烦恼!9大主流平台直链下载神器LinkSwift完全指南
  • Unity新手必看:5分钟搞懂编辑器窗口布局,别再对着界面发懵了
  • 保姆级教程:在银河麒麟V10系统上,为飞腾FT2000 ARM64平台手动编译grub2(附完整模块清单)
  • Agent的记忆系统
  • 以 Wine Recognition 数据集为例:AI 论文实验部分怎么设计与撰写
  • 2026年现阶段,河北锌钢护栏实力源头厂家综合评估:宇轩金属制品靠谱吗? - 2026年企业资讯
  • 2026年近期,陕西地区液体包装机平台推荐哪家?这份综合指南为您解析 - 2026年企业资讯
  • 杰理之spdif 信息位给过来的采样率信息不正确【篇】
  • Win Server 2019远程桌面多用户登录踩坑实录:从RDPWrap配置到组策略避坑
  • 理工科论文避坑指南:能精准生成公式图表、参考文献真实可溯源的 5 款 AI 工具实测盘点
  • 杰理之打开广播,会报死机【篇】
  • YOLOv5猫狗检测实战:除了训练,你的模型部署和优化思路准备好了吗?
  • 深入解析jsdiff:JavaScript文本差异比对的终极解决方案
  • 企业级MR平台AI赋能升级路径(2024 Gartner验证的3层架构模型)
  • RapidOCR深度解析:从毫秒级响应到微秒级突破的实时推理架构揭秘
  • AI用于PLC可视化编程,靠谱吗?
  • Ubuntu 18.04下Tesla M40显卡驱动安装避坑:BIOS里这个‘Above 4G Decoding’开关千万别关
  • SpringBoot多数据源实战:dynamic-datasource完整配置与最佳实践指南
  • 3分钟告别激活弹窗:KMS_VL_ALL_AIO智能激活方案完全指南
  • 手机AI应用如何改变我们的日常交互方式
  • 2026 滁州卫生间漏水、外墙、楼顶、地下室、阳光房渗漏维修师傅推荐|同城附近上门防水补漏公司测评 - 防水百科
  • 免费开源图片去重神器:AntiDupl.NET 终极指南帮你告别重复照片困扰
  • iPaaS平台哪家好?五条iPaaS技术路线的选择逻辑
  • 终极LyricsX配置指南:macOS歌词工具完全设置手册