FreeRTOS 任务计数信号量,任务二值信号量,任务事件标志组

本章节为大家讲解 FreeRTOS 计数信号量的另一种实现方式----基于任务通知(Task Notifications)
的计数信号量,这里我们将这种方式实现的计数信号量称之为任务计数信号量。 任务计数信号量效率更高,
需要的 RAM 空间更小。当然,缺点也是有的,它没有之前介绍的计数信号量实现的功能全面。

任务通知(Task Notifications)介绍
FreeRTOS 每个已经创建的任务都有一个任务控制块(task control block),任务控制块就是一个
结构体变量,用于记录任务的相关信息。 结构体变量中有一个 32 位的变量成员 ulNotifiedValue 是专门
用于任务通知的。
通过任务通知方式可以实现计数信号量,二值信号量,事件标志组和消息邮箱(消息邮箱就是消息队
列长度为 1 的情况)。 使用方法与前面章节讲解的事件标志组和信号量基本相同,只是换了不同的函数来
实现。 任务通知方式实现的计数信号量,二值信号量,事件标志组和消息邮箱是通过修改变量
ulNotifiedValue 实现的:
? 设置接收任务控制块中的变量 ulNotifiedValue 可以实现消息邮箱。
? 如果接收任务控制块中的变量 ulNotifiedValue 还没有被其接收到,也可以用新数据覆盖原有数据
,这就是覆盖方式的消息邮箱。
? 设置接收任务控制块中的变量 ulNotifiedValue 的 bit0-bit31 数值可以实现事件标志组。
? 设置接收任务控制块中的变量 ulNotifiedValue 数值进行加一或者减一操作可以实现计数信号量和二
值信号量。
介绍了这么多,那么问题来了,采用这种方式有什么优势呢?根据官方的测试数据,唤醒由于信号量
和事件标志组而处于阻塞态的任务,速度提升了 45%,而且这种方式需要的 RAM 空间更小。 但这种方式
实现的信号量和事件标志组也有它的局限性,主要表现在以下两个方面:

? 任务通知方式仅可以用在只有一个任务等待信号量,消息邮箱或者事件标志组的情况,不过实际项目
项目中这种情况也是最多的。
? 使用任务通知方式实现的消息邮箱替代前面章节讲解的消息队列时,发送消息的任务不支持超时等待,
即消息队列中的数据已经满了,可以等待消息队列有空间可以存新的数据,而任务通知方式实现的消
息邮箱不支持超时等待。

任务计数信号量
前面第 21 章,我们对计数信号量进行了讲解,计数信号量就是对一个变量进行计数,变量的范围是
从 0 到用户创建计数信号量时所设置的大小。当计数变量大于 0 的时候计数信号量管理的资源才可以使用,
计数变量的具体数值就是可用的资源大小。
本章节讲解的任务计数信号量与第 21 章讲解的计数信号量要实现的功能是一样的,不同的是调用的
函数和使用的计数变量:
? 任务计数信号量的计数变量是通过任务控制块中的一个 32 位变量 ulNotifiedValue 实现计数。 前面讲解的计数信号量创建后会有自己的计数变量。
? 任务计数信号量是通过函数 ulTaskNotifyTake()替代之前讲解的函数 xSemaphoreTake()实现
资源获取,即对计数信号量数值进行减一操作。
? 任务计数信号量是通过函数 xTaskNotifyGive() 和 vTaskNotifyGiveFromISR()替代前面讲解的
函数 xSemaphoreGive() 和 xSemaphoreGiveFromISR()实现资源释放,即对计数信号量的数值进
行加一操作。
实际项目中,如果使用计数信号量和任务计数信号量都能实现相应功能,强烈建议使用任务计数信号量。 
任务计数信号量 API 函数
使用如下 9 个函数可以实现 FreeRTOS 的任务信号量(含任务计数信号量和任务二值信号量):

? xTaskNotifyGive()
? vTaskNotifyGiveFromISR()
? ulTaskNotifyTake()
? xTaskNotify()
? xTaskNotifyAndQuery()
? xTaskNotifyAndQueryFromISR()
? xTaskNotifyFromISR()

? xTaskNotifyWait()
? xTaskNotifyStateClear()
关于这 9 个函数的讲解及其使用方法可以看 FreeRTOS 在线版手册 。
这里我们重点的说以下 3 个函数:
? xTaskNotifyGive
? vTaskNotifyGiveFromISR
? ulTaskNotifyTake
因为本章节配套的例子使用的是这 3 个函数。

函数 xTaskNotifyGive
函数原型:
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); /* 任务句柄 */
函数描述:
函数 xTaskNotifyGive 用于释放信号量(含任务二值信号量,任务计数信号量)。
? 第 1 个参数是任务句柄。
? 返回值,仅有一个返回值 pdPASS。
使用这个函数要注意以下问题:

1. 任务信号量的初始计数值是 0。 任务信号量不像前面章节讲解的信号量,无需单独创建即可使用。
2. 默认配置此函数可以使用的的宏定义已经在 FreeRTOS.h 文件中使能:
#define configUSE_TASK_NOTIFICATIONS 1
当然,如果用户不需要使用任务通知功能相关的函数,可以在 FreeRTOSConfig.h 文件中配置此宏定
义为 0 来禁止,这样创建的每个任务可以节省 8 个字节的需求。
3. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是
vTaskNotifyGiveFromISR。

函数 vTaskNotifyGiveFromISR
函数原型:
void vTaskNotifyGiveFromISR(
      TaskHandle_t   xTaskToNotify, /* 任务句柄 */
      BaseType_t *  pxHigherPriorityTaskWoken ); /* 高优先级任务是否被唤醒的状态保存 */
函数描述:

函数 xTaskNotifyGive 用于释放信号量(含任务二值信号量,任务计数信号量)。
? 第 1 个参数是任务句柄。
? 第 2 个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE,
说明有高优先级任务要执行,否则没有。
使用这个函数要注意以下问题:
1. 任务信号量的初始计数值是 0。任务信号量不像前面章节讲解的信号量,无需单独创建即可使用。
2. 默认配置此函数可以使用的的宏定义已经在 FreeRTOS.h 文件中使能:
#define configUSE_TASK_NOTIFICATIONS 1
当然,如果用户不需要使用任务通知功能相关的函数,可以在 FreeRTOSConfig.h 文件中配置此宏定
义为 0 来禁止,这样创建的每个任务可以节省 8 个字节的需求。
3. 此函数是用于中断服务程序中调用的,故不可以在任务代码中调用此函数,任务代码中使用的是xTaskNotifyGive。 

函数 ulTaskNotifyTake
函数原型:
uint32_t ulTaskNotifyTake( BaseType_t   xClearCountOnExit, /* 选择是否清零用于任务通知的 ulNotifiedValue */
             TickType_t    xTicksToWait ); /* 等待信号量可用的最大等待时间 */
函数描述:

函数 ulTaskNotifyTake用于获取信号量(含任务二值信号量,任务计数信号量)。
? 第 1 个参数配置为 pdFALSE 表示函数返回前用于任务信号量的内部变量 ulNotifiedValue(详情看
24.1 小节的解释说明)数值减一,这种方式用于任务计数信号量。 参数配置为 pdTRUE 表示函数返
回前用于任务信号量的内部变量 ulNotifiedValue(详情看 24.1 小节的解释说明)数值被清零,这种
方式用于任务二值信号量。
? 第 2 个参数是没有任务信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。

返回值:任务的通知值在其递减或清除之前的值
使用这个函数要注意以下问题:
1. 任务信号量的初始计数值是 0。任务信号量不像前面章节讲解的信号量,无需单独创建即可使用。
2. 默认配置此函数可以使用的的宏定义已经在 FreeRTOS.h 文件中使能:
#define configUSE_TASK_NOTIFICATIONS 1
当然,如果用户不需要使用任务通知功能相关的函数可以在 FreeRTOSConfig.h 文件中配置此宏定义
为 0 来禁止,这样创建的每个任务可以节省 8 个字节的需求。
3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第 2 个参数配
置为 portMAX_DELAY,那么此函数会永久等待直到信号量可用。

FreeRTOS 任务二值信号量
任务二值信号量
前面,我们对二值信号量进行了讲解,二值信号量只有两种数值 0 和 1。 本章节讲解的任务
二值信号量与前面章节讲解的二值信号量要实现的功能是一样的,不同的是调用的函数和使用的计数变量:
? 任务二值信号量的计数变量是通过任务控制块中的一个 32 位变量 ulNotifiedValue 实现计数。前面讲解的二值信号量创建后会有自己的计数变量。
? 任务二值信号量是通过函数 ulTaskNotifyTake()替代前面章节讲解的函数 xSemaphoreTake()实现
资源获取,即对二值信号量数值进行清零操作。
? 任务二值信号量是通过函数 xTaskNotifyGive() 和 vTaskNotifyGiveFromISR()替代第前面章节讲解的
函数 xSemaphoreGive() 和 xSemaphoreGiveFromISR()实现资源释放,即对二值信号量的数值进行加一操作。

多次调用函数 xTaskNotifyGive ()难免会出现计数值大于 1 的情况,用作任务二值信号量时,我们可
以将所有大于 1 的计数理解为一种情况,即二值信号量管理的资源可用。因此,不管当前的计数是多少,
大于 0 的计数在通过函数 ulTaskNotifyTake()获取二值信号量的时候统一清零,这样就实现了二值信号量的功能。

实际项目中,如果使用二值信号量和任务二值信号量都能实现相应功能,强烈建议使用任务二值信号量。
 
FreeRTOS 任务事件标志组

任务事件标志组
前面第 18 章,我们对事件标志组进行了讲解。 本章节讲解的任务事件标志组与第 18 章讲解的事件标
志组要实现的功能是一样的,不同的是调用的函数和支持的事件标志个数,任务事件标志组支持 32 个事
件标志设置,而第 18 章中介绍的事件标志组,每创建一个支持 24 个事件标志设置:
? 任务事件标志组的事件标志位是通过任务控制块中的一个 32 位变量 ulNotifiedValue 实现。 第 18
章讲解的事件标志组创建后会有自己可以设置的事件标志位。
? 任务事件标志组是通过函数 xTaskNotifyWait()替代第 18 章讲解的函数 xEventGroupWaitBits ()
实现等待事件标志位被设置。
? 任务事件标志组是通过函数 xTaskNotify() 和 xTaskNotifyFromISR()替代第 18 章讲解的函数
xEventGroupSetBits() 和 xEventGroupSetBitsFromISR 实现对事件标志位的设置。
? 第 18 章中讲解的函数 xEventGroupSetBitsFromISR 是通过给 Daemon 任务(定时器任务)发消
息,在定时器任务中执行实际的操作,而我们本章节要介绍的函数 xTaskNotifyFromISR 是直接在
中断服务程序里面执行操作,效率要高很多。

函数 xTaskNotify

函数描述:
函数 xTaskNotify 通过设置任务控制块中的变量 ulNotifiedValue 可以在任务代码中实现任务事件标志组,
任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息通知。
? 第 1 个参数是任务句柄。
? 第 2 个参数是用来更新任务控制块中的 32 位变量 ulNotifiedValue。
? 第 3 个参数是任务通知模式设置,支持以下 5 个参数:

? 返回值,根据上面第 3 个参数的说明,将其设置为 eSetValueWithoutOverwrite,有可能返回
pdFALSE,其余所有情况都返回值 pdPASS。
使用这个函数要注意以下问题:
1. 任务创建后,任务控制块中的变量 ulNotifiedValue 初始计数值是 0。
2. 默认配置此函数可以使用的的宏定义已经在 FreeRTOS.h 文件中使能:
#define configUSE_TASK_NOTIFICATIONS 1
当然,如果用户不需要使用任务通知功能相关的函数,可以在 FreeRTOSConfig.h 文件中配置此宏定
义为 0 来禁止,这样创建的每个任务可以节省 8 个字节的需求。
3. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是
xTaskNotifyFromISR。
4. 根据 FreeRTOS 的建议,实现二值信号量和计数信号量时使用函数 xTaskNotifyGive()替代此函数xTaskNotify()。 
函数 xTaskNotifyFromISR

函数描述:
函数 xTaskNotifyFromISR 通过设置任务控制块中的变量 ulNotifiedValue 可以在中断服务程序中实现任
务事件标志组,任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息通知(见 26.1 说明)。
? 第 1 个参数是任务句柄。
? 第 2 个参数是用来更新任务控制块中的 32 位变量 ulNotifiedValue。
? 第 3 个参数是任务通知模式设置,支持5 个参数,如上面5个参数一样:

? 第 4 个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE,
说明有高优先级任务要执行,否则没有。
? 返回值,根据上面第 3 个参数的说明,将其设置为 eSetValueWithoutOverwrite,有可能返回
pdFALSE,其余所有情况都返回值 pdPASS。
使用这个函数要注意以下问题:
1. 任务创建后,任务控制块中的变量 ulNotifiedValue 初始计数值是 0。
2. 默认配置此函数可以使用的的宏定义已经在 FreeRTOS.h 文件中使能:
#define configUSE_TASK_NOTIFICATIONS 1
当然,如果用户不需要使用任务通知功能相关的函数,可以在 FreeRTOSConfig.h 文件中配置此宏定
义为 0 来禁止,这样创建的每个任务可以节省 8 个字节的需求。
3. 此函数是用于中断服务程序中调用的,故不可以在任务代码中调用此函数,任务代码中使用的是xTaskNotify。
4. 根据 FreeRTOS 的建议,实现二值信号量和计数信号量时使用函数 vTaskNotifyGiveFromISR ()替代此函数 xTaskNotifyFromISR ()。 
函数 xTaskNotifyWait

函数描述:
函数 xTaskNotifyWait 可以在任务代码中实现任务事件标志组,任务计数信号量,任务消息邮箱和任务二
值信号量四种方式的消息获取。
? 第 1 个参数 ulBitsToClearOnEntry 用于函数执行之前,将任务控制块中的变量 ulNotifiedValue 进
行如下操作 :
ulNotifiedValue &= ~ulBitsToClearOnEntry
简单的说就是参数 ulBitsToClearOnEntry 哪个位是 1,那么变量 ulNotifiedValue 的那个位就会被
清零。 比如 ulBitsToClearOnEntry = 0x01 表示将变量 ulNotifiedValue 的 bit0 清零,又比如
ulBitsToClearOnEntry = 0xffffffff 表示将变量 ulNotifiedValue 的所有位清零。
? 第 2 个参数 ulBitsToClearOnExit 用于函数退出前,将任务控制块中的变量 ulNotifiedValue 进行如下操作 :

简单的说就是参数 ulBitsToClearOnExit 哪个位是 1,那么变量 ulNotifiedValue 的那个位就会被清
零。 比如 ulBitsToClearOnExit= 0x01 表示将变量 ulNotifiedValue 的 bit0 清零,又比如
ulBitsToClearOnExit= 0xffffffff 表示将变量 ulNotifiedValue 的所有位清零。
? 第 3 个参数用于将任务控制块中的变量 ulNotifiedValue 保存到此参数指针所指向的存储单元。 如果
此参数没有用上,可以将其设置为 NULL。
? 第 4 个参数是没有消息时,等待消息的最大等待时间,单位系统时钟节拍。
? 返回值,如果成功接收到消息返回 pdTRUE,否则返回 pdFALSE,比如在设置的超时时间内没有收
到消息。

使用这个函数要注意以下问题:
1. 任务创建后,任务控制块中的变量 ulNotifiedValue 初始计数值是 0。
2. 默认配置此函数可以使用的的宏定义已经在 FreeRTOS.h 文件中使能:
#define configUSE_TASK_NOTIFICATIONS 1
当然,如果用户不需要使用任务通知功能相关的函数,可以在 FreeRTOSConfig.h 文件中配置此宏定
义为 0 来禁止,这样创建的每个任务可以节省 8 个字节的需求。
3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第 2 个参数配
置为 portMAX_DELAY,那么此函数会永久等待直到消息可用。
4. 根据 FreeRTOS 的建议,实现二值信号量和计数信号量时使用函数 ulTaskNotifyTake ()替代此函数
xTaskNotifyWait ()。

时间: 2024-10-23 18:59:34

FreeRTOS 任务计数信号量,任务二值信号量,任务事件标志组的相关文章

FreeRTOS 事件标志组

为什么要使用事件标志事件标志组是实现多任务同步的有效机制之一.也许有不理解的初学者会问采用事件标志组多麻烦,搞个全局变量不是更简单?其实不然,在裸机编程时,使用全局变量的确比较方便,但是在加上 RTOS 后就是另一种情况了. 使用全局变量相比事件标志组主要有如下三个问题:? 使用事件标志组可以让 RTOS 内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现.? 使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心.? 使用事件标志组

