当前位置: 首页 > ai >正文

ESP32开发之freeRTOS的任务通知

  • 什么是任务通知
  • 任务通知的应用场景
  • 任务通知使用的函数
  • 任务通知举例
  • 总结

什么是任务通知

任务通知是直接给任务发送通知,不同于信号量、队列以及事件组,需要借助一个结构体句柄作为传输信息的桥梁。所以任务通知相对而言也更加高效。

在这里插入图片描述

任务通知的应用场景

跟信号量、队列以及事件组基本一样。任务直接的通信,通知,任务协调,同步等。

任务通知使用的函数

任务通知的API函数分为两种,一种是简化版的,一种是专业版的

  1. 简化版
  • 发送通知
/*给指定的任务句柄发出通知,此任务句柄在创建任务时创建*/
/*返回值:pdPASS*/
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );/*ISR中必须使用此函数。
参数pxHigherPriorityTaskWoken如果被设置成pdTURE,则表示有比当前任务更高级的任务被唤醒,需要在中断返回之前做任务切换
*/
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
函数作用:
1. 使通知值加一
2. 使得通知状态变为"pending",也就是taskNOTIFICATION_RECEIVED,表示有数据了、待处理
  • 获取通知
/*参数:
xClearCountOnExit:在退出时是否清除通知值,pdFALSE:如果通知值大于0,则通知值减一;pdTURE:清零通知值
xTicksToWait:等待时间,等于0,则不等待,立刻返回;如果设置为portMAX_DELAY,则一直等,直到通知值大于0*/
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
函数作用:
1. 使通知值减一或者清零
2. 如果通知值等于0 ,则根据设置的阻塞时间阻塞
3. 如果通知值大于0,使得任务从阻塞进入就绪状态
  1. 专业版
  • 发送通知
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );/*ISR中使用如下函数*/
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t *pxHigherPriorityTaskWoken );
参数说明
xTaskToNotify : 给哪个任务发通知,此参数在创建任务时创建
ulValue : 如何使用由eAction决定
eAction : 取值情况说明:
eNoAction:只更新通知状态为"pending",不使用ulValue,属于轻量级的二值信号量
eSetBits:通知值=eSetBits|原来的值,类似轻量级的事件组
eIncrement:通知值=原来的值+1,即是以上简化版额实现,轻量级的二值信号量或计数型信号量
eSetValueWithoutOverwrite: 不进行覆盖操作。如果通知状态为 “pending”(表示存在未读取的数据),则本次调用 xTaskNotify 将不会执行任何操作,并返回 pdFAIL。若通知状态不是 “pending”(即没有新数据),则将通知值设为 ulValue.
eSetValueWithOverwrite:进行覆盖操作。不论通知状态是否为”pending",通知值为ulValue
  • 等待通知
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, //刚进入xTaskNotifyWait函数前要清除哪些位uint32_t ulBitsToClearOnExit,  //退出之前要清除哪些位,清除前将通知值赋值给*pulNotificationValueuint32_t *pulNotificationValue,//取出通知值TickType_t xTicksToWait );    //阻塞超时时间
参数说明:
ulBitsToClearOnEntry:刚进入xTaskNotifyWait函数前,通知状态不是“pending”,则清除。清除方式:通知值=通知值&& ~(ulBitsToClearOnEntry)。即在等待事件发生之前,我先把旧数据给清除掉。
ulBitsToClearOnExit:在得到数据而退出的情况下,清除通知值的某些位,清除方式:通知值 = 通知值 & ~(ulBitsToClearOnExit),在退出之前先将通知值赋值给 *pulNotificationValue
pulNotificationValue:如果不取通知值,可以设置为NULL
xTicksToWait:等待通知状态变为“pending”,不等待设置为0,无限等待设置为 portMAX_DELAY,也可以设置其他值

应用举例

  1. 简化版
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file 	 Filename without the absolute path* @brief 	 Brief description* @author 	 HalfMoon2* @date 	 2025-05-14* @version	 v0.1* * @revision history:* 	 2025-05-14 - Initial version.*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>static TaskHandle_t  receiveTask1Handle;
static TaskHandle_t  receiveTask2Handle;void sendTask(void* pvPARAM)
{while(1){ESP_LOGI("sendTask","=============================");if(receiveTask1Handle!=NULL){  //因为最开始的值为receiveTask1Handle为NULL,程序启动时xTaskNotifyGive函数中校验参数报错xTaskNotifyGive(receiveTask1Handle);	vTaskDelay(pdMS_TO_TICKS(1000));}if(receiveTask2Handle!=NULL){xTaskNotifyGive(receiveTask2Handle);vTaskDelay(pdMS_TO_TICKS(1000));}}
}void receiveTask1(void* pvPARAM)
{while(1){ulTaskNotifyTake(pdTRUE,portMAX_DELAY);ESP_LOGI("receiveTask1","receive success!!!");}
}void receiveTask2(void* pvPARAM)
{while(1){ulTaskNotifyTake(pdTRUE,portMAX_DELAY);ESP_LOGI("receiveTask2","receive success!!!");}
}void app_main(void)
{xTaskCreatePinnedToCore(receiveTask1,"receiveTask1",2048,NULL,3,&receiveTask1Handle,1);xTaskCreatePinnedToCore(receiveTask2,"receiveTask2",2048,NULL,3,&receiveTask2Handle,1);xTaskCreatePinnedToCore(sendTask,"sendTask",2048,NULL,4,NULL,1);
}

打印如下:

在这里插入图片描述

  1. 专业版
  • 实现轻量级二值信号量
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file 	 Filename without the absolute path* @brief 	 Brief description* @author 	 HalfMoon2* @date 	 2025-05-16* @version	 v0.1* * @revision history:* 	 2025-05-16 - Initial version.*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>static TaskHandle_t  receiveTask1Handle;void sendTask(void* pvPARAM)
{while(1){ESP_LOGI("sendTask","=============================");if(receiveTask1Handle!=NULL){  //因为最开始的值为receiveTask1Handle为NULL,程序启动时xTaskNotifyGive函数中校验参数报错xTaskNotify(receiveTask1Handle,0,eIncrement);	//ulvalue参数用不到随意设置vTaskDelay(pdMS_TO_TICKS(1000));}}
}void receiveTask1(void* pvPARAM)
{while(1){xTaskNotifyWait(ULONG_MAX,ULONG_MAX,NULL,portMAX_DELAY);//等到之前清除所有位,退出之前也清除所有位,一直等待通知ESP_LOGI("receiveTask1","receive success!!!");}
}void app_main(void)
{xTaskCreatePinnedToCore(receiveTask1,"receiveTask1",2048,NULL,3,&receiveTask1Handle,1);xTaskCreatePinnedToCore(sendTask,"sendTask",2048,NULL,4,NULL,1);
}

在这里插入图片描述

  • 实现轻量级事件组
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file 	 Filename without the absolute path* @brief 	 Brief description* @author 	 HalfMoon2* @date 	 2025-05-16* @version	 v0.1* * @revision history:* 	 2025-05-16 - Initial version.*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>static TaskHandle_t  receiveTask1Handle;void sendTask(void* pvPARAM)
{while(1){ESP_LOGI("sendTask","=============================");if(receiveTask1Handle!=NULL){  //因为最开始的值为receiveTask1Handle为NULL,程序启动时xTaskNotifyGive函数中校验参数报错xTaskNotify(receiveTask1Handle,0x11,eSetBits);vTaskDelay(pdMS_TO_TICKS(1000));}}
}void receiveTask1(void* pvPARAM)
{uint32_t value=0;while(1){ESP_LOGI("receiveTask1","the value is %ld",value);xTaskNotifyWait(ULONG_MAX,ULONG_MAX,&value,portMAX_DELAY);//等到之前清除所有位,退出之前也清除所有位,一直等待通知,退出之前将通知值存放在了value的地址ESP_LOGI("receiveTask1","receive success!!!the value is %ld",value);value=0;}
}void app_main(void)
{xTaskCreatePinnedToCore(receiveTask1,"receiveTask1",2048,NULL,4,&receiveTask1Handle,1);xTaskCreatePinnedToCore(sendTask,"sendTask",2048,NULL,3,NULL,1);
}

在这里插入图片描述

总结

  • 简化版任务通知,属于轻量级的二值信号量和计数型信号量,但相比二值信号量和计数型信号量更高效
  • 使用专业版任务通知实现的轻量级事件组,不好做到只接受一部分事件,有一定的局限性,虽然高效
http://www.xdnf.cn/news/6673.html

相关文章:

  • OpenCV CUDA模块中矩阵操作------归一化与变换操作
  • window nvidia-smi命令 Failed to initialize NVML: Unknown Error
  • 【学习笔记】因果推理导论第1课
  • 3D一览通为山东融科MES系统补全车间看图能力
  • 车道线检测----CLRNet
  • Elasticsearch倒排索引核心原理面试题
  • 视频孪生智慧风电场解决方案
  • 【C++/Qt shared_ptr 与 线程池】合作使用案例
  • 模板分享:网络最小费用流
  • css:倒影倾斜效果
  • Jenkins 最佳实践
  • 从数据包到可靠性:UDP/TCP协议的工作原理分析
  • 【localstorage、sessionStorage和cookie】
  • python报错:typeerror:type object is not subcriptable问题原因及解决方案
  • socket通信中的accept函数
  • 【vue】封装接口,全局字典,表格表头及使用
  • 子查询对多层join优化记录
  • 汉诺塔超算堆栈结构编码和流程详细设计(附源代码)
  • 什么是有向图 无向图 求图的邻接矩阵 软考
  • 搭建游戏云服务器的配置要求包括哪些条件?
  • S32DS使用JLINK编译调试问题点记录
  • Nginx常用命令
  • 在24GB显存大小的GPU上运行27GB的Pytorch模型
  • 基于 Java Socket 的多线程网络聊天程序
  • 依赖倒转原则:Java 架构设计的核心准则
  • 【数据机构】2. 线性表之“链表”
  • 如何使用 Solana Yellowstone gRPC 重新连接和重放插槽
  • Leetcode76覆盖最小子串
  • 软件架构风格系列(4):事件驱动架构
  • 【八股战神篇】Java高频基础面试题