stm32f091rc 使用 rt-thread时UART串口的阻塞问题

tech2023-02-26  94

1. 问题描述

在项目中用到了两个串口uart4和uart6,在使用uart6发送数据的时候会阻塞,猜想应该是驱动的问题,所以接下来进行分析

2. 分析过程

先分析串口驱动,关于串口驱动这部分的架构不再赘述,我们看下源文件,在发送的地方看到了一处while循环读取串口发送完成寄存器的代码

while (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) == RESET);

在整个发送数据的过程中也只有这处可能阻塞,这块阻塞的话肯定是在别处有清这个中断的动作,还是继续在这个驱动文件继续找线索

我们知道 F091 这个芯片的 UART3~UART8 用的是同一个中断入口函数,我使用的UART4主要是用来接收数据的,在测试的时候也发现关闭UART4就不会引起UART6发送的阻塞,所以从中断这块看起

USART3_8_IRQHandler 这个是我后期加入的rt-thread并没有实现这块的代码,我们接着分析uart_isr这个中断处理函数,在里面发现了有清除UART_FLAG_TC 这个状态寄存器的动作,按道理来说这个中断应该在发送前清除就可以了,而且这个芯片多个串口共用一个中断入口函数,很有可能是别的串口触发中断的时候误清除了这个寄存器,导致UART6在发送的时候阻塞

以下是这个中断处理函数的代码,我们可以简单的分析下 第一个if是判断是否有数据接收 下面的else if 是判断空闲中断的,这块一般是配合DMA来用的 再往下的一个 else if 是判断发送完成状态寄存器的,并在里面做了清除中断的处理,由于UART6和别的串口共用一个串口入口函数,也就是说,在别的串口产生中断的话,也会进来这个函数,那么这时候上面两个if条件UART6肯定不成立,但是正好到第三个条件的时候如果UART6这时候在发送数据的话就有可能会成立,并且在这里把UART_FLAG_TC这个中断清除了,等中断退出后,UART6 读这个已经被清除过的寄存器条件就始终成立,导致了发送阻塞,我的 UART4 接受数据的频率比较多这个问题容易复现出来,如果是数据少的话,这个问题还很难被发现

static void uart_isr(struct rt_serial_device *serial) { struct stm32_uart *uart; #ifdef RT_SERIAL_USING_DMA rt_size_t recv_total_index, recv_len; rt_base_t level; #endif RT_ASSERT(serial != RT_NULL); uart = rt_container_of(serial, struct stm32_uart, serial); /* UART in mode Receiver -------------------------------------------------*/ if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) && (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_RXNE) != RESET)) { rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); } #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)) { level = rt_hw_interrupt_disable(); recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle)); recv_len = recv_total_index - uart->dma_rx.last_index; uart->dma_rx.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); } else if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) != RESET) { if ((serial->parent.open_flag & RT_DEVICE_FLAG_DMA_TX) != 0) { HAL_UART_IRQHandler(&(uart->handle)); } else { //UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC); } } #endif else { if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_ORE) != RESET) { __HAL_UART_CLEAR_OREFLAG(&uart->handle); } if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_NE) != RESET) { __HAL_UART_CLEAR_NEFLAG(&uart->handle); } if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_FE) != RESET) { __HAL_UART_CLEAR_FEFLAG(&uart->handle); } if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_PE) != RESET) { __HAL_UART_CLEAR_PEFLAG(&uart->handle); } #if !defined(SOC_SERIES_STM32L4) && !defined(SOC_SERIES_STM32F7) && !defined(SOC_SERIES_STM32F0) \ && !defined(SOC_SERIES_STM32L0) && !defined(SOC_SERIES_STM32G0) && !defined(SOC_SERIES_STM32H7) \ && !defined(SOC_SERIES_STM32G4) if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_LBD) != RESET) { UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_LBD); } #endif if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_CTS) != RESET) { UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_CTS); } if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TXE) != RESET) { UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TXE); } if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) != RESET) { //UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC); } if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) { UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_RXNE); } } }

3. 问题解决

我们把这块代码注释掉UART6阻塞的问题就不存在了,而且在发送之前都是会清中断的,这块代码也确实没用,所以在使用别人的代码的时候还是要多做测试时,要分析源代码,要不然挖出来的坑只能自己来填

最新回复(0)