6.FreeRTOS之任务通知

什么是任务通知?
FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能,每个任务都有一个 32 位的通知值。按照 FreeRTOS 官方的说法,使用消息通知比通过二进制信号量方式解除阻塞任务快 45% , 并且更加 省内存(无需创建队列)。
在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件标志组,可以替代长度为 1 的队列(可以保存一个 32 位整数或指针值),并且任务通知速度更快、使用的 RAM 更少!

任务通知值的更新方式

FreeRTOS 提供以下几种方式发送通知给任务 :
  1. 发送消息给任务,如果有通知未读, 不覆盖通知值
  2. 发送消息给任务,直接覆盖通知值
  3. 发送消息给任务,设置通知值的一个或者多个位
  4. 发送消息给任务,递增通知值
通过对以上方式的合理使用,可以在一定场合下替代原本的队列、信号量、事件标志组等。

任务通知的优势和劣势 

任务通知的优势
1. 使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。
2. 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。
任务通知的劣势
1. 只有任务可以等待通知,中断服务函数中不可以,因为中断没有 TCB 。
2. 通知只能一对一,因为通知必须指定任务。
3. 等待通知的任务可以被阻塞, 但是发送消息的任务,任何情况下都不会被阻塞等待。
4. 任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据

任务通知相关 API 函数 

1. 发送通知
参数:
xTaskToNotify :需要接收通知的任务句柄;
ulValue :用于更新接收任务通知值, 具体如何更新由形参 eAction 决定;
eAction :一个枚举,代表如何使用任务通知的值;
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS 。
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t *pulPreviousNotifyValue );
参数:
xTaskToNotify :需要接收通知的任务句柄; ulValue :用于更新接收任务通知值, 具体如何更新 由形参 eAction 决定; eAction :一个枚举,代表如何使用任务通知的值;
pulPreviousNotifyValue :对象任务的上一个任务通知值,如果为 NULL , 则不需要回传, 这个时 候就等价于函数 xTaskNotify() 。
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS 。
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
参数:
xTaskToNotify :接收通知的任务句柄, 并让其自身的任务通知值加 1 。
返回值:
总是返回 pdPASS 。

2. 等待通知 

等待通知API函数只能用在任务,不可应用于中断中! 

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,TickType_t xTicksToWait );
参数:
xClearCountOnExit :指定在成功接收通知后,将通知值清零或减 1 , pdTRUE :把通知值清零(二值信号量);pdFALSE :把通知值减一(计数型信号量); xTicksToWait :阻塞等待任务通知值的最大时间;
返回值:
0 :接收失败 非 0 :接收成功,返回任务通知的通知值
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );
ulBitsToClearOnEntry :函数执行前清零任务通知值那些位 。 ulBitsToClearOnExit :表示在函数退出前,清零任务通知值那些位,在清 0 前,接收到的任务通知值会先被保存到形参
*pulNotificationValue 中。 pulNotificationValue :用于保存接收到的任务通知值。 如果 不需要使用,则设置为 NULL 即可 。 xTicksToWait :等待消息通知的最大等待时间。

实验Demo 

Demo1:模拟二值信号量

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xTaskNotifyGive(TaskReceiveHandle);printf("任务通知:模拟二值信号量发送成功!\r\n");}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t rece = 0;/* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){rece = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);if(rece != 0)printf("任务通知:模拟二值信号量接收成功!\r\n");}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskReceive */
}

Demo2:模拟计数型信号量

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xTaskNotifyGive(TaskReceiveHandle);printf("任务通知:模拟计数型信号量发送成功!\r\n");}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t rece = 0;/* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){rece = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);if(rece != 0)printf("任务通知:模拟计数型信号量接收成功!rece=%d\r\n",rece);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskReceive */
}

Demo3:模拟事件标志组

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){printf("将bit0位置1\r\n");xTaskNotify(TaskReceiveHandle, 0x01, eSetBits);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){printf("将bit1位置1\r\n");xTaskNotify(TaskReceiveHandle, 0x02, eSetBits);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t notify_val,event_bit = 0;/* Infinite loop */for(;;){xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);if(notify_val & 0x01)event_bit |= 0x01;	//bit0置1if(notify_val & 0x02)event_bit |= 0x02;	//bit1置1if(event_bit == (0x01 | 0x02))	//0x01|0x02=0x03{printf("任务通知:模拟时间标志组接收成功!event_bit=%d\r\n",event_bit);event_bit = 0;}osDelay(1);}/* USER CODE END StartTaskReceive */
}

Demo4:模拟邮箱

 

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){printf("KEY1按下\r\n");xTaskNotify(TaskReceiveHandle, 0x01, eSetValueWithoutOverwrite);//eSetValueWithOverwrite不带覆写,eSetValueWithoutOverwrite带覆写}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){printf("KEY2按下\r\n");xTaskNotify(TaskReceiveHandle, 0x02, eSetValueWithoutOverwrite);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t notify_val;/* Infinite loop */for(;;){xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);printf("notify_val=%d\r\n",notify_val);osDelay(1);}/* USER CODE END StartTaskReceive */
}

记得点赞+收藏

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

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

相关文章

前端之html(一)

HTML定义: HTML 超文本标记语言 (1)骨架: HTML:整个网页 head:网页头部 boby:网页主体 title:网页标题 (2)标签关系: 1.嵌套 2.并列 (3)注释 语法:<!-- ... --> 基础: (4) 标签:双标签:<> ... </> 单标签:<> <br> …

书生第四期实训营基础岛——L1G3000浦语提示词工程实践

基础任务 任务要求 背景问题&#xff1a;近期相关研究指出&#xff0c;在处理特定文本分析任务时&#xff0c;语言模型的表现有时会遇到挑战&#xff0c;例如在分析单词内部的具体字母数量时可能会出现错误。任务要求&#xff1a;利用对提示词的精确设计&#xff0c;引导语言…

Android启动流程_SystemServer阶段

前言 上一篇文档我们描述了在 Android 启动流程中 Zygote 部分的内容&#xff0c;从 Zygote 的配置、启动、初始化等内容展开&#xff0c;描述了 Zygote 在 Android 启动中的功能逻辑。本篇文档将会继续 Android 启动流程的描述&#xff0c;从 SystemServer 进程的内容展开&am…

FastAPI 核心概念:构建高性能的 Python Web 服务

FastAPI 核心概念&#xff1a;构建高性能的 Python Web 服务 本文详细解析了如何利用 FastAPI 构建高性能的 Python Web 服务&#xff0c;帮助开发者从零开始创建功能丰富、响应迅速的 API。结合完整的代码示例和分步操作指导&#xff0c;文章深入讲解了 FastAPI 的核心概念&a…

Redis 实战 问题

前言 相关系列 《Redis & 目录》《Redis & 实战 & 源码》《Redis & 实战 & 总结》《Redis & 实战 & 问题》 什么缓存击穿&#xff1f;怎么避免&#xff1f; 所谓缓存击穿是指请求因缓存失效而直接访问数据库的情况&#xff0c;由于热点数据访…

Flutter CustomScrollView 效果-顶栏透明与标签栏吸顶

CustomScrollView 效果 1. 关键组件 CustomScrollView, SliverOverlapAbsorber, SliverPersistentHeader 2. 关键内容 TLDR SliverOverlapAbsorber 包住 pinned为 true 的组件 可以被CustomScrollView 忽略高度。 以下的全部内容的都为了阐述上面这句话。初阶 Flutter 开发知…

Litctf-web

Litctf-web exx xxe&#xff0c; <?xml version"1.0" encoding"utf-8"?> <!DOCTYPE xxe [<!ELEMENT name ANY ><!ENTITY xxe SYSTEM "file:///flag" >]><user><username>&xxe;</username> …

线程模型介绍

线程模型的介绍 线程有三种模型&#xff1a;N:1用户线程模型&#xff0c;1:1核心线程模式&#xff0c;N:M混合线程模型 POSIX: Portable Operating System Interface(可移值操作系统接口) N&#xff1a;1用户线程模型 线程的实现建立在进程控制的机制之上&#xff0c;有用户…

2024 Rust现代实用教程:1.3获取rust的库国内源以及windows下的操作

文章目录 一、使用Cargo第三方库1.直接修改Cargo.toml2.使用cargo-edit插件3.设置国内源4.与windows下面的rust不同点 参考 一、使用Cargo第三方库 1.直接修改Cargo.toml rust语言的库&#xff1a;crate 黏贴至Cargo.toml 保存完毕之后&#xff0c;自动下载依赖 拷贝crat…

