任务通知 | 高效的任务间通讯机制¶
参考出处
本文档参考了DURUOFU的ESP32-教程。
任务通知(Task Notifications)是一种轻量级的任务间通信和同步机制,它比队列或事件组更加高效,因为它不需要动态分配内存。每个任务都内置了一个任务通知值,其他任务或中断服务例程(ISR)可以用它来通知该任务事件的发生。
特点:
-
每个任务都有一个任务通知值(32 位整数),可以用于存储信息。
-
任务通知值默认初始化为 0。
-
一个任务可以通过 通知函数 来操作另一个任务的通知值。
-
可以将任务通知值用作二值信号量、计数信号量或简单的 32 位变量。
-
当一个任务收到通知时,它可以选择阻塞(等待通知)或处理该通知。
1. API说明:¶
任务通知主要涉及以下几个 API:
函数名 | 功能 |
---|---|
xTaskNotify | 向指定任务发送通知,通知值可以被覆盖 |
xTaskNotifyGive | 简化版通知函数,用于发送信号量通知 |
xTaskNotifyWait | 等待通知值更新,并选择是否清除通知值 |
ulTaskNotifyTake | 等待任务通知并自动减少通知值(通常用于计数信号量) |
xTaskNotifyStateClear | 清除任务的通知状态 |
xTaskNotify¶
功能: 向指定任务发送通知,并修改该任务的通知值。
原型:
参数:-
xTaskToNotify:接收通知的任务句柄。
-
ulValue:要发送的通知值。
-
eAction:通知值的操作类型。
-
eSetBits:设置通知值的指定位。
-
eIncrement:通知值递增。
-
eSetValueWithOverwrite:覆盖通知值。
-
eSetValueWithoutOverwrite:如果通知值未处理,则不覆盖当前值。
返回值:
-
pdPASS:操作成功。
-
pdFAIL:操作失败(通常在 eSetValueWithoutOverwrite 时发生)。
示例:
xTaskNotifyGive¶
功能: 向任务发送一个 "信号量风格" 的通知,等效于 xTaskNotify() 的 eIncrement 模式。
原型:
参数:
- xTaskToNotify:接收通知的任务句柄。
示例:
ulTaskNotifyTake¶
功能: 任务等待通知,并在接收到通知时自动减少通知值(通常用于实现计数信号量)。
原型:
参数:-
xClearCountOnExit: pdTRUE:退出等待时将通知值清零。 pdFALSE:退出等待时保留剩余通知值。
-
xTicksToWait:等待通知的时间(Tick 数,portMAX_DELAY 表示无限等待)。
返回值:返回通知值(如果是计数信号量,表示剩余信号量计数)。
示例:
xTaskNotifyWait¶
功能: 任务等待通知,可以选择获取通知值的内容,并决定是否清除通知值。
原型:
BaseType_t xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait
);
-
ulBitsToClearOnEntry:进入等待时清除的通知值位。
-
ulBitsToClearOnExit:退出等待时清除的通知值位。
-
pulNotificationValue:保存通知值的指针。
-
xTicksToWait:等待时间(Tick 数)。
返回值:
-
pdTRUE:收到通知。
-
pdFALSE:超时未收到通知。
示例:
uint32_t ulNotificationValue;
if (xTaskNotifyWait(0x00, 0xFFFFFFFF, &ulNotificationValue, portMAX_DELAY) == pdTRUE) {
// 收到通知,ulNotificationValue 保存通知值
}
xTaskNotifyStateClear¶
功能: 清除任务的通知状态(即标记任务为“未通知”)。
原型:
参数:xTask:需要清除通知状态的任务句柄。示例:
2. 示例代码:¶
1.直接任务通知¶
由task2控制task1的运行:
// 事件组
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "main";
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
while (1)
{
ESP_LOGI(TAG, "task1: 等待task通知");
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
ESP_LOGI(TAG, "task1: 收到task通知");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task2启动!");
while (1)
{
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知");
xTaskNotifyGive(xTask1);
}
}
void app_main(void)
{
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, &xTask1);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 1, &xTask2);
}
2.任务通知值¶
任务通知值按位判断-代替队列邮箱或者事件组
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "main";
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
uint32_t ulNotifiedValue;
while (1)
{
ESP_LOGI(TAG, "task1: 等待task通知");
xTaskNotifyWait(0x00, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY);
// 通过不同的bit位来判断通知的来源
if ((ulNotifiedValue & 0x01) != 0)
{
ESP_LOGI(TAG, "task1: 收到task通知-bit0");
}
if ((ulNotifiedValue & 0x02) != 0)
{
ESP_LOGI(TAG, "task1: 收到task通知-bit1");
}
if ((ulNotifiedValue & 0x04) != 0)
{
ESP_LOGI(TAG, "task1: 收到task通知-bit2");
}
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task2启动!");
while (1)
{
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知-bit0");
xTaskNotify(xTask1, 0x01, eSetValueWithOverwrite);
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知-bit1");
xTaskNotify(xTask1, 0x02, eSetValueWithOverwrite);
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知-bit2");
xTaskNotify(xTask1, 0x04, eSetValueWithOverwrite);
}
}
void app_main(void)
{
xTaskCreate(task1, "task1", 1024 * 4, NULL, 1, &xTask1);
xTaskCreate(task2, "task2", 1024 * 4, NULL, 1, &xTask2);
}