freeRTOS任务通知(Task Notifications)

news2024/11/26 9:59:23

目录

前言

 一、任务通知概述

1.优势及限制

2.通知状态和通知值

二、任务通知的使用

两类函数

1.xTaskNotifyGive/ulTaskNotifyTake

2.xTaskNotify/xTaskNotifyWait

三、传输计数值代码示例

四、传输任意值代码示例


前言

所谓"任务通知",你可以反过来读"通知任务"。

我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通知哪个任务。

使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构体通信:

使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知":

 一、任务通知概述

1.优势及限制

任务通知的优势:
\bullet 效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组     都有大的优势。
\bullet 更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。

任务通知的限制:
\bullet 不能发送数据给ISR:
   ISR并没有任务结构体,所以无法使用任务通知的功能给ISR发送数据。但是ISR可以使用任务通     知的功能,发数据给任务。
\bullet 数据只能给该任务独享
   使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务、ISR都可以访问这些数         据。使用任务通知时,数据存放入目标任务中,只有它可以访问这些数据。
   在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是     把一个数据源的数据发给多个任务。 
\bullet 无法缓冲数据
   使用队列时,假设队列深度为N,那么它可以保持N个数据。
   使用任务通知时,任务结构体中只有一个任务通知值,只能保持一个数据。
\bullet 无法广播给多个任务
   使用事件组可以同时给多个任务发送事件。
   使用任务通知,只能发个一个任务。
\bullet 如果发送受阻,发送方无法进入阻塞状态等待
   假设队列已经满了,使用 xQueueSendToBack() 给队列发送数据时,任务可以进入阻塞状态等       待发送完成。
   使用任务通知时,即使对方无法接收数据,发送方也无法阻塞等待,只能即刻返回错误。

2.通知状态和通知值

每个任务都有一个结构体:TCB(Task Control Block),里面有2个成员:
\bullet 一个是uint8_t类型,用来表示通知状态
\bullet 一个是uint32_t类型,用来表示通知值

typedef struct tskTaskControlBlock
{
    ......
    /* configTASK_NOTIFICATION_ARRAY_ENTRIES = 1 */
    volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    ......
} tskTCB;

通知状态有3种取值:
\bullet taskNOT_WAITING_NOTIFICATION:任务没有在等待通知(默认)
\bullet taskWAITING_NOTIFICATION:任务在等待通知
\bullet taskNOTIFICATION_RECEIVED:任务接收到了通知,也被称为pending(有数据了,待处理)

通知值可以有很多种类型:
\bullet 计数值
\bullet 位(类似事件组)
\bullet 任意数值

所以我们可以通过任务通知来实现轻量级的队列,信号量和事件组


二、任务通知的使用

使用任务通知,可以实现轻量级的队列(长度为1)、邮箱(覆盖的队列)、计数型信号量、二进制信号量、 事件组。

两类函数

任务通知有2套函数,简化版、专业版,列表如下:
\bullet 简化版函数的使用比较简单,它实际上也是使用专业版函数实现的
\bullet 专业版函数支持很多参数,可以实现很多功能

1.xTaskNotifyGive/ulTaskNotifyTake

在任务中使用xTaskNotifyGive函数,在ISR中使用vTaskNotifyGiveFromISR函数,都是直接给其他任务发送通知:
\bullet 使得通知值加一
\bullet 并使得通知状态变为"pending",也就是 taskNOTIFICATION_RECEIVED ,表示有数据了、待     处理

可以使用ulTaskNotifyTake函数来取出通知值:
\bullet 如果通知值等于0,则阻塞(可以指定超时时间)
\bullet 当通知值大于0时,任务从阻塞态进入就绪态
\bullet 在ulTaskNotifyTake返回之前,还可以做些清理工作:把通知值减一,或者把通知值清零
使用ulTaskNotifyTake函数可以实现轻量级的、高效的二进制信号量、计数型信号量。

这几个函数的原型如下:

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t
*pxHigherPriorityTaskWoken );

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait
);

xTaskNotifyGive函数的参数说明如下: 

ulTaskNotifyTake函数的参数说明如下:

2.xTaskNotify/xTaskNotifyWait

xTaskNotify 函数功能更强大,可以使用不同参数实现各类功能,比如: 
\bullet 让接收任务的通知值加一:这时 xTaskNotify() 等同于 xTaskNotifyGive()
\bullet 设置接收任务的通知值的某一位某些位,这就是一个轻量级的、更高效的事件组
\bullet 把一个新值写入接收任务的通知值:上一次的通知值被读走后,写入才成功。这就是轻量级的、     长度为1的队列
\bullet 用一个新值覆盖接收任务的通知值:无论上一次的通知值是否被读走,覆盖都成功。
   类似xQueueOverwrite()函数,这就是轻量级的邮箱

xTaskNotifyFromISR() 是它对应的ISR版本,这两个函数用来发出任务通知,使用哪个函数来取出任务通知呢?

使用 xTaskNotifyWait() 函数!它比 ulTaskNotifyTake() 更复杂:
\bullet 可以让任务等待(可以加上超时时间),等到任务状态为"pending"(也就是有数据)
\bullet 还可以在函数进入、退出时,清除通知值的指定位

这几个函数的原型如下:

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 );

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
                            uint32_t ulBitsToClearOnExit,
                            uint32_t *pulNotificationValue,
                            TickType_t xTicksToWait );

重点:
xTaskNotify
函数的参数说明如下:

eNotifyAction参数说明: 

eNotifyAction取值说明
eNoAction仅仅是更新通知状态为"pending",未使用ulValue。
这个选项相当于轻量级的、更高效的二进制信号量
eSetBits通知值 = 原来的通知值 | ulValue,按位或。
相当于轻量级的、更高效的事件组
eIncrement通知值 = 原来的通知值 + 1,未使用ulValue。
相当于轻量级的、更高效的二进制信号量、计数型信号量。
相当于 xTaskNotifyGive() 函数。
eSetValueWithoutOverwrite不覆盖
如果通知状态为"pending"(表示有数据未读),
则此次调用xTaskNotify不做任何事,返回pdFAIL。
如果通知状态不是"pending"(表示没有新数据),
则:通知值 = ulValue。
eSetValueWithOverwrite覆盖
无论如何,不管通知状态是否为"pendng",
通知值 = ulValue。

xTaskNotifyFromISR函数跟xTaskNotify很类似,就多了最后一个参数
pxHigherPriorityTaskWoken 。在很多ISR函数中,这个参数的作用都是类似的,使用场景如下:
\bullet 被通知的任务,可能正处于阻塞状态
\bullet xTaskNotifyFromISR 函数发出通知后,会把接收任务从阻塞状态切换为就绪态
\bullet 如果被唤醒的任务的优先级,高于当前任务的优先级,则"*pxHigherPriorityTaskWoken"被设置       为pdTRUE,这表示在中断返回之前要进行任务切换。


xTaskNotifyWait函数列表如下:


三、传输计数值代码示例

本程序创建2个任务:
        \bullet 发送任务:把数据写入环形缓冲区,使用 xTaskNotifyGive() 让通知值加一
        \bullet 接收任务:使用 ulTaskNotifyTake() 取出通知值,这表示字符数,打印字符
main函数代码如下: 

int main( void )
{
    prvSetupHardware();
    /* 创建1个任务用于发送任务通知
    * 优先级为2
    */
    xTaskCreate( vSenderTask, "Sender", 1000, NULL, 2, NULL );
   
    /* 创建1个任务用于接收任务通知
    * 优先级为1
    */
    xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, &xRecvTask );
    
    /* 启动调度器 */
    vTaskStartScheduler();
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

发送任务代码:

接收任务代码:

发送任务、接收任务的执行流程如下: 
\bullet A:发送任务优先级最高,先执行。连续存入3个字符、发出3次任务通知:通知值累加为3
​​​​​​​\bullet B:发送任务阻塞,让接收任务能执行
​​​​​​​\bullet C:接收任务读到通知值为3,并把通知值清零
​​​​​​​\bullet D:把3个字符依次读出、打印
​​​​​​​\bullet E:再次读取任务通知,阻塞

本程序使用 xTaskNotifyGive/ulTaskNotifyTake 实现了轻量级的计数型信号量,代码更简单:
​​​​​​​\bullet 无需创建信号量
​​​​​​​\bullet 消耗内存更少
​​​​​​​\bullet 效率更高 

信号量是个公开的资源,任何任务、ISR都可以使用它:可以释放、获取信号量。

而本节程序中,发送任务只能给指定的任务发送通知,目标明确;接收任务只能从自己的通知值中得到数据,来源明确。


四、传输任意值代码示例

上述例子中使用任务通知来传输计数值、传输通知。
本节程序使用任务通知来传输任意数据,它创建2个任务:
​​​​​​​\bullet 发送任务:把数据通过 xTaskNotify() 发送给其他任务
​​​​​​​\bullet 接收任务:使用 xTaskNotifyWait 取出通知值,这表示字符,并打印出来

main函数和上个例子一样。

发送任务代码:

接受任务代码:

发送任务、接收任务的执行流程如下: 
​​​​​​​\bullet A:发送任务优先级最高,先执行。连续给对方任务发送3个字符,只成功了1次
​​​​​​​\bullet B:发送任务阻塞,让接收任务能执行
​​​​​​​\bullet C:接收任务读取通知值
​​​​​​​\bullet D:把读到的通知值作为字符打印出来
​​​​​​​\bullet E:再次读取任务通知,阻塞

本程序使用 xTaskNotify/xTaskNotifyWait 实现了轻量级的队列(该队列长度只有1),代码更简单: 
​​​​​​​\bullet 无需创建队列
​​​​​​​\bullet 消耗内存更少
​​​​​​​\bullet 效率更高

队列是个公开的资源,任何任务、ISR都可以使用它:可以存入数据、取出数据。 而本节程序中,发送任务只能给指定的任务发送通知,目标明确;接收任务只能从自己的通知值中得到数据,来源明确。

注意:任务通知值只有一个,数据可能丢失,设计程序时要考虑这点。

任务通知还可以实现事件组的功能,只不过写入各个位的时候,每写一次都会唤醒一次任务(事件组是监测的位都为1(视具体情况)才会将任务唤醒),所以需要程序员手动判断通知值的监测的位是否为1。

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

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

相关文章

螺旋矩阵 | LeetCode-59 | LeetCode-54 | 分类讨论

🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 🎃分类不好,这道题就做不出来!🎈 📌LeetCode链接:59. 螺旋矩阵 II 📌LeetCode链接…

李晨晨的嵌入式学习 DAY25

今天对昨天的fork函数进行了补充,并且学习了exec函数 一,fork函数补充 1.open在fork之前 子进程会继承父进程已打开的相关信息,父子进程会影响同一个offset值 2.open在fork之后 父子进程各自有各自打开的文件信息,不相互影响 …

Luatos-lua For MacOSX

