在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。大家使用的时候一定要注意区分。
为什么使用不同的方法
- 数组:
- 数组的大小是固定的,并且包含了所有元素的总大小。使用
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.}
}
我的程序:
参数解释
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)循环
- 任务的持续性
无限循环:while (1) 循环确保任务不会退出。一旦任务函数返回,FreeRTOS 会自动删除该任务并释放其资源。如果任务需要持续运行以执行某些操作(如周期性地发送或接收数据、监控传感器等),则必须使用无限循环。
避免任务结束:如果任务函数没有无限循环并且直接返回,任务将被删除,导致该任务的功能无法继续执行。 - 任务的状态管理
阻塞和等待:在 while (1) 循环中,任务可以执行一些操作,然后进入阻塞状态(例如通过 vTaskDelay 或其他阻塞 API)。这样可以有效地管理 CPU 资源,避免任务一直占用 CPU 时间。
事件驱动:任务可以在 while (1) 循环中等待特定的事件(例如消息队列中的消息、信号量等),并在事件发生时进行处理。 - 任务调度
抢占式调度: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
相关文章:

在ESP-IDF环境中如何进行多文件中的数据流转-FreeRTOS实时操作系统_流缓存区“xMessageBuffer”
一、建立三个源文件和对应的头文件 建立文件名,如图所示 图 1-1 二、包含相应的头文件 main.h 图 2-1 mess_send.h mess_rece.h和这个中类似,不明白的大家看我最后面的源码分享 图2-2 三、声明消息缓存区的句柄 大家注意,在main.c中定义的是全局变…...
ConcurrentLinkedQueue适合什么样的使用场景?
ConcurrentLinkedQueue 是 Java 中一种无界线程安全的队列,适合多线程环境中的高并发场景。以下是一些它特别适合的使用场景: 1. 高频读操作,低频写操作 ConcurrentLinkedQueue 对于实际应用中读操作相对频繁,写操作较少的场景非…...

C语言 | Leetcode C语言题解之第480题滑动窗口中位数
题目: 题解: struct Heap {int* heap;int heapSize;int realSize;bool (*cmp)(int, int); };void init(struct Heap* obj, int n, bool (*cmp)(int, int)) {obj->heap malloc(sizeof(int) * (n 1));obj->heapSize 0;obj->cmp cmp; }bool c…...

LabVIEW开发如何实现降维打击
在LabVIEW开发中实现“降维打击”可以理解为利用软件优势和高效工具来解决复杂的问题,将多维度、多层次的技术简化为容易操作和管理的单一维度,达到出其不意的效果。以下是几种关键策略: 1. 模块化设计与封装 将复杂系统分解为若干模块&…...
docker 文件目录迁移
文章参考 du -hs /var/lib/docker/ 命令查看磁盘使用情况。 du -hs /var/lib/docker/docker system df命令,类似于Linux上的df命令,用于查看Docker的磁盘使用情况: rootnn0:~$ docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 7 2 122.2…...
Markdown 标题
Markdown 标题 Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式化的HTML代码。Markdown 的语法简洁明了,广泛用于撰写文档、博客文章、笔记等。本文将详细介绍 Markdown 的标题语法及其在文档中的应用。 Markdown 标题语法 在…...

【动手学电机驱动】TI InstaSPIN-FOC(5)Lab04 力矩控制
TI InstaSPIN-FOC(1)电机驱动和控制测试平台 TI InstaSPIN-FOC(2)Lab01 闪灯实验 TI InstaSPIN-FOC(3)Lab03a 测量电压电流漂移量 TI InstaSPIN-FOC(4)Lab02b 电机参数辨识 TI Insta…...
Mysql的CommunicationsException
一、报错内容 com.mysql.cj.jdbc.exceptions.CommunicationsException: The last packet successfully received from the server was 1,500,378 milliseconds ago. The last packet sent successfully to the server was 1,500,378 milliseconds ago. is longer than the s…...
C++学习笔记----9、发现继承的技巧(二)---- 重用目的的继承
现在你对继承的基本语法已经比较熟悉了,是时候探索继承是c语言中重要属性的一个主要原因了。继承是一个装备允许你平衡既有代码。本节会举出基于代码重用目的的继承的例子。 1、WeatherPrediction类 假想你有一个任务,写一个程序来发出简单的天气预报&a…...
锐评 Nodejs 设计模式 - 创建与结构型
本系列文章的思想,都融入了 让 Java 再次伟大 这个全新设计的脚手架产品中,欢迎大家使用。 单例模式与模块系统 Node 的单例模式既特殊又简单——凡是从模块中导出的实例天生就是单例。 // database.js function Database(connect, account, password)…...

【RoadRunner】自动驾驶模拟3D场景构建 | 软件简介与视角控制
💯 欢迎光临清流君的博客小天地,这里是我分享技术与心得的温馨角落 💯 🔥 个人主页:【清流君】🔥 📚 系列专栏: 运动控制 | 决策规划 | 机器人数值优化 📚 🌟始终保持好奇心&…...

15分钟学Go 第4天:Go的基本语法
第4天:基本语法 在这一部分,将讨论Go语言的基本语法,了解其程序结构和基础语句。这将为我们后续的学习打下坚实的基础。 1. Go语言程序结构 Go语言程序的结构相对简单,主要包括: 包声明导入语句函数语句 1.1 包声…...

【Qt】Qt的介绍——Qt的概念、使用Qt Creator新建项目、运行Qt项目、纯代码方式、可视化操作、认识对象模型(对象树)
文章目录 Qt1. Qt的概念2. 使用Qt Creator新建项目3. 运行Qt项目3.1 纯代码方式实现3.2 可视化操作实现 4. 认识对象模型(对象树) Qt 1. Qt的概念 Qt 是一个跨平台的 C 图形用户界面应用程序开发框架。它是软件开发者提供的用于界面开发的程序框架&#…...

论文笔记:PTR: Prompt Tuning with Rules for Text Classification
Abstract 手动设计大量语言提示麻烦且易出错,而自动生成的提示,在非小样本场景下验证其有效性昂贵且耗时。因此,提示调优以处理多类别分类任务仍然具有挑战。为此,本文提出使用规则进行多类别文本分类提示调优(PTR&…...
服务器和中转机协同工作以提高网络安全
服务器和中转机(代理服务器)可以通过多种方式协同工作来提高网络安全。 常见的协同工作策略: 1. 使用代理服务器作为安全网关 访问控制:代理服务器可以作为网络的入口点,实施访问控制策略,如基于IP地址、…...

Java利用itextpdf实现pdf文件生成
前言 最近公司让写一个数据页面生成pdf的功能,找了一些市面代码感觉都太麻烦,就自己综合性整合了一个便捷的工具类,开发只需简单组装数据直接调用即可快速生成pdf文件。望大家一起学习!!! 代码获取方式&am…...

2010年国赛高教杯数学建模C题输油管的布置解题全过程文档及程序
2010年国赛高教杯数学建模 C题 输油管的布置 某油田计划在铁路线一侧建造两家炼油厂,同时在铁路线上增建一个车站,用来运送成品油。由于这种模式具有一定的普遍性,油田设计院希望建立管线建设费用最省的一般数学模型与方法。 1. 针对两炼…...

datawhale大模型bot应用开发--task3:工作流
目录 一、介绍:Coze工作流 1.1工作流应用场景 1.2什么是工作流 1.3思考环节 二、各个工作流详情 2.1情感分类工作流 2.2 随机数工作流 2.3 必应搜索工作流 2.4 天气查询工作流 三、集合上面五个工作流的总工作流 一、介绍:Coze工作流 1.1工作…...

期货配资系统风控逻辑开发/完整源代码
期货配资系统风控逻辑的开发是确保系统安全、稳定、高效运行的关键环节。以下是对期货配资系统风控逻辑开发的详细分析: 一、风险识别与评估 风险来源分析: 市场风险:期货市场价格波动带来的风险。信用风险:投资者或配资方违约的…...

汽车免拆诊断案例 | 2023款零跑C01纯电车后备厢盖无法电动打开和关闭
故障现象 一辆2023款零跑C01纯电车,累计行驶里程约为2万km,车主进厂反映,后备厢盖无法电动打开和关闭。 故障诊断 接车后试车,操作后备厢盖外侧、驾驶人侧及遥控钥匙上的后备厢盖开启按钮,可以听到后备厢盖解锁的…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...