FreeRTOS入门(06):任务通知

news2025/1/13 10:34:26

文章目录

  • 目的
  • 基础说明
  • 使用演示
    • 作为二进制信号量
    • 作为计数信号量
    • 作为事件组
    • 作为队列或邮箱
  • 相关函数
  • 总结

目的

任务通知(TaskNotify)是RTOS中相对常用的用于任务间交互的功能,这篇文章将对相关内容做个介绍。

本文代码测试环境见前面的文章:《FreeRTOS入门(01):基础说明与使用演示》

基础说明

前面介绍的队列、信号量、互斥量、队列集、事件组等功能都需要有个独立于任务的对象,任务通过主动去访问对象来使用相关的功能。事实上目前FreeRTOS的任务句柄本身就带有一个对象,用于任务间交互使用,这就是任务通知。

任务通知因为上面的原因有两大优势:一是轻量(因为不需要额外的对象);二是可以直接通知某个任务(也是因为没有中间商)。

任务中的任务通知结构包含状态和一个32位的数据。FreeRTOS v10.4.0 起支持单任务多条通知( 任务通知数组 ),所以现在很多函数都是因为兼容性冗余存在的。使用任务通知时需要设置下面参数:

configUSE_TASK_NOTIFICATIONS // 为1才能使用任务通知(默认为1)
configTASK_NOTIFICATION_ARRAY_ENTRIES // 为任务通知的每个任务数组中的索引数量(默认为1)

任务通知根据使用的不同可以当作二进制信号量、计数信号量、事件组、队列、邮箱等功能来使用。

使用演示

作为二进制信号量

任务通知作为二进制信号量使用就和真正的信号量一样,使用 givetake 函数来操作:

#include "debug.h"
#include "FreeRTOS.h"     // 引入头文件
#include "task.h"         // 引入头文件

TaskHandle_t Task1_Handler; // 任务句柄
TaskHandle_t Task2_Handler; // 任务句柄

void task1(void *pvParameters) {
    while(1) {
        uint32_t data = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待任务通知(注意第一个参数)
        printf("%u task1: %u\r\n", xTaskGetTickCount(), data); // 打印任务通知的值
        vTaskDelete(NULL);
    }
}

void task2(void *pvParameters) {
    while(1) {
        vTaskDelay(500);
        xTaskNotifyGive(Task1_Handler); // 向task1给出任务通知
        vTaskDelete(NULL);
    }
}

int main(void) {
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);

    xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);
    xTaskCreate(task2, "task2", 256, NULL, 5, &Task2_Handler);

    vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环

    while(1) {} // 程序不会运行到这里
}

在这里插入图片描述

作为计数信号量

任务通知作为计数信号量使用和作为二进制信号量一样,使用 givetake 函数,唯一的区别就是 take 函数的参数不同:

#include "debug.h"
#include "FreeRTOS.h"     // 引入头文件
#include "task.h"         // 引入头文件

TaskHandle_t Task1_Handler; // 任务句柄
TaskHandle_t Task2_Handler; // 任务句柄

void task1(void *pvParameters) {
    vTaskDelay(500);
    while(1) {
        uint32_t data = ulTaskNotifyTake(pdFALSE, portMAX_DELAY); // 等待任务通知(注意第一个参数)
        printf("%u task1: %u\r\n", xTaskGetTickCount(), data); // 打印任务通知的值
    }
}

void task2(void *pvParameters) {
    while(1) {
        xTaskNotifyGive(Task1_Handler); // 向task1给出任务通知
        xTaskNotifyGive(Task1_Handler); // 向task1给出任务通知
        xTaskNotifyGive(Task1_Handler); // 向task1给出任务通知
        vTaskDelete(NULL);
    }
}

int main(void) {
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);

    xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);
    xTaskCreate(task2, "task2", 256, NULL, 5, &Task2_Handler);

    vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环

    while(1) {} // 程序不会运行到这里
}

在这里插入图片描述

作为事件组

任务通知作为事件组使用时,任务通知的 notify 函数相当于事件组的 setBits 函数,任务通知的 wait 函数相当于事件组的 waitBits 函数。

#include "debug.h"
#include "FreeRTOS.h"     // 引入头文件
#include "task.h"         // 引入头文件

TaskHandle_t Task1_Handler; // 任务句柄

void task1(void *pvParameters) {
    while(1) {
        uint32_t value = 0;
        BaseType_t ret = xTaskNotifyWait(0xfffffffa, 0, &value, portMAX_DELAY); // 在等待前清除bit2和bit0之外的值,事件触发后不清除任何位
        if (ret == pdPASS ) {
            printf("%u task1: %u\r\n", xTaskGetTickCount(), value); // 打印任务通知的值
        }
    }
}

void task2(void *pvParameters) {
    while(1) {
        vTaskDelay(500);
        xTaskNotify(Task1_Handler, 0b0100, eSetBits ); // bit2设置为1
        vTaskDelete(NULL);
    }
}

void task3(void *pvParameters) {
    while(1) {
        vTaskDelay(1000);
        xTaskNotify(Task1_Handler, 0b0001, eSetBits ); // bit0设置为1
        vTaskDelete(NULL);
    }
}

int main(void) {
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);

    xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);
    xTaskCreate(task2, "task2", 256, NULL, 5, NULL);
    xTaskCreate(task3, "task3", 256, NULL, 5, NULL);

    vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环

    while(1) {} // 程序不会运行到这里
}

在这里插入图片描述

作为队列或邮箱

任务通知不管是作为队列还是作为邮箱使用都相当于一个长度只有 1 的队列,使用的是 notifysetBits 函数,对这两个参数使用不同的方式会相当于不同的功能。

#include "debug.h"
#include "FreeRTOS.h"     // 引入头文件
#include "task.h"         // 引入头文件

TaskHandle_t Task1_Handler; // 任务句柄

void task1(void *pvParameters) {
    while(1) {
        uint32_t value = 0;
        BaseType_t ret = xTaskNotifyWait(0xffffffff, 0, &value, portMAX_DELAY); // 事件触发后清除数据相当于队列
//        BaseType_t ret = xTaskNotifyWait(0xffffffff, 0xffffffff, &value, portMAX_DELAY); // 事件触发后不清除数据相当于邮箱
        if (ret == pdPASS ) {
            printf("%u task1: %u\r\n", xTaskGetTickCount(), value); // 打印任务通知的值
        }
    }
}

void task2(void *pvParameters) {
    while(1) {
        vTaskDelay(500);
        xTaskNotify(Task1_Handler, 233, eSetValueWithoutOverwrite); // 写入数据
                                                                    // eSetValueWithoutOverwrite相当于队列,eSetValueWithOverwrite相当于邮箱
    }
}

int main(void) {
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);

    xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);
    xTaskCreate(task2, "task2", 256, NULL, 5, NULL);

    vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环

    while(1) {} // 程序不会运行到这里
}

在这里插入图片描述

相关函数

// 给出通知(索引0)
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify )
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken );

// 给出通知,uxIndexToNotify为指定索引值
BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify )
void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify,  BaseType_t *pxHigherPriorityTaskWoken );

// 等待通知(索引0)
// xClearCountOnExit为pdFALSE则任务通知的值在该函数退出时递减,为pdTRUE则在退出时清零
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
// 等待通知,uxIndexToWaitOn为指定索引值
uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, BaseType_t xClearCountOnExit, TickType_t xTicksToWait );


// 给出通知(索引0)
// ulValue为通知的值
// eAction可选值如下:
// eNoAction - 目标任务接收事件,但不用更新值,此时不关心ulValue
// eSetBits - 目标通知的值使用按位或与ulValue进行运算(这通常被当作事件组使用)
// eIncrement - 目标通知的值自增,此时不关心ulValue(这通常被当作信号量使用)
// eSetValueWithOverwrite - 使用ulValue覆盖目标通知的值(这通常被当作邮箱使用)
// eSetValueWithoutOverwrite - (这通常被当作长度为1的队列使用)
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );

// 给出通知,uxIndexToNotify为指定索引值
BaseType_t xTaskNotifyIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction );
BaseType_t xTaskNotifyIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );

// 等待通知(索引0)
// ulBitsToClearOnEntry表示等待前清零的任务通知值的位
// ulBitsToClearOnExit表示通知发生后清零的任务通知值的位
// pulNotificationValue用来接收任务通知发生时的值
// 返回值pdPASS表示成功,pdFAIL表示超时
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
// 等待通知,uxIndexToWaitOn为指定索引值
BaseType_t xTaskNotifyWaitIndexed( UBaseType_t uxIndexToWaitOn, uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );

// 给出通知
// 功能类似xTaskNotify
// pulPreviousNotifyValue可以用来接收修改前的目标任务通知的值
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );
BaseType_t xTaskNotifyAndQueryFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue, BaseType_t *pxHigherPriorityTaskWoken );
BaseType_t xTaskNotifyAndQueryIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );
BaseType_t xTaskNotifyAndQueryIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue, BaseType_t *pxHigherPriorityTaskWoken );

// 将通知的状态设置为默认
BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );
BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear );
// 将通知的值设置为默认
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );
uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear, uint32_t ulBitsToClear );

总结

任务通知功能丰富、又轻量,如果可以满足业务功能需求的话,使用任务通知是个不错的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/379461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

「JVM 高效并发」锁优化

为了线程间更高效的共享数据及解决竞争问题,提高程序执行效率,JDK 6 做了大量锁优化,如适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁膨胀(Lock Coarsening&#xf…

2、监控界面设计

【任务描述】本任务要求使用相对布局或约束布局以及相应的控件完成智慧园区监控系统界面开发一、相对布局(RelativeLayout)概述相对布局(RelativeLayout)是一种根据父容器和兄弟控件作为参照来确定控件位置的布局方式。使用相对布…

《机器学习》- 习题解析 - 第一章

《机器学习》- 习题 - 第一章 文章目录《机器学习》- 习题 - 第一章一、示例-计算表1.1中的版本空间二、习题 1 - 计算题目中的版本空间三、单个合取式&析合范式的概念四、习题 2 - 计算题目中假设空间的规模大小一、示例-计算表1.1中的版本空间 首先从概念上理解版本空间…

一起玩转开源数据库!OceanBase DevCon 之开源生态全景解析

​ 2023 年 3 月 25 日,首次 OceanBase 开发者大会将在北京举办,OceanBase 首席科学家阳振坤与 OceanBase CTO 杨传辉领携众多技术专家,将与开发者共同探讨单机分布式、云原生、HTAP 等数据库前沿趋势,OceanBase 开源技术全景生…

数据库——1.数据库设计的三大范式

这篇文章我们主要来讲一下数据库设计的三大范式,这个还是很有用的。 目录 1.概述 2.第一范式 3.第二范式 4.第三范式 5.小结 1.概述 为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范…

Python每日一练(20230301)

目录 1. 只出现一次的数字 2. 以特殊格式处理连续增加的数字 3. 最短回文串 1. 只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 说明: 你的算法应该具有线性…

「TCG 规范解读」基础设施架构和协议 (2)

可信计算组织(Ttrusted Computing Group,TCG)是一个非盈利的工业标准组织,它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立,并采纳了由可信计算平台联盟(the Trusted Computing Platform Alli…

有什么好用的在线统计表单吗?

有什么好用的在线统计表单吗?最好是免费的?市面上这样的表单工具其实很多,先来看看题主的需求: 收集信息,数据统计数据分析,报表展示 以简道云在线表单为例,能完美实现题主这两个需求—— http…

携程面经1

面经 HDFS读写流程 1.读流程 客户端向NameNode发起读请求(如果存在)NameNode返回一批block地址客户端与第一个block的拓扑距离最近的节点建立连接以packet(64kb)的单位读取数据块。一个block读取完成后客户端会断开与该DataNod…

算法训练营 day59 动态规划 两个字符串的删除操作 编辑距离

算法训练营 day59 动态规划 两个字符串的删除操作 编辑距离 两个字符串的删除操作 583. 两个字符串的删除操作 - 力扣(LeetCode) 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符…

DBeaver连接mysql数据库图文教程

文章目录前言一、DBeaver连接mysql数据库二、文档下载地址前言 DBeaver是免费、开源、通用数据库工具,是许多开发开发人员和数据库管理员的所选。下面详细介绍Dbeaver连接mysql数据库的过程。 一、DBeaver连接mysql数据库 1、 打开Dbeaver后,按下图操…

【Unity】P4 脚本文件(基础)

Unity脚本文件(基础)适配的C#代码编辑器如何添加一个脚本文件获取蘑菇当前位置基础代码改变物体位置帧与帧更新前言 上一篇博文主要围绕Unity Inspector部分,围绕组件,资源文件,父子节点部分做介绍。 链接:…

阿里黑客入门学习资料流出来了!!

各位粉丝朋友大家好,最近看到很多粉丝朋友给我留言,希望我给大家找一些学习内容。前段时间整理了我平时常看的一些黑客相关的技术书籍,这些内容从未对外公开,今天分享给大家 ! 内容非常详细且全面,覆盖了W…

5分钟轻松拿下Java枚举

文章目录一、枚举(Enum)1.1 枚举概述1.2 定义枚举类型1.2.1 静态常量案例1.2.2 枚举案例1.2.3 枚举与switch1.3 枚举的用法1.3.1 枚举类的成员1.3.2 枚举类的构造方法1)枚举的无参构造方法2)枚举的有参构造方法1.3.3 枚举中的抽象方法1.4 Enum 类1.4.1 E…

c++系列12:使用vscode进行编译

1. 入门 1.1 操作方法 1)下载安装vscode 2)在扩展中搜索c/c extension pack并安装(或者直接打开cpp文件,会自动提示进行安装) 3)创建项目目录,会自动生成.vscode文件夹,里面是编译…

虹科分享 | Domo零售行业商业智能白皮书:《从零售企业的数据中获取价值》

市场因素、技术创新和不断增长的客户期望,给电子商务带来了新的机遇,与此同时也给传统零售行业带来了压力。零售业正面临着新的挑战:不断变化的需求模式和渠道、不断变化的服务期望、复杂的库存以及交付问题。为了解决这些问题,零…

Linux系统介绍及熟悉Linux基础操作

一、什么是Liunx Linux,全称GNU/Linux,是一种免费使用和自由传播的类UNIX操作系统,其内核由林纳斯本纳第克特托瓦兹(Linus Benedict Torvalds)于1991年10月5日首次发布,它主要受到Minix和Unix思想的启发&am…

机器学习知识总结 —— 21. 什么是主成分分析

文章目录什么是PCA(Principal Component Analysis)协方差矩阵什么是协方差协方差矩阵特征值与特征向量PCA降维什么是PCA(Principal Component Analysis) 在机器学习中,PCA(Principal Component Analysis&a…

除了Confluence,还有哪些好用的文档管理软件?测评

在早期,文档管理软件主要是为了将企业内部海量的电子文档集中存储、管理,通过设置共享权限进行内部员工的文档分发,有些甚至可能要提供API接口,便于将ERP、OA等系统的文档纳入其中,形成企业文档管理中心。而随着时间的…

window下的快捷程序链怎么设置环境变量|cmd直接运行快捷方式

对于需要在命令行执行的程序,每次都需要设置环境变量很是麻烦,而且也会导致非必要的文件也在环境变量里并且如果多版本共存软件也会导致只能一个存在环境变量里不然会冲突,这时候如果可以通过快捷方式那不就完美解决了么? 快捷方…