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