在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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java 类和对象详解(上 )

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

github下载文件的两种方式(非git形式)

1.以下面的图为例 &#xff0c;可以直接点击右上方的绿色Code按键&#xff0c;在弹出的列表中选择Download Zip选项&#xff0c;即可下载。 2.如果下载的是单独的某一个文件&#xff0c;则可以按照下图的格式点击下图所示的那个下载的图标即可。

研发线上事故风险解读之缓存篇

专业在线打字练习平台-巧手打字通&#xff0c;只输出有价值的知识。 一 前言 本文继续基于《线上事故案例集》&#xff0c;进一步深入梳理线上事故缓存使用方面的问题点&#xff0c;重点关注缓存在使用和优化过程中可能出现的问题&#xff0c;旨在为读者提供具有实践指导意义的…

ThinkPHP5bootstrapMySQL开发学习平台(包括后台管理功能、PC端网页、移动端网页)手把手运行源码

一、项目预览(全部源码链接在最下面) 功能及页面持续优化中...... 二、本地运行方式 1、下载源码包进行解压&#xff08;源码在最下面&#xff09; 2、下载phpstudy_pro&#xff0c;并运行Apache&MySQL 3、打开phpstudy_pro按照根目录&#xff0c;复制粘贴解压好的源码包&…

【时时三省】(C语言基础)函数介绍strcmp

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 假如有一个这样的代码 这个代码这样写对不对呢 它是不对的 因为p是一个地址 里面可能是0x5546643 q也是一个地址 所以她们没法这样比较 这个时候就可以用到我们的 strcmp 他就说用来比较字…

微软十月补丁星期二发现了 118 个漏洞

微软将在2024 年 10 月补丁星期二解决 118 个漏洞&#xff0c;并且有证据表明发布的 5 个漏洞被野蛮利用和/或公开披露&#xff0c;尽管微软尚未将其中任何一个漏洞评定为严重漏洞。 在这五个漏洞中&#xff0c;微软列出了两个已被利用的漏洞&#xff0c;这两个漏洞现在都已列…

如何实现安川MP3300运动控制器与西门子1200系列PLC进行ModbusTCP通讯

在工业自动化中&#xff0c;实现不同品牌、不同型号设备之间的通讯是确保生产流程顺畅、高效运行的关键。本文详细介绍了安川MP3300运动控制器与西门子1200系列PLC进行ModbusTCP通讯的具体方法。 一&#xff0e;软硬件需求 1.一台安川MP3300CPU301&#xff0c;其IP地址是192.…

SpringBoot项目热部署-devtools

DevTools 会使用两个类加载器&#xff08;一个用于加载不变的类&#xff0c;一个用于加载可能会变化的类&#xff09;&#xff0c;每次重启只重新加载管理变化的类的加载器&#xff0c;因此会快很多 1.导入依赖 <dependency> <groupId>org.springframework.boot&l…

uni-app使用v-show编译成微信小程序的问题

问题 在uni-app使用v-show语法编译成微信小程序会有一个问题 当我们设置成v-show"false" 在Hbuilder X里面确实没有显示 然后运行到 微信开发程序里面 发现显示了出来&#xff0c;说明设置的 v-show"false"没有起作用 解决办法 首先去uniapp官网查看v…

考研C语言程序设计_编程题相关(持续更新)

目录 零、说明一、程序设计经典编程题(C语言实现)T1 求1~100的奇数T2 求n!T3 求1!2!3!...10!T4 在一个有序数组中查找具体的某个数字n(二分查找)T5 编写代码&#xff0c;演示多个字符从两端移动&#xff0c;向中间汇聚T6 模拟用户登录(三次机会)T7 输入三个数 并从大到小输出T8…

【Ubuntu】在Ubuntu上安装IDEA

【Ubuntu】在Ubuntu上安装IDEA 零、前言 最近换了Ubuntu系统&#xff0c;但是还得是要写代码&#xff0c;这样就不可避免地用到IDEA&#xff0c;接下来介绍一下如何在Ubuntu上安装IDEA。 壹、下载 这一步应该很容易的&#xff0c;直接打开IDEA的下载页面&#xff0c;点击下…

精密仪器制造企业如何保障安全高效的跨网文件交换?

在数字化时代&#xff0c;精密仪器制造企业面临着日益增长的跨网文件交换需求。这些企业通常拥有多个隔离的网络环境&#xff0c;如内网、外网、测试网等&#xff0c;以确保数据安全和合规性。然而&#xff0c;如何在保障数据安全的同时&#xff0c;实现文件的快速、稳定传输&a…

前端学习-css的元素显示模式(十五)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 什么是元素显示模式 块元素 常见的块元素 块元素的特点 注意 行内元素 行内元素的特点 注意 行内块元素 行内块元素的特点 元素显示模式的转换 语法格…

黑马程序员-redis项目实践笔记1

目录 一、 基于Session实现登录 发送验证码 验证用户输入验证码 校验登录状态 Redis代替Session登录 发送验证码修改 验证用户输入验证码 登录拦截器的优化 二、 商铺查询缓存 缓存更新策略 数据库和缓存不一致解决方案 缓存更新策略的最佳实践方案 实现商铺缓…

【UML】一个UML学习的还不错的几个帖子

https://segmentfault.com/a/1190000042775634 寂然解读设计模式 - UML类图&类的六大关系-阿里云开发者社区

学习threejs,网格深度材质MeshDepthMaterial

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️网格深度材质MeshDepthMate…

【数据结构与算法】栈和队列

文章目录 一.栈1.1定义 顺序栈和链式栈1.2基本操作1.2.1表示1.2.2初始化1.2.3清空1.2.4销毁1.2.5入栈1.2.6出栈1.2.7取栈顶 1.3共享栈1.3.1定义1.3.2进栈出栈 二.队列2.1定义 顺序队列和链式队列循环队列2.2基本操作2.2.1初始化2.2.2判空2.2.3求队列长度2.2.4取队头元素2.2.5销…

Spring 的依赖注入的最常见方式

在 Spring 中&#xff0c;依赖注入的方式有多种选择。下面我们来逐一分析它们的特点、适用场景和注意事项&#xff1a; 1. 构造函数注入 构造函数注入要求在对象创建时提供所有依赖。这种方式确保依赖在对象创建后不可变&#xff0c;特别适合必须强制存在的依赖。所有依赖在对…

我谈Sobel算子与高斯一阶微分的关系

现在算力提升了&#xff0c;最常用的一阶差分边缘检测算子已经不是Sobel算子了&#xff0c;而是高斯一阶微分。 高斯一阶微分 顾名思义&#xff0c;高斯函数的一阶导数。 Derivative of Gaussian 1D 一维直接扩展到二维。 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面…

【python爬虫】携程旅行景点游客数据分析与可视化

一.选题背景 随着旅游业的快速发展&#xff0c;越来越多的人选择通过互联网平台预订旅行产品&#xff0c;其中携程网作为国内领先的在线旅行服务提供商&#xff0c;拥有大量的旅游产品和用户数据。利用爬虫技术可以获取携程网上各个景点的游客数据&#xff0c;包括游客数量、游…