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
  • 相关阅读:
    vue.js代码开发最常见的功能集合
    干货|程序员常去的14个顶级开发社区
    17个Web前端开发工程师必看的国外网站
    识别“百度权重”作弊的方法
    问题与对策:CSS的margin塌陷(collapse)
    程序猿,你们这么拼是找不到妹纸的!
    Jquery UI的datepicker插件使用方法
    初识Ajax---简单的Ajax应用实例
    Ajax解决缓存的5种方法
    Jquery+bootstrap实现静态博客主题
  • 原文地址:https://www.cnblogs.com/silencehuan/p/10917650.html
Copyright © 2011-2022 走看看