外观
UART DMA 示例
2026-05-29
概述
本文将详细介绍如何通过 UART DMA 实现串口数据的收发。重点介绍 DMA 轮询模式,该模式无需中断参与,完全通过软件轮询方式管理 DMA 传输过程。该方式利用 DMA 的硬件数据搬运能力,结合轮询机制检测传输状态,最终实现高效、可靠的串口数据通信。
DMA 轮询模式简介
UART DMA 轮询模式 是一种不依赖中断的传输方式,其核心特点如下:
- 无需注册接收回调函数:通过 rt_device_set_rx_indicate(_serial, RT_NULL) 禁用中断回调,完全依靠软件查询传输状态
- 发送无阻塞:发送函数可以直接返回,无需等待数据发送完成,提高程序执行效率
- 独立轮询线程:需要一个单独的线程负责轮询 DMA 状态,且该线程优先级应高于数据处理线程
- 状态可控:通过特殊控制命令实时查询发送/接收缓冲区状态及 DMA 传输进度
硬件连接

创建工程点击展开
依次点击 “文件” -> “新建” -> "RT-Thread RuiChing App 项目"。

在弹出新建向导中选择 开发版 、BSP: 、示例 、 调试器/下载器。选择好之后点击 “完成”。

点击 “完成” 后,等待工程创建完成。

创建完成。

构建工程点击展开
单击工程使工程进入 Active-Debug 模式。

点击工具栏上的构建按钮进行工程编译。

构建成功后,会显示构建成功的信息。

固件下载点击展开
固化设备树

固化 APP

模式选择
- 注册接收回调函数 -> DMA 中断模式
- 不注册接收回调函数 -> DMA 轮询模式
rt_device_set_rx_indicate(_serial, rx_call); // DMA 中断模式
rt_device_set_rx_indicate(_serial, RT_NULL); // DMA 轮询模式UART DMA 轮询模式相关控制参数说明
- #define RT_UART_CTRL_GET_TX_STATUS : 获取串口发送状态
- #define RT_UART_CTRL_GET_RX_STATUS : 获取串口接收状态
- #define RT_UART_CTRL_GET_TX_DMA_STATUS : 获取串口发送 DMA 状态
- #define RT_UART_CTRL_GET_RX_DMA_STATUS : 获取串口接收 DMA 状态
- #define RT_UART_CTRL_START_RX_DMA : 启动串口接收 DMA
- #define RT_UART_CTRL_TX_DMA_DONE : DMA 发送完成
- #define RT_UART_CTRL_RX_DMA_DONE : DMA 接收完成
UART DMA 示例轮询模式相关代码说明
rt_device_control(_serial, RT_UART_CTRL_GET_TX_STATUS, &status):获取串口发送状态- status 为 0 :发送缓冲区还有数据,不可以发送数据
- status 为 1 :发送缓冲区为空,可以发送数据
rt_device_control(_serial, RT_UART_CTRL_GET_RX_STATUS, &status):获取串口接收状态- status 为 0 :接收缓冲区为空,没有数据接收
- status 为 1 :接收缓冲区有数据,可以接收数据
rt_device_control(_serial, RT_UART_CTRL_GET_TX_DMA_STATUS, &dma_status):获取串口发送 DMA 状态- dma_status 为 0 :表示数据没有搬运完成
- dma_status 为 1 :表示数据搬运完成
rt_device_control(_serial, RT_UART_CTRL_GET_RX_DMA_STATUS, &dma_status):获取串口接收 DMA 状态- dma_status 为 0 :表示数据没有搬运完成
- dma_status 为 1 :表示数据搬运完成
rt_device_control(_serial, RT_UART_CTRL_START_RX_DMA, RT_NULL):启动串口接收 DMArt_device_control(_serial, RT_UART_CTRL_TX_DMA_DONE, RT_NULL):DMA 发送完成- 发送完成后调用
rt_device_control(_serial, RT_UART_CTRL_RX_DMA_DONE, RT_NULL):DMA 接收完成- 接收完成后调用
核心代码
uart_sample.c
static void _uart_rx_dma_poll(void *parameter)
{
rt_uint32_t dma_status = 0, status = 0;
while (1)
{
rt_device_control(_serial, RT_UART_CTRL_GET_RX_STATUS, &status);
if (status != 0)
{
rt_device_control(_serial, RT_UART_CTRL_START_RX_DMA, RT_NULL);
while (1)
{
rt_device_control(_serial, RT_UART_CTRL_GET_RX_DMA_STATUS,
&dma_status);
if (dma_status != 0)
{
break;
}
}
rt_device_control(_serial, RT_UART_CTRL_RX_DMA_DONE, RT_NULL);
continue;
}
rt_thread_delay(1);
}
}
static void _uart_run_thread(void *parameter)
{
char out_str[UART_DATA_NUM];
rt_ssize_t size = 0, total_size = 0;
rt_uint32_t status = 0, index = 0;
rt_uint32_t mode = (rt_uint32_t)parameter;
for (index = 0; index < UART_DATA_NUM - 3; index++)
{
out_str[index] = 'a' + (index % 26);
}
out_str[index] = '\r';
out_str[index + 1] = '\n';
out_str[index + 2] = '\0';
rt_device_control(_serial, RT_UART_CTRL_GET_TX_STATUS, &status);
if (status != 0)
{
rt_device_write(_serial, 0, out_str, strlen(out_str));
if (mode == UART_DMA_POLL)
{
while (1)
{
rt_device_control(_serial, RT_UART_CTRL_GET_TX_DMA_STATUS,
&status);
if (status != 0)
{
break;
}
}
rt_device_control(_serial, RT_UART_CTRL_TX_DMA_DONE, RT_NULL);
}
}
rt_kprintf("uart dma tx done\n");
memset(out_str, 0, UART_DATA_NUM);
while (1)
{
if (mode == UART_DMA_INT)
{
rt_sem_take(_sem, RT_WAITING_FOREVER);
}
size = rt_device_read(_serial, 0, &out_str[total_size], UART_DATA_NUM);
total_size += size;
if (total_size > 0 && out_str[total_size - 1] == '#')
{
out_str[total_size - 1] = '\0';
rt_kprintf("size:%d\n%s\n", total_size, out_str);
memset(out_str, 0, UART_DATA_NUM);
total_size = 0;
}
rt_thread_mdelay(10);
}
}
int uart_dma_test(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_uint32_t mode = 0;
struct serial_configure uart_cfg;
uart_cfg.baud_rate = BAUD_RATE_115200;
uart_cfg.data_bits = DATA_BITS_8;
uart_cfg.stop_bits = STOP_BITS_1;
uart_cfg.parity = PARITY_NONE;
uart_cfg.invert = NRZ_NORMAL;
uart_cfg.bufsz = UART_DATA_NUM;
uart_cfg.flowcontrol = 0;
if (argc > 1)
{
mode = atoi(argv[1]);
}
_serial = rt_device_find(UART_NAME);
if (_serial == RT_NULL)
{
rt_kprintf("can't find %s\n", UART_NAME);
goto exit;
}
if (_serial->ref_count != 0)
{
if (_uart_tid != RT_NULL)
{
rt_thread_delete(_uart_tid);
_uart_tid = RT_NULL;
}
if (_dma_poll_tid != RT_NULL)
{
rt_thread_delete(_dma_poll_tid);
_dma_poll_tid = RT_NULL;
}
if (_sem != RT_NULL)
{
rt_sem_delete(_sem);
_sem = RT_NULL;
}
rt_device_close(_serial);
}
if (mode == UART_DMA_INT)
{
rt_device_set_rx_indicate(_serial, rx_call);
if (_sem == RT_NULL)
{
_sem = rt_sem_create(UART_SEM_NAME, 0, RT_IPC_FLAG_FIFO);
}
}
else
{
rt_device_set_rx_indicate(_serial, RT_NULL);
}
rt_device_open(_serial, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX |
RT_DEVICE_FLAG_DMA_RX);
rt_device_control(_serial, RT_DEVICE_CTRL_CONFIG, &uart_cfg);
if (mode == UART_DMA_POLL)
{
_dma_poll_tid = rt_thread_create(UART_DMA_POLL_NAME, _uart_rx_dma_poll,
RT_NULL, 1024, 15, 10);
if (_dma_poll_tid == RT_NULL)
{
rt_kprintf("create dma poll thread failed\n");
goto exit;
}
rt_thread_startup(_dma_poll_tid);
}
_uart_tid = rt_thread_create(UART_TID_NAME, _uart_run_thread, (void *)mode,
4096, 15, 10);
if (_uart_tid == RT_NULL)
{
rt_kprintf("create thread failed\n");
goto exit;
}
rt_thread_startup(_uart_tid);
return ret;
exit:
ret = -RT_ERROR;
return ret;
}运行程序
在调试串口下输入 uart_dma_test 0:表示使用串口 DMA 中断模式
在调试串口下输入 uart_dma_test 1:表示使用串口 DMA 轮询模式
输入后打印如下:
串口终端打印
msh />uart_dma_test 1
uart dma tx done串口 3 打印
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmno串口 3 输入
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi#串口终端打印
msh />size:426
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi