在ESP-IDF环境中如何进行多文件中的数据流转-FreeRTOS实时操作系统_流缓存区“xMessageBuffer”

一、建立三个源文件和对应的头文件

建立文件名,如图所示

图 1-1

二、包含相应的头文件

main.h

图 2-1

mess_send.h

mess_rece.h和这个中类似,不明白的大家看我最后面的源码分享

图2-2

三、声明消息缓存区的句柄

大家注意,在main.c中定义的是全局变量图3-1所示

mess_send.c和mess_rece.c则定义的是外部变量,如图3-2所示

图 3-1

图3-2

四、xMessageBufferCreate函数

xMessageBufferCreate:此函数的作用是为句柄创造一个消息缓存区,用来存储在不同任务之间流转的数据,需要在流转任务执行之间进行创建。

configASSERT(xMessageBuffer != NULL);

此函数是用来确认是否创建成功了,是否成功初始化了资源

xMessageBuffer = xMessageBufferCreate(1024);  // 缓冲区大小为100字节
configASSERT(xMessageBuffer != NULL);

图 4-1

五、xMessageBufferSend发送函数

注意图 5-1中所示的方框,这里要用strlen,千万注意,用错了,会有发送错误的可能

图 5-1

官方示例程序:

大家注意看,当发送的信息是数组形式的时候,用的sizeof;发送字符串的时候用的是strlen。大家使用的时候一定要注意区分。

为什么使用不同的方法

  1. 数组
  • 数组的大小是固定的,并且包含了所有元素的总大小。使用 sizeof 可以确保你发送整个数组的内容。
  • 如果数组不是以空字符结尾的,使用 strlen 会得到错误的结果,因为它会一直读取直到遇到第一个空字符,可能会导致越界访问或未定义行为。

     2.字符串

  • 字符串是以空字符 \0 结尾的字符数组。strlen 函数计算的是从字符串开始到第一个空字符之间的字符数,不包括空字符本身。
  • 使用 strlen 可以确保只发送字符串的有效内容,而不包括结尾的空字符。这对于消息缓冲区来说是合理的,因为你通常不需要发送多余的空字符。
void vAFunction( MessageBufferHandle_t xMessageBuffer )
{
size_t xBytesSent;
uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
char *pcStringToSend = "String to send";
const TickType_t x100ms = pdMS_TO_TICKS( 100 );// Send an array to the message buffer, blocking for a maximum of 100ms to// wait for enough space to be available in the message buffer.xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );if( xBytesSent != sizeof( ucArrayToSend ) ){
// The call to xMessageBufferSend() times out before there was enough
// space in the buffer for the data to be written.}// Send the string to the message buffer.  Return immediately if there is// not enough space in the buffer.xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );if( xBytesSent != strlen( pcStringToSend ) ){
// The string could not be added to the message buffer because there was
// not enough free space in the buffer.}
}

我的程序:
 

参数解释

  1. xMessageBuffer:
  • 类型: MessageBufferHandle_t
  • 描述: 这是消息缓冲区的句柄,由 xMessageBufferCreate 创建并返回。你需要传递这个句柄来指定将数据发送到哪个消息缓冲区。

  2.pvTxData:

  • 类型: const void *
  • 描述: 这是一个指向要发送的数据的指针。函数将从这个指针开始复制数据到消息缓冲区中。

  3.xDataLengthBytes:

  • 类型: size_t
  • 描述: 这是要发送的数据的长度(以字节为单位)。函数将尝试将 xDataLengthBytes 字节的数据发送到消息缓冲区。如果消息缓冲区没有足够的空间来容纳这些数据,任务可能会被阻塞,直到有足够的空间。

  4.xTicksToWait:

  • 类型: TickType_t
  • 描述: 这是指定任务在消息缓冲区没有足够空间时等待的最大时间。你可以使用以下几种值:
  • 0: 不等待,立即返回。如果消息缓冲区没有足够的空间,则返回 0。
  • portMAX_DELAY: 无限等待,直到消息缓冲区有足够空间。
  • 其他正值: 指定等待的滴答数(ticks)。例如,pdMS_TO_TICKS(100) 表示等待 100 毫秒。
#include "mess_send.h"const static char *TAG = "mess_send";
extern MessageBufferHandle_t xMessageBuffer;/*  */void mess_send()
{while (1){char *jsondata = "da_di_ji_de_xiao_bai";size_t xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)jsondata, strlen(jsondata) ,0);  // 包括终止符if ( xBytesSent != strlen(jsondata)) {   ESP_LOGE(TAG, "Failed to send message");} else {ESP_LOGI(TAG, "Message sent: %s", jsondata); }vTaskDelay(pdMS_TO_TICKS(1000)); // 适当延时}
}

六、xMessageBufferReceive函数

char received_message [1024];参考我的程序

使用接收缓存区的时候,上来一定要先定义一个接收信息的地方,要不然,你一直接收人家,也不给人家安排地方休息,那不是让人家不得不乱转,那你到时候最后出事了,你就不能怪别人了。

你不仅要安排,你还要安排足够多的空间。

官方示例程序:

void vAFunction( MessageBuffer_t xMessageBuffer )
{
uint8_t ucRxData[ 20 ];
size_t xReceivedBytes;
const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );// Receive the next message from the message buffer.  Wait in the Blocked// state (so not using any CPU processing time) for a maximum of 100ms for// a message to become available.xReceivedBytes = xMessageBufferReceive( xMessageBuffer,( void * ) ucRxData,sizeof( ucRxData ),xBlockTime );if( xReceivedBytes > 0 ){
// A ucRxData contains a message that is xReceivedBytes long.  Process
// the message here....}
}

我的程序:

我的程序比较多,因为我加入了一些调试信息,这样运行后可以清楚的看出错误出在哪里

参数解释:

1.xMessageBuffer:

  • 类型: MessageBufferHandle_t
  • 描述: 这是消息缓冲区的句柄,由 xMessageBufferCreate 创建并返回。你需要传递这个句柄来指定从哪个消息缓冲区接收数据。

2.pvRxData:

  • 类型: void *
  • 描述: 这是一个指向接收数据的缓冲区的指针。函数将从消息缓冲区中读取的数据复制到这个缓冲区中。你需要确保这个缓冲区足够大,以容纳你希望接收的数据。

3.xBufferLengthBytes:

  • 类型: size_t
  • 描述: 这是 pvRxData 缓冲区的大小(以字节为单位)。函数将尝试从消息缓冲区中读取最多 xBufferLengthBytes 字节的数据。如果消息缓冲区中的数据少于 xBufferLengthBytes,则只读取可用的数据量。

4.xTicksToWait:

  • 类型: TickType_t
  • 描述: 这是指定任务在没有数据可接收时等待的最大时间。你可以使用以下几种值:
  • 0: 不等待,立即返回。如果没有数据可接收,则返回 0。
  • portMAX_DELAY: 无限等待,直到有数据可接收。
  • 其他正值: 指定等待的滴答数(ticks)。例如,pdMS_TO_TICKS(100) 表示等待 100 毫秒。
#include "mess_rece.h"
extern MessageBufferHandle_t xMessageBuffer;
const static char *TAG = "mess_rece";void mess_rece()
{while(1){char received_message [1024];const TickType_t xBlockTime = pdMS_TO_TICKS(100);size_t received_size = xMessageBufferReceive(xMessageBuffer, (void*)received_message,sizeof(received_message)-1, xBlockTime);if(received_size >0 ){received_message[received_size] = '\0';  // 添加终止符// 打印接收到的消息ESP_LOGI(TAG, "mess_rece: %s", received_message);// 检查字符串是否有效if (strlen(received_message) != received_size){ESP_LOGE(TAG, "String length mismatch: expected %zu, got %zu", received_size, strlen(received_message));continue;  // 跳过无效的字符串}} else {ESP_LOGW(TAG, "No message received, waiting...");}vTaskDelay(pdMS_TO_TICKS(900));  // 如果没有接收到数据,则等待一段时间后再次尝试}}

七、关于创建任务的一些补充(挺重要的,建议多看)

补充点:为什么创建任务对应的函数内必须有一个while(1)循环

  1. 任务的持续性
    无限循环:while (1) 循环确保任务不会退出。一旦任务函数返回,FreeRTOS 会自动删除该任务并释放其资源。如果任务需要持续运行以执行某些操作(如周期性地发送或接收数据、监控传感器等),则必须使用无限循环。
    避免任务结束:如果任务函数没有无限循环并且直接返回,任务将被删除,导致该任务的功能无法继续执行。
  2. 任务的状态管理
    阻塞和等待:在 while (1) 循环中,任务可以执行一些操作,然后进入阻塞状态(例如通过 vTaskDelay 或其他阻塞 API)。这样可以有效地管理 CPU 资源,避免任务一直占用 CPU 时间。
    事件驱动:任务可以在 while (1) 循环中等待特定的事件(例如消息队列中的消息、信号量等),并在事件发生时进行处理。
  3. 任务调度
    抢占式调度:FreeRTOS 是一个抢占式实时操作系统。当高优先级任务准备好运行时,当前正在运行的低优先级任务会被中断并切换到高优先级任务。因此,任务函数中的 while (1) 循环允许任务在需要时被调度器暂停和恢复。
    时间片轮转:在相同优先级的任务之间,FreeRTOS 可以使用时间片轮转调度。while (1) 循环允许任务在每次时间片结束后重新开始执行。

八、源码分享

main.c

#include "main.h"
MessageBufferHandle_t xMessageBuffer;const static char *TAG = "main";void app_main(void)
{xMessageBuffer = xMessageBufferCreate(1024);  // 缓冲区大小为100字节configASSERT(xMessageBuffer != NULL);// 创建任务xTaskCreate(mess_send,          // 任务函数"mess_sendTask",      // 任务名称4096,               // 任务堆栈大小(以字为单位)NULL,               // 传递给任务函数的参数1,                  // 任务优先级NULL);              // 任务句柄(不需要)xTaskCreate(mess_rece,          // 任务函数"mess_receTask",      // 任务名称4096,               // 任务堆栈大小(以字为单位)NULL,               // 传递给任务函数的参数1,                  // 任务优先级NULL);              // 任务句柄(不需要)// 主任务可以继续执行其他操作while (1) {vTaskDelay(pdMS_TO_TICKS(5000)); // 主任务每 5 秒打印一次ESP_LOGI(TAG, "successful");}
}

main.h

#ifndef __MAIN_H
#define __MAIN_H#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_log.h"
#include "freertos/message_buffer.h"                //使用消息缓存区应该包含的头文件#include "stdio.h"
#include "string.h"#include "mess_rece.h"                              //包含创建的接收和发送的两个文件的头文件
#include "mess_send.h"#endif

mess_send.c

#include "mess_send.h"const static char *TAG = "mess_send";
extern MessageBufferHandle_t xMessageBuffer;/*  */void mess_send()
{while (1){char *jsondata = "da_di_ji_de_xiao_bai";size_t xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)jsondata, strlen(jsondata) ,0);  // 包括终止符if ( xBytesSent != strlen(jsondata)) {   ESP_LOGE(TAG, "Failed to send message");} else {ESP_LOGI(TAG, "Message sent: %s", jsondata); }vTaskDelay(pdMS_TO_TICKS(1000)); // 适当延时}
}

mess_send.h

#ifndef __MESS_SEND_H
#define __MESS_SEND_H#include "main.h"/* 函数声明区 */void mess_send();
#endif

mess_rece.c

#include "mess_rece.h"
extern MessageBufferHandle_t xMessageBuffer;
const static char *TAG = "mess_rece";void mess_rece()
{while(1){char received_message [1024];const TickType_t xBlockTime = pdMS_TO_TICKS(100);size_t received_size = xMessageBufferReceive(xMessageBuffer, (void*)received_message,sizeof(received_message)-1, xBlockTime);if(received_size >0 ){received_message[received_size] = '\0';  // 添加终止符// 打印接收到的消息ESP_LOGI(TAG, "mess_rece: %s", received_message);// 检查字符串是否有效if (strlen(received_message) != received_size){ESP_LOGE(TAG, "String length mismatch: expected %zu, got %zu", received_size, strlen(received_message));continue;  // 跳过无效的字符串}} else {ESP_LOGW(TAG, "No message received, waiting...");}vTaskDelay(pdMS_TO_TICKS(900));  // 如果没有接收到数据,则等待一段时间后再次尝试}}

mess_rece.h

#ifndef __MESS_RECE_H
#define __MESS_RECE_H#include "main.h"void mess_rece();
#endif

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

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

相关文章

Nodemon 深入解析与使用

目录 1. 基本知识2. Demo3. 高级配置 1. 基本知识 Nodemon 深入解析与使用指南 Nodemon 是一个强大的开发工具&#xff0c;用于监控 Node.js 应用程序中的文件变更&#xff0c;能自动重启应用&#xff0c;极大提高开发效率 基本的优点如下&#xff1a; 自动化&#xff1a;避…

【C语言】循环嵌套:乘法表

循环嵌套&#xff0c;外层循环执行一次&#xff0c;内层循环执行i次。分别控制 在循环的过程中加一层循环。 多层循环属于循环嵌套、嵌套循环 #include <stdio.h> #include <math.h> /* 功能&#xff1a;循环嵌套 乘法表 时间&#xff1a;2024年10月 地点&#xf…

Chrome谷歌浏览器加载ActiveX控件之JT2Go控件

背景 JT2Go是一款西门子公司出品的三维图形轻量化预览解决工具&#xff0c;包含精确3D测量、基本3D剖面、PMI显示和改进的选项过滤器等强大的功能。JT2Go控件是一个标准的ActiveX控件&#xff0c;曾经主要在IE浏览器使用&#xff0c;由于微软禁用IE浏览器&#xff0c;导致JT2Go…

视频画面变糊是平台在做手脚吗?

人气视频博主“影视飓风”一条新发布的视频被全网下架&#xff0c;这两日更是引起轩然大波&#xff0c;不仅频上热门&#xff0c;甚至还有不少数码与视频同行为其抱不平。那么影视飓风到底发布了一条什么视频&#xff1f;又为何会惨被全网下架呢&#xff1f; 10月8日&#xff0…

笔记-static关键字

1.static关键字内存说明 2.访问特点 package com.test.Statics2;import com.test.statics.Student;public class Test {public static void main(String[] args) {// 静态成员中访问非静态成员// method3() // 错误-不能直接调用&#xff0c;需要new对象调用Test test01 new T…

Java 类和对象详解(上 )

个人主页&#xff1a; 鲤鱼王打挺-CSDN博客 Java专栏&#xff1a;https://blog.csdn.net/2401_83779763/category_12801101.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12801101&sharereferPC&sharesource2401_83779763&sharefromfrom_link &…

Vue3的Composition组合式API(computed计算属性、watch监视属性、watchEffect函数)

目录 1. computed计算属性2. watch监视属性2.1 watch监视ref定义的数据2.2 watch监视reactive定义的数据 3. watchEffect函数 1. computed计算属性 与Vue2中的computed配置功能一致。使用示例如下&#xff1a; Student.vue 计算属性的简写只能进行读取&#xff1b;完整写法有…

【vivado】vivado联合modelsim仿真

操作步骤 1 编译Vivado仿真库2 设置仿真工具和库路径3 启动modelsim仿真 1 编译Vivado仿真库 等待编译完成。 2 设置仿真工具和库路径 打开vivado工程文件&#xff0c; 点击tool–>Setting 更改编译器为modelsimulator&#xff0c;并悬着编译库文件 3 启动modelsim仿真…

整理—MySQL

目录 NOSQL和SQL的区别 数据库三大范式 MySQL 怎么连表查询 MySQL如何避免重复插入数据&#xff1f; CHAR 和 VARCHAR有什么区别&#xff1f; Text数据类型可以无限大吗&#xff1f; 说一下外键约束 MySQL的关键字in和exist mysql中的一些基本函数 SQL查询语句的执行顺…

Java爬虫:从入门到精通实战指南

在信息技术飞速发展的今天&#xff0c;数据已成为最宝贵的资源之一。Java作为一种成熟且功能强大的编程语言&#xff0c;不仅在企业级应用开发中占据主导地位&#xff0c;也成为了编写爬虫程序的理想选择。Java爬虫能够自动化地从网页或API中提取数据&#xff0c;为数据分析、机…

如何利用边缘计算网关进行工厂设备数据采集?天拓四方

边缘计算网关集成了数据采集、处理和传输功能&#xff0c;位于传感器和执行器组成的设备层与云计算平台之间。它能够实时处理和响应本地设备的数据请求&#xff0c;减轻云平台的压力&#xff0c;提高数据处理的速度和效率。同时&#xff0c;边缘计算网关还可以将处理后的数据上…

第J2周:ResNet50V2算法实战与解析

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 1.ResNetV2结构与ResNet结构对比 &#x1f9f2; 改进点&#xff1a;(a)original 表示原始的 ResNet 的残差结构&#xff0c;(b)proposed 表示新的 ResNet 的残…

【HTML + CSS 魔法秀】打造惊艳 3D 旋转卡片

HTML结构 box 类是整个组件的容器。item-wrap 类是每个旋转卡片的包装器&#xff0c;每个都有一个内联样式–i&#xff0c;用于控制动画的延迟。item类是实际的卡片内容&#xff0c;包含一个图片。 <template><div class"box"><div class"item…

【完-网络安全】Windows防火墙及出入站规则

文章目录 防火墙入站和出站的区别域网络、专用网络、公用网络的区别 防火墙 防火墙默认状态一般是出站允许&#xff0c;入站阻止。 入站和出站的区别 入站就是别人来访问我们的主机&#xff0c;也就是正向shell的操作 出站就是反向shell&#xff0c;主机需要主动连接kali&am…

机器学习—特性缩放

特性缩放的技术能使梯度下降运行得更快&#xff0c;让我们先来看看功能大小之间的关系&#xff0c;这就是该特性的数字和相关参数的大小&#xff0c;作为一个具体的例子&#xff0c;让我们用两个特征来预测房子的价格&#xff0c;X1代表一个房子的大小&#xff0c;X2代表两个卧…

【黑马点评优化】之使用Caffeine+Redis实现应用级二层缓存

【黑马点评优化】之使用CaffeineRedis实现应用级二层缓存 1 缓存雪崩定义及解决方案2 为什么要使用多级缓存3 RedisCaffeine实现应用层二级缓存原理4 利用CaffeineRedis解决Redis突然宕机导致的缓存雪崩问题4.1 pom.xml文件引入相关依赖4.2 本地缓存配置类4.3 修改ShopServiceI…

UE5 使用Animation Budget Allocator优化角色动画性能

Animation Budget Allocator是UE内置插件&#xff0c;通过锁定动画系统所占CPU的预算&#xff0c;在到达预算计算量时对动画进行限制与优化。 开启Animation Budget Allocator需要让蒙皮Mesh使用特定的组件&#xff0c;并进行一些编辑器设置即可开启。 1.开启Animation Budget…

Tailwind Starter Kit 一款极简的前端快速启动模板

Tailwind Starter Kit 是基于TailwindCSS实现的一款开源的、使用简单的极简模板扩展。会用Tailwincss就可以快速入手使用。Tailwind Starter Kit 是免费开源的。它不会在原始的TailwindCSS框架中更改或添加任何CSS。它具有多个HTML元素&#xff0c;并附带了ReactJS、Vue和Angul…

k8s中的微服务

一、什么是微服务 用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&#xff1f;需要通过微服务暴漏出去后才能被访问 Service是一组提供相同服务的Pod对外开放的接口。 借助Service&#xff0c;应用可以实现服务发现和负载均衡。 service默认只支持4层负载均…

二叉树与堆讲解

目录 1.树的概念及结构 1.树的概念 2.树的相关概念 3.树的表示 2.二叉树 1.概念 2.特殊的二叉树 1.满二叉树 2.完全二叉树 3.二叉树的性质 4.二叉树的存储结构 1.顺序结构 2.链式存储 3.堆 1.堆的概念及结构 2.堆的实现 1.堆的创建 2.堆的初始化&#xff08;H…