Warning: There is no enough buffer for saving data, please increase the RT_SERIAL_RB_BUFSZ option.
Warning: There is no enough buffer for saving data, please increase the RT_SERIAL_RB_BUFSZ option.已解决
项目场景:
使用RT-Thread操作系统的串口设备 DMA 接收回环使用例程;
问题描述
使用RT-Thread操作系统的串口设备 DMA 接收使用例程时,串口仅能实现发送功能,而无法接收显示,多次接收后在shell终端触发警告:
msh >[W/UART] Warning: There is no enough buffer for saving data, please increase the RT_SERIAL_RB_BUFSZ option.
串口助手能够接收到开发板初始发送的 "hello RT-Thread!";
但是串口助手发送给开发板的字符串没有回传;
连续多次发送后,shell命令端发出警告:
原因分析:
这个警告表示用于存储串口接收数据的缓冲区(ring buffer)大小不够。当使用串口接收数据并且开启了某些接收机制(如 DMA 接收)时,如果接收数据的速度较快或者数据量较大,现有的缓冲区大小RT_SERIAL_RB_BUFSZ可能无法容纳所有接收到的数据,从而导致这个警告。
解决方案:
通过询问 AI ,找到以下方法能够解决该问题:
🔅优化数据接收处理速度
- 如果数据接收处理速度过慢,可能会导致缓冲区中的数据来不及被处理而溢出。可以尝试优化接收处理函数,使其能够更快速地处理接收到的数据。例如,减少在接收处理函数中的复杂计算、延时操作等。
- 可以采用中断方式及时处理接收的数据。在串口接收中断服务程序中,只进行必要的数据搬运等操作,将主要的数据处理工作放在中断外部的线程中进行,以提高接收效率。
👆👆👆上面是 AI 的回答👆👆👆
基于此,发现原例程代码在串口回调函数中,使用的消息队列花费时间过长,导致缓冲区中的数据来不及被处理而溢出;

🔴原例程代码:
引用自UART设备
/*
* 程序清单:这是一个串口设备 DMA 接收使用例程
* 例程导出了 uart_dma_sample 命令到控制终端
* 命令调用格式:uart_dma_sample uart3
* 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
* 程序功能:通过串口输出字符串"hello RT-Thread!",并通过串口输出接收到的数据,然后打印接收到的数据。
*/
#include <rtthread.h>
#define SAMPLE_UART_NAME "uart3" /* 串口设备名称 */
/* 串口接收消息结构*/
struct rx_msg
{
rt_device_t dev;
rt_size_t size;
};
/* 串口设备句柄 */
static rt_device_t serial;
/* 消息队列控制块 */
static struct rt_messagequeue rx_mq;
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
struct rx_msg msg;
rt_err_t result;
msg.dev = dev;
msg.size = size;
result = rt_mq_send(&rx_mq, &msg, sizeof(msg));
if ( result == -RT_EFULL)
{
/* 消息队列满 */
rt_kprintf("message queue full!\n");
}
return result;
}
static void serial_thread_entry(void *parameter)
{
struct rx_msg msg;
rt_err_t result;
rt_uint32_t rx_length;
static char rx_buffer[RT_SERIAL_RB_BUFSZ + 1];
while (1)
{
rt_memset(&msg, 0, sizeof(msg));
/* 从消息队列中读取消息*/
result = rt_mq_recv(&rx_mq, &msg, sizeof(msg), RT_WAITING_FOREVER);
if (result > 0)
{
/* 从串口读取数据*/
rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size);
rx_buffer[rx_length] = '\0';
/* 通过串口设备 serial 输出读取到的消息 */
rt_device_write(serial, 0, rx_buffer, rx_length);
/* 打印数据 */
rt_kprintf("%s\n",rx_buffer);
}
}
}
static int uart_dma_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
char uart_name[RT_NAME_MAX];
static char msg_pool[256];
char str[] = "hello RT-Thread!\r\n";
if (argc == 2)
{
rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
}
/* 查找串口设备 */
serial = rt_device_find(uart_name);
if (!serial)
{
rt_kprintf("find %s failed!\n", uart_name);
return RT_ERROR;
}
/* 初始化消息队列 */
rt_mq_init(&rx_mq, "rx_mq",
msg_pool, /* 存放消息的缓冲区 */
sizeof(struct rx_msg), /* 一条消息的最大长度 */
sizeof(msg_pool), /* 存放消息的缓冲区大小 */
RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */
/* 以 DMA 接收及轮询发送方式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);
/* 设置接收回调函数 */
rt_device_set_rx_indicate(serial, uart_input);
/* 发送字符串 */
rt_device_write(serial, 0, str, (sizeof(str) - 1));
/* 创建 serial 线程 */
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
/* 创建成功则启动线程 */
if (thread != RT_NULL)
{
rt_thread_startup(thread);
}
else
{
ret = RT_ERROR;
}
return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_dma_sample, uart device dma sample);
由于消息队列花费时间过长,这里考虑修改为信号量,通过信号量替代原本消息队列的逻辑,最终实现串口 DMA 接收回环;
修改代码如下:
/*
* 程序清单:这是一个串口设备 DMA 接收使用例程
* 例程导出了 uart_dma_sample 命令到控制终端
* 命令调用格式:uart_dma_sample uart3
* 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
* 程序功能:通过串口输出字符串"hello RT-Thread!",并通过串口输出接收到的数据,然后打印接收到的数据。
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#define PIN_LED_B GET_PIN(F, 11) // PF11 : LED_B --> LED
#define PIN_LED_R GET_PIN(F, 12) // PF12 : LED_R --> LED
#define SAMPLE_UART_NAME "uart3" /* 串口设备名称 */
/* 串口接收消息结构*/
struct rx_msg
{
rt_device_t dev;
rt_size_t size;
};
/* 串口设备句柄 */
static rt_device_t serial;
rt_uint32_t rx_length;
static char rx_buffer[RT_SERIAL_RB_BUFSZ + 1];
/* 信号量 */
static struct rt_semaphore rx_sem;
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
rt_err_t result = RT_EOK;
rx_length = rt_device_read(dev, 0, rx_buffer, size);
rt_sem_release(&rx_sem);
return result;
}
static void serial_thread_entry(void *parameter)
{
while (1)
{
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
rt_device_write(serial, 0, rx_buffer, rx_length);
}
}
static int uart_dma_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
char uart_name[RT_NAME_MAX];
static char msg_pool[256];
char str[] = "hello RT-Thread!\r\n";
rt_pin_mode(PIN_LED_R, PIN_MODE_OUTPUT);
if (argc == 2)
{
rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
}
/* 查找串口设备 */
serial = rt_device_find(uart_name);
if (!serial)
{
rt_kprintf("find %s failed!\n", uart_name);
return RT_ERROR;
}
/* 初始化信号量*/
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/* 以 DMA 接收及轮询发送方式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);
/* 设置接收回调函数 */
rt_device_set_rx_indicate(serial, uart_input);
/* 发送字符串 */
rt_device_write(serial, 0, str, (sizeof(str) - 1));
/* 创建 serial 线程 */
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 0, 10);
/* 创建成功则启动线程 */
if (thread != RT_NULL)
{
rt_thread_startup(thread);
}
else
{
ret = RT_ERROR;
}
return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_dma_sample, uart device dma sample);
总结:
在串口设备使用 DMA 接收时,串口回调函数中不能使用复杂的逻辑来操作;
原例程中使用的消息队列逻辑花费时间过长,导致缓冲区中的数据来不及被处理而溢出;经测试,使用信号量代替消息队列的逻辑,能够解决该问题。
更多推荐


所有评论(0)