RT-Thread UART驱动

一、 概述

  • 核心目标:掌握在RT-Thread操作系统下,UART(串口)驱动的使用与基本原理,并在QEMU模拟的vexpress-a9平台上进行实践。
  • 实践环境:RT-Thread + qemu-vexpress-a9 BSP。

二、 RT-Thread UART驱动框架核心概念

RT-Thread提供了一套统一的I/O设备模型,UART作为字符设备被纳入该框架管理。应用程序通过设备管理层提供的标准接口访问底层硬件,这使得驱动与应用解耦,提高了可移植性。

关键数据结构与流程

  1. 设备模型:所有设备都派生自 struct rt_device,其中包含了设备类型、操作函数集(ops)、回调函数指针等。
  2. 访问流程:应用程序使用UART的一般流程为:查找设备 -> 打开设备 -> 读写/控制设备 -> 关闭设备

具体参考RT-Thread设备模型


三、 QEMU vexpress-a9 UART硬件模拟与驱动对接

在QEMU vexpress-a9 BSP中,UART设备通常已被模拟并集成在BSP的驱动层。我们的重点在于理解驱动如何注册以及应用如何对接

  1. 硬件模拟:QEMU模拟了PL011 UART等串口硬件,其寄存器地址映射到固定的内存位置。BSP中的驱动(如serial.c)会操作这些地址来实现收发。
  2. 驱动注册:在初始化阶段,RT-Thread自动初始化机制会调用rt_hw_uart_init()等函数,最终通过rt_device_register()将UART设备(例如命名为“uart0”或“uart1”)注册到RT-Thread的I/O设备管理器中。
  3. 控制台设备:在qemu-vexpress-a9 BSP中,通常会将一个UART指定为控制台(console),用于连接FinSH shell。这通过在rt_hw_board_init()中调用rt_console_set_device(RT_CONSOLE_DEVICE_NAME)实现。

UART设备名

  • qemu-vexpress-a9的串口设备名通常记录在 BSP下的board.h或rtconfig.h文件,找到控制台设备名的宏定义,通常是:
#define RT_CONSOLE_DEVICE_NAME "uart0"
  • 在board.c中连接FinSh Shell
/**
 * This function will initialize beaglebone board
 */
void rt_hw_board_init(void)
{
    /* initialize hardware interrupt */
    rt_hw_interrupt_init();
    /* initialize system heap */
    rt_system_heap_init(HEAP_BEGIN, HEAP_END);

    rt_components_board_init();
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);

    rt_thread_idle_sethook(idle_wfi);

#ifdef RT_USING_SMP
    /* install IPI handle */
    rt_hw_ipi_handler_install(RT_SCHEDULE_IPI, rt_scheduler_ipi_handler);
#endif
}

四、 工作模式与应用层操作模式

概念 含义 谁来决定? 常见配置
硬件工作模式 驱动底层与硬件交互的方式:轮询、中断、DMA。 驱动实现决定。驱动在注册设备时确定其支持的硬件模式。优先级通常是DMA > 中断 > 轮询。 在BSP的serial.c中通过#define或初始化代码确定。
应用层操作模式 应用程序调用read/write时的行为:阻塞非阻塞 应用程序在open时通过oflags参数指定 RT_DEVICE_FLAG_RX_NON_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING 是最常用组合。

要点解析

  • 阻塞 vs 非阻塞
    • 阻塞读:当调用rt_device_read()时,如果接收缓冲区(FIFO)中没有数据,当前线程将挂起等待,直到有数据到达。
    • 非阻塞读:如果没有数据,read函数立即返回0,不会等待。
    • 阻塞写:如果发送缓冲区满,线程会等待直到有空间写入数据。
    • 非阻塞写:如果缓冲区满,立即返回0,表示本次无法写入。
    • 提示:阻塞操作依赖于RT-Thread内核的线程调度机制。
  • 缓冲(FIFO)机制:为了提高效率,UART驱动内部通常会维护软件FIFO缓冲区,用于暂存中断接收到的数据或等待发送的数据。应用层的read/write操作实际上是与这个软件FIFO进行数据交换。
  • 中断与回调:在中断模式下,当硬件接收到一个字节时,会触发中断,中断服务程序(ISR)将数据存入接收FIFO。如果应用程序设置了接收回调函数(rx_indicate),ISR还会调用此回调,通知应用层有数据可读。这是实现高效异步通信的关键。

在serial.c中,串口驱动程序在注册给IO设备管理器时采用的是中断接收的方式。以下为对串口驱动程序的完整解析,在整份工程中存在两份serial.c,会分别进行完整的解析。

...\drivers\serial.c  
驱动层,与底层寄存器、时钟、收发控制强相关,通常由芯片厂家完成

...\rt-thread\components\drivers\serial\serial.c  
驱动框架层,RT-Thread提供了一个统一的抽象层用于适配不同,
可通过该适配层适配到IO设备模型,支持多种模式(轮询、中断、DMA)
/*
 *  ...\drivers\serial.c
 */
#include <rthw.h>
#include <rtdevice.h>
#include "serial.h"


struct hw_uart_device
{
    rt_uint32_t hw_base;       //UART 寄存器基地址
    rt_uint32_t irqno;         //中断编号
};

#define UART_DR(base)   __REG32(base + 0x00)  //数据寄存器 DATA REGISTER
#define UART_FR(base)   __REG32(base + 0x18)  //标志寄存器 FLAG REGISTER,通常用于记录发送或者接收状态
#define UART_CR(base)   __REG32(base + 0x30)  //控制寄存器 CONTROL REGISTER
#define UART_IMSC(base) __REG32(base + 0x38)  //中断掩码设置/清除寄存器 INTERRUPT MASK SET/CLEAR
#define UART_ICR(base)  __REG32(base + 0x44)  //中断清除寄存器 INTERRUPT CLEAR REGISTER 用于清除已挂起的中断标志

//串口位定义
#define UARTFR_RXFE     0x10  //RXFE对应 Receive FIFO Empty,表示接收缓冲区空时UART_FR设置对应的位为0x10             
#define UARTFR_TXFF     0x20  //TXFF对应 Transmit FIFO Full,表示发送缓冲区满时UART_FR设置对应的位为0x20        
#define UARTIMSC_RXIM   0x10  //RXIM对应 Receive Interrupt Mask,表示需设置接收中断时设置UART_IMSC对应的位为0x10        
#define UARTIMSC_TXIM   0x20  //TXIM对应 Transmit Interrupt Mask,表示需设置发送中断时设置UART_IMSC对应的位为0x20        
#define UARTICR_RXIC    0x10  //RXIC对应 Receive Interrupt Clear,表示需清除接收中断挂起标志时设置UART_ICR对应的位为0x10       
#define UARTICR_TXIC    0x20  //TXIC对应 Transmit Interrupt Clear,表示需清除接收中断挂起标志时设置UART_ICR对应的位为0x20        

 
 /*
  串口中断服务函数
  内部直接调用的 RT-Thread 的通用串行中断处理函数,路径在...\rt-thread\components\drivers\serial\serial.c
  该操作表示将所有的中断都视为接收事件处理
*/
static void rt_hw_uart_isr(int irqno, void *param)
{
    struct rt_serial_device *serial = (struct rt_serial_device *)param;

    rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
}

/*
  串口配置函数
  初始化时通过 RT_SERIAL_CONFIG_DEFAULT 设置,且硬件复位后的默认配置已满足要求,或者配置工作在其他地方完成。
*/
static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    return RT_EOK;
}

/*
  串口控制函数
  支持两个控制命令CLR_INT清除中断,SET_INT打开接收中断,并打开中断控制器的中断线
*/
static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct hw_uart_device *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct hw_uart_device *)serial->parent.user_data;
    //注册的时候定义了该结构体作为私有数据注册给串口驱动框架,用于以下的基地址和中断号获取

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        UART_IMSC(uart->hw_base) &= ~UARTIMSC_RXIM;
        break;

    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        UART_IMSC(uart->hw_base) |= UARTIMSC_RXIM;
        rt_hw_interrupt_umask(uart->irqno);
        break;
    }

    return RT_EOK;
}

/*
  串口字符发送函数
*/
static int uart_putc(struct rt_serial_device *serial, char c)
{
    struct hw_uart_device *uart; //需要知道是哪个串口,获取其基地址和中断号

    RT_ASSERT(serial != RT_NULL);
    uart = (struct hw_uart_device *)serial->parent.user_data; 
    //同上,在注册的时候就将串口的完整数据作为私有数据传递给串口驱动框架

    while (UART_FR(uart->hw_base) & UARTFR_TXFF);//循环读取当前串口的状态寄存器,当发送FIFO满则等待
    UART_DR(uart->hw_base) = c;//塞入字符

    return 1;//返回表示发送成功1个字符
}

/*
  串口字符接收函数
*/
static int uart_getc(struct rt_serial_device *serial)
{
    int ch;
    struct hw_uart_device *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct hw_uart_device *)serial->parent.user_data;
    //同上
    
    ch = -1;//全F
    if (!(UART_FR(uart->hw_base) & UARTFR_RXFE))//接收缓冲区非空
    {
        ch = UART_DR(uart->hw_base) & 0xff;//取一个字节数据
    }

    return ch;
}

/*
  串口操作实例,为操作函数集合,用于注册给串口驱动设备层
*/
static const struct rt_uart_ops _uart_ops =
{
    uart_configure,
    uart_control,
    uart_putc,
    uart_getc,
};

/*
  串口实例,通过条件编译,REALVIEW_UART0_BASE和IRQ_PBA8_UART0在...\drivers\realview.h中定义
  以下都是全局结构体
*/
#ifdef RT_USING_UART0
/* UART device driver structure */
static struct hw_uart_device _uart0_device =
{
    REALVIEW_UART0_BASE,
    IRQ_PBA8_UART0,
};
static struct rt_serial_device _serial0;
#endif
#ifdef RT_USING_UART1
/* UART1 device driver structure */
static struct hw_uart_device _uart1_device =
{
    REALVIEW_UART1_BASE,
    IRQ_PBA8_UART1,
};
static struct rt_serial_device _serial1;
#endif

/*
  串口初始化函数
*/
int rt_hw_uart_init(void)
{
    struct hw_uart_device *uart;  //串口实例结构体指针
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

#ifdef RT_USING_UART0
    uart = &_uart0_device;      //指向本地全局结构体

    _serial0.ops    = &_uart_ops;  //串口配置指向本地操作集实例
    _serial0.config = config;

    /*调用串口驱动框架提供的注册函数
      _serial0用于间接调用驱动函数
      "uart0"表示名称,可用于list_device显示
      RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX表示可读可写,接收中断标志打开
      uart表示将uart作为私有数据传递给RT-Thread的设备管理器中,
      该做法可自定义不同的串口基地址或者是其他的内容,并且其操作的函数可以通过获取该参数灵活区分。
    */
    rt_hw_serial_register(&_serial0, "uart0",
                          RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                          uart);
                          
    /*调用中断框架提供的安装函数
      uart->irqno用于中断编号,通常在芯片出厂已确定
      rt_hw_uart_isr表示中断服务函数名称
      _serial0是私有参数,这里传递进去方便其他服务函数可直接调用ops提供的函数
      "uart0"表示名称,
    */                                   
    rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, &_serial0, "uart0");
    
    //打开串口的收发功能
    UART_CR(uart->hw_base) = (1 << 0) | (1 << 8) | (1 << 9);
#endif

#ifdef RT_USING_UART1
    //同上uart0
    uart = &_uart1_device;
    _serial1.ops = &_uart_ops;
    _serial1.config = config;

    /* register UART1 device */
    rt_hw_serial_register(&_serial1, "uart1",
                          RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, uart);
    /* enable Rx and Tx of UART */
    UART_CR(uart->hw_base) = (1 << 0) | (1 << 8) | (1 << 9);
    rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, &_serial1, "uart1");
#endif

    return 0;
}
INIT_BOARD_EXPORT(rt_hw_uart_init);
//将初始化函数导出到板级初始化段,系统启动时会自动调用该段中的所有函数(按优先级顺序执行一次)

/*
 * RTT提供的完整的驱动框架层
*/

#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
/* 调试标签 */
#define DBG_TAG    "UART"
#define DBG_LVL    DBG_INFO
#include <rtdbg.h>
/* 是否支持POSIX操作*/
#ifdef RT_USING_POSIX
#include <dfs_posix.h>
#include <dfs_poll.h>

#ifdef RT_USING_POSIX_TERMIOS
#include <posix_termios.h>
#endif

/* 不允许在该文件内再次被定义 */
#ifdef getc
#undef getc
#endif

#ifdef putc
#undef putc
#endif

/*
	当串口设备有数据到达时,
	由设备驱动层调用此回调,
	用于唤醒等待队列中的线程(通常是在执行 poll 或阻塞读的线程)
*/
static rt_err_t serial_fops_rx_ind(rt_device_t dev, rt_size_t size)
{
    rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN);

    return RT_EOK;
}

/* 
	打开文件描述符对应的串口设备
*/
static int serial_fops_open(struct dfs_fd *fd)
{
    rt_err_t ret = 0;
    rt_uint16_t flags = 0;
    rt_device_t device;	//创建一个设备

    device = (rt_device_t)fd->data;
    RT_ASSERT(device != RT_NULL);

    switch (fd->flags & O_ACCMODE)//符合只读、只写、可读可写
    {
    case O_RDONLY:
        LOG_D("fops open: O_RDONLY!");
        flags = RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDONLY;
        break;
    case O_WRONLY:
        LOG_D("fops open: O_WRONLY!");
        flags = RT_DEVICE_FLAG_WRONLY;
        break;
    case O_RDWR:
        LOG_D("fops open: O_RDWR!");
        flags = RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDWR;
        break;
    default:
        LOG_E("fops open: unknown mode - %d!", fd->flags & O_ACCMODE);
        break;
    }

    if ((fd->flags & O_ACCMODE) != O_WRONLY) //如果不是只写模式,设置接收回调为serial_fops_rx_ind,数据到达时唤醒队列。
        rt_device_set_rx_indicate(device, serial_fops_rx_ind);
    ret = rt_device_open(device, flags);//打开设备,由此完成文件描述符到RT-Thread的设备适配
    if (ret == RT_EOK) return 0;

    return ret;
}

/* 
	关闭文件描述符对应的串口设备
*/
static int serial_fops_close(struct dfs_fd *fd)
{
    rt_device_t device;

    device = (rt_device_t)fd->data;

    rt_device_set_rx_indicate(device, RT_NULL);//清空接收回调
    rt_device_close(device);//关闭文件描述符对应的串口设备

    return 0;
}

/* 
	文件描述符对应的串口设备操控函数
*/
static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args)
{
    rt_device_t device;

    device = (rt_device_t)fd->data;
    switch (cmd)
    {
    case FIONREAD:
        break;
    case FIONWRITE:
        break;
    }

    return rt_device_control(device, cmd, args);//直接通过 rt_device_control 将命令转发给设备驱
}

/* 
	文件描述符对应的串口设备读取函数
*/
static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count)
{
    int size = 0;
    rt_device_t device;

    device = (rt_device_t)fd->data;

    do	//循环读取,直至数据错误或读到数据
    {
        size = rt_device_read(device, -1,  buf, count);//读取数据,返回实际读取数据
        if (size <= 0)	//如果没有数据则等待接收队列唤醒
        {
            if (fd->flags & O_NONBLOCK)
            {
                size = -EAGAIN;
                break;
            }

            rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER);
        }
    }while (size <= 0);

    return size;
}

/* 
	文件描述符对应的串口设备写入函数
*/
static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count)
{
    rt_device_t device;

    device = (rt_device_t)fd->data;
    return rt_device_write(device, -1, buf, count);//向串口写入数据。直接调用设备写接口,返回写入字节数
}

/* 
	文件poll函数
*/
static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req)
{
    int mask = 0;
    int flags = 0;
    rt_device_t device;
    struct rt_serial_device *serial;

    device = (rt_device_t)fd->data;
    RT_ASSERT(device != RT_NULL);

    serial = (struct rt_serial_device *)device;

    /* 只支持pollIn事件 */
    flags = fd->flags & O_ACCMODE;
    if (flags == O_RDONLY || flags == O_RDWR)
    {
        rt_base_t level;
        struct rt_serial_rx_fifo* rx_fifo;

        rt_poll_add(&(device->wait_queue), req);//将当前 poll 请求添加到设备等待队列,以便后续唤醒。

        rx_fifo = (struct rt_serial_rx_fifo*) serial->serial_rx;//指向串口设备维护的fifo缓冲区

        level = rt_hw_interrupt_disable();
        if ((rx_fifo->get_index != rx_fifo->put_index) || (rx_fifo->get_index == rx_fifo->put_index && rx_fifo->is_full == RT_TRUE)) //检查环形缓冲区是否有数据
            mask |= POLLIN;
        rt_hw_interrupt_enable(level);
    }

    return mask;
}

/*文件操作函数集*/
const static struct dfs_file_ops _serial_fops =
{
    serial_fops_open,
    serial_fops_close,
    serial_fops_ioctl,
    serial_fops_read,
    serial_fops_write,
    RT_NULL, /* flush */
    RT_NULL, /* lseek */
    RT_NULL, /* getdents */
    serial_fops_poll,
};
#endif

/*
 * 轮询模式下从串口读取数据,直到读取指定长度或遇到换行符(类似行缓冲)
 */
rt_inline int _serial_poll_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
{
    int ch;
    int size;

    RT_ASSERT(serial != RT_NULL);
    size = length;

    while (length)
    {
        ch = serial->ops->getc(serial);
        if (ch == -1) break;

        *data = ch;
        data ++; length --;

        if (ch == '\n') break;
    }

    return size - length;
}

/*
	轮询模式下向串口发送数据,如果设备开启了流模式(RT_DEVICE_FLAG_STREAM),
	则在发送 \n 前自动添加 \r,使输出符合终端习惯
*/
rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{
    int size;
    RT_ASSERT(serial != RT_NULL);

    size = length;
    while (length)
    {
        if (*data == '\n' && (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM))
        {
            serial->ops->putc(serial, '\r');
        }

        serial->ops->putc(serial, *data);

        ++ data;
        -- length;
    }

    return size - length;//返回实际发送的字节数(size - length)
}

