FreeRTOS 体验教程:8.什么是freeRTOS的任务通知?

FreeRTOS 任务通知使用教程

任务通知是 FreeRTOS 提供的一种高效的线程间通信机制。每个 RTOS 任务都有一个 32 位的通知值,任务创建时,这个值被初始化为 0。任务通知相当于直接向任务发送一个事件,接收到通知的任务可以解除阻塞状态,前提是这个阻塞事件是因等待通知而引起的。发送通知的同时,也可以可选地改变接收任务的通知值。本文将详细介绍如何在 FreeRTOS 中使用任务通知。

任务通知的更新方式

可以通过以下方法向接收任务更新通知:

  • 不覆盖接收任务的通知值
  • 覆盖接收任务的通知值
  • 设置接收任务通知值的某些位
  • 增加接收任务的通知值

1. 任务控制块的通知定义

任务通知的定义在 taskTCB 任务控制块中,受控于宏 configUSE_TASK_NOTIFICATIONS。该宏默认开启,所以任务通知功能默认启用。
image.png
image.png

2. 任务通知的基本用法

任务通知可以只发送通知,不附带任何值。这种用法类似于信号量,因此任务通知的接口也提供了类似信号量接口的形式,方便使用和区分:

  • xTaskNotifyGive:释放任务通知(类似于 xSemaphoreGive 释放信号量)
  • ulTaskNotifyTake:获取任务通知(类似于 xSemaphoreTake 获取信号量)

2.1 使用示例

以下示例展示了 task1 每隔 8 秒释放一个任务通知给 task2task2 每隔 1 秒尝试获取任务通知,获取超时时间为 10 秒。

void NotifyTask1_entry(void* parameter)
{
	uint32_t send1 = 1;
	uint32_t ret_num;
	while(1)
	{
		vTaskDelay(configTICK_RATE_HZ*8);
		ret = xTaskNotifyGive(task2StaticHandle);
	}
}
void NotifyTask2_entry(void* parameter)
{
	while(1)
	{
		ret = ulTaskNotifyTake(pdTRUE, configTICK_RATE_HZ*10);
		vTaskDelay(configTICK_RATE_HZ);
	}
}

image.png
image.png

2.2 烧写验证

将上述代码烧写到设备中进行验证,可以观察到 task2 每获取通知后间隔 1 秒再尝试获取任务通知,并在超时时间内成功获取通知。
image.png

3. 附带值的任务通知用法

任务通知也可以附带值使用,以下是相关函数:

  • xTaskNotify:释放任务通知,并附带更新的任务通知值,可以设定任务通知值的更新方式
  • xTaskNotifyWait:获取任务通知

3.1 使用示例

以下示例展示了 task3 每隔 6 秒释放一个任务通知给 task4,通知值为 1,并以通知值累加的方式进行。task4 每隔 1 秒尝试获取任务通知,获取超时时间为 8 秒。由于没有清除通知值,且使用了通知值累加的方式,可以预期到获取到的通知值会一直累加。

void NotifyTask3_entry(void* parameter)
{
	while(1)
	{
		vTaskDelay(configTICK_RATE_HZ*6);
		ret = xTaskNotifyGive(task4StaticHandle, 1, eIncrement);
	}
}
void NotifyTask4_entry(void* parameter)
{
	uint32_t comeIMG = 0;
	while(1)
	{
		ret = ulTaskNotifyWait(pdFALSE, pdFALSE, &comeIMG, configTICK_RATE_HZ*8);
		vTaskDelay(configTICK_RATE_HZ);
	}
}

image.png
image.png

3.2 烧写验证

将上述代码烧写到设备中进行验证,可以观察到 task4 每隔 1 秒尝试获取任务通知,并成功获取累加的通知值。如果在 8 秒内没有收到通知,则会超时。
image.png

4. 任务通知的高级用法

任务通知还提供一个接口,释放通知时可以获取到前一个未被获取的通知值。可以根据这个参数以及函数的返回值,判断前一个通知值是什么以及是否被更新:

  • xTaskNotifyAndQuery:如果通知值更新方式被设置为 eSetValueWithoutOverwrite,则不能被覆写。如果前一次释放的任务通知值未被获取,就再次触发任务通知的释放,那么其会返回 pdFALSE,通知值更新失败。此时可以根据最后一个参数,查询前一个通知值是什么。

4.1 使用示例

以下示例展示了 task5 每隔 2 秒释放一次任务通知,通知值以非覆写的方式进行,通知值在 5 和 6 之间来回变化;task6 每隔 5 秒获取一次任务通知,获取超时时间同样为 5 秒。预期的结果是 task5 在成功释放一次任务通知后,会出现一次释放任务通知失败,第一次失败的通知值应该为 6。

void NotifyTask5_entry(void* parameter)
{
	static uint8_t notify6 = 0;
	uint32_t ret_num;
	while(1)
	{
		vTaskDelay(configTICK_RATE_HZ*2);
		if (notify6 == 1)
		{
			ret = xTaskNotifyAndQuery(task6StaticHandle, 6, eSetValueWithoutOverwrite, &ret_num);
			notify6 = 0;
		}
		else
		{
			ret = xTaskNotifyAndQuery(task6StaticHandle, 5, eSetValueWithoutOverwrite, &ret_num);
   			notify6 = 1;
		}
	}
}
void NotifyTask6_entry(void* parameter)
{
	uint32_t comeIMG = 0;
	while(1)
	{
		ret = ulTaskNotifyWait(pdFALSE, pdFALSE, &comeIMG, configTICK_RATE_HZ*5);
		vTaskDelay(configTICK_RATE_HZ*5);
	}
}

image.png
image.png

4.2 烧写验证

将上述代码烧写到设备中进行验证,可以观察到 task5 在成功发送一次通知后,会出现一次发送失败,并打印前一个通知值。同时,task6 每隔 5 秒成功接收一次通知。
image.png

4.3 非覆写模式的效果

使用 xTaskNotifyAndQuery 接口时,如果通知值的更新方式没有设置为非覆写模式,则每次发送通知都会成功,之前的通知值会被覆写。

void NotifyTask5_entry(void* parameter)
{
	static uint8_t notify6 = 0;
	uint32_t ret_num;
	while(1)
	{
		vTaskDelay(configTICK_RATE_HZ*2);
		ret = xTaskNotifyAndQuery(task6StaticHandle, 6, eIncrement, &ret_num);
	}
}

image.png

4.4 烧写验证

将上述代码烧写到设备中进行验证,可以观察到每次发送通知都会成功,通知值不断被覆写。
image.png

通过本教程,我们详细介绍了 FreeRTOS 任务通知的基本用法和高级用法。任务通知提供了一种高效、灵活的任务间通信机制,可以选择不同的通知值更新方式来满足不同的需求。希望这篇文章能帮助您更好地理解和使用 FreeRTOS 的任务通知功能。如果有任何疑问或需要进一步的指导,欢迎在评论区留言,我们将尽快回复。

对应的 demo 源码, 请点击 RtosExPro at freertos_communication_notify

也可扫码关注博主同名公众号"不解之榬",回复 “freeRTOS” 获取
不解之榬

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值