欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > 在ESP-IDF环境中如何进行多文件中的数据流转-FreeRTOS实时操作系统_流缓存区“xMessageBuffer”

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

2024/10/25 2:56:38 来源:https://blog.csdn.net/weixin_54210362/article/details/143030370  浏览:    关键词:在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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com