FreeRTOS任务通知(13)

任务通知的简介

任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue 就是这个通知值。

  • 使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信!
    在这里插入图片描述

  • 使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知"
    在这里插入图片描述

任务通知值的更新方式

  • 不覆盖接受任务的通知值

  • 覆盖接受任务的通知值

  • 更新接受任务通知值的一个或多个bit

  • 增加接受任务的通知值

只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组!

任务通知的优势及劣势

任务通知在性能和 RAM 占用上存在优势,具体为以下两点:

  1. 使用任务通知向任务发送事件或数据比使用队列、信号量或事件组执行等效操作要快得多

  2. 启用任务通知功能的固定开销仅为每个任务 8 个字节的 RAM ,而队列、信号量、事件组等在使用前都必须创建,占用空间较大

任务通知的劣势:

  1. 通信对象可用于将事件和数据从 ISR 发送到任务,以及从任务发送到 ISR;任务通知只能用于将事件和数据从 ISR 发送到任务,不能用于将事件或数据从任务发送到 ISR
  2. 任何知道通信对象句柄的任务和 ISR 都可以访问通信对象,因此多个任务或 ISR 都可以发送或接收消息;任务通知只能将事件和数据发送到某个具体的接收任务中,发送的事件和数据只能由接收任务使用处理
  3. 队列是一种通信对象,一次可以保存多个数据项,已发送到队列但尚未从队列接收的数据将缓冲在队列对象内;任务通知通过更新接收任务的通知值来向任务发送数据,任务的通知值一次只能保存一个值
  4. 事件组是一种通信对象,可用于一次向多个任务发送事件;任务通知直接发送给接收任务,因此只能由接收任务处理
  5. 如果通信对象暂时处于无法向其写入更多数据或事件的状态(例如,当队列已满时,无法向队列发送更多数据),则尝试写入该对象的任务可以选择进入阻塞状态以等待其写操作完成;如果任务尝试向已经有待处理通知的任务发送任务通知,则发送任务不可能在阻塞状态下等待接收任务重置其通知状态

任务通知值和通知状态

设置 configUSE_TASK_NOTIFICATIONS 参数为 1 启动任务通知功能,启动该功能之后,会在每个任务的 TCB (任务控制块)中增加 8 字节空间,此时每个任务都有一个“通知状态”(可以是 “挂起” 或 “未挂起” )和一个 “通知值” (32 位无符号整数)。当任务收到通知时,其通知状态将设置为挂起,当任务读取其通知值时,其通知状态将设置为未挂起

typedef struct tskTaskControlBlock 
{
    … …
        #if ( configUSE_TASK_NOTIFICATIONS  ==  1 )
            volatile  uint32_t    ulNotifiedValue [ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
            volatile  uint8_t      ucNotifyState [ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        endif
    … …
} tskTCB;
#define  configTASK_NOTIFICATION_ARRAY_ENTRIES    1      /* 定义任务通知数组的大小, 默认: 1 */

任务通知状态

其中任务通知状态共有3种取值:

#define     taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* 任务没有在等待通知。初始状态或者任务已经完成了对通知的等待,准备进入下一个等待通知的周期。 */
#define     taskWAITING_NOTIFICATION     ( ( uint8_t ) 1 ) /* 任务正在等待通知。也被称为Not-Pending(未挂起)。当任务调用ulTaskNotifyTake 等待通知时,它的状态将变为等待通知状态。任务会一直保持在这个状态,直到它收到通知或者等待超时。 */
#define     taskNOTIFICATION_RECEIVED    ( ( uint8_t ) 2 ) /* 任务已经收到通知。也被称为pending(挂起)。当任务成功接收到通知时,其状态将从等待通知状态切换到通知已接收状态。任务可以通过调用 ulTaskNotifyTake 函数获取通知的值,并执行相应的操作。 */

任务通知 API 函数概述

  1. 强大通用但较复杂的 xTaskNotify() 和 xTaskNotifyWait() API 函数
  2. 用作二进制或计数信号量的更轻量级且更快的替代方案的 xTaskNotifyGive() 和 ulTaskNotifyTake() API 函数
  3. 在序号 1 的基础上增加 pulPreviousNotifyValue 参数值的 xTaskNotifyAndQuery() API函数

xTaskNotifyGive() 和 ulTaskNotifyTake() API 函数

xTaskNotifyGive() 直接向任务发送通知,并对接收任务的通知值进行递增(加1,因为是模拟信号量),如果接收任务尚未挂起,则调用 xTaskNotifyGive() 会将接收任务的通知状态设置为挂起,该 API 实际上是作为宏实现的,而不是函数,其具体声明如下所述

/**
  * @brief  任务通知用作轻量级且更快的二进制或计数信号量替代方案时所使用的通知发送函数
  * @param  xTaskToNotify:通知发送到的任务的句柄
  * @retval 只会返回pdPASS
  */
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify);
 
/**
  * @brief  上述函数的的中断安全版本函数
  * @param  xTaskToNotify:通知发送到的任务的句柄
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval None
  */
void vTaskNotifyGiveFromISR(TaskHandle_t xTaskToNotify,
							BaseType_t *pxHigherPriorityTaskWoken);

当一个任务使用 xTaskNotifyGive() API 函数将通知值用作二值或等效计数信号量时, 则被通知的任务应使用 ulTaskNotifyTake() API 函数来接收或等待通知值

ulTaskNotifyTake() 允许任务在阻塞状态下等待其通知值大于零,并在返回之前递减(减一)或清除任务的通知值,其具体函数声明如下所述

/**
  * @brief  任务通知被用作更快、更轻的二进制或计数信号量替代时使用通知接收函数
  * @param  xClearCountOnExit:设置为pdTRUE,则该函数返回之前,调用任务的通知值将被清零;设置为pdFASLE,并且通知值大于0,则调用任务的通知值将在该函数返回之前递减
  * @param  xTicksToWait:调用任务应保持阻塞状态以等待其通知值大于零的最长时间
  * @retval 阻塞时间到期也没能等到消息则返回 0 ,阻塞时间到期前等到消息则返回之前的通知值
  */
uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait);

xTaskNotify() API 函数

xTaskNotify() 是 xTaskNotifyGive() 的功能更强大的版本,可用于通过以下任意方式更新接收任务的通知值

  1. 接收任务的通知值递增(加一),在这种情况下 xTaskNotify() 相当于 xTaskNotifyGive()
  2. 接收任务的通知值中设置一位或多位,这允许任务的通知值用作事件标志组的更轻量级和更快的替代方案
  3. 将一个全新的数字写入接收任务的通知值,但前提是接收任务自上次更新以来已读取其通知值,这允许任务的通知值提供与长度为 1 的队列提供的功能类似的功能
  4. 将一个全新的数字写入接收任务的通知值,即使接收任务自上次更新以来尚未读取其通知值,这允许任务的通知值提供与 xQueueOverwrite() API 函数提供的功能类似的功能,由此产生的行为有时被称为**“邮箱”**

xTaskNotify() 比 xTaskNotifyGive() 更灵活、更强大,并且由于额外的灵活性和强大功能,它的使用也稍微复杂一些,使用 xTaskNotify() 函数时,如果接收任务尚未挂起,则调用 xTaskNotify() 将始终将其设置为挂起状态,如下所示为其具体函数声明

/**
  * @brief  任务通知函数
  * @param  xTaskToNotify:通知发送到的任务的句柄
  * @param  ulValue:ulValue的使用方式取决于eAction值,参考 “eAction 参数” 小节
  * @param  eAction:一个枚举类型,指定如何更新接收任务的通知值,参考 “eAction 参数” 小节
  * @retval 除 “eAction 参数” 小节提到的一种情况外,均返回pdPASS
  */
BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify,
					   uint32_t ulValue,
					   eNotifyAction eAction);
 
