rt-thread UART串口使用详解
- 一、UART串口使用详解
-
- 1、配置串口设备
- 2、初始化与打开串口设备
- 3、配置串口参数(可选)
- 4、发送数据
- [5、 接收数据(中断模式推荐)](#5、 接收数据(中断模式推荐))
- 6、关闭串口设备
- [7、 调试与常见问题](#7、 调试与常见问题)
- 8、总结
- 二、代码示例

一、UART串口使用详解
UART(通用异步收发传输器)是嵌入式系统中非常常见且重要的通信接口,用于设备间的串行数据传输。RT-Thread 提供了完善的 UART 设备驱动框架,使得开发者能够方便地使用串口进行通信。
1、配置串口设备
在使用串口之前,通常需要确认它已在系统中正确配置和启用。
- 设备树配置 (针对支持设备树的平台):
在设备树源文件(.dts)中定义 UART 节点,指定其寄存器地址、时钟、引脚复用等信息。 - RT-Thread 配置工具 (Env / menuconfig):
在 RT-Thread 的配置工具中:- 进入
Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable UART。 - 选择需要使用的具体 UART 接口(如 UART1, UART2 等)。
- 配置其引脚(通常自动关联设备树,但也可手动指定)。
- 选择驱动模式(轮询 Polling、中断 Interrupt、DMA)。
- 配置缓冲区大小(RX/TX Buffer Size)。
- 保存配置并退出。
- 进入
2、初始化与打开串口设备
配置完成后,在应用程序代码中需要查找设备并打开它。
c
#include <rtdevice.h> // 包含 RT-Thread 设备驱动头文件
int main(void)
{
rt_device_t serial; // 设备控制块指针
/* 查找串口设备 */
serial = rt_device_find("uart1"); // "uart1" 是设备名称,根据实际配置修改
if (serial == RT_NULL)
{
rt_kprintf("find uart1 device failed!\n");
return -RT_ERROR;
}
/* 以中断接收 & 轮询发送模式打开设备 */
if (rt_device_open(serial, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
{
rt_kprintf("open uart1 device failed!\n");
return -RT_ERROR;
}
// ... 后续操作 (发送、接收、设置参数等)
return 0;
}
rt_device_find: 根据设备名称查找设备,返回设备控制块指针。设备名称通常为"uart1","uart2"等。rt_device_open: 打开设备。第二个参数是打开模式标志:RT_DEVICE_FLAG_RDWR: 可读可写(默认)。RT_DEVICE_FLAG_INT_RX: 开启中断接收模式(推荐)。RT_DEVICE_FLAG_DMA_RX: 开启 DMA 接收模式(如果支持)。RT_DEVICE_FLAG_DMA_TX: 开启 DMA 发送模式(如果支持)。- 模式可以组合使用,例如
RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_DMA_TX。
3、配置串口参数(可选)
打开设备后,可以设置波特率、数据位、停止位、校验位等参数。RT-Thread 使用 struct serial_configure 结构体来描述这些参数。
c
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; // 获取默认配置 (115200, 8N1)
/* 修改配置 */
config.baud_rate = BAUD_RATE_9600; // 修改波特率为9600
config.data_bits = DATA_BITS_8;
config.stop_bits = STOP_BITS_1;
config.parity = PARITY_NONE;
/* 应用配置 */
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
RT_SERIAL_CONFIG_DEFAULT: 宏定义,表示默认配置(波特率 115200,8 位数据位,1 位停止位,无校验)。rt_device_control: 用于控制设备。命令RT_DEVICE_CTRL_CONFIG用于配置串口参数,第三个参数是struct serial_configure结构体指针。
4、发送数据
发送数据通常使用轮询模式(阻塞式)或 DMA 模式(非阻塞式)。这里介绍常用的轮询发送:
c
char send_buffer[] = "Hello RT-Thread!\r\n";
rt_size_t len = rt_strlen(send_buffer);
rt_size_t bytes_written = rt_device_write(serial, 0, send_buffer, len); // 阻塞式写入
if (bytes_written != len)
{
rt_kprintf("write data failed! written: %d, total: %d\n", bytes_written, len);
}
rt_device_write: 向设备写入数据。- 第一个参数:设备控制块指针。
- 第二个参数:写入数据的偏移量(通常为 0)。
- 第三个参数:待发送数据的缓冲区指针。
- 第四个参数:要发送的数据长度(字节数)。
- 返回值:实际写入的字节数。在阻塞模式下,通常等于请求写入的长度,除非发生错误。
5、 接收数据(中断模式推荐)
为了及时接收数据而不占用 CPU 进行轮询,强烈建议使用中断接收模式。这需要设置接收回调函数。
c
/* 定义接收回调函数 */
static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
{
char ch;
while (rt_device_read(dev, 0, &ch, 1) == 1) // 每次尝试读取1个字节
{
// 处理接收到的字节 'ch'
// 例如:放入队列、解析协议、打印等
rt_kprintf("%c", ch);
}
return RT_EOK;
}
/* 在打开设备后,设置接收回调 */
rt_device_set_rx_indicate(serial, uart_rx_ind); // 设置接收中断回调函数
rt_device_set_rx_indicate: 设置串口接收中断的回调函数。当串口接收到数据并触发中断时,RT-Thread 会调用这个回调函数。- 回调函数
uart_rx_ind:- 第一个参数:触发中断的设备指针。
- 第二个参数:表示接收缓冲区中有多少新数据到达(这个值可能不精确,仅供参考)。
- 在回调函数内部,通常使用
rt_device_read循环读取接收缓冲区中的数据,直到读完为止(返回读取字节数为 0 或错误)。注意 :在中断上下文(或信号量、邮箱等 IPC 的接收线程)中,应避免使用可能导致挂起的操作(如rt_kprintf在未初始化控制台时可能挂起)。
rt_device_read: 从设备读取数据。- 参数与
rt_device_write类似。 - 返回值:实际读取的字节数。
- 参数与
6、关闭串口设备
当不再需要使用串口时,应关闭设备以释放资源。
c
rt_device_close(serial);
7、 调试与常见问题
- 设备名称确认: 确保
rt_device_find使用的设备名称(如"uart1")与 RT-Thread 驱动注册的名称一致。可以在 FinSH 控制台使用list_device命令查看所有注册的设备及其状态。 - 引脚冲突: 检查 UART 使用的引脚是否与其他功能(如 I2C, SPI)冲突。确认引脚配置正确。
- 波特率匹配: 通信双方(发送端和接收端)的波特率、数据位、停止位、校验位必须设置一致。
- 缓冲区溢出: 如果接收数据过快,而应用层读取不及时,可能导致接收缓冲区溢出。增大
menuconfig中的RX Buffer Size或提高应用层读取速度。 - 回调函数上下文: 记住接收回调函数
uart_rx_ind是在中断上下文中被调用的。在其中进行耗时的操作(如大量打印、复杂的协议解析)会影响系统实时性,甚至导致丢数据。建议在回调函数中只进行简单的数据读取,然后通过 RT-Thread 的 IPC(如消息队列、邮箱、信号量)将数据传递给一个专门的线程进行处理。 - DMA 模式: 对于高速或大数据量传输,可以考虑配置和使用 DMA 模式,减轻 CPU 负担。配置方法类似,打开时使用
RT_DEVICE_FLAG_DMA_RX或RT_DEVICE_FLAG_DMA_TX标志,并设置相应的 DMA 回调函数 (rt_device_set_tx_complete,rt_device_set_rx_indicate用于 DMA RX)。
8、总结
使用 RT-Thread 的 UART 驱动主要步骤为:配置设备 -> 查找设备 -> 打开设备(设置模式)-> (可选)配置参数 -> 设置接收回调(中断模式)-> 读写数据 -> 关闭设备。
二、代码示例
c
/*
* 程序清单:这是一个 串口 设备使用例程
* 例程导出了 uart_sample 命令到控制终端
* 命令调用格式:uart_sample uart2
* 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
* 程序功能:通过串口输出字符串"hello RT-Thread!",然后错位输出输入的字符
*/
#include <rtthread.h>
#define SAMPLE_UART_NAME "uart2"
/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
return RT_EOK;
}
static void serial_thread_entry(void *parameter)
{
char ch;
while (1)
{
/* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
while (rt_device_read(serial, -1, &ch, 1) != 1)
{
/* 阻塞等待接收信号量,等到信号量后再次读取数据 */
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
/* 读取到的数据通过串口错位输出 */
ch = ch + 1;
rt_device_write(serial, 0, &ch, 1);
}
}
static int uart_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
char uart_name[RT_NAME_MAX];
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_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/* 以中断接收及轮询发送模式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_INT_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_sample, uart device sample);
