【FreeRTOS(十三)】任务通知

任务通知

任务通知在 FreeRTOS 中是一个可选的功能,要使用任务通知的话就需要将宏configUSE_TASK_NOTIFICATIONS 定义为 1。

FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:

  • 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
  • 覆盖接收任务的通知值。
  • 更新接收任务通知值的一个或多个 bit。
  • 增加接收任务的通知值。

合理、灵活的使用上面这些更改任务通知值的方法可以在一些场合中替代队列、二值信号量、计数型信号量和事件标志组。使用任务通知来实现二值信号量功能的时候,解除任务阻塞的时间比直接使用二值信号量要快 45%(FreeRTOS 官方测试结果,使用 v8.1.2 版本中的二值信号量,GCC 编译器,-O2 优化的条件下测试的,没有使能断言函数 configASSERT()),并且使用的 RAM 更少!

任务通知的发送使用函数xTaskNotify()或者 xTaskNotifyGive()(还有此函数的中断版本)来完 成 , 这 个 通 知 值 会 一 直 被 保 存 着 , 直 到 接 收 任 务 调 用 函 数 xTaskNotifyWait() 或 者ulTaskNotifyTake()来获取这个通知值。假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态。
任务通知虽然可以提高速度,并且减少 RAM 的使用,但是任务通知也是有使用限制的:

  • FreeRTOS 的任务通知只能有一个接收任务,其实大多数的应用都是这种情况。
  • 接收任务可以因为接收任务通知而进入阻塞态,但是发送任务不会因为任务通知发送
    失败而阻塞。


发送通知


带有通知值发送 xTaskNotify

发送任务通知,此函数发送任务通知的时候带有通知值,此函数是个宏,真正执行的函数 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。

不带通知值发送 xTaskNotifyGive

发送任务通知,相对于函数 xTaskNotify(),此函数发送任务通知的时候不带有通知值。此函数只是将任务通知值简单的加一,此函数是个宏,真正执行的是函数 xTaskGenericNotify()

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

参数:
	xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。

返回值:
	pdPASS: 此函数只会返回 pdPASS。


任务通知


获取任务通知 ulTaskNotifyTake

获取任务通知函数,当任务通知用作二值信号量或者计数型信号量的时候可以使用此函数来获取信号量。

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, 
						   TickType_t xTicksToWait );
						   
参数:
	xClearCountOnExit: 参数为 pdFALSE 的话在退出函数 ulTaskNotifyTake()的时候任务通知值减一,类似计数型信号量。当此参数为 pdTRUE 的话在退出函数的时候任务任务通知值清零,类似二值信号量。
	xTickToWait: 阻塞时间。

返回值:
	任何值 : 任务通知值减少或者清零之前的值。

等待任务通知 xTaskNotifyWait

是用来获取任务通知的,不过此函数比 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: 任务通知获取失败。

代码示例:任务通知同步(ulTaskNotifyTake、xTaskNotifyGive)

#include <stdio.h>
#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();
}

代码示例:任务通知值(xTaskNotify 、xTaskNotifyWait )

#include <stdio.h>
#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();
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eiker_3169

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值