任务通知是FreeRTOS提供的一种轻量级通信机制,允许任务间直接发送事件或数据,无需创建队列、信号量等中间对象。每个任务拥有独立的通知值(32位)和状态标志,效率高且节省内存。
任务通知通常用于替代二值信号量或事件标志组,提供了更轻量级的任务间通信方式。大多数任务间通信方法通过中间对象,如队列、信号量或事件组。发送任务写入通信对象,接收任务从通信对象读取。使用任务通知时,发送任务直接向接收任务发送通知,而无需中间对象。(相当直接发到别人的邮箱里,缺点:只能一对一或者多对一)。
参考尚硅谷FreeRTOS
任务通知值设置在在哪里?
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;
函数的实现可以看我的另一篇文章:FreeRTOS源码分析:任务通知-TaskNotify-CSDN博客
发送通知
xTaskNotifyGive()
: 发送通知并增加接收任务的通知值(类似二进制信号量)。xTaskNotify()
: 发送通知并自定义操作(如设置/增加值、更新部分位)。xTaskNotifyFromISR()
:中断服务程序中使用。接收通知
ulTaskNotifyTake()
:等待通知并清零或递减通知值(类似信号量)。xTaskNotifyWait()
: 等待通知并可选择保留部分值(支持复杂条件)。 函数 |
描述 |
xTaskNotify() |
发送通知,带有通知值(通知值一块发过去) |
xTaskNotifyAndQuery() |
发送通知,带有通知值并且保留接收任务的原通知值 (给task4发b,函数返回task4返回原来值a) |
xTaskNotifyGive() |
发送通知,不带通知值(对值进行增量(添加 1),类似信号量) |
xTaskNotifyFromISR() |
在中断中发送任务通知 |
xTaskNotifyAndQueryFromISR() |
|
vTaskNotifyGiveFromISR() |
|
ulTaskNotifyTake() |
获取任务通知,可选退出函数时对通知值清零或减1(类似信号量的做法) |
xTaskNotifyWait() |
获取任务通知,可获取通知值和清除通知值的指定位 |
注意:发送通知有相关ISR函数,接收通知没有ISR函数,不能在ISR中接收任务通知。
函数:
xTaskNotifyGive() |
发送通知,不带通知值(对值进行增量(添加 1),类似信号量) |
ulTaskNotifyTake() |
获取任务通知,可选退出函数时对通知值清零或减1(类似信号量的做法) |
实验:
void task1(void *p)
{
my_printf("task1 running............\r\n");
HAL_Delay(3);
while (1)
{
uint8_t key = 0;
Key_scan();
key = Key_GetValue();
if (key)
{
My_OLED_ShowNum(2, 1, key, 2);
HAL_Delay(1);
xTaskNotifyGive(task_2);
my_printf("任务通知成功.....\r\n");
HAL_Delay(3);
}
vTaskDelay(1);
}
}
void task2(void *p)
{
my_printf("task2 running............\r\n");
HAL_Delay(3);
uint32_t rev = 0;
while (1)
{
rev = ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
if(rev != 0)
{
my_printf("接收任务通知成功,模拟获取二值信号量.....\r\n");
HAL_Delay(3);
}
vTaskDelay(500);
}
}
实验现象:
task1执行xTaskNotifyGive(task_2),之后执行task2,再返回task1执行!!!
函数:
xTaskNotify() |
发送通知,带有通知值(通知值一块发过去) |
xTaskNotifyWait() |
获取任务通知,可获取通知值和清除通知值的指定位 |
实验:
void task1(void *p)
{
my_printf("task1 running............\r\n");
HAL_Delay(3);
while (1)
{
uint8_t key = 0;
Key_scan();
key = Key_GetValue();
if (key!=0 && task_2!=NULL)
{
My_OLED_ShowNum(2, 1, key, 2);
HAL_Delay(1);
xTaskNotify(task_2, key, eSetValueWithOverwrite); //覆盖写入,不管前一个通知是否被读走
my_printf("任务通知模拟消息队列发送成功.....\r\n");
HAL_Delay(3);
}
vTaskDelay(1);
}
}
void task2(void *p)
{
my_printf("task2 running............\r\n");
HAL_Delay(3);
uint32_t rev = 0;
while (1)
{
xTaskNotifyWait(0, 0xFFFFFFFF, &rev, portMAX_DELAY);
if(rev != 0)
{
my_printf("接收任务通知成功,value=%d.....\r\n",rev);
HAL_Delay(3);
}
vTaskDelay(500);
}
}
实验现象:
函数:
xTaskNotify() |
发送通知,带有通知值(通知值一块发过去) |
xTaskNotifyWait() |
获取任务通知,可获取通知值和清除通知值的指定位 |
还是这两个函数
实验:
#define EVENTBIT_1 1
void task1(void *p) //优先级为2
{
my_printf("task1 running............\r\n");
HAL_Delay(3);
while (1)
{
uint8_t key = 0;
Key_scan();
key = Key_GetValue();
if (key)
{
My_OLED_ShowNum(2, 1, key, 2);
HAL_Delay(1);
my_printf("将%d位置1..........\r\n", key);
HAL_Delay(2);
xTaskNotify(task_2, EVENTBIT_1 << (key - 1), eSetBits);
}
vTaskDelay(1);
}
}
void task2(void *p) //优先级为3
{
my_printf("task2 running............\r\n");
HAL_Delay(3);
uint32_t notify_val = 0, event_bit = 0;
while (1)
{
xTaskNotifyWait(0, 0xFFFFFFFF, ¬ify_val, portMAX_DELAY);
event_bit |= notify_val;
if (event_bit == 0x05)
{
my_printf("task2等待到的标志位为第1位和第三位..\r\n");
HAL_Delay(3);
}
vTaskDelay(500);
}
}
实验现象:
任务通知适用于简单事件通知和轻量数据传递,能显著减少资源占用。