任务通知
- [1. 任务通知的数据结构](#1. 任务通知的数据结构)
- [2. 常用函数](#2. 常用函数)
-
- [2.1 发送任务通知 xTaskGenericNotify()](#2.1 发送任务通知 xTaskGenericNotify())
- [2.2 发送任务通知( 二值信号量 / 计数信号量 ) xTaskNotifyGive()](#2.2 发送任务通知( 二值信号量 / 计数信号量 ) xTaskNotifyGive())
- [2.3 发送任务通知( 二值信号量 / 计数信号量 ) vTaskNotifyGiveFromISR()](#2.3 发送任务通知( 二值信号量 / 计数信号量 ) vTaskNotifyGiveFromISR())
- [2.4 发送任务通知 xTaskNotify()](#2.4 发送任务通知 xTaskNotify())
- [2.5 发送任务通知 xTaskNotifyFromISR()](#2.5 发送任务通知 xTaskNotifyFromISR())
- [2.6 中断中发送任务通知 xTaskGenericNotifyFromISR()](#2.6 中断中发送任务通知 xTaskGenericNotifyFromISR())
- [2.7 发送任务通知 xTaskNotifyAndQuery()](#2.7 发送任务通知 xTaskNotifyAndQuery())
- [2.8 发送任务通知 xTaskNotifyAndQueryFromISR()](#2.8 发送任务通知 xTaskNotifyAndQueryFromISR())
- [2.9 获取任务通知 ulTaskNotifyTake()](#2.9 获取任务通知 ulTaskNotifyTake())
- [2.10 获取任务通知 xTaskNotifyWait()](#2.10 获取任务通知 xTaskNotifyWait())
FreeRTOS 从V8 .2.0 版本开始提供任务通知这个功能,每个任务都有一个32 位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件组,也可以替代长度为1 的队列(可以保存一个32 位整数或指针值)。相对于以前使用 FreeRTOS 内核通信的资源,必须创建队列、二进制信号量、计数信号量或事件组的情况,使用任务通知显然更灵活。按照FreeRTOS 官方的说法,使用任务通知比通过信号量等ICP 通信方式解除阻塞的任务要
快45%,并且更加省 RAM 内存空间(使用 GCC 编译器,-o2 优化级别),任务通知的使用无需创建队列。但也有以下限制:
- 只能有一个任务接收通知消息,因为必须指定接收通知的任务
- 只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会因为发送失败而进入阻塞态
1. 任务通知的数据结构
任务通知是属于任务中附带的资源,所以在任务被创建的时候,任务通知也被初始化的。
任务控制块 TBC 中的成员变量
ulNotifiedValue是任务通知值,可以保存一个32 位整数或指针值。任务控制块 TBC 中的成员变量
ucNotifyState是任务通知状态,用于标识任务是否在等待通知。
2. 常用函数
2.1 发送任务通知 xTaskGenericNotify()
(1)被通知的任务句柄,指定通知的任务
(2)发送的通知值
(3)枚举类型,指明更新通知值的方式
(4)任务原本的通知值返回
(5)回传任务原本的任务通值,保存在 pulPreviousNotificationValue 中
(6)获取任务通知的状态,看看任务是否在等待通知,方便在发送通知后恢复任务
(7)不管该任务的通知状态是怎么样的,现在调用发送通知函数,任务通知状态就要设置为收到任务通知,因为发送通知是肯定能被收到
(8)指定更新任务通知的方式
(9)通知值与原本的通知值按位或,使用这种方法可以某些场景下代替事件组,执行速度更快
(10)被通知任务的通知值增加1,这种发送通知方式,参数ulValue 的值未使用,在某些场景可以代替信号量通信,并且执行速度更快
(11)将被通知任务的通知值设置为ulValue,无论任务是否还有通知,都覆盖当前任务通知值。这种方法是覆盖写入,使用这种方法,可以在某些场景下代替 xQueueoverwrite()函数,执行速度更快
(12)如果被通知任务当前没有通知,则被通知任务的通知值设置为 ulValue;在某些场景下替代队列长度为1 的xQueuesend(),并且执行速度更快
(13)如果被通知任务还没取走上一个通知,本次发送通知,任务又接收到了一个通知,则这次通知值将被丢弃,在这种情况下,函数调用失败并返回 pdFALSE
(14)发送通知但不更新通知值,这意味着参数ulValue 未使用
(15)如果被通知的任务由于等待任务通知而挂起,系统将唤醒任务,将任务从阻塞列表中移除,添加到就绪列表中
(16)如果刚刚唤醒的任务优先级比当前任务高,就进行一次任务切换
2.2 发送任务通知( 二值信号量 / 计数信号量 ) xTaskNotifyGive()
2.3 发送任务通知( 二值信号量 / 计数信号量 ) vTaskNotifyGiveFromISR()
(1)保存任务通知的原始状态,看看任务是否处于等待通知的阻塞态,方便在中断发送通知完成后恢复任务
(2)不管状态是怎么样的,反正现在发送通知,任务就收到任务通知
(3)通知值自加,类似于信号量的释放操作
(4)如果任务在阻塞等待通知,并且系统调度器处于运行状态
(5)唤醒任务,将任务从阻塞列表中移除,添加到就绪列表中
(6)调度器处于挂起状态,中断依然正常发生,但是不能直接操作就绪列表,将任务加入到就绪挂起列表,任务调度恢复后会移动到就绪列表中
(7)如果刚刚唤醒的任务优先级比当前任务高,则设置上下文切换标识,等退出函数后手动切换上下文,或者在系统节拍中断服务程序中自动切换上下文
(8)设置返回参数,表示需要任务切换,在退出中断前进行任务切换
(9)否则就设置自动切换标志
2.4 发送任务通知 xTaskNotify()
2.5 发送任务通知 xTaskNotifyFromISR()
2.6 中断中发送任务通知 xTaskGenericNotifyFromISR()
(1)指定接收通知的任务句柄
(2)用于更新接收任务通知值,具体如何更新由形参eAction 决定
(3)任务通知值更新方式
(4)用于保存任务上一个通知值
(5):pxHigherPriorityTaskWoken 在使用之前必须先初始化为pdFALSE。当调用该函数发送一个任务通知时,目标任务接收到通知后将从阻塞态变为就绪态,并且如果其优先级比当前运行的任务的优先级高,那么pxHigherPriorityTaskWoken 会被设置为pdTRUE,然后在中断退出前执行一次上下文切换,去执行刚刚被唤醒的中断优先级较高的任务。pxHigherPriorityTaskWoken 是一个可选的参数可以设置为NULL
(6)进入中断临界区
(7)如果pulPreviousNotificationValue 参数不为空,就需要返回上一次的任务通知值
(8)保存任务通知的原始状态,看看任务是否在等待通知,方便在发送通知后恢复任务
(9)不管当前任务通知状态是怎么样的,现在调用发送通知函数。任务通知肯定是发送到指定任务,那么任务通知的状态就设置为收到任务通知
(10)指定更新任务通知的方式
(11)景通知值与原本的通知值按位或,使用这种方法可以某些场景下代替事件组,执行速度更快
(12)被通知任务的通知值增加1,这种发送通知方式,参数ulValue 的值未使用,在某些场景可以代替信号量通信,并且执行速度更快
(13)将被通知任务的通知值设置为 ulValue,无论任务是否还有通知,都覆盖当前任务通知值。这种方法是覆盖写入,使用这种方法,可以在某些场景下代替 xQueueoverwrite()函数,执行速度更快
(14)采用不覆盖发送通知方式,如果被通知任务当前没有通知,则被通知任务的通知值设置为ulValue;在某些场景下替代队列长度为1 的xQueuesend(),并且执行速度更快
(15)如果被通知任务还没取走上一个通知,本次发送通知,任务又接收到了一个通知,则这次通知值将被丢弃,在这种情况下,函数调用失败并返回 pdFALSE
(16)如果任务在阻塞等待通知
(17)如果任务调度器在运行中,表示可用操作就绪级列表。那么系统将唤醒任务,将任务从阻塞列表中移除,添加到就绪列表中
(18)如果调度器处于挂起状态,中断依然正常发生,但是不能直接操作就绪列表,系统会将任务加入到就绪挂起列表,任务调度恢复后会将在该列表的任务移动到就绪列表中。
(19)如果刚刚唤醒的任务优先级比当前任务高,则设置上下文切换标识,等退出函数后手动切换上下文,或者按照任务优先级自动切换上下文
(20)设置返回参数,表示需要任务切换,在退出中断前进行任务切换
(21)设置自动切换标志,等高优先级任务释放CPU 使用权
(22)离开中断临界区
2.7 发送任务通知 xTaskNotifyAndQuery()
2.8 发送任务通知 xTaskNotifyAndQueryFromISR()
2.9 获取任务通知 ulTaskNotifyTake()
(1)进入临界区,先看看任务通知值是否有效,有效才能获取,无效则根据指定超时时间等待,标记一下任务状态,表示任务在等待通知。任务通知在任务初始化的时候是默认为无效的
(2)用户指定超时时间了,那就进入等待状态,根据用户指定超时时间将任务添加到延时列表,然后切换任务,触发PendSV 中断,等到退出临界区时立即执行任务切换
(3)进入临界区,程序能执行到这里说明其它任务或中断向这个任务发送了一个任务通知,或者任务本身的阻塞超时时间到了,现在无论有没有任务通知都要继续处理
(4)先获取一下任务通知值,因为现在并不知道任务通知是否有效,所以还是要再判断一下任务通知是否有效,有效则返回通知值,无效则退出,并且返回 0,代表无效的任务通知值
(5)如果任务通知有效,那在函数前判断一下是否要清除任务通知,根据用户指定的参数 xClearCountOnExit 处理,设置为 pdFALSE 时,函数 xTaskNotifyTake()退出前,将任务的通知值减 1,可以用来实现计数信号量;设置为pdTRUE 时,函数 xTaskNotifyTake()退出前,将任务通知值清零,可以用来实现二值信号量
(6)不清除,那任务通知值就减1
(7)恢复任务通知状态
2.10 获取任务通知 xTaskNotifyWait()
(1)进入临界段。因为下面的操作可能会对任务的状态列表进行操作,系统不希望被打扰
(2)只有任务当前没有收到任务通知,才会将任务阻塞,先看看任务通知是否有效,无效的话就将任务阻塞
(3)使用任务通知值之前, 根据用户指定参数 ulBitsToClearOnEntryClear 将通知值的某些或全部位清零。然后设置任务状态标识,表示当前任务在等待通
(4)如果用户指定了阻塞超时时间,那么系统将挂起任务等待通知或进入阻塞态,根据用户指定超时时间将任务添加到延时列表
(5)然后进行任务切换。触发PendSV 悬挂中断,在退出临界区的时候,进行任务切换
(6)程序能执行到这里说明其它任务或中断向这个任务发送了通知或者任务阻塞超时,任务从阻塞态变成运行态,现在继续处理
(7)返回当前通知值,通过指针参数传递
(8)判断是否是因为任务阻塞超时才退出阻塞的,还是因为其他任务或中断发送了任务通知导致任务被恢复,为什么简单判断一下任务状态就知道?因为如果有任务发送了通知的话,任务通知状态会被改变,而阻塞退出的时候,任务通知状态还是原来的,现在看来是阻塞超时时间到来才恢复运行的,并没有接收到如何通知,那么返回 pdFALSE
(9)收到任务值,先将参数ulBitsToClearOnExit 取反后与通知值位做按位与运算,在退出函数前,将通知值的某些或者全部位清零
(10)重新设置任务通知状态















