欢迎光临散文网 会员登陆 & 注册

FreeRTOS 计数信号量

2023-07-12 11:12 作者:自闭选手的Z23  | 我要投稿

计数信号量是FreeRTOS 任务间的同步和资源共享机制,计数信号量的源码实现是基于消息队列实现的。

1 信号量的概念及其作用

    使用信号量的最初目的是为了给共享资源建立一个标志,该标志表示该共享资源被占用情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,再来决定自己的行为。

    实际的应用中,信号量的作用又该如何体现呢?

    比如有 30 台电脑的机房,我们就可以创建信号量的初始化值是 30,表示 30 个可用资源。

    另外我们要求一个同学使用一台电脑,每有一个同学使用一台电脑,信号量的数值就减一,直到 30 台电脑都被占用,此时信号量的数值就是 0。如果此时还有几个同学没有电脑可以使用,那么这几个同学就得等待,直到有同学离开。

    有一个同学离开,那么信号量的数值就加 1,有两个就加 2,依此类推。刚才没有电脑用的同学此时就有电脑可以用了,有几个同学用,信号量就减几,直到再次没有电脑可以用,这么一个过程就是使用信号量来管理共享资源的过程。 


    平时使用信号量主要实现以下两个功能:

两个任务之间或者中断函数跟任务之间的同步功能,这个和前面章节讲解的事件标志组是类似的。其实就是共享资源(信号量)为 1 的时候。

◆ 多个共享资源的管理,就像上面举的机房上机的例子。 针对这两种功能,FreeRTOS 分别提供了二值信号量计数信号量,其中二值信号量可以理解成计数信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API 函数,而像 RTX,uCOS-II 和 III 是仅提供了一个信号量功能,设置不同的初始值就可以分别实现二值信 号量和计数信号量。当然,FreeRTOS 使用计数信号量也能够实现同样的效果。

    

2 FreeRTOS 任务间计数信号量的实现

    任务间信号量的实现是指各个任务之间使用信号量实现任务的同步或者资源共享功能。下面的框图来说明一下 FreeRTOS 计数信号量的实现:

运行条件:

◆ 创建 2 个任务 Task1 和 Task2。 

◆ 创建计数信号量可用资源为 1

运行过程描述如下: 

◆ 任务 Task1 运行过程中调用函数 xSemaphoreTake 获取信号量资源

    如果信号量没有被任务 Task2 占用,Task1 将直接获取资源;

    如果信号量被 Task2 占用,任务 Task1 将由运行态转到阻塞状态, 等待资源可用。一旦获取了资源并使用完毕后会通过函数 xSemaphoreGive 释放掉资源。 

◆ 任务 Task2 运行过程中调用函数 xSemaphoreTake 获取信号量资源

    如果信号量没有被任务 Task1 占用,Task2 将直接获取资源。

    如果信号量被 Task1 占用,任务 Task2 将由运行态转到阻塞状态, 等待资源可用。一旦获取了资源并使用完毕后会通过函数 xSemaphoreGive 释放掉资源。


3 FreeRTOS 中断方式计数信号量的实现

     中断方式信号量的实现是指中断函数和 FreeRTOS 任务之间使用信号量。信号量的中断方 式主要是用于实现任务同步,如下的框图来说明一下 FreeRTOS 中断方式信号量的实现:

运行条件:

◆ 创建一个任务 Task1 和一个串口接收中断。 

◆ 信号量的初始值为 0,串口中断调用函数 xSemaphoreGiveFromISR 释放信号量

    任务 Task1 调用函数 xSemaphoreTake 获取信号量资源

运行过程描述如下:

◆ 任务 Task1 运行过程中调用函数 xSemaphoreTake,由于信号量的初始值是 0,没有信号量资源可用,任务 Task1 由运行态进入到阻塞态。 

◆ Task1 阻塞的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中调用函数 xSemaphoreGiveFromISR 释放信号量资源,信号量数值加 1,此时信号量计数值为 1,任务 Task1 由阻塞态进入到就绪态,在调度器的作用下由就绪态又进入到运行态,任务 Task1 获得信号量后,信号量数值减 1,此时信号量计数值又变成了 0。 

◆ 再次循环执行时,任务 Task1 调用函数 xSemaphoreTake 由于没有资源可用再次进入到阻塞态,等待串口释放信号量资源,如此往复循环。



重点的了解以下 4 个函数:

◆ xSemaphoreCreateCounting () 

◆ xSemaphoreGive () 

◆ xSemaphoreGiveFromISR () 

◆ xSemaphoreTake ()


函数 xSemaphoreCreateCounting 用于创建计数信号量。

◆ 第 1 个参数是设置此计数信号量支持的最大计数值。

◆ 第 2 个参数是设置计数信号量的初始值。

◆ 返回值,如果创建成功会返回消息队列的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足, 无法为此消息队列提供所需的空间会返回 NULL。

使用这个函数要注意以下问题:

1. 此函数是基函数 xQueueCreateCountingSemaphore 实现的:

#define     xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )         \ xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )

函数 xQueueCreateCountingSemaphore 的实现是基于消息队列函数 xQueueGenericCreate 实现的。

2. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:

#define configUSE_COUNTING_SEMAPHORES     1

举例:


函数 xSemaphoreGive 用于在任务代码中释放信号量。

◆ 第 1 个参数是信号量句柄。

◆ 返回值,如果信号量释放成功返回 pdTRUE,否则返回 pdFALSE,因为计数信号量的实现是基于消息队列,返回失败的主要原因是消息队列已经满了。

使用这个函数要注意以下问题: 

1. 此函数是基于消息队列函数 xQueueGenericSend 实现的:

#define xSemaphoreGive( xSemaphore )         \ 

xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, \ queueSEND_TO_BACK ) 

2. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是 xSemaphoreGiveFromISR。 

3. 使用此函数前,一定要保证用函数 xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者 xSemaphoreCreateCounting()创建了信号量。

4. 此函数不支持使用 xSemaphoreCreateRecursiveMutex()创建的信号量。

举例:



函数 xSemaphoreGiveFromISR 用于中断服务程序中释放信号量。

◆ 第 1 个参数是信号量句柄。 

◆ 第2 个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE, 说明有高优先级任务要执行,否则没有。 

◆ 返回值,如果信号量释放成功返回 pdTRUE,否则返回 errQUEUE_FULL。

举例:


函数 xSemaphoreTake 用于在任务代码中获取信号量

◆ 第 1 个参数是信号量句柄。 

◆ 第 2 个参数是没有信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。

◆ 返回值,如果创建成功会获取信号量返回 pdTRUE,否则返回 pdFALSE。

使用这个函数要注意以下问题:

1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是 xSemaphoreTakeFromISR。 

2. 如果消息队列为空且第 2 个参数为 0,那么此函数会立即返回。

3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第 2 个参数配 置为 portMAX_DELAY,那么此函数会永久等待直到信号量可用。

举例:








    






FreeRTOS 计数信号量的评论 (共 条)

分享到微博请遵守国家法律