一、简介
串口是指数据一位一位地顺序传送,其特点是通讯线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。串行通讯的特点是:数据位的传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯的距离可以从几米到几千米;根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。
运行序列图如下图所示:
当串口接收到一批数据后会调用接收回调函数,接收回调函数会把此时缓冲区的数据大小通过消息队列发送给等待的数据处理线程。线程获取到消息后被激活,并读取数据。一般情况下 DMA 接收模式会结合 DMA 接收完成中断和串口空闲中断完成数据接收。
二、RTT配置
(1)在board.h文件中进行配置,具体操作在board.h文件中有介绍:
/** After configuring corresponding UART or UART DMA, you can use it.
*
* STEP 1, define macro define related to the serial port opening based on the serial port number
* such as #define BSP_USING_UART1
*
* STEP 2, according to the corresponding pin of serial port, define the related serial port information macro
* such as #define BSP_UART1_TX_PIN "PA9"
* #define BSP_UART1_RX_PIN "PA10"
*
* STEP 3, if you want using SERIAL DMA, you must open it in the RT-Thread Settings.
* RT-Thread Setting -> Components -> Device Drivers -> Serial Device Drivers -> Enable Serial DMA Mode
*
* STEP 4, according to serial port number to define serial port tx/rx DMA function in the board.h file
* such as #define BSP_UART1_RX_USING_DMA
*
*/
#define BSP_USING_UART1
#define BSP_UART1_TX_PIN "PA9"
#define BSP_UART1_RX_PIN "PA10"
#define BSP_USING_UART3
#define BSP_UART3_TX_PIN "PD8"
#define BSP_UART3_RX_PIN "PD9"
#define BSP_UART3_RX_USING_DMA
(2)打开可视化界面上驱动使能,使能串口DMA模式,并且可以根据自己需要设置缓冲区的大小:
(3)打开DMA所需要的HAL库文件,只需要将相应的宏定义打开即可。
三、使用信号量接收
1.uart3.h文件
#ifndef APPLICATIONS_UART3_H_
#define APPLICATIONS_UART3_H_
#include <rtthread.h>
#include <drv_common.h>
#define SAMPLE_UART_NAME "uart3"
rt_size_t rx_len;
/* 串口设备句柄 */
static rt_device_t serial;
/* 信号量控制变量 */
rt_sem_t rx_sem;
#endif /* APPLICATIONS_UART3_H_ */
2.uart3.c文件
#include "uart3.h"
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
rx_len = size;
rt_sem_release(rx_sem);
return RT_EOK;
}
static void serial_thread_entry(void *parameter)
{
rt_size_t len = 0;
char buffer[256] = {0};
while (1)
{
rt_memset(&buffer, 0, sizeof(buffer));
/* 从信号量中读取消息 */
rt_sem_take(rx_sem, RT_WAITING_FOREVER);
len = rt_device_read(serial, 0, buffer, 256);
buffer[len] = '\0';
rt_device_write(serial, 0, buffer, len);
rt_kprintf("%s\n", buffer);
}
}
void Create_Uart3_Test_Entry(void)
{
char str[] = "hello, rt-thread!";
/* 查找串口设备 */
serial = rt_device_find(SAMPLE_UART_NAME);
if (!serial)
{
rt_kprintf("find %s failed!\n", SAMPLE_UART_NAME);
}
/* 以 DMA 接收及轮询发送方式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);
/* 初始化配置参数 */
struct serial_configure uart3_config = RT_SERIAL_CONFIG_DEFAULT;
uart3_config.baud_rate = BAUD_RATE_19200;
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, (void *) &uart3_config);
/* 创建信号量 */
rx_sem = rt_sem_create("rx_sem", 0, RT_IPC_FLAG_PRIO);
/* 设置接收回调函数 */
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);
}
}
MSH_CMD_EXPORT_ALIAS(Create_Uart3_Test_Entry, test, Create Uart3 Test Entry);
四、使用消息队列接收
1.uart3.h文件
#ifndef APPLICATIONS_UART3_H_
#define APPLICATIONS_UART3_H_
#include <rtthread.h>
#include <drv_common.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;
#endif /* APPLICATIONS_UART3_H_ */
2.uart3.c文件
#include "uart3.h"
/* 接收数据回调函数 */
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 == RT_EOK)
{
/* 从串口读取数据 */
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);
}
}
}
void Create_Uart3_Test_Entry(void)
{
static char msg_pool[256];
char str[] = "hello RT-Thread!\r\n";
/* 查找串口设备 */
serial = rt_device_find(SAMPLE_UART_NAME);
if (!serial)
{
rt_kprintf("find %s failed!\n", SAMPLE_UART_NAME);
}
/* 初始化消息队列 */
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);
/* 初始化配置参数 */
struct serial_configure uart3_config = RT_SERIAL_CONFIG_DEFAULT;
uart3_config.baud_rate = BAUD_RATE_19200;
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, (void *) &uart3_config);
/* 设置接收回调函数 */
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);
}
}
MSH_CMD_EXPORT_ALIAS(Create_Uart3_Test_Entry, test, Create Uart3 Test Entry);
五、测试验证
通过将上面的两个程序烧录到控制板中,通过RTT特有的控制台进行指令操作,通过大宋质量test
来执行串口函数,从而实现对串口3的参数配置以及DMA配置,通过信号量或者消息队列的方式来接收数据,从而将接收到的数据重新发送到串口3,实际的实验效果如下所示: