任务通知
使用队列、信号量、事件组等方法时,无法知道发送方身份。使用任务通知时,可以明确指定:通知哪个任务。
优势
效率更高。
使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都有优势。
更节省内存。
使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。
限制
不能发送数据给ISR
ISR并没有任务结构体,所以无法使用任务通知的功能给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
数据只能给该任务独享
使用队列、信号量、事件组时,数据保存在结构体中,其他任务、ISR都可以访问结构体的数据。
使用任务通知时,数据存放入目标任务中,只有它可以访问这些数据。
在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是把一个数据源的数据发给多个任务。
无法缓存数据
使用队列时,假设队列深度为N,则它可以保存N个数据。
使用任务通知时,任务结构体只有一个任务通知值,只能保存一个数据。
无法广播给多个任务
使用事件组时,可以同时给多个任务发送事件。
使用任务通知时,只能发一个任务。
如果发送受阻,发送方无法进入阻塞状态等待
假设队列已满,使用xQueueSendToBack()给队列发送数据时,任务可以进入阻塞状态等待发送完成。
使用任务通知时,计数接双方无法接收数据,发送方也无法阻塞等待,只能立即返回错误。
任务状态和通知值
每个人物都有一个结构体:TCB。里面有两个成员:uint8_t ucNotifyState(表示通知状态)、uint32_t ulNotifiedValue(表示通知值)。
typedef struct tskTaskControlBlock { ...... /* configTASK_NOTIFICATION_ARRAY_ENTRIES = 1 */ volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; ...... } tskTCB;
ucNotifyState有三种取值:
##define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* 默认状态,任务没有在等待通知 */ ##define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) /* 任务在等待通知 */ ##define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) /* 任务接收到了通知 */
ulNotifiedValue可以有很多类型:
计数值、位(类似事件组)、任意数值。
事件通知函数
使用任务通知,可以实现轻量级的队列(长度为1)、邮箱(覆盖的队列)、计数型信号量、二进制信号量、事件组。
简化版,调用简单 | 专业版,参数很多 | |
发出通知 | xTaskNotifyGive xTaskNotifyGiveFromISR | xTaskNotify xTaskNotifyFromISR |
取出通知 | xTaskNotifyTake | xTaskNotifyWait |
xTaskNotifyGive/xTaskNotifyTake
xTaskNotifyGive / xTaskNotifyGiveFromISR都是直接给其他任务发送通知,使得通知值+1,并使得通知状态变为taskNOTIFICATION_RECEIVED,表示有数据了,待处理。
xTaskNotifyTake取出通知值。可以实现轻量级的、高效的二进制信号量、计数型信号量。
如果通知值为0,则阻塞(可以指定超时时间)。
如果通知值大于0,任务从阻塞态进入就绪态。
在该函数返回之前,还可以做一些清理工作:把通知值-1,或清零通知值。
/* xTaskToNotify:任务句柄 */
/* 返回值:只能是pdPASS */
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
/* xTaskToNotify:任务句柄 */
/* pxHigherPriorityTaskWoken:
被通知的任务可能正处于阻塞状态。此函数发出通知后,会把它从阻塞状态切换为就绪态。
如果被唤醒任务的优先级高于当前任务的优先级,则*pxHigherPriorityTaskWoken为pdTRUE,表示在中断返回前要进行任务切换
*/
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
/* xClearCountOnExit:函数返回前是否清零。
pdTRUE:清零通知值。
pdFALSE:如果通知值大于0,则通知值减一。
xTicksToWait:任务进入阻塞态的超时时间,等待通知值大于0。
0:不等待,立即返回。
portMAX_DELAY:一直等待,直到通知值大于0。
其他值:Tick Count。可用pdMS_TO_TICKS()。
返回值:
函数返回前清零/减一通知值。
如果xTicksToWait非0,则返回值有两种情况:
大于0:在超时前,通知值被增加了
等于0:一直没有其他任务增加通知值,最后超时返回0
*/
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait);
xTaskNotify/xTaskNotifyWait
xTaskNotify可以使用不同参数实现各类功能,如:
让接收任务的通知值+1:此时xTaskNotify()等同于xTaskNotifyGive()
设置接收任务的通知值的某一位、某些位。此时为轻量级、更高效的事件组
把一个新值写入接收任务的通知值:上一次的通知值被读走后,写入才成功。此时为轻量级、长度为1的队列
用一个新值覆盖接收任务的通知值:无论上一次的通知值是否被读走,覆盖都成功。此时为轻量级邮箱,类似xQueueOverwrite()函数