/**
  * @brief  任务通知的中断安全版本函数
  * @param  xTaskToNotify:通知发送到的任务的句柄
  * @param  ulValue:ulValue的使用方式取决于eAction值,参考 “eAction 参数” 小节
  * @param  eAction:一个枚举类型,指定如何更新接收任务的通知值,参考 “eAction 参数” 小节
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 除 “eAction 参数” 小节提到的一种情况外,均返回pdPASS
  */
BaseType_t xTaskNotifyFromISR(TaskHandle_t xTaskToNotify,
							  uint32_t ulValue,
							  eNotifyAction eAction
							  BaseType_t *pxHigherPriorityTaskWoken);

eAction 参数

eAction 参数是一个 eNotifyAction 枚举类型,其定义了 5 中不同枚举类型,用于模拟二值信号量、计数信号量、队列、事件组和 ”邮箱“ 等功能,其具体定义如下所述

typedef enum
{    
    eNoAction = 0,                /* 无操作 */
    eSetBits                      /* 更新指定bit */
    eIncrement                    /* 通知值加一 */
    eSetValueWithOverwrite        /* 覆写的方式更新通知值 */
    eSetValueWithoutOverwrite     /* 不覆写通知值 */
} eNotifyAction;
eNotifyAction 值对接收任务的最终影响
eNoAction接收任务的通知状态设置为待处理,而不更新其通知值,未使用 xTaskNotify() 中 ulValue 参数
eSetBits接收任务的通知值与 xTaskNotify() 中 ulValue 参数中传递的值进行按位或运算,例如:如果 ulValue 设置为 0x01,则接收任务的通知值中将置位第 0 位
eIncrement接收任务的通知值递增,未使用 xTaskNotify() 中 ulValue 参数
eSetValueWithoutOverwrite如果接收任务在调用 xTaskNotify() 之前有待处理的通知,则不执行任何操作,并且 xTaskNotify() 将返回 pdFAIL;如果在调用 xTaskNotify() 之前接收任务没有待处理的通知,则接收任务的通知值将设置为 xTaskNotify() 中 ulValue 参数中传递的值
eSetValueWithOverwrite接收任务的通知值设置为 xTaskNotify() ulValue 参数中传递的值,无论接收任务在调用 xTaskNotify() 之前是否有待处理的通知

xTaskNotifyWait() API 函数

xTaskNotifyWait() 是 ulTaskNotifyTake() 的功能更强大的版本,它允许任务以可选的超时等待调用任务的通知状态变为待处理(如果它尚未处于待处理状态),xTaskNotifyWait() 提供了在进入函数和退出函数时清除调用任务的通知值中的位的参数 ulBitsToClearOnEntryulBitsToClearOnExit

/**
  * @brief  任务通知的中断安全版本函数
  * @param  ulBitsToClearOnEntry:参考 “ulBitsToClearOnEntry 参数” 小节
  * @param  ulBitsToClearOnExit:参考 “_ulBitsToClearOnExit_ 参数” 小节
  * @param  pulNotificationValue:用于传递任务的通知值,因为等待通知的函数可能由于 ulBitsToClearOnExit 参数在函数退出时收到的消息值已被更改
  * @param  xTicksToWait:调用任务应保持阻塞状态以等待其通知状态变为挂起状态的最长时间
  * @retval 参考 “xTaskNotifyWait() 函数返回值” 小节
  */
BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
						   uint32_t ulBitsToClearOnExit,
						   uint32_t *pulNotificationValue,
						   TickType_t xTicksToWait);

ulBitsToClearOnEntry 参数

如果调用任务在调用 xTaskNotifyWait() 之前没有待处理的通知,则在进入该函数时,将在任务的通知值中清除参数 ulBitsToClearOnEntry 中设置的任何位

例如,如果参数 ulBitsToClearOnEntry 为 0x01,则任务通知值的位 0 将被清除,再举一个例子,将参数 ulBitsToClearOnEntry 设置为 0xffffffff(ULONG_MAX)将清除任务通知值中的所有位,从而有效地将值清除为 0

ulBitsToClearOnExit参数

如果调用任务因为收到通知而退出 xTaskNotifyWait() ,或者因为在调用 xTaskNotifyWait() 时已经有通知挂起,那么在参数 ulBitsToClearOnExit 中设置的任何位将在任务退出 xTaskNotifyWait() 函数之前在任务的通知值中被清除

例如,如果参数 ulBitsToClearOnExit 为 0x03 ,则任务通知值的位 0 和位 1 将在函数退出之前被清除,再举个例子,将参数 ulBitsToClearOnExit 为 0xffffffff(ULONG_MAX)将清除任务通知值中的所有位,从而有效地将值清除为 0

xTaskNotifyWait() 函数返回值

有两种可能的返回值,分别为 pdPASS 和 pdFALSE ,具体如下所述

① pdPASS

  1. 调用 xTaskNotifyWait() 时调用任务已经有待处理的通知
  2. 调用 xTaskNotifyWait() 时调用任务没有待处理的通知,由于设置了阻塞时间因此进入阻塞状态等待消息挂起,在阻塞时间到期之前成功等到消息挂起

② pdFALSE

  1. 调用 xTaskNotifyWait() 时调用任务没有待处理的通知,由于设置了阻塞时间因此进入阻塞状态等待消息挂起,但是直到阻塞时间到期都没有等到消息挂起

其他 API 函数

除了上面的一些常用 API 之外,还有一些工具或不常用的 API 函数,因为启用任务通知后会在任务控制块中增加一个任务状态和一个任务通知值,因此 FreeRTOS 提供了清除任务状态的 xTaskNotifyStateClear() API 函数和 清除任务通知值的 ulTaskNotifyValueClear() API 函数

另外增加了 “任务通知 API 概述” 小节中提到的在 xTaskNotify() API 函数上增加了 pulPreviousNotifyValue 参数的 xTaskNotifyAndQuery() API函数和其中断安全版本函数,上述提到的四个函数声明具体如下所述

/**
  * @brief  清除任务通知状态
  * @param  xTask:要操作的任务句柄
  * @retval 如果要操作的任务有待处理的通知,并且该通知已清除,则返回pdTRUE;如果该任务没有待处理的通知,则返回pdFALSE
  */
BaseType_t xTaskNotifyStateClear(TaskHandle_t xTask);
 
/**
  * @brief  清除任务通知值
  * @param  xTask:要操作的任务句柄
  * @param  ulBitsToClear:xTask的通知值中要清除的位的位掩码,比如设置为0x01表示将通知值的第0位清除
  * @retval ulBitsToClear指定的位被清除之前目标任务的通知值的值
  */
uint32_t ulTaskNotifyValueClear(TaskHandle_t xTask,
								uint32_t ulBitsToClear);
 
/**
  * @brief  执行与xTaskNotify()相同的操作,此外它还在附加的pulPreviousNotifyValue中返回目标任务的先前通知值(调用函数时的通知值,而不是函数返回时的通知值)
  * @param  xTaskToNotify:被通知任务的句柄
  * @param  ulValue:通知值,ulValue的使用方式取决于eAction值,参考 “eAction 参数” 小节
  * @param  eAction:一个枚举类型,指定如何更新接收任务的通知值,参考 “eAction 参数” 
  * @param  pulPreviousNotifyValue:返回目标任务的先前通知值
  * @retval 除 “eAction 参数” 小节提到的一种情况外,均返回pdPASS
  */
BaseType_t xTaskNotifyAndQuery(TaskHandle_t xTaskToNotify,
							   uint32_t ulValue,
							   eNotifyAction eAction,
							   uint32_t *pulPreviousNotifyValue);
							   

/**
  * @brief  上述函数的中断安全版本
  * @param  pxHigherPriorityTaskWoken:通知应用程序编程者是否需要进行上下文切换
  */
BaseType_t xTaskNotifyAndQueryFromISR(TaskHandle_t xTaskToNotify,
									  uint32_t ulValue,
									  NotifyAction eAction,
									  uint32_t *pulPreviousNotifyValue,
									  BaseType_t *pxHigherPriorityTaskWoken)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值