/*
 * 中断模式接收
 */
rt_inline int _serial_int_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
{
    int size;
    struct rt_serial_rx_fifo* rx_fifo;

    RT_ASSERT(serial != RT_NULL);
    size = length;

    rx_fifo = (struct rt_serial_rx_fifo*) serial->serial_rx;
    RT_ASSERT(rx_fifo != RT_NULL);

    /* 从软件fifo中读取*/
    while (length)
    {
        int ch;
        rt_base_t level;

        level = rt_hw_interrupt_disable();

        /* 如果没有数据,或者缓冲区为空*/
        if ((rx_fifo->get_index == rx_fifo->put_index) && (rx_fifo->is_full == RT_FALSE))
        {
            rt_hw_interrupt_enable(level);
            break;
        }

        /* 有数据存储,指针后移,直至超出缓冲区大小回绕为0*/
        ch = rx_fifo->buffer[rx_fifo->get_index];
        rx_fifo->get_index += 1;
        if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0;

        /* 如果之前是满状态,清除(因为读走了一个字节)*/
        if (rx_fifo->is_full == RT_TRUE)
        {
            rx_fifo->is_full = RT_FALSE;
        }

        rt_hw_interrupt_enable(level);

        *data = ch & 0xff;//存储用户缓冲区
        data ++; length --;
    }

    return size - length;//返回实际读取字节数
}

/*
 * 中断模式发送
 */
rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{
    int size;
    struct rt_serial_tx_fifo *tx;

    RT_ASSERT(serial != RT_NULL);

    size = length;
    tx = (struct rt_serial_tx_fifo*) serial->serial_tx;
    RT_ASSERT(tx != RT_NULL);

    while (length)
    {
        if (serial->ops->putc(serial, *(char*)data) == -1)//发送失败或者是缓冲区满则阻塞当前线程
        {
            rt_completion_wait(&(tx->completion), RT_WAITING_FOREVER);//等待发送完成事件唤醒
            continue;
        }

        data ++; length --;
    }

    return size - length;
}


/*
 * 检测串口缓存是否准备输出
 */
static void _serial_check_buffer_size(void)
{
    static rt_bool_t already_output = RT_FALSE;
		
    if (already_output == RT_FALSE)
    {
        LOG_W("Warning: There is no enough buffer for saving data,"
              " please increase the RT_SERIAL_RB_BUFSZ option.");
        already_output = RT_TRUE;
    }
}	

#if defined(RT_USING_POSIX) || defined(RT_SERIAL_USING_DMA)
/*
	计算接收 FIFO 中当前可读的数据长度
*/
static rt_size_t _serial_fifo_calc_recved_len(struct rt_serial_device *serial)
{
    struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx;

    RT_ASSERT(rx_fifo != RT_NULL);

    if (rx_fifo->put_index == rx_fifo->get_index)//缓冲区空或满
    {
        return (rx_fifo->is_full == RT_FALSE ? 0 : serial->config.bufsz);//若满返回缓冲区大小,空返回0
    }
    else //返回数据长度的差值,就是数据长度大小
    {
        if (rx_fifo->put_index > rx_fifo->get_index)
        {
            return rx_fifo->put_index - rx_fifo->get_index;
        }
        else
        {
            return serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index);
        }
    }
}
#endif /* RT_USING_POSIX || RT_SERIAL_USING_DMA */

#ifdef RT_SERIAL_USING_DMA
/**
	调用以上处理函数
 */
static rt_size_t rt_dma_calc_recved_len(struct rt_serial_device *serial)
{
    return _serial_fifo_calc_recved_len(serial);
}

/**
	DMA模式下读取缓冲区的Len字节后更新get_index,清除缓冲区满标志
 */
static void rt_dma_recv_update_get_index(struct rt_serial_device *serial, rt_size_t len)
{
    struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx;
	
    RT_ASSERT(rx_fifo != RT_NULL);
    RT_ASSERT(len <= rt_dma_calc_recved_len(serial));
	//若接收缓冲满,清空缓冲区满标志
    if (rx_fifo->is_full && len != 0) rx_fifo->is_full = RT_FALSE;
	//更新get_index
    rx_fifo->get_index += len;
    //要是get_index超过缓冲区大小,取余得到具体的位置
    if (rx_fifo->get_index >= serial->config.bufsz)
    {
        rx_fifo->get_index %= serial->config.bufsz;
    }
}

/**
	当 DMA 接收完成 len 字节后,更新 put_index,并处理 FIFO 满的情况
 */
static void rt_dma_recv_update_put_index(struct rt_serial_device *serial, rt_size_t len)
{
    //指向接收fifo缓存
    struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx;

    RT_ASSERT(rx_fifo != RT_NULL);

    if (rx_fifo->get_index <= rx_fifo->put_index)//数据未绕回,put增加,get也会增加,直到put到缓冲区满
    {
        rx_fifo->put_index += len;
       
        if (rx_fifo->put_index >= serial->config.bufsz)
        {
            rx_fifo->put_index %= serial->config.bufsz; //绕回
            /* 如果绕回的值已经和get一样或者更大,说明get之前的数据已经覆盖了,将缓冲区标志置满 */
            if (rx_fifo->put_index >= rx_fifo->get_index)
            {
                rx_fifo->is_full = RT_TRUE;
            }
        }
    }
    else//get前还有数据未读取,put和get之间还有空间可以存
    {
        rx_fifo->put_index += len;
        if (rx_fifo->put_index >= rx_fifo->get_index)//put一旦超过了get或者相等意味已经满或者是要覆盖前面的未读取的值。
        {
            if (rx_fifo->put_index >= serial->config.bufsz)
            {
                rx_fifo->put_index %= serial->config.bufsz;
            }
            rx_fifo->is_full = RT_TRUE;
        }
    }
    
    //丢失覆盖的数据,调用函数输出警告
    if(rx_fifo->is_full == RT_TRUE)
    {
        _serial_check_buffer_size();
        rx_fifo->get_index = rx_fifo->put_index;
    }
}

/*
 * DMA读取数据
 */
rt_inline int _serial_dma_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
{
    rt_base_t level;

    RT_ASSERT((serial != RT_NULL) && (data != RT_NULL));

    level = rt_hw_interrupt_disable();	//关中断保护

    if (serial->config.bufsz == 0)	//无缓冲区的配置
    {
        int result = RT_EOK;
        struct rt_serial_rx_dma *rx_dma;

        rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx;
        RT_ASSERT(rx_dma != RT_NULL);

        if (rx_dma->activated != RT_TRUE)//调用串口内部的DMA传输,激活DMA
        {
            rx_dma->activated = RT_TRUE;
            RT_ASSERT(serial->ops->dma_transmit != RT_NULL);
            serial->ops->dma_transmit(serial, data, length, RT_SERIAL_DMA_RX);
        }
        else result = -RT_EBUSY;
        rt_hw_interrupt_enable(level);

        if (result == RT_EOK) return length;

        rt_set_errno(result);
        return 0;
    }
    else	//有缓冲区的配置
    {
        struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx;
        rt_size_t recv_len = 0, fifo_recved_len = rt_dma_calc_recved_len(serial);//计算当前可读的大小

        RT_ASSERT(rx_fifo != RT_NULL);

        if (length < (int)fifo_recved_len)
            recv_len = length;
        else
            recv_len = fifo_recved_len;

        if (rx_fifo->get_index + recv_len < serial->config.bufsz)
            rt_memcpy(data, rx_fifo->buffer + rx_fifo->get_index, recv_len);
        else
        {
            rt_memcpy(data, rx_fifo->buffer + rx_fifo->get_index,
                    serial->config.bufsz - rx_fifo->get_index);
            rt_memcpy(data + serial->config.bufsz - rx_fifo->get_index, rx_fifo->buffer,
                    recv_len + rx_fifo->get_index - serial->config.bufsz);
        }
        rt_dma_recv_update_get_index(serial, recv_len);//DMA读取完成后更新读指针
        rt_hw_interrupt_enable(level);
        return recv_len;
    }
}

/*
	DMA 模式下发送数据
*/
rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{
    rt_base_t level;
    rt_err_t result;
    struct rt_serial_tx_dma *tx_dma;

    tx_dma = (struct rt_serial_tx_dma*)(serial->serial_tx);
	//压入队列,然后激活DMA标志,调用DMA发送
    result = rt_data_queue_push(&(tx_dma->data_queue), data, length, RT_WAITING_FOREVER);
    if (result == RT_EOK)
    {
        level = rt_hw_interrupt_disable();
        if (tx_dma->activated != RT_TRUE)
        {
            tx_dma->activated = RT_TRUE;
            rt_hw_interrupt_enable(level);

            /* make a DMA transfer */
            serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX);
        }
        else
        {
            rt_hw_interrupt_enable(level);
        }

        return length;
    }
    else
    {
        rt_set_errno(result);
        return 0;
    }
}
#endif /* RT_SERIAL_USING_DMA */

/* RT-Thread 设备接口 */
/*
 * 这个函数初始化设备接口
 */
static rt_err_t rt_serial_init(struct rt_device *dev)
{
    rt_err_t result = RT_EOK;
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;

    /* 初始化内部指针 */
    serial->serial_rx = RT_NULL;
    serial->serial_tx = RT_NULL;

    /* 如果有配置,则调用 */
    if (serial->ops->configure)
        result = serial->ops->configure(serial, &serial->config);

    return result;
}

/*
 * 打开串口设备,根据打开标志分配接收/发送缓冲区,并配置底层硬件
 */
static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag)
{
    rt_uint16_t stream_flag = 0;
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;

    LOG_D("open serial device: 0x%08x with open flag: 0x%04x",
        dev, oflag);
    /* 确认设备是否具有和标志相同的能力 */
    if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX))
        return -RT_EIO;
    if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX))
        return -RT_EIO;
    if ((oflag & RT_DEVICE_FLAG_INT_RX) && !(dev->flag & RT_DEVICE_FLAG_INT_RX))
        return -RT_EIO;
    if ((oflag & RT_DEVICE_FLAG_INT_TX) && !(dev->flag & RT_DEVICE_FLAG_INT_TX))
        return -RT_EIO;

    /* 如果设备支持或者是open时选择了,那就默认打开流标志 */
    if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM))
        stream_flag = RT_DEVICE_FLAG_STREAM;

    /* 保留原始标志 */
    dev->open_flag = oflag & 0xff;

    /* 第一次打开串口,根据oflag来初始化,分配对应的数据结构 */
    if (serial->serial_rx == RT_NULL)
    { 
        if (oflag & RT_DEVICE_FLAG_INT_RX) //中断接收
        {
            struct rt_serial_rx_fifo* rx_fifo;
		//动态申请rxfifo,初始化指针内容,并配置串口设备为接收中断
            rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) +
                serial->config.bufsz);
            RT_ASSERT(rx_fifo != RT_NULL);
            rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
            rt_memset(rx_fifo->buffer, 0, serial->config.bufsz);
            rx_fifo->put_index = 0;
            rx_fifo->get_index = 0;
            rx_fifo->is_full = RT_FALSE;

            serial->serial_rx = rx_fifo;
            dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
            /* configure low level device */
            serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
        }
#ifdef RT_SERIAL_USING_DMA        
        else if (oflag & RT_DEVICE_FLAG_DMA_RX) //DMA接收
        {
            if (serial->config.bufsz == 0) {
                struct rt_serial_rx_dma* rx_dma;
				//用户要求bufsz为0,仅保存激活标志,不分配环形缓冲区。
                rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma));
                RT_ASSERT(rx_dma != RT_NULL);
                rx_dma->activated = RT_FALSE;

                serial->serial_rx = rx_dma;
            } else {//用户要求bufsz不为0,分配环形缓冲区,同上配置好缓冲区
                struct rt_serial_rx_fifo* rx_fifo;

                rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) +
                    serial->config.bufsz);
                RT_ASSERT(rx_fifo != RT_NULL);
                rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
                rt_memset(rx_fifo->buffer, 0, serial->config.bufsz);
                rx_fifo->put_index = 0;
                rx_fifo->get_index = 0;
                rx_fifo->is_full = RT_FALSE;
                serial->serial_rx = rx_fifo;
                /* configure fifo address and length to low level device */
                serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX);
            }
            dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
        }
#endif /* RT_SERIAL_USING_DMA */
        else	//轮询方式,直接置空
        {
            serial->serial_rx = RT_NULL;
        }
    }
    else /* 非第一次打开串口,不重复分配内存,更新内部的打开标志*/
    {
        if (oflag & RT_DEVICE_FLAG_INT_RX)
            dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
#ifdef RT_SERIAL_USING_DMA
        else if (oflag & RT_DEVICE_FLAG_DMA_RX)
            dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
#endif /* RT_SERIAL_USING_DMA */  
    }

    if (serial->serial_tx == RT_NULL)//和接收类似,第一次打开串口,配置发送缓冲区
    {
        if (oflag & RT_DEVICE_FLAG_INT_TX)
        {
            struct rt_serial_tx_fifo *tx_fifo;

            tx_fifo = (struct rt_serial_tx_fifo*) rt_malloc(sizeof(struct rt_serial_tx_fifo));
            RT_ASSERT(tx_fifo != RT_NULL);

            rt_completion_init(&(tx_fifo->completion));
            serial->serial_tx = tx_fifo;

            dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
            /* configure low level device */
            serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
        }
#ifdef RT_SERIAL_USING_DMA
        else if (oflag & RT_DEVICE_FLAG_DMA_TX)
        {
            struct rt_serial_tx_dma* tx_dma;

            tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma));
            RT_ASSERT(tx_dma != RT_NULL);
            tx_dma->activated = RT_FALSE;

            rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL);
            serial->serial_tx = tx_dma;

            dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
            /* configure low level device */
            serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX);
        }
#endif /* RT_SERIAL_USING_DMA */
        else
        {
            serial->serial_tx = RT_NULL;
        }
    }
    else
    {
        if (oflag & RT_DEVICE_FLAG_INT_TX)
            dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
#ifdef RT_SERIAL_USING_DMA
        else if (oflag & RT_DEVICE_FLAG_DMA_TX)
            dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
#endif /* RT_SERIAL_USING_DMA */    
    }

    /* set stream flag */
    dev->open_flag |= stream_flag;

    return RT_EOK;
}


/*关闭串口设备,释放缓冲区,并通知底层禁用中断*/
static rt_err_t rt_serial_close(struct rt_device *dev)
{
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;

    /*如果设备引用计数大于1,则直接返回(因为还有其他引用,不真正关闭) */
    if (dev->ref_count > 1) return RT_EOK;

    if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)//接收中断
    {
        struct rt_serial_rx_fifo* rx_fifo;

        rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
        RT_ASSERT(rx_fifo != RT_NULL);

        rt_free(rx_fifo);
        serial->serial_rx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_INT_RX;
        /* 配置清除设备中断*/
        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void*)RT_DEVICE_FLAG_INT_RX);
    }
#ifdef RT_SERIAL_USING_DMA
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)//DMA接收
    {
        if (serial->config.bufsz == 0) {//无缓冲区,直接使用DMA,则直接关闭DMA
            struct rt_serial_rx_dma* rx_dma;

            rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx;
            RT_ASSERT(rx_dma != RT_NULL);

            rt_free(rx_dma);
        } else {
            struct rt_serial_rx_fifo* rx_fifo;

            rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
            RT_ASSERT(rx_fifo != RT_NULL);

            rt_free(rx_fifo);
        }
        /* configure low level device */
        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_RX);
        serial->serial_rx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_DMA_RX;
    }
#endif /* RT_SERIAL_USING_DMA */
    
    if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)//发送同接收
    {
        struct rt_serial_tx_fifo* tx_fifo;

        tx_fifo = (struct rt_serial_tx_fifo*)serial->serial_tx;
        RT_ASSERT(tx_fifo != RT_NULL);

        rt_free(tx_fifo);
        serial->serial_tx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_INT_TX;
        /* configure low level device */
        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void*)RT_DEVICE_FLAG_INT_TX);
    }
#ifdef RT_SERIAL_USING_DMA
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
    {
        struct rt_serial_tx_dma* tx_dma;

        tx_dma = (struct rt_serial_tx_dma*)serial->serial_tx;
        RT_ASSERT(tx_dma != RT_NULL);

        rt_free(tx_dma);
        serial->serial_tx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_DMA_TX;
    }
#endif /* RT_SERIAL_USING_DMA */
    return RT_EOK;
}


/*根据打开标志选择对应的接收函数:中断模式、DMA 模式或轮询模式。*/
static rt_size_t rt_serial_read(struct rt_device *dev,
                                rt_off_t          pos,
                                void             *buffer,
                                rt_size_t         size)
{
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    if (size == 0) return 0;
	//根据open_flag调用不同的接收函数,返回实际读取的大小
    serial = (struct rt_serial_device *)dev;

    if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
    {
        return _serial_int_rx(serial, (rt_uint8_t *)buffer, size);
    }
#ifdef RT_SERIAL_USING_DMA
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
    {
        return _serial_dma_rx(serial, (rt_uint8_t *)buffer, size);
    }
#endif /* RT_SERIAL_USING_DMA */    

    return _serial_poll_rx(serial, (rt_uint8_t *)buffer, size);
}

/*根据打开标志选择对应的发送函数:中断模式、DMA 模式或轮询模式。*/
static rt_size_t rt_serial_write(struct rt_device *dev,
                                 rt_off_t          pos,
                                 const void       *buffer,
                                 rt_size_t         size)
{
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    if (size == 0) return 0;

    serial = (struct rt_serial_device *)dev;

    if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
    {
        return _serial_int_tx(serial, (const rt_uint8_t *)buffer, size);
    }
#ifdef RT_SERIAL_USING_DMA    
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
    {
        return _serial_dma_tx(serial, (const rt_uint8_t *)buffer, size);
    }
#endif /* RT_SERIAL_USING_DMA */
    else
    {
        return _serial_poll_tx(serial, (const rt_uint8_t *)buffer, size);
    }
}

#ifdef RT_USING_POSIX_TERMIOS
struct speed_baudrate_item
{
    speed_t speed;
    int baudrate;
};

const static struct speed_baudrate_item _tbl[] =
{
    {B2400, BAUD_RATE_2400},
    {B4800, BAUD_RATE_4800},
    {B9600, BAUD_RATE_9600},
    {B19200, BAUD_RATE_19200},
    {B38400, BAUD_RATE_38400},
    {B57600, BAUD_RATE_57600},
    {B115200, BAUD_RATE_115200},
    {B230400, BAUD_RATE_230400},
    {B460800, BAUD_RATE_460800},
    {B921600, BAUD_RATE_921600},
    {B2000000, BAUD_RATE_2000000},
    {B3000000, BAUD_RATE_3000000},
};

static speed_t _get_speed(int baudrate)
{
    int index;

    for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++)
    {
        if (_tbl[index].baudrate == baudrate)
            return _tbl[index].speed;
    }

    return B0;
}

static int _get_baudrate(speed_t speed)
{
    int index;

    for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++)
    {
        if (_tbl[index].speed == speed)
            return _tbl[index].baudrate;
    }

    return 0;
}

/*刷新输入(TCIFLUSH)或输入输出(TCIOFLUSH)缓冲区*/
static void _tc_flush(struct rt_serial_device *serial, int queue)
{
    rt_base_t level;
    int ch = -1;
    struct rt_serial_rx_fifo *rx_fifo = RT_NULL;
    struct rt_device *device = RT_NULL;

    RT_ASSERT(serial != RT_NULL);

    device = &(serial->parent);
    rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx;

    switch(queue)
    {
        case TCIFLUSH:
        case TCIOFLUSH:

            RT_ASSERT(rx_fifo != RT_NULL);

            if((device->open_flag & RT_DEVICE_FLAG_INT_RX) || (device->open_flag & RT_DEVICE_FLAG_DMA_RX)) //中断接收或DMA接收,都会有缓冲区,在这更新
            {
                RT_ASSERT(RT_NULL != rx_fifo);
                level = rt_hw_interrupt_disable();
                rt_memset(rx_fifo->buffer, 0, serial->config.bufsz);
                rx_fifo->put_index = 0;
                rx_fifo->get_index = 0;
                rx_fifo->is_full = RT_FALSE;
                rt_hw_interrupt_enable(level);
            }
            else	//轮询 清空缓冲区内的字符数据
            {
                while (1)
                {
                    ch = serial->ops->getc(serial);
                    if (ch == -1) break;
                }
            }

            break;

         case TCOFLUSH:
            break;
    }

}

#endif

/*处理设备控制命令,包括挂起/恢复、配置串口参数、POSIX termios 命令、FIONREAD 等*/
static rt_err_t rt_serial_control(struct rt_device *dev,
                                  int              cmd,
                                  void             *args)
{
    rt_err_t ret = RT_EOK;
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;

    switch (cmd)
    {
        case RT_DEVICE_CTRL_SUSPEND:
            /* 挂起设备 */
            dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
            break;

        case RT_DEVICE_CTRL_RESUME:
            /* 恢复设备 */
            dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
            break;

        case RT_DEVICE_CTRL_CONFIG:
            if (args)
            {
                struct serial_configure *pconfig = (struct serial_configure *) args;
                if (pconfig->bufsz != serial->config.bufsz && serial->parent.ref_count)
                {
                    /*如果设备已经打开,并且缓冲区大小改变,返回错误*/
                    return RT_EBUSY;
                }
                /* 配置 */
                serial->config = *pconfig;
                if (serial->parent.ref_count)
                {
                    /* 设备已打开并配置*/
                    serial->ops->configure(serial, (struct serial_configure *) args);
                }
            }

            break;

#ifdef RT_USING_POSIX_TERMIOS
        case TCGETA:
            {
                struct termios *tio = (struct termios*)args;
                if (tio == RT_NULL) return -RT_EINVAL;

                tio->c_iflag = 0;
                tio->c_oflag = 0;
                tio->c_lflag = 0;

                /* update oflag for console device */
                if (rt_console_get_device() == dev)
                    tio->c_oflag = OPOST | ONLCR;

                /* set cflag */
                tio->c_cflag = 0;
                if (serial->config.data_bits == DATA_BITS_5)
                    tio->c_cflag = CS5;
                else if (serial->config.data_bits == DATA_BITS_6)
                    tio->c_cflag = CS6;
                else if (serial->config.data_bits == DATA_BITS_7)
                    tio->c_cflag = CS7;
                else if (serial->config.data_bits == DATA_BITS_8)
                    tio->c_cflag = CS8;

                if (serial->config.stop_bits == STOP_BITS_2)
                    tio->c_cflag |= CSTOPB;

                if (serial->config.parity == PARITY_EVEN)
                    tio->c_cflag |= PARENB;
                else if (serial->config.parity == PARITY_ODD)
                    tio->c_cflag |= (PARODD | PARENB);

                cfsetospeed(tio, _get_speed(serial->config.baud_rate));
            }
            break;

        case TCSETAW:
        case TCSETAF:
        case TCSETA:
            {
                int baudrate;
                struct serial_configure config;

                struct termios *tio = (struct termios*)args;
                if (tio == RT_NULL) return -RT_EINVAL;

                config = serial->config;

                baudrate = _get_baudrate(cfgetospeed(tio));
                config.baud_rate = baudrate;

                switch (tio->c_cflag & CSIZE)
                {
                case CS5:
                    config.data_bits = DATA_BITS_5;
                    break;
                case CS6:
                    config.data_bits = DATA_BITS_6;
                    break;
                case CS7:
                    config.data_bits = DATA_BITS_7;
                    break;
                default:
                    config.data_bits = DATA_BITS_8;
                    break;
                }

                if (tio->c_cflag & CSTOPB) config.stop_bits = STOP_BITS_2;
                else config.stop_bits = STOP_BITS_1;

                if (tio->c_cflag & PARENB)
                {
                    if (tio->c_cflag & PARODD) config.parity = PARITY_ODD;
                    else config.parity = PARITY_EVEN;
                }
                else config.parity = PARITY_NONE;

                serial->ops->configure(serial, &config);
            }
            break;
        case TCFLSH:
            {
                int queue = (int)args;

                _tc_flush(serial, queue);
            }

            break;
        case TCXONC:
            break;
#endif
#ifdef RT_USING_POSIX
        case FIONREAD: //返回接收fifo中的可读数据长度
            {
                rt_size_t recved = 0;
                rt_base_t level;

                level = rt_hw_interrupt_disable();
                recved = _serial_fifo_calc_recved_len(serial);
                rt_hw_interrupt_enable(level);

                *(rt_size_t *)args = recved;
            }
            break;
#endif
        default :
            /* control device */
            ret = serial->ops->control(serial, cmd, args);
            break;
    }

    return ret;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops serial_ops = 
{
    rt_serial_init,
    rt_serial_open,
    rt_serial_close,
    rt_serial_read,
    rt_serial_write,
    rt_serial_control
};
#endif

/*
 * 串口设备注册函数,通常是驱动层程序调用
 */
rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
                               const char              *name,
                               rt_uint32_t              flag,
                               void                    *data)
{
    rt_err_t ret;
    struct rt_device *device;
    RT_ASSERT(serial != RT_NULL);

    device = &(serial->parent);

    device->type        = RT_Device_Class_Char;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    device->ops         = &serial_ops;
#else
    device->init        = rt_serial_init;
    device->open        = rt_serial_open;
    device->close       = rt_serial_close;
    device->read        = rt_serial_read;
    device->write       = rt_serial_write;
    device->control     = rt_serial_control;
#endif
    device->user_data   = data;

    /* 注册给 IO设备管理器*/
    ret = rt_device_register(device, name, flag);

#if defined(RT_USING_POSIX)
    /* set fops */
    device->fops        = &_serial_fops;
#endif

    return ret;
}

/* 
	统一的中断处理入口,由底层硬件驱动在中断发生时调用。根据事件类型处理接收、发送完成、DMA 完成等
*/
void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
{
    switch (event & 0xff) //低八位
    {
        case RT_SERIAL_EVENT_RX_IND://接收中断
        {
            int ch = -1;
            rt_base_t level;
            struct rt_serial_rx_fifo* rx_fifo;

            /* interrupt mode receive */
            rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
            RT_ASSERT(rx_fifo != RT_NULL);

            while (1)
            {
                //轮询方式读取出字符,存储在rx_fifo中
                ch = serial->ops->getc(serial);
                if (ch == -1) break;


                /* disable interrupt */
                level = rt_hw_interrupt_disable();

                rx_fifo->buffer[rx_fifo->put_index] = ch;
                //每读取一个put+1
                rx_fifo->put_index += 1;
                //put到最大缓冲区大小,清0,回绕
                if (rx_fifo->put_index >= serial->config.bufsz) rx_fifo->put_index = 0;

                /* put追上了get,说明已经满了 */
                if (rx_fifo->put_index == rx_fifo->get_index)
                {
                    rx_fifo->get_index += 1;
                    rx_fifo->is_full = RT_TRUE;
                    if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0;

                    _serial_check_buffer_size();//输出警告
                }

                /* enable interrupt */
                rt_hw_interrupt_enable(level);
            }

            /* 接收回调 */
            if (serial->parent.rx_indicate != RT_NULL)
            {
                rt_size_t rx_length;

                /* 获取接收长度 */
                level = rt_hw_interrupt_disable();
                rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index):
                    (serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index));
                rt_hw_interrupt_enable(level);

                if (rx_length)
                {
                    serial->parent.rx_indicate(&serial->parent, rx_length);
                }
            }
            break;
        }
        case RT_SERIAL_EVENT_TX_DONE: //发送完成
        {
            struct rt_serial_tx_fifo* tx_fifo;

            tx_fifo = (struct rt_serial_tx_fifo*)serial->serial_tx;
            rt_completion_done(&(tx_fifo->completion));//唤醒因发送阻塞的线程
            break;
        }
#ifdef RT_SERIAL_USING_DMA
        case RT_SERIAL_EVENT_TX_DMADONE://DMA发送完成
        {
            const void *data_ptr;
            rt_size_t data_size;
            const void *last_data_ptr;
            struct rt_serial_tx_dma *tx_dma;

            tx_dma = (struct rt_serial_tx_dma*) serial->serial_tx;

            rt_data_queue_pop(&(tx_dma->data_queue), &last_data_ptr, &data_size, 0);
            //从队列中弹出完成的块,如果存在就启动下一块DMA发送,没有则清除DMA标志
            if (rt_data_queue_peak(&(tx_dma->data_queue), &data_ptr, &data_size) == RT_EOK)
            {
                /* transmit next data node */
                tx_dma->activated = RT_TRUE;
                serial->ops->dma_transmit(serial, (rt_uint8_t *)data_ptr, data_size, RT_SERIAL_DMA_TX);
            }
            else
            {
                tx_dma->activated = RT_FALSE;
            }

            /* 发送回调 */
            if (serial->parent.tx_complete != RT_NULL)
            {
                serial->parent.tx_complete(&serial->parent, (void*)last_data_ptr);
            }
            break;
        }
        case RT_SERIAL_EVENT_RX_DMADONE://接收DMA完成
        {
            int length;
            rt_base_t level;

            /* 从 event 高字节提取本次接收的长度 length */
            length = (event & (~0xff)) >> 8;

            if (serial->config.bufsz == 0)//无fifo,直接接收通知上层,清除DMA激活标志
            {
                struct rt_serial_rx_dma* rx_dma;

                rx_dma = (struct rt_serial_rx_dma*) serial->serial_rx;
                RT_ASSERT(rx_dma != RT_NULL);

                RT_ASSERT(serial->parent.rx_indicate != RT_NULL);
                serial->parent.rx_indicate(&(serial->parent), length);
                rx_dma->activated = RT_FALSE;
            }
            else//有fifo,直接接收通知上层,清除DMA激活标志
            {
                /* disable interrupt */
                level = rt_hw_interrupt_disable();
                /* 更新fifo的put指针 */
                rt_dma_recv_update_put_index(serial, length);
                /* 计算接收的总长度 */
                length = rt_dma_calc_recved_len(serial);
                /* enable interrupt */
                rt_hw_interrupt_enable(level);
                /* 接收回调 */
                if (serial->parent.rx_indicate != RT_NULL)
                {
                    serial->parent.rx_indicate(&(serial->parent), length);
                }
            }
            break;
        }
#endif /* RT_SERIAL_USING_DMA */
    }
}


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