任务通知在 FreeRTOS 中是一个可选的功能,要使用任务通知的话就需要将宏configUSE_TASK_NOTIFICATIONS
定义为 1。
FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量
ulNotifiedValue
就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:
- 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
- 覆盖接收任务的通知值。
- 更新接收任务通知值的一个或多个 bit。
- 增加接收任务的通知值。
合理、灵活的使用上面这些更改任务通知值的方法可以在一些场合中替代队列、二值信号量、计数型信号量和事件标志组。使用任务通知来实现二值信号量功能的时候,解除任务阻塞的时间比直接使用二值信号量要快 45%(FreeRTOS 官方测试结果,使用 v8.1.2 版本中的二值信号量,GCC 编译器,-O2 优化的条件下测试的,没有使能断言函数 configASSERT()),并且使用的 RAM 更少!
任务通知的发送使用函数
xTaskNotify()
或者xTaskNotifyGive()
(还有此函数的中断版本)来完 成 , 这 个 通 知 值 会 一 直 被 保 存 着 , 直 到 接 收 任 务 调 用 函 数xTaskNotifyWait()
或 者ulTaskNotifyTake()
来获取这个通知值。假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态。
任务通知虽然可以提高速度,并且减少 RAM 的使用,但是任务通知也是有使用限制的:
- FreeRTOS 的任务通知只能有一个接收任务,其实大多数的应用都是这种情况。
- 接收任务可以因为接收任务通知而进入阻塞态,但是发送任务不会因为任务通知发送
失败而阻塞。
发送任务通知,此函数发送任务通知的时候带有通知值,此函数是个宏,真正执行的函数
xTaskGenericNotify()
。
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction );
参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法,eNotifyAction 是个枚举类型
typedef enum
{
eNoAction = 0,
eSetBits, //更新指定的 bit
eIncrement, //通知值加一
eSetValueWithOverwrite, //覆写的方式更新通知值
eSetValueWithoutOverwrite //不覆写通知值
} eNotifyAction;
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。
发送任务通知,相对于函数
xTaskNotify()
,此函数发送任务通知的时候不带有通知值。此函数只是将任务通知值简单的加一,此函数是个宏,真正执行的是函数xTaskGenericNotify()
。
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
返回值:
pdPASS: 此函数只会返回 pdPASS。
获取任务通知函数,当任务通知用作二值信号量或者计数型信号量的时候可以使用此函数来获取信号量。
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
TickType_t xTicksToWait );
参数:
xClearCountOnExit: 参数为 pdFALSE 的话在退出函数 ulTaskNotifyTake()的时候任务通知值减一,类似计数型信号量。当此参数为 pdTRUE 的话在退出函数的时候任务任务通知值清零,类似二值信号量。
xTickToWait: 阻塞时间。
返回值:
任何值 : 任务通知值减少或者清零之前的值。
是用来获取任务通知的,不过此函数比
ulTaskNotifyTake()
更为强大,不管任务通知用作二值信号量、计数型信号量、队列和事件标志组中的哪一种,都可以使用此函数来获取任务通知。但是当任务通知用作位置信号量和计数型信号量的时候推荐使用函数
ulTaskNotifyTake()
。
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t * pulNotificationValue,
TickType_t xTicksToWait );
参数:
ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取反值进行按位与运算,当此参数为 0xffffffff 或者 ULONG_MAX 的时候就会将任务通知值清零。
ulBitsToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将任务通知值与此参数的取反值进行按位与运算,当此参数为 0xffffffff 或者
ULONG_MAX 的时候就会将任务通知值清零。
pulNotificationValue:此参数用来保存任务通知值。
xTickToWait: 阻塞时间。
返回值:
pdTRUE: 获取到了任务通知。
pdFALSE: 任务通知获取失败。
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
static TaskHandle_t xTask1 = NULL;
void Task1(void *pvParam)
{
while (1)
{
printf("----------------!\n");
printf("task1 wait notification!\n");
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
printf("----------------!\n");
printf("task1 got notification!\n");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void Task2(void *pvParam)
{
while (1)
{
vTaskDelay(pdMS_TO_TICKS(5000));
printf("----------------!\n");
printf("task2 notify task1!\n");
xTaskNotifyGive(xTask1);
}
}
void app_main(void)
{
vTaskSuspendAll();
xTaskCreate(Task1, "Task1", 1024 * 5, NULL, 1, &xTask1);
xTaskCreate(Task2, "Task2", 1024 * 5, NULL, 1, NULL);
xTaskResumeAll();
}
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
static TaskHandle_t xTask1 = NULL;
void Task1(void *pvParam)
{
uint32_t ulNotifiedValue;
while (1)
{
printf("----------------!\n");
printf("task1 wait notification!\n");
xTaskNotifyWait(0x00, /* Don't clear any notification bits on entry. */
ULONG_MAX, /* Reset the notification value to 0 on exit. */
&ulNotifiedValue, /* Notified value pass out in ulNotifiedValue. */
portMAX_DELAY); /* Block indefinitely. */
/* Process any events that have been latched in the notified value. */
if ((ulNotifiedValue & 0x01) != 0)
{
/* Bit 0 was set - process whichever event is represented by bit 0. */
printf("task1 process bit0 event!\n");
}
if ((ulNotifiedValue & 0x02) != 0)
{
/* Bit 1 was set - process whichever event is represented by bit 1. */
printf("task1 process bit1 event!\n");
}
if ((ulNotifiedValue & 0x04) != 0)
{
/* Bit 2 was set - process whichever event is represented by bit 2. */
printf("task1 process bit2 event!\n");
}
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void Task2(void *pvParam)
{
while (1)
{
vTaskDelay(pdMS_TO_TICKS(5000));
printf("----------------!\n");
printf("task2 notify bit0!\n");
xTaskNotify(xTask1, 0x01, eSetValueWithOverwrite);
vTaskDelay(pdMS_TO_TICKS(5000));
printf("task2 notify bit1!\n");
xTaskNotify(xTask1, 0x02, eSetValueWithOverwrite);
vTaskDelay(pdMS_TO_TICKS(5000));
printf("task2 notify bit2!\n");
xTaskNotify(xTask1, 0x04, eSetValueWithOverwrite);
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
void app_main(void)
{
vTaskSuspendAll();
xTaskCreate(Task1, "Task1", 1024 * 5, NULL, 1, &xTask1);
xTaskCreate(Task2, "Task2", 1024 * 5, NULL, 1, NULL);
xTaskResumeAll();
}