FreeRTOS 二值信号量,互斥信号量

本章节讲解 FreeRTOS 任务间的同步和资源共享机制,二值信号量. 二值信号量是计数信号量的一种特殊形式,即共享资源为 1 的情况. FreeRTOS 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API函数,而像 RTX,uCOS-II 和 III 是仅提供了一个信号量功能,设置不同的初始值就可以分别实现二值信号量和计数信号量. 当然,FreeRTOS 使用计数信号量也能够实现

Freertos-事件标志组,消息队列,信号量,二值信号量,互斥信号量

任务间的通信和同步机制  在裸机编程时,使用全局变量的确比较方便,但是在加上 RTOS 后就是另一种情况了. 使用全局变量相比事件标志组主要有如下三个问题: 1.使用事件标志组可以让 RTOS 内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现.2.使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心.3.使用事件标志组可以有效地解决中断服务程序和任务之间的同步问题. 事件标志组:事件标志组是实现多任务同步的有效机制之一. 每创建一

μC/OS-III---I笔记5---多值信号量

多值信号量 操作系统中利用信号量解决进程间的同步和互斥(互斥信号量)的问题,在多道程序环境下,操作系统如何实现进程之间的同步和互斥显得极为重要.比如对同一部分资源的访问是要互斥,不能在另一个进程A在访问的时候被其他的进程再访问这样两个进程相互影响就无法保证正常的访问操作.另一方面是任务间的同步,比如一个任务A依赖于另一个任务B的处理结果,在B处理完成时给A一个就绪信号让A开始使用结果进行相关处理. 同样在使用之前要先定义一个信号量,然后调用创建OSSemCreate()函数创建信号量注意到函数内

将指定的计数添加到该信号量中会导致其超过最大计数

将指定的计数添加到该信号量中会导致其超过最大计数 简介:当我在本地IIS的网站上进行快递分拣操作时,会调用快递分拣的服务WebService, 点击后程序不执行,跟踪发现出现如题错误异常,如图: 解决方案:          解决此问题其实很简单,重启IIS就OK啦!

二值信号量和互斥锁到底有什么区别?

在说明之前我先抛出结论:互斥锁和二值信号量在使用上非常相似,但是互斥锁解决了优先级翻转的问题 假定我们现在有三个任务,task1,task2,task3,任务优先级task1最高,然后依次降低.我们知道在系统调度的时候当两个任务同时处于就绪态的时候,系统会优先执行优先级高的任务 好了,让我们来看两个案例 优先级翻转分析(使用信号量) 在例子中,我们使用pend()函数来表示获取信号量,用post()函数来表示释放信号量 如上图所示,过程分下面几步 1.一开始task3开始运行,先获取到信号量 2

15.3-uC/OS-III时间管理(多值信号量)

多值信号量是 uC/OS 操作系统的一个内核对象, 主要用于标志事件的发生和共享资源管理. 1.如果想要使用多值信号量,就必须事先使能多值信号量. 多值信号量的使能位于"os_cfg.h". 2.OSSemCreate () 要使用 uC/OS 的多值信号量必须先声明和创建多值信号量,调用 OSSemCreate () 函数可以创建一个多值信号量. 3.OSSemPost () OSSemPost () 函数用于发布多值信号量. 4.OSSemPend () 与 OSSemPost (

大话Linux内核中锁机制之信号量、读写信号量

大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程才能执行临界区的代码:不同的是获取不到信号量时,进程不会原地打转而是进入休眠等待状态.它的定义是include\linux\semaphore.h文件中,结构体如图6.1所示.其中的count变量是计数作用,通过使用lock变量实现对count变量的保

RadioButtonList根据值触发OnSelectedIndexChanged事件

Insus.NET有使用Iframe来处理另外一个站点的enter form,由于需要自动循环填入数据,免去人手操作.但是原来的Enter from有RadioButtonList控件以及OnSelectedIndexChanged关联另外一个DropDownList控件.这样的话,问题来了,怎样根据与指定RadioButtonList的值之后,并触发其OnSelectedIndexChanged事件? 先来看看原aspx的代码: <asp:radiobuttonlist id=rdblstLo