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 的一个子类
structrt_serial_device
{structrt_device parent;const struct rt_uart_ops *ops;structserial_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_DMAelse 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)
{caseRT_SERIAL_EVENT_RX_DMADONE:
{intlength;
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