任务通知
任务通知简介
在 FreeRTOS 中,每一个任务都有两个用于任务通知功能的数组,分别为任务通知数组和 任务通知状态数组。其中任务通知数组中的每一个元素都是一个 32 位无符号类型的通知值;而 任务通知状态数组中的元素则表示与之对应的任务通知的状态。
任务通知数组中的 32 位无符号通知值,用于任务到任务或中断到任务发送通知的“媒介”。 当通知值为 0 时,表示没有任务通知;当通知值不为 0 时,表示有任务通知,并且通知值就是 通知的内容。
任务通知状态数组中的元素,用于标记任务通知数组中通知的状态,任务通知有三种状态, 分别为未等待通知状态、等待通知状态和等待接收通知状态。其中未等待通知状态为任务通知 的复位状态;当任务在没有通知的时候接收通知时,在任务阻塞等待任务通知的这段时间内, 任务所等待的任务通知就处于等待通知状态;当有其他任务向任务发送通知,但任务还未接收 这一通知的这段期间内,任务通知就处于等待接收通知状态。
任务通知功能所使用到的任务通知数组和任务通知状态数组为任务控制块中的成员变量, 因此任务通知的传输是直接传出到任务中的,不同通过任务的通讯对象(队列、事件标志组和 信号量就属于通讯对象)这个间接的方式。间接通讯示意图如下所示:
任务通知则是直接地往任务中发送通知,直接通讯示意图如下所示:
任务通知的优势
使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多;并且使用 任务通知代替队列、事件标志组或信号量,可以节省大量的内存,这是因为每个通讯对象在使 用之前都需要被创建,而任务通知功能中的每个通知只需要在每个任务中占用固定的 5 字节内 存。
任务通知的缺点
虽然任务通知功能相比通讯对象,有着更快、占用内存少的优点,但是任务通知功能并不 能适用于所有情况,例如以下列出的几种情况:
1. 发送事件或数据到中断
通讯对象可以发送事件或数据从中断到任务,或从任务到中断,但是由于任务通知依赖于 任务控制块中的两个成员变量,并且中断不是任务,因此任务通知功能并不适用于从任务往中 断发送事件或数据的这种情况,但是任务通知功能可以在任务之间或从中断到任务发送事件或 数据。
2. 存在多个接收任务
通讯对象可以被已知通讯对象句柄的任意多个任务或中断访问(发送或接收),但任务通知 是直接发送事件或数据到指定接收任务的,因传输的事件或数据只能由接收任务处理。然而在 实际中很少受到这种情况的限制,因为,虽然多个任务和中断发送事件或数据到一个通讯对象 是很常见的,但很少出现多个任务或中断接收同一个通讯对象的情况。
3. 缓冲多个数据项
通讯对象中的队列是可以一次性保存多个已经被发送到队列,但还未被接收的事件或数据 的,也就是说,通讯对象有着一定的缓冲多个数据的能力,但是任务通知是通过更新任务通知 值来发送事件或数据的,一个任务通知值只能保存一次。
4. 广播到多个任务
通讯对象中的事件标志组是可以将一个事件同时发送到多个任务中的,但任务通知只能是 被指定的一个接收任务接收并处理。
5. 阻塞等待接收任务
当通讯对象处于暂时无法写入的状态(例如队列已满,此时无法再向队列写入消息)时, 发送任务是可以选择阻塞等待接收任务接收,但是任务因尝试发送任务通知到已有任务通知但 还未处理的任务而进行阻塞等待的。但是任务通知也很少在实际情况中收到这种情况的限制。
任务通知相关 API 函数
以上两表列出了 FreeRTOS 提供的几个任务通知相关的操作函数,从《任务通知简介》中,可以知道任务的任务控制块中,与任务通知功能相关的两个成员变量,任务通知值和任务通知状态,是两个数组,也就是说,一个任务可以有多个任务通知,多个通知就通过数组的下标进行索引。
表 1 所列出的 API 函数都是对任务通知相关数组中下标为 0 的元素进行操作,而表 2 中列出的 API 函数可以指定对任务通知相关数组中的元素进行操作。两表中对应的 API 函数原理上是一样的,只是表 1 中的 API 是固定对任务的任务通知 0 进行操作,而表 2 中的 API 函数可以对任务的指定任务通知进行操作,下面以表 1 中的函数为例进行讲解。
发送任务通知
#define xTaskNotify( xTaskToNotify, \
ulValue, \
eAction) \
xTaskGenericNotify( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
(ulValue), \
(eAction), \
NULL)
#define xTaskNotifyAndQuery( xTaskToNotify, \
ulValue, \
eAction, \
pulPreviousNotifyValue) \
xTaskGenericNotify( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
(ulValue), \
(eAction), \
(pulPreviousNotifyValue))
#define xTaskNotifyGive( xTaskToNotify) \
xTaskGenericNotify( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
(0), \
eIncrement, \
NULL)
从上面的代码中可以看出,三个用于在任务中发送任务通知的函数,实际上都是调用了函数 xTaskGenericNotify()来发送任务通知的,只是传入了不同的参数。
函数 xTaskGenericNotify()
函数原型
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t * pulPreviousNotificationValue);
形参描述
返回值
函数 xTaskNotify()
此函数用于往指定任务发送任务通知,通知方式可以自由指定,并且不获取发送任务通知前任务通知的通知值。
函数 xTaskNotifyAndQuery()
此函数用于往指定任务发送任务通知,通知方式可以自由指定,并且获取发送任务通知前任务通知的通知值。
函数 xTaskNotifyGive()
此函数用于往指定任务发送任务通知,通知方式为将通知值加 1,并且不获取发送任务通知前任务通知的通知值。
在中断中发送任务通知
#define xTaskNotifyFromISR( xTaskToNotify, \
ulValue, \
eAction, \
pxHigherPriorityTaskWoken) \
xTaskGenericNotifyFromISR( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
(ulValue), \
(eAction), \
NULL, \
(pxHigherPriorityTaskWoken))
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, \
ulValue, \
eAction, \
pulPreviousNotificationValue, \
pxHigherPriorityTaskWoken) \
xTaskGenericNotifyFromISR( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
(ulValue), \
(eAction), \
(pulPreviousNotificationValue), \
(pxHigherPriorityTaskWoken))
#define vTaskNotifyGiveFromISR( xTaskToNotify, \
pxHigherPriorityTaskWoken ) \
vTaskGenericNotifyGiveFromISR( (xTaskToNotify), \
(tskDEFAULT_INDEX_TO_NOTIFY), \
( pxHigherPriorityTaskWoken));
从上面的代码可以看出,函数 xTaskNotifyFromISR()和函数 xTaskNotifyAndQueryFromISR() 实际上都是调用了函数 xTaskGenericNotifyFromISR(),而函数 vTaskNotifyGiveFromISR()实际上 则是调用了函数 vTaskGenericNotifyGiveFromISR()。
函数 xTaskGenericNotifyFromISR()
函数原型
BaseType_t xTaskGenericNotifyFromISR(
TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t * pulPreviousNotificationValue,
BaseType_t * pxHigherPriorityTaskWoken);
形参描述
返回值
函数 vTaskGenericNotifyGiveFromISR()
函数原型
void vTaskGenericNotifyGiveFromISR(
TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
BaseType_t * pxHigherPriorityTaskWoken);
形参描述
返回值
函数 xTaskNotifyFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式可以自由指定,并且不获取发送任务通知前任务通知的通知值,但获取发送通知后是否需要进行任务切换的标志。
函数 xTaskNotifyAndQueryFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式可以自由指定,并且获取发送任务通知前任务通知的通知值,和发送通知后是否需要进行任务切换的标志。
函数 vTaskNotifyGiveFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式为将通知值加 1,并且不获取发送任务通知前任务通知的通知值,但获取发送通知后是否需要进行任务切换的标志。
接收任务通知
函数 ulTaskNotifyTake()
此函数用于获取任务通知的通知值,并且在成功获取任务通知的通知值后,可以指定将通知 值清零或减 1。此函数实际上是一个宏定义,具体的代码如下所示:
#define ulTaskNotifyTake( xClearCountOnExit, \
xTicksToWait) \
ulTaskGenericNotifyTake( (tskDEFAULT_INDEX_TO_NOTIFY), \
(xClearCountOnExit), \
(xTicksToWait))
从 上 面 的 代 码 中 可 以 看 出 , 函 数 ulTaskNotifyTake() 实 际 上 是 调 用 了 函 数 ulTaskGenericNotifyTake()。
函数 ulTaskGenericNotifyTake()
函数原型
uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn,
BaseType_t xClearCountOnExit,
TickType_t xTicksToWait);
形参描述
返回值
函数 xTaskNotifyWait()
此函数用于等待任务通知通知值中的指定比特位被置一,此函数可以在等待前和成功等待 到任务通知通知值中的指定比特位被置一后清零指定比特位,并且还能获取等待超时后任务通 知的通知值。此函数实际上是一个宏定义,具体的代码如下所示:
#define xTaskNotifyWait( ulBitsToClearOnEntry, \
ulBitsToClearOnExit, \
pulNotificationValue, \
xTicksToWait) \
xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, \
(ulBitsToClearOnEntry), \
(ulBitsToClearOnExit), \
(pulNotificationValue), \
(xTicksToWait))
从 上 面 的 代 码 中 可 以 看 出 , 函 数 xTaskNotifyWait() 实 际 上 是 调 用 了 函 数 xTaskGenericNotifyWait()。
函数 xTaskGenericNotifyWait()
函数原型
BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t * pulNotificationValue,
TickType_t xTicksToWait);
Comments NOTHING