ML 系列:第 18 部 - 高级概率论:条件概率、随机变量和概率分布

文章目录 一、说明二、关于条件概率2.1 为什么我们说条件概率&#xff1f;2.2 为什么条件概率在统计学中很重要 三、 随机变量的定义3.1 定义3.2 条件概率中的随机变量 四、概率分布的定义五、结论 一、说明 条件概率是极其重要的概率概念&#xff0c;它是因果关系的数学表述&…

基于springboot的社区团购系统设计与实现

一、项目背景 网络交易&#xff08;Electronic Commerce&#xff09;&#xff1a;是指实现整个贸易过程中各阶段的贸易活动的电子化。网络交易是一种多技术的集合体。其业务可包括&#xff1a;信息交换、售后服务、销售、电子支付、运输、组建虚拟企业、公司和贸易伙伴可以共同…

挑战Java面试题复习第4天,坚持就是胜利

挑战第 4 天 Excption与Error包结构OOM 知识点SOF 知识点线程程序进程知识点有些字段不想序列化&#xff0c;怎么办&#xff1f;说说 IO 流Java IO与 NIO的区别 Excption与Error包结构 运行时异常&#xff08;RuntimeException&#xff09;&#xff1a; 包括RuntimeException…

一文读懂系列:SSL加密流量检测技术详解

SSL加密流量检测功能的主要目的是为了对加密流量做解密处理&#xff0c;并对解密后的流量做内容安全检查&#xff08;比如反病毒、入侵防御、URL远程查询、内容过滤、文件过滤和邮件过滤等&#xff09;和审计&#xff08;防止信息泄露&#xff09;。接下来我们详细介绍SSL加密流…

dependencyManagement保持maven的多模块依赖版本一致

在maven的多模块中, 为保持jar包在每个子模块中版本一致, 一般会有两种选择&#xff1a; 在父pom的dependencies标签中声明一个jar&#xff0c;每个子模块都会默认继承该jar 如果只有部分子模块用到了一个jar, 则会选择将该jar坐标信息声明在父pom的dependencyManagement标签中…

axis 参数的方向

axis0&#xff1a;表示沿着行的方向进行操作&#xff0c;即对每一列进行操作。 axis1&#xff1a;表示沿着列的方向进行操作&#xff0c;即对每一行进行操作。 示例&#xff1a; import numpy as np# 创建一个二维数组 array np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9] ])axis0…

golang的循环引用解决方法

循环引用是指在数据结构中&#xff0c;两个或多个实体相互引用&#xff0c;形成一个闭环。例如&#xff0c;在 Golang 的结构体中&#xff0c;如果结构体 A 包含结构体 B 的实例&#xff0c;而结构体 B 又包含结构体 A 的实例&#xff0c;这种情况就称为循环引用。以下是关于循…

【C++】关联式容器

1.Set和Map 1.1 set的介绍 set是一个常用的关联式容器&#xff0c;它存储唯一的元素&#xff0c;这些元素默认情况下按照升序排序。其底层是一种自平衡的二叉搜索树(红黑树)。 set元素的键值就是实值&#xff0c;实值就是键值。set的元素允许插入删除但是不允许修改(具有const…

Spring Cloud Function快速入门Demo

1.什么是Spring Cloud Function&#xff1f; Spring Cloud Function是一个具有以下高级目标的项目&#xff1a; 通过功能促进业务逻辑的实现。将业务逻辑的开发生命周期与任何特定的运行时目标脱钩&#xff0c;以便可以将相同的代码作为Web终结点&#xff0c;流处理器或任务来…

第3章 继承与多态

Java面向对象程序设计-T3(继承与多态) 一、封装 1、概述 封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。 public class Student {//1、私有化属性private String name;private int age;//2、提供公共方法供外部访问//传入数据public void setName(String nam…

荣誉证书PSD素材(59套免费)

本作品提供荣誉证书PSD素材下载&#xff0c;格式为PSD&#xff1b; 请使用软件Photoshop进行编辑&#xff0c;作品中文字及图均可以通过软件修改和编辑&#xff1b; 点击下载: 荣誉证书PSD素材