zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Get distinct count of rows in the DataSet
    单引号双引号的html转义符
    PETS Public English Test System
    Code 39 basics (39条形码原理)
    Index was outside the bounds of the array ,LocalReport.Render
    Thread was being aborted Errors
    Reportviewer Error: ASP.NET session has expired
    ReportDataSource 值不在预期的范围内
    .NET/FCL 2.0在Serialization方面的增强
    Perl像C一样强大,像awk、sed等脚本描述语言一样方便。
  • 原文地址:https://www.cnblogs.com/silencehuan/p/10917650.html
Copyright © 2011-2022 走看看