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

FreeRTOS Semaphore信号量-笔记

FreeRTOS Semaphore信号量-笔记

      • **一、信号量与互斥量的核心区别**
      • **二、二值信号量(Binary Semaphore)**
        • **1. 功能与使用场景**
        • **2. 示例:ADC中断与任务同步**
      • **三、计数信号量(Counting Semaphore)**
        • **1. 功能与使用场景**
        • **2. 示例:ADC双缓冲区管理**
      • **四、互斥量(Mutex)**
        • **1. 功能与使用场景**
        • **2. 示例:保护共享资源**
      • **五、关键注意事项**
      • **六、总结**

在这里插入图片描述


队列的功能是将进程间需要传递的数据存在其中。所以在有的RTOS系统里,队列也被称为邮箱。

一、信号量与互斥量的核心区别

特性信号量(Semaphore)互斥量(Mutex)
用途进程间同步(如事件通知)或资源计数(如ADC双缓冲区)互斥访问共享资源(如保护全局变量或外设)
所有权无所有权(任何任务或ISR均可释放)有所有权(仅持有者可释放)
优先级继承机制(可能导致优先级翻转)(缓解优先级翻转)
是否可中断使用(通过 xSemaphoreGiveFromISR不可(ISR中无法使用)
初始值二值信号量(0或1)或计数信号量(任意值)固定为1(表示资源可用)

信号量和互斥量。信号量和互斥量的实现都是基于队列的,信号量更适用于进程间同步,而互斥量更适用于共享资源的互斥性访问。
在这里插入图片描述


二、二值信号量(Binary Semaphore)

在这里插入图片描述
如果不使用二值信号量,而是使用一个自定义标志变量来实现以上的同步过程,则任务需要不断的查询标志变量的值,而不是像使用二值信号那样可以使任务进入阻塞动态状态。所以使用二值信号量进行进程间同步的效率更高。

1. 功能与使用场景
  • 功能:作为“标志”实现任务同步,例如:
    • ADC中断通知任务:当ADC中断写入缓冲区后,释放二值信号量通知任务处理数据。
    • 任务等待事件:任务阻塞等待信号量,避免忙等待。
  • 创建函数
  // 动态分配xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE );//这是一个宏函数。调用的函数类似于队列(xQueueGenericCreate)创建队列时,调用的也是这个函数。

参数解析

  • ( UBaseType_t ) 1

    • 队列长度:表示队列最多能容纳的“消息”数量。
    • 二进制信号量特性:二进制信号量只能处于“空”(0)或“满”(1)状态,因此队列长度设为 1。
  • semSEMAPHORE_QUEUE_ITEM_LENGTH

    • 队列项大小:定义队列中每个消息的字节长度。
    • 二进制信号量特殊性:该宏被定义为 0(#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U )),表示信号量不存储实际数据,仅用队列的空/满状态表示信号量状态。
  • queueQUEUE_TYPE_BINARY_SEMAPHORE

    • 队列类型:指定此队列用于二进制信号量,而非普通队列或其他类型(如互斥锁或计数信号量)。

  // 静态分配SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer);
  • 操作函数
    • 释放信号量(任务):
    xSemaphoreGive( xSemaphore )    xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

参数解析

  • (QueueHandle_t)(xSemaphore)

    • 信号量即队列:FreeRTOS 的信号量句柄 xSemaphore 实际上是队列句柄(QueueHandle_t)的别名。信号量通过队列实现,其行为由队列的配置参数控制(如长度、项大小等)。
  • NULL

    • 无实际数据:信号量本身不传递数据,仅用队列的空/满状态表示信号量状态。因此,发送的“消息”无需有效数据,使用 NULL 即可。
  • semGIVE_BLOCK_TIME

    • 阻塞时间:定义为 0(#define semGIVE_BLOCK_TIME (0)),表示调用 xSemaphoreGive 时不等待。如果队列已满(信号量已处于“可用”状态),则直接返回错误 errQUEUE_FULL。
  • queueSEND_TO_BACK

    • 入队位置:将“消息”添加到队列的尾部。信号量的顺序无关紧要,因此使用尾部入队是合理的。
  • 返回类型为BaseType_t,pdFALSE或者pdTRUE。


  • 释放信号量(ISR):
xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )    xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )

参数解析

  • xSemaphore
    • 信号量句柄:实际是一个队列句柄(QueueHandle_t)的别名。信号量通过队列实现,其行为由队列的配置参数控制(如长度、项大小等)。
  • pxHigherPriorityTaskWoken
    • 优先级唤醒标志:
    • 类型:BaseType_t *(指向布尔值的指针)。
    • 功能:如果释放信号量导致更高优先级的任务被唤醒,该指针会被设置为 pdTRUE,表示需要在退出中断前请求上下文切换。
    • 可选参数:从 FreeRTOS V7.3.0 开始,此参数可以设为 NULL。
    • 注意:如果释放信号量导致了一个任务解锁,而解锁的任务比当前任务的优先级高,这里就会返回pdTRUE。这就需要在退出ISR之前申请任务调度,以便及时的解锁高优先级的任务。

在这里插入图片描述


  • 获取信号量(任务):
xSemaphoreTake( xSemaphore, xBlockTime )    xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )

参数解析

  • xSemaphore
  • 信号量句柄:实际是一个队列句柄(QueueHandle_t)的别名。信号量通过队列实现,其行为由队列的配置参数控制(如长度、项大小等)。
  • 类型:SemaphoreHandle_t(底层为 QueueHandle_t)。
    ● xBlockTime
  • 阻塞时间:以 Tick 为单位指定任务等待信号量的最长时间。
  • portMAX_DELAY:无限期等待,直到信号量可用。
  • 0:非阻塞模式,立即返回。
  • 转换:使用 pdMS_TO_TICKS(ms) 将毫秒转换为 Tick(例如 pdMS_TO_TICKS(100))。

2. 示例:ADC中断与任务同步
// 创建二值信号量
SemaphoreHandle_t xADCSemaphore = xSemaphoreCreateBinary();// ADC中断服务程序
void ADC_IRQHandler(void) {// 写入数据到缓冲区WriteDataToBuffer();// 释放信号量通知任务xSemaphoreGiveFromISR(xADCSemaphore, NULL);
}// 数据处理任务
void vDataProcessingTask(void *pvParameters) {while (1) {// 等待信号量xSemaphoreTake(xADCSemaphore, portMAX_DELAY);// 处理缓冲区数据ProcessData();}
}

三、计数信号量(Counting Semaphore)

在这里插入图片描述
资源类比成一个餐馆中的四个餐桌。
管理多个共享资源。例如ADC连续数据采集时,一般使用双缓冲区,就可以使用计数信号量来进行管理。

1. 功能与使用场景
  • 功能:管理多个同类型资源(如ADC双缓冲区),允许同时访问多个资源。
  • 创建函数
    // 动态分配
    SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
    // 静态分配
    SemaphoreHandle_t xSemaphoreCreateCountingStatic(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer);
    
  • 操作函数
    • 释放资源
      xSemaphoreGive(xSemaphore);  // 资源计数+1
      
    • 获取资源
      xSemaphoreTake(xSemaphore, xTicksToWait);  // 资源计数-1
      
2. 示例:ADC双缓冲区管理
// 创建计数信号量(2个缓冲区)
SemaphoreHandle_t xADCBufferSemaphore = xSemaphoreCreateCounting(2, 2);// ADC中断服务程序
void ADC_IRQHandler(void) {// 获取一个缓冲区if (xSemaphoreTakeFromISR(xADCBufferSemaphore, NULL) == pdTRUE) {// 写入数据到缓冲区WriteDataToBuffer();// 释放信号量(资源可用)xSemaphoreGiveFromISR(xADCBufferSemaphore, NULL);}
}// 数据处理任务
void vDataProcessingTask(void *pvParameters) {while (1) {// 获取缓冲区资源xSemaphoreTake(xADCBufferSemaphore, portMAX_DELAY);// 处理缓冲区数据ProcessData();// 释放缓冲区资源xSemaphoreGive(xADCBufferSemaphore);}
}

四、互斥量(Mutex)

在这里插入图片描述
在这里插入图片描述

二值信号量更适用于进程间同步,而互斥量更适用于控制对互斥型资源的访问。二值信号量没有优先级继承机制,将二值信号量用于互斥型资源访问时,容易出现优先级翻转问题。而互斥量有优先级继承机制,可以减缓优先级翻转问题。

1. 功能与使用场景
  • 功能:保护共享资源的独占访问(如串口),避免数据竞争。
  • 创建函数
    // 动态分配
    SemaphoreHandle_t xSemaphoreCreateMutex(void);
    // 静态分配
    SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t *pxSemaphoreBuffer);
    
  • 操作函数
    • 释放互斥量(可以释放二值信号量、计数信号量或者是互斥量。)
      xSemaphoreGive(xSemaphore);
      
    • 获取互斥量
      xSemaphoreTake(xSemaphore, xTicksToWait);
      
    • 递归互斥量(允许任务多次获取同一互斥量):
      // 创建
      SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);
      // 释放
      xSemaphoreGiveRecursive(xSemaphore);
      // 获取
      xSemaphoreTakeRecursive(xSemaphore, xTicksToWait);
      
2. 示例:保护共享资源
// 创建互斥量
SemaphoreHandle_t xSharedResourceMutex = xSemaphoreCreateMutex();// 任务A
void vTaskA(void *pvParameters) {while (1) {xSemaphoreTake(xSharedResourceMutex, portMAX_DELAY);// 访问共享资源SharedResource++;xSemaphoreGive(xSharedResourceMutex);}
}// 任务B
void vTaskB(void *pvParameters) {while (1) {xSemaphoreTake(xSharedResourceMutex, portMAX_DELAY);// 访问共享资源SharedResource--;xSemaphoreGive(xSharedResourceMutex);}
}

五、关键注意事项

  1. 优先级翻转问题

    • 二值信号量:无优先级继承机制,可能导致低优先级任务阻塞高优先级任务。
    • 互斥量:通过优先级继承机制缓解问题,但仍需谨慎设计任务优先级。
  2. ISR中的使用限制

    • 信号量:可在ISR中使用(xSemaphoreGiveFromISR)。
    • 互斥量禁止在ISR中使用(因其依赖优先级继承)。
  3. 内存分配选择

    • 动态分配:适合资源充足的系统,但可能产生内存碎片。
    • 静态分配:适合资源受限的嵌入式系统,需手动管理内存。
  4. 调试工具

    • uxSemaphoreGetCount():获取信号量当前值(适用于计数信号量)。
    • xSemaphoreGetMutexHolder():检查互斥量当前持有者。

六、总结

其他相关函数
● xSemaphoreGiveRecursive(用于递归互斥锁的释放)。
●xSemaphoreTakeRecursive()获取二值信号量、计数信号量或者是互斥量。释放递归互斥量依然有一个专用的函数。
●uxSemaphoreGetCount()返回传进去的这个信号量的当前值。
●xSemaphoreGetMutexHolder()作用:返回当前持有指定互斥锁(mutex)的任务的句柄(TaskHandle)。如果互斥锁未被任何任务持有,则返回 NULL。
xSemaphoreTakeFromISR()

  • 选择信号量还是互斥量
    • 同步需求(如事件通知)→ 信号量
    • 资源互斥访问(如保护共享变量)→ 互斥量
  • 避免常见错误
    • 不要在ISR中释放互斥量。
    • 确保每次获取互斥量后最终释放。
    • 使用递归互斥量时,获取与释放需严格配对。

通过合理使用信号量和互斥量,可以高效实现FreeRTOS中的任务同步与资源共享,提升系统的实时性和稳定性。

http://www.xdnf.cn/news/376399.html

相关文章:

  • 项目管理从专家到小白
  • Pale Moon:速度优化的Firefox定制浏览器
  • 棒球裁判员学习指南·棒球1号位
  • 【数据结构与算法】图的基本概念与遍历
  • 嵌入式硬件篇---麦克纳姆轮(简单运动实现)
  • Linux系统入门第十二章 --Shell编程之正则表达式
  • [架构之美]Windows系统安装MySQL 8.0详细图文教程(十八)
  • 论文精读:YOLOE: Real-Time Seeing Anything
  • 从0开始学习大模型--Day05--理解prompt工程
  • 零知识证明:区块链隐私保护的变革力量
  • HTTPS加密握手与加密算法
  • Kotlin 内联函数深度解析:从源码到实践优化
  • 分书问题的递归枚举算法
  • [思维模式-25]:《本质思考力》-6- 马哲的三大规律:对立统一规律、质量互变规律、否定之否定规律,以及在计算机领域中的体现
  • RHCE实验:远程控制qq邮箱发送邮件
  • 20250510解决NanoPi NEO core开发板在Ubuntu core22.04.3系统下适配移远的4G模块EC200A-CN的问题
  • C++内存管理
  • 仓库管理系统,Java+Vue,含源码及文档,高效管理仓库物资,实现入库、存储、出库全流程数字化精准管控
  • 基于CNN卷积神经网络的带频偏QPSK调制信号检测识别算法matlab仿真
  • MySQL 从入门到精通(五):索引深度解析 —— 性能优化的核心武器
  • idea如何快速生成测试类
  • 【赵渝强老师】TiDB SQL层的工作机制
  • Yocto中`${B}`变量的作用
  • 论文图表自动编号与交叉引用
  • python中的继承和多态
  • FreeRTOS Queue消息队列-笔记
  • AlimaLinux设置静态IP
  • 护网HVV初级蓝队面试题总结
  • Axure :基于中继器的列表删除 、 列表编辑
  • 自动语音拨号系统V2.6.0产品说明书