FreeRTOS 信号量

本文深入介绍了FreeRTOS中的信号量,包括二进制信号量、计数信号量、互斥锁和递归互斥锁的使用和实现细节。二进制信号量作为任务同步工具,可用于中断和任务间的协调。互斥锁则引入了优先级继承机制,防止优先级反转问题。递归互斥锁允许同一任务多次获取。所有信号量都是基于队列实现的,具有轻量级的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@(嵌入式)

Freertos
FreeRtos

简述

FreeRTOS 信号量和互斥锁是基于队列实现的, 队列介绍见 << FreeRTOS 消息队列 >>。 使用信号量需要在源文件中包含头文件 semphr.h , 该文件定义了信号量的 API, 实际我们使用的信号量 API 都是宏定义, 宏的实际是队列提供的函数。

FreeRTOS 信号量包括二进制信号量、计数信号量、互斥锁和递归互斥锁。 这篇文章介绍如何使用这些信号量就行任务间同步以及其实现。

分析的源码版本是 v9.0.0


二进制信号量

二进制信号量可以用于互斥和同步, 多用于同步。 可以把二进制信号量看成一个深度为1的队列(实际FreeRTOS也是这么实现的), 调用信号量获取函数, 设置阻塞时间, 在该时间内没有收到信号, 任务会被挂起, 当收到信号或者超时, 任务恢复,函数返回。
多个任务同时阻塞在一个信号量, 当信号量有效时, 最高优先级的任务最先解除阻塞。

二进制信号量使用

举个使用场景, 一个任务读取一个外设,一直等待外设可读占用CPU效率低, 可以调用信号量获取函数阻塞等待, 当外设可读,在其中断函数中发送信号量,唤醒任务执行读取操作。
(中断中必须使用带有 FromISR结尾的 API)

semaphore

// 信号量句柄
SemaphoreHandle_t xSemaphore;

void vATask( void * pvParameters )
{
    // 创建二进制信号量
    xSemaphore = xSemaphoreCreateBinary();

    if( xSemaphore == NULL )
    {
        //heap 空间不够 ,创建失败
    }
    else
    {
        // 信号量获取 设置阻塞时间 10 ticks
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
        {
            // 获取到信号量 ! 
            //...
            // 如果任务中发送信号量
            //xSemaphoreGive( xSemaphore );
        }
        else
        {
                // 等待 10 tick 无法获取信号量
                // 超时返回
        }
    }
}

// 中断
void vTimerISR( void * pvParameters )
{
    static signed BaseType_t xHigherPriorityTaskWoken;
    xHigherPriorityTaskWoken = pdFALSE;
    // 发送信号量
    // 传递参数判断是否有高优先级任务就绪
    xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
    // 判断是否需要触发任务切换
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

如果把信号量作为互斥锁使用, 则任务在获取信号后,处理完毕需要返回。

FreeRTOS 在 8.20 版本提供了一种更加轻量级的任务同步, 任务通知, 由于该方式是集合在任务控制块的,所以不需要额外的内存消耗,推荐使用。

二进制信号量实现

以下看看 FreeRTOS 如何基于队列实现信号量的。

创建信号量

在信号量定义头文件可以找到该宏的定义, 可以发现, 创建一个信号量,实际上是创建了一个队列, 队列深度设置为1个, 同时, semSEMAPHORE_QUEUE_ITEM_LENGTH 这个宏的值为0, 即 item size 为0, 表示信号量这个队列没有队列项存储空间, 因为对于信号量,没有这个需要,
对于二进制信号量, 创建后默认初始化其 uxMessageWaiting 为0, 当有信号发出时, 该值变为1(最大也只能为1),此时信号量有效, 如果有任务获取消费了信号量,该变量再次变为0, 信号量无效, 有任务在次调用获取信号量,可能阻塞等待或者返回信号量空。

#define xSemaphoreCreateBinary()    \
    xQueueGenericCreate( ( UBaseType_t ) 1, \
        semSEMAPHORE_QUEUE_ITEM_LENGTH, \
        queueQUEUE_TYPE_BINARY_SEMAPHORE )
获取信号量

任务调用接口获取信号量, 可以通过如下宏实现 :

#define xSemaphoreTake( xSemaphore, xBlockTime )    \
    xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), \
    NULL,( xBlockTime ), pdFALSE )

这个函数是供任务调用的, 可以看到该函数实际上调用的是队列的接收函数, 由于没有数据可读, 传递的指针为 NULL。
函数调用设置阻塞时间,如果调用函数时信号量无效, 则会阻塞任务等待。

如果是在中断中, 则必须调用如下宏

#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )\
    xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), \
        NULL, ( pxHigherPriorityTaskWoken ) )

函数 xSemaphoreTakeFromISR是供中断调用的,做了中断优先级处理,并且不会阻塞。

释放信号量

释放信号量的地方可能是中断,或者是任务中, 对应调用不同接口。

中断中释放

如果在中断中调用发送信号量, 需要调用的 API 是 xSemaphoreGiveFromISR

<think>好的,我现在要帮助用户了解FreeRTOS信号量的使用教程和示例代码。首先,我需要回顾一下用户提供的引用内容,确保信息准确。用户给出的三个引用都是关于FreeRTOS信号量的,特别是二值信号量和计数型信号量的创建、释放和获取。 接下来,我应该按照用户的要求,结构清晰地分步骤讲解,并提供示例代码。用户可能需要知道信号量的基本概念,比如二值信号量和计数型信号量的区别。根据引用3,二值信号量用于任务同步,而计数型信号量用于资源管理,这点需要明确。 然后,我需要整理使用信号量的步骤:创建、释放、获取。引用1提到了这三个步骤,而引用2和3提供了具体的函数名和用法。例如,创建二值信号量用xSemaphoreCreateBinary(),释放用xSemaphoreGive(),获取用xSemaphoreTake()。 用户可能需要示例代码来理解如何在实际中使用这些函数。代码应该包括创建信号量、任务函数中使用Give和Take,以及主函数的初始化部分。需要确保代码语法正确,比如在FreeRTOS中正确包含头文件,任务优先级和堆栈大小的设置是否合理。 此外,用户的问题可能还涉及信号量的常见问题,比如优先级反转、死锁,或者如何选择动态和静态创建。根据引用3,动态创建不需要预先分配内存,而静态创建需要,这点需要解释清楚。 可能还需要提醒用户信号量使用的注意事项,比如不能在中断服务例程中使用Give函数,而应该使用xSemaphoreGiveFromISR()。这点虽然引用中没有提到,但作为常见问题应该补充,以帮助用户避免错误。 最后,生成相关问题,确保覆盖用户可能进一步想了解的内容,比如二值与计数型信号量的区别、动态与静态创建的差异、在中断中的使用以及常见错误排查。这些问题应该基于前面的讲解,帮助用户深入理解。 总结一下,回答的结构应该是:概述信号量类型,使用步骤,示例代码,注意事项,相关问题。确保每个部分都引用正确的来源,并在末尾添加引用标识。同时,检查LaTeX格式是否符合要求,比如变量用$...$,但在这个问题中可能用不上数学表达式,但如果有的话需要正确应用。</think>### FreeRTOS 信号量使用教程 #### 一、信号量类型 1. **二值信号量**:状态为“0”或“1”,用于任务同步(如中断与任务通信)[^3]。 2. **计数型信号量**:通过计数值管理资源数量(如缓冲池资源分配)[^3]。 #### 二、信号量使用步骤 1. **创建信号量**: - **二值信号量动态创建**: ```c SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary(); // 返回句柄[^3] ``` - **计数型信号量动态创建**: ```c SemaphoreHandle_t xSemaphore = xSemaphoreCreateCounting(uxMaxCount, uxInitialCount); // 最大计数值和初始值 ``` 2. **释放信号量**: ```c xSemaphoreGive(xSemaphore); // 释放信号量(任务中调用)[^2] ``` - 中断服务程序中使用: ```c BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken); // 需检查返回值 ``` 3. **获取信号量**: ```c xSemaphoreTake(xSemaphore, xBlockTime); // 阻塞时间单位为Tick,若为0则非阻塞[^1] ``` #### 三、示例代码(二值信号量同步任务) ```c #include "FreeRTOS.h" #include "task.h" #include "semphr.h" SemaphoreHandle_t xBinarySemaphore; void TaskSender(void *pvParameters) { while (1) { // 模拟事件触发 xSemaphoreGive(xBinarySemaphore); // 释放信号量 vTaskDelay(pdMS_TO_TICKS(1000)); // 延时1秒 } } void TaskReceiver(void *pvParameters) { while (1) { if (xSemaphoreTake(xBinarySemaphore, pdMS_TO_TICKS(500)) == pdTRUE) { // 成功获取信号量,执行同步操作 } else { // 超时处理 } } } int main() { xBinarySemaphore = xSemaphoreCreateBinary(); // 创建二值信号量 xTaskCreate(TaskSender, "Sender", 128, NULL, 2, NULL); xTaskCreate(TaskReceiver, "Receiver", 128, NULL, 1, NULL); vTaskStartScheduler(); return 0; } ``` #### 四、注意事项 1. **优先级反转**:高优先级任务可能因等待低优先级任务持有的信号量而阻塞,需使用优先级继承(如互斥量)。 2. **死锁风险**:避免任务互相等待信号量,设计时需规划资源获取顺序。 3. **动态 vs 静态创建**: - 动态创建(`xSemaphoreCreateBinary`)由FreeRTOS自动分配内存。 - 静态创建需预分配内存(`xSemaphoreCreateBinaryStatic`),适合内存受限场景。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值