Analysis of RT thread device driving UART

OS version: RT thread 4.0.0

Chip: STM32F407

RT thread's serial driver framework is familiar with Linux, which is divided into I/O device framework + device bottom driver;

1. Initialization and use of serial device

Register the enabled UART obj [] with the device

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

After the device is registered, you can use the device operation mode to use the serial port

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 device

Device layer RT & U device registration and ops implementation

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
};

 

The serial device RT ﹣ serial ﹣ device is a subclass of 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;
};

The ops in RT ﹣ serial ﹣ device is implemented by ﹣ STM32 ﹣ uart ﹣ ops, so that ﹣ RT ﹣ device, RT ﹣ serial ﹣ device and uart bottom layer are all related

 

Hardware driver layer file

drv_usart.c

drv_usart.h

stm32f4xx_hal_msp.c

Main content: ops implementation, interrupt processing

static const struct rt_uart_ops stm32_uart_ops =
{
    .configure = stm32_configure,  //Default configuration
    .control = stm32_control,
    .putc = stm32_putc,
    .getc = stm32_getc,
};

Serial port hardware initialization Hal UART mspinit initialization of serial port pin and clock is generated by configuring CubeMX in stm32f4xx Hal MSP. C;

 

3. Driving analysis

The control operation of serial is designed not to set interrupt, that is, the operation of RT device Ctrl set int and RT device Ctrl CLR int is missing, so as to avoid the wrong setting;

At the same time, because the serial port reception has been designed into one of three ways: interrupt, DMA, polling;

Talk about DMA, because it is also the most commonly used serial data receiving and processing mode;

The serial DMA receiving of RTT adopts IDLE interrupt to control the DMA receiving data,

In DRV? USART. C

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

    /* enable interrupt */
    __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_IDLE);  //Enable idle interrupt and DMA interrupt
    
    /* 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);

    ....
}

It is implemented in the interrupt processing function 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 interrupt
    {
        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);  //Application callback receive processing function
                }
            }
            break;
        }


}

 

If big data is transferred, it is found that the default cache (64 bytes) is not enough to use the modifiable RT ﹣ serial ﹣ RB ﹣ BUFSZ value

#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

Tags: Ruby Linux Big Data

Posted on Tue, 05 Nov 2019 07:20:53 -0800 by rcatal02