ESP32开发之freeRTOS的任务通知
- 什么是任务通知
- 任务通知的应用场景
- 任务通知使用的函数
- 任务通知举例
- 总结
什么是任务通知
任务通知是直接给任务发送通知,不同于信号量、队列以及事件组,需要借助一个结构体句柄作为传输信息的桥梁。所以任务通知相对而言也更加高效。
任务通知的应用场景
跟信号量、队列以及事件组基本一样。任务直接的通信,通知,任务协调,同步等。
任务通知使用的函数
任务通知的API函数分为两种,一种是简化版的,一种是专业版的
- 简化版
- 发送通知
/*给指定的任务句柄发出通知,此任务句柄在创建任务时创建*/
/*返回值:pdPASS*/
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );/*ISR中必须使用此函数。
参数pxHigherPriorityTaskWoken如果被设置成pdTURE,则表示有比当前任务更高级的任务被唤醒,需要在中断返回之前做任务切换
*/
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
函数作用: |
---|
1. 使通知值加一 |
2. 使得通知状态变为"pending",也就是taskNOTIFICATION_RECEIVED,表示有数据了、待处理 |
- 获取通知
/*参数:
xClearCountOnExit:在退出时是否清除通知值,pdFALSE:如果通知值大于0,则通知值减一;pdTURE:清零通知值
xTicksToWait:等待时间,等于0,则不等待,立刻返回;如果设置为portMAX_DELAY,则一直等,直到通知值大于0*/
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
函数作用: |
---|
1. 使通知值减一或者清零 |
2. 如果通知值等于0 ,则根据设置的阻塞时间阻塞 |
3. 如果通知值大于0,使得任务从阻塞进入就绪状态 |
- 专业版
- 发送通知
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );/*ISR中使用如下函数*/
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t *pxHigherPriorityTaskWoken );
参数说明 |
---|
xTaskToNotify : 给哪个任务发通知,此参数在创建任务时创建 |
ulValue : 如何使用由eAction决定 |
eAction : 取值情况说明: eNoAction:只更新通知状态为"pending",不使用ulValue,属于轻量级的二值信号量 eSetBits:通知值=eSetBits|原来的值,类似轻量级的事件组 eIncrement:通知值=原来的值+1,即是以上简化版额实现,轻量级的二值信号量或计数型信号量 eSetValueWithoutOverwrite: 不进行覆盖操作。如果通知状态为 “pending”(表示存在未读取的数据),则本次调用 xTaskNotify 将不会执行任何操作,并返回 pdFAIL。若通知状态不是 “pending”(即没有新数据),则将通知值设为 ulValue. eSetValueWithOverwrite:进行覆盖操作。不论通知状态是否为”pending",通知值为ulValue |
- 等待通知
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, //刚进入xTaskNotifyWait函数前要清除哪些位uint32_t ulBitsToClearOnExit, //退出之前要清除哪些位,清除前将通知值赋值给*pulNotificationValueuint32_t *pulNotificationValue,//取出通知值TickType_t xTicksToWait ); //阻塞超时时间
参数说明: |
---|
ulBitsToClearOnEntry:刚进入xTaskNotifyWait函数前,通知状态不是“pending”,则清除。清除方式:通知值=通知值&& ~(ulBitsToClearOnEntry)。即在等待事件发生之前,我先把旧数据给清除掉。 |
ulBitsToClearOnExit:在得到数据而退出的情况下,清除通知值的某些位,清除方式:通知值 = 通知值 & ~(ulBitsToClearOnExit),在退出之前先将通知值赋值给 *pulNotificationValue |
pulNotificationValue:如果不取通知值,可以设置为NULL |
xTicksToWait:等待通知状态变为“pending”,不等待设置为0,无限等待设置为 portMAX_DELAY,也可以设置其他值 |
应用举例
- 简化版
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file Filename without the absolute path* @brief Brief description* @author HalfMoon2* @date 2025-05-14* @version v0.1* * @revision history:* 2025-05-14 - Initial version.*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>static TaskHandle_t receiveTask1Handle;
static TaskHandle_t receiveTask2Handle;void sendTask(void* pvPARAM)
{while(1){ESP_LOGI("sendTask","=============================");if(receiveTask1Handle!=NULL){ //因为最开始的值为receiveTask1Handle为NULL,程序启动时xTaskNotifyGive函数中校验参数报错xTaskNotifyGive(receiveTask1Handle); vTaskDelay(pdMS_TO_TICKS(1000));}if(receiveTask2Handle!=NULL){xTaskNotifyGive(receiveTask2Handle);vTaskDelay(pdMS_TO_TICKS(1000));}}
}void receiveTask1(void* pvPARAM)
{while(1){ulTaskNotifyTake(pdTRUE,portMAX_DELAY);ESP_LOGI("receiveTask1","receive success!!!");}
}void receiveTask2(void* pvPARAM)
{while(1){ulTaskNotifyTake(pdTRUE,portMAX_DELAY);ESP_LOGI("receiveTask2","receive success!!!");}
}void app_main(void)
{xTaskCreatePinnedToCore(receiveTask1,"receiveTask1",2048,NULL,3,&receiveTask1Handle,1);xTaskCreatePinnedToCore(receiveTask2,"receiveTask2",2048,NULL,3,&receiveTask2Handle,1);xTaskCreatePinnedToCore(sendTask,"sendTask",2048,NULL,4,NULL,1);
}
打印如下:
- 专业版
- 实现轻量级二值信号量
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file Filename without the absolute path* @brief Brief description* @author HalfMoon2* @date 2025-05-16* @version v0.1* * @revision history:* 2025-05-16 - Initial version.*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>static TaskHandle_t receiveTask1Handle;void sendTask(void* pvPARAM)
{while(1){ESP_LOGI("sendTask","=============================");if(receiveTask1Handle!=NULL){ //因为最开始的值为receiveTask1Handle为NULL,程序启动时xTaskNotifyGive函数中校验参数报错xTaskNotify(receiveTask1Handle,0,eIncrement); //ulvalue参数用不到随意设置vTaskDelay(pdMS_TO_TICKS(1000));}}
}void receiveTask1(void* pvPARAM)
{while(1){xTaskNotifyWait(ULONG_MAX,ULONG_MAX,NULL,portMAX_DELAY);//等到之前清除所有位,退出之前也清除所有位,一直等待通知ESP_LOGI("receiveTask1","receive success!!!");}
}void app_main(void)
{xTaskCreatePinnedToCore(receiveTask1,"receiveTask1",2048,NULL,3,&receiveTask1Handle,1);xTaskCreatePinnedToCore(sendTask,"sendTask",2048,NULL,4,NULL,1);
}
- 实现轻量级事件组
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file Filename without the absolute path* @brief Brief description* @author HalfMoon2* @date 2025-05-16* @version v0.1* * @revision history:* 2025-05-16 - Initial version.*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>static TaskHandle_t receiveTask1Handle;void sendTask(void* pvPARAM)
{while(1){ESP_LOGI("sendTask","=============================");if(receiveTask1Handle!=NULL){ //因为最开始的值为receiveTask1Handle为NULL,程序启动时xTaskNotifyGive函数中校验参数报错xTaskNotify(receiveTask1Handle,0x11,eSetBits);vTaskDelay(pdMS_TO_TICKS(1000));}}
}void receiveTask1(void* pvPARAM)
{uint32_t value=0;while(1){ESP_LOGI("receiveTask1","the value is %ld",value);xTaskNotifyWait(ULONG_MAX,ULONG_MAX,&value,portMAX_DELAY);//等到之前清除所有位,退出之前也清除所有位,一直等待通知,退出之前将通知值存放在了value的地址ESP_LOGI("receiveTask1","receive success!!!the value is %ld",value);value=0;}
}void app_main(void)
{xTaskCreatePinnedToCore(receiveTask1,"receiveTask1",2048,NULL,4,&receiveTask1Handle,1);xTaskCreatePinnedToCore(sendTask,"sendTask",2048,NULL,3,NULL,1);
}
总结
- 简化版任务通知,属于轻量级的二值信号量和计数型信号量,但相比二值信号量和计数型信号量更高效
- 使用专业版任务通知实现的轻量级事件组,不好做到只接受一部分事件,有一定的局限性,虽然高效