FreeRTOS 信号量

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

@(嵌入式)

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值