RT-Thread 设备驱动UART浅析

OS版本:RT-Thread 4.0.0

芯片:STM32F407

RT-Thread的串口驱动框架与Linux相识,分成 I/O设备框架 + 设备底层驱动;

1. serial设备初始化及使用

将配置使能的 uart_obj[ ] 进行设备注册

rtthread_startup --> rt_hw_usart_init() --> rt_hw_serial_register --> rt_device_register

设备注册之后就可使用设备操作方式来使用串口

rt_device_find("uart3") -->  rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX) -->  rt_device_set_rx_indicate(serial, uart_dma_rx_handle)

2. serial设备

设备层 rt_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
};

 

而serial设备 rt_serial_device 为 rt_device 的一个子类

struct rt_serial_device
{
  struct rt_device parent;

  const struct rt_uart_ops *ops;
  struct serial_configure config;

  void *serial_rx;
  void *serial_tx;
};

其中 rt_serial_device 中的 ops 通过 stm32_uart_ops 实现,这样 rt_device、rt_serial_device 和 uart底层就都关联起来了

 

硬件驱动层文件

drv_usart.c

drv_usart.h

stm32f4xx_hal_msp.c

主要内容 ops 实现,中断处理

static const struct rt_uart_ops stm32_uart_ops =
{
    .configure = stm32_configure,  //默认配置
    .control = stm32_control,
    .putc = stm32_putc,
    .getc = stm32_getc,
};

串口硬件初始化  HAL_UART_MspInit  对串口引脚和时钟的初始化 在 stm32f4xx_hal_msp.c 中,通过配置CubeMX生成;

 

3. 驱动分析

serial 的 control 操作 设计成 不能设置中断,即缺少 RT_DEVICE_CTRL_SET_INT 和 RT_DEVICE_CTRL_CLR_INT 操作, 这样可以避免误设置;

同时也是由于串口接收已经设计成三选一方式:中断、DMA、轮询;

说一下DMA,因为这也是我们最常用的串口数据接收处理方式;

RTT的 serial 的DMA接收,采用的 IDLE 中断来控制DMA接收数据的,

在 drv_usart.c 中

static void stm32_dma_config(struct rt_serial_device *serial)
{
    ......   

    /* enable interrupt */
    __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_IDLE);  //使能了空闲中断和DMA中断
    
    /* enable rx irq */
    HAL_NVIC_SetPriority(uart->config->dma_rx->dma_irq, 0, 0);
    HAL_NVIC_EnableIRQ(uart->config->dma_rx->dma_irq);
    
    HAL_NVIC_SetPriority(uart->config->irq_type, 1, 0);
    HAL_NVIC_EnableIRQ(uart->config->irq_type);

    ....
}

在中断处理函数 uart_isr 中 实现了

static void uart_isr(struct rt_serial_device *serial)
{
    ....

    #ifdef RT_SERIAL_USING_DMA
    else if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE) != RESET) &&
             (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE) != RESET))  //IDLE空闲中断
    {
        level = rt_hw_interrupt_disable();
        recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma.handle));
        recv_len = recv_total_index - uart->dma.last_index;
        uart->dma.last_index = recv_total_index;
        rt_hw_interrupt_enable(level);

        if (recv_len)
        {
            rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));
        }
        __HAL_UART_CLEAR_IDLEFLAG(&uart->handle);
    }
#endif

.....  
}
void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
{

        case RT_SERIAL_EVENT_RX_DMADONE:
        {
            int length;
            rt_base_t level;

            /* get DMA rx length */
            length = (event & (~0xff)) >> 8;

            if (serial->config.bufsz == 0)
            {
                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
            {
                /* disable interrupt */
                level = rt_hw_interrupt_disable();
                /* update fifo put index */
                rt_dma_recv_update_put_index(serial, length);
                /* calculate received total length */
                length = rt_dma_calc_recved_len(serial);
                /* enable interrupt */
                rt_hw_interrupt_enable(level);
                /* invoke callback */
                if (serial->parent.rx_indicate != RT_NULL)
                {
                    serial->parent.rx_indicate(&(serial->parent), length);  //应用回调接收处理函数
                }
            }
            break;
        }


}

 

如果进行大数据传输,发现默认缓存(64字节)不够用 可修改 RT_SERIAL_RB_BUFSZ 值

#define RT_SERIAL_CONFIG_DEFAULT           \
{                                          \
    BAUD_RATE_115200, /* 115200 bits/s */  \
    DATA_BITS_8,      /* 8 databits */     \
    STOP_BITS_1,      /* 1 stopbit */      \
    PARITY_NONE,      /* No parity  */     \
    BIT_ORDER_LSB,    /* LSB first sent */ \
    NRZ_NORMAL,       /* Normal mode */    \
    RT_SERIAL_RB_BUFSZ, /* Buffer size */  \
    0                                      \
}

#define RT_SERIAL_RB_BUFSZ              64

 

posted @ 2019-05-24 15:36  silencehuan  阅读(6089)  评论(1编辑  收藏  举报