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