目标:串口接收串口助手发来的数据,并且将数据重新发送出去。
- RT-Thread studio,版本: 2.2.6,不一样其实区别也不大
- RT-Thread:标准版,4.0.3版本
- 芯片包版本:0.1.9
- 开发板:正点原子战舰开发板,主控STM32F103ZET6。或者正点原子潘多拉开发板,主控STM32L475VET6。有两个串口接口,并且能想办法转 USB 就行。
1. 使用串口二
因为串口二默认是被 Shell 功能占用了,所以这里先用串口2进行测试。调通后再移植串口一。
第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> 使用UART设备驱动程序 -> 使能串口DMA模式,勾选上。
因为串口一用于SHELL窗口了。所以使用UART设备驱动程序 默认是打开的,这里只要把下面的DMA点开即可。
第二步:在 board.h 中,添加如下宏定义。注意根据自己使用的开发板引脚修改引脚编号。
// 新增 串口2 设备
#define BSP_USING_UART2
#define BSP_UART2_TX_PIN "PA2"
#define BSP_UART2_RX_PIN "PA3"
// 串口2 使用接收DMA
#define BSP_UART2_RX_USING_DMA
此时如果直接编译,然后在 Shel l窗口输入 list_device 命令,就已经能看到uart2设备了。
第三步:编写测试代码
/*
* 程序清单:这是一个串口设备 DMA 接收使用例程
* 例程导出了 uart2_dma_test 命令到控制终端
* 命令调用格式:uart2_dma_test
* 程序功能:运行后通过串口输出字符串"usart2 test is running!",之后,串口2会将接收到的数据重新发送出去。
* 此功能需要做出的适配如下:1. 在board.h串口部分添加如下宏定义
* #define BSP_USING_UART2
* #define BSP_UART2_TX_PIN "PA2"
* #define BSP_UART2_RX_PIN "PA3"
* #define BSP_UART2_RX_USING_DMA
* 2.在rt-thread settings-组件-设备驱动程序-串口中打开DMA设置
*/
#include <rtthread.h>
// 串口接收消息结构
struct rx_msg
{
rt_device_t dev;
rt_size_t size;
};
// 串口设备名称
#define TEST_UART_NAME "uart2"
// 串口设备句柄
static rt_device_t usart_serial;
// 消息队列控制块
static struct rt_messagequeue usart_rx_mq;
/* 接收数据回调函数 */
// 若串口以 DMA 接收模式打开,当 DMA 完成一批数据的接收后会调用此回调函数。
// dev 是设备句柄
// size 是缓冲区中接收到的数据长度
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
struct rx_msg msg;
rt_err_t result;
// 把接收消息的对象 和 消息的大小放到msg结构体
msg.dev = dev;
msg.size = size;
// 把包装好的msg发送到消息队列
result = rt_mq_send(&usart_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)
{
// 将msg中的内容清零
rt_memset(&msg, 0, sizeof(msg));
/* 从消息队列中读取消息 放到msg中,采用一直等待的方式 */
result = rt_mq_recv(&usart_rx_mq, &msg, sizeof(msg), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
/* 从串口读取 msg.size个字节的数据,并放到rx_buffer中 */
rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size);
// 最后补 \0
rx_buffer[rx_length] = '\0';
/* 通过串口设备 serial 输出读取到的消息 */
rt_device_write(usart_serial, 0, rx_buffer, rx_length);
/* 打印数据 */
rt_kprintf("%s\n", rx_buffer);
}
}
}
static int uart2_dma_test(void)
{
rt_err_t ret = RT_EOK;
static char msg_pool[256];
char str[] = "usart2 test is running!\r\n";
// 步骤1 通过串口名字找到串口句柄, 名称定义在最上边。
usart_serial = rt_device_find(TEST_UART_NAME);
if (!usart_serial)
{
// 未找到输出提示
rt_kprintf(TEST_UART_NAME);
rt_kprintf("not find!\n");
return RT_ERROR;
}
// 步骤2 设置串口初始化参数 这里跳过
// 步骤3 以DMA接收及轮询发送方式打开串口设备
rt_device_open(usart_serial, RT_DEVICE_FLAG_DMA_RX);
// 步骤4 设置串口接收回调函数
// 3.1 初始化消息队列(回调函数中需要使用)
rt_mq_init(&usart_rx_mq,
"usart_rx_mq",
msg_pool, /* 存放消息的缓冲区 */
sizeof(struct rx_msg), /* 一条消息的最大长度 */
sizeof(msg_pool), /* 存放消息的缓冲区大小 */
RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */
// 3.2 设置接收回调函数
rt_device_set_rx_indicate(usart_serial, uart_input);
// 步骤4 创建并启动 串口线程
// 发送开始了的提示信息
rt_device_write(usart_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(uart2_dma_test, uart2 device dma test);
编译运行后,在SHELL窗口(串口一)输入uart2_dma_test
命令,然后在串口二中发送信息,串口二会将收到的信息重新发送。
2. 使用串口一
如果想要使用串口一(被 Shell 占用的串口),需要做出的改动如下:
- board.h中的宏定义
#define BSP_USING_UART1
#define BSP_UART1_TX_PIN "PA9"
#define BSP_UART1_RX_PIN "PA10"
#define BSP_UART1_RX_USING_DMA
- 在 RT-Thread Settings 中 -> 组件 -> shell命令中,将其关闭。
- 将上述程序中的
#define TEST_UART_NAME "uart2"
,2 改为 1。 - 其余的函数名、命令名改不改都行,不影响运行,要能分辨就行。
Shell 也不是非要关闭,在
rtconfig.h
中把 Shell 使用的串口改到其他串口上也行。
默认如下#define RT_CONSOLE_DEVICE_NAME "uart1"
3. 串口发送DMA
关于串口发送DMA:
经测试,在部分芯片包中貌似不支持发送DMA。
在潘多拉STM32L475VET6中,增加#define BSP_UART2_TX_USING_DMA
宏定义会报错。
而在STM32F407VET6中,增加#define BSP_UART2_TX_USING_DMA
宏定义就可以正常编译通过。