0x00 缘起 看到Luatos-soc-pc项目能够编译到MacOS平台并且运行,所以尝试编译;可是Apple Clang编译器太过于严格,导致编译不通过。遂换到gcc-11编译通过,虽然其中依旧会报错(宏定义LUA_USE_MACOSX不起作用,导…

Linux驱动入门实验班——LED驱动(附百问网视频链接)

目录 一、确定引脚编号 二、编写思路 2.1驱动层 2.2应用层 三、源码 四、实现 课程链接 一、确定引脚编号 首先,可以在开发板上执行如下命令查看已经在使用的GPIO状态: cat /sys/kernel/debug/gpio 可以看到每个gpio都有对应的编号,…

岗位信息采集全攻略:两种方法快速获取招聘信息

摘要 本文将揭秘两大实战策略,助你在激烈的人才市场中迅速捕捉前程无忧上的宝贵岗位信息,无论是手动搜索还是利用现代技术手段,都能事半功倍,抢占先机。 正文 一、手动搜索的艺术:精准定位,深度挖掘 1.…

【网络】传输层TCP协议的报头和传输机制

目录 引言 报头和有效载荷 确认应答机制 超时重传机制 排序和去重 连接管理机制 个人主页:东洛的克莱斯韦克-CSDN博客 引言 TCP是传输层协议,全称传输控制协议。TCP报头中有丰富的字段以及协议本身会制定完善的策略来保证网络传输的可靠性。 TCP…

ICM-20948芯片详解(12)

接前一篇文章:ICM-20948芯片详解(11) 六、寄存器详解 2. USER BANK 0寄存器详述 (56)FIFO_EN_1 参考代码: 无。 (57)FIFO_EN_2 ACCEL_FIFO_EN 1 —— 以采样率将ACCEL-XOUT_H、…

haproxy实例

什么是haproxy Haproxy是一款提供高可用性,负载均衡以及基于tcp和http的的应用交付控制器的开源软件。它由法国人威利塔罗使用c语言开发的。它广泛用于管理和路由网络流量,并确保应用程序的高可用性和高性能。 haproxy的功能 提供第4层(TCP层…

vulnhub系列:Hackademic.RTB1

vulnhub系列:Hackademic.RTB1 靶机下载 一、信息收集 nmap 扫描存活,根据 mac 地址寻找 IP nmap 192.168.23.0/24nmap 扫描端口,开放端口:22、80 nmap 192.168.23.143 -p- -Pn -sV -O访问80端口,页面发现 target …

DirectX修复工具解决问题:一步步教你排除常见错误

在日常使用电脑的过程中,许多用户可能会遇到与DirectX相关的问题,特别是在运行大型游戏或图形密集型应用程序时。这种情况下,选择一款合适的DirectX修复工具免费版来解决问题至关重要! 我们将分享六款好用的DirectX修复工具&…

字节Java后端二面也太难了吧...

粉丝投稿,字节二面直接连环问场景题,难以招架,已经准备好市场上常见的场景题了,希望能帮助你! 由于平台篇幅原因,很多内容展示不了,需要这份《java面试宝典》的伙伴们转发文章关注后&#xff…

Linux_Shell变量及运算符-05

一、Shell基础 1.1 什么是shell Shell脚本语言是实现Linux/UNIX系统管理及自W动化运维所必备的重要工具, Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚本的内容。Shell是一种编程语言, 它像其它编程语言如: C, Java, Python等一样也有变量/函数/运算…

MySQL基础练习题38-每位教师所教授的科目种类的数量

目录 题目 准备数据 分析数据 总结 题目 查询每位老师在大学里教授的科目种类的数量。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Teacher (teacher_id int, subject_id int, dept_id int)## 向表中插入数据 Truncate table…

ALB使用指南|深入探究 lj-lua-stacks:如何生成 Lua 代码级别的火焰图?

前言 ALB(Another Load Balancer)是一款由灵雀云基于 OpenResty 开发的开源Kubernetes(K8s) 网关,拥有多年的生产环境使用经验。Openresty框架高性能的基础上,提供了一系列高级特性,包括多租户支…

JsonUtility和JsonConvert.DeserializeObject

文章目录 JsonUtility 和 JsonConvert在字符串转类型时的不同总结 JsonUtility 和 JsonConvert在字符串转类型时的不同 不同: JsonUtility.FromJson要转的字符串内不能有注释的字符串 但JsonConvert.DeserializeObject要转的字符串里可以有 原因:. Jso…

新品|暴雨发布第四代工作站静安系列-TR1176

极致性能引领未来 暴雨信息推出全新一代工作站——静安系列TR1176,搭载了强劲的第四代处理器,为客户带来前所未有的计算速度与效率。无论是处理大规模数据集、复杂算法还是高负载多任务,都能轻松应对,确保客户的工作流程顺畅无阻…

【随笔】Java 连接操作FTP与SFTP 详细指南

引言 在Java开发中,文件传输协议(FTP)和安全文件传输协议(SFTP)是处理文件传输的两种常见方式。FTP是标准的网络文件传输协议,而SFTP则在FTP基础上增加了安全层(SSH),提…

设计模式 之 —— 组合模式

目录 什么是组合模式? 定义 特点 结构 组合模式(java代码示例) 首先定义接口 定义叶节点(Leaf类) 定义容器节点(Composite类) 测试类: 树形图 运行结果: 组…

CUDA Programming - (1) CUDA简介

1. GPU 简介 处理器一般包含以下几部分:Cache 缓存,ALU 计算单元,Control 控制中心,RAM 内存。 CPU(Central Processing Unit):中央处理器。适合进行逻辑,ALU计算核心较少。适合控…

乒乓球桌上的AI新星:谷歌机器人Agent,是竞技的未来,还是科技的幻想?

在巴黎奥运会乒乓球的激烈角逐中,一个不同寻常的选手悄然登场——谷歌的乒乓球机器人Agent。 这是首个在竞技水平上达到人类标准的AI机器人。 01 AI机器人颠覆初级玩家 Agent不仅能够在正反手之间自如转换,连续进攻,甚至在面对诸如长球、高…