rt-thread UART串口使用详解

rt-thread UART串口使用详解

一、UART串口使用详解

UART(通用异步收发传输器)是嵌入式系统中非常常见且重要的通信接口,用于设备间的串行数据传输。RT-Thread 提供了完善的 UART 设备驱动框架,使得开发者能够方便地使用串口进行通信。

1、配置串口设备

在使用串口之前,通常需要确认它已在系统中正确配置和启用。

  • 设备树配置 (针对支持设备树的平台):
    在设备树源文件(.dts)中定义 UART 节点,指定其寄存器地址、时钟、引脚复用等信息。
  • RT-Thread 配置工具 (Env / menuconfig):
    在 RT-Thread 的配置工具中:
    1. 进入 Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable UART
    2. 选择需要使用的具体 UART 接口(如 UART1, UART2 等)。
    3. 配置其引脚(通常自动关联设备树,但也可手动指定)。
    4. 选择驱动模式(轮询 Polling、中断 Interrupt、DMA)。
    5. 配置缓冲区大小(RX/TX Buffer Size)。
    6. 保存配置并退出。

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_RXRT_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);
相关推荐
洲洲不是州州1 小时前
单片机onenet云平台的万能APP
单片机·onenet·app·嵌入式·云平台
钿驰科技2 小时前
无刷电机的驱动原理及驱动电路解析
单片机·嵌入式硬件
小锋学长生活大爆炸2 小时前
【教程】树莓派驱动 0.96 寸 SSD1315 OLED 屏幕完整指南
单片机·嵌入式硬件·嵌入式·教程·树莓派·oled·屏幕
ye150127774553 小时前
12V-24V升110V升压转换WT3207
单片机·嵌入式硬件·其他·硬件工程
yong99904 小时前
基于 STM32 的数字控制实现双向 DC-DC 电源
stm32·单片机·嵌入式硬件
12.=0.4 小时前
【stm32_9】RTOS的概念、种类对比,FressRTOS的概述、FressRTOS的源码结构、FressRTOS的源码移植
stm32·单片机·嵌入式硬件
Yeats_Liao4 小时前
智能感知低功耗设计:MCU上的AI异常检测与能效优化
人工智能·单片机·物联网·neo4j
Y多了个想法4 小时前
RK3576 android14 I2C总线,硬件I2C 与 GPIO模拟I2C 比对
经验分享·嵌入式硬件·i2c·rk·rk3576
blevoice4 小时前
JL杰理AC696N开发板上调试蓝牙音质优化:开启AAC高清音频支持
单片机·ffmpeg·音视频·aac·ac6966b蓝牙音响方案·杰理智能音箱开发·杰理ac6965e蓝牙音频开发