文章目录
- STM32F4 HAL库串口死锁问题调试记录
- 调试方法
- 结果
- 分析
- 解决
- 方法一:
- 方法二:
STM32F4 HAL库串口死锁问题调试记录
使用方法:通过串口DMA固定周期向外发送数据,同时开启串口DMA接收用于接收其它板卡发来的数据。
问题:在程序运行一段时间后会出现程序不再接收数据的情况,但向外发送数据正常。
分析:一开始认为是触发了串口ORE错误导致的这个问题呢,但奇怪的是并没有触发串口错误中断的回调函数,通过进一步分析排查发现是由__HAL_LOCK()
引起的,而串口ORE错误是在触发这个问题之后出现的。
调试方法
- 通过如下函数开启串口DMA接收
HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);
- 通过如下函数以10ms周期定时向外发送数据
HAL_UART_Transmit_DMA(&JOYSTICK_HUART,(uint8_t*)&joystick_display_frame,sizeof(JoystickDisplayFrame_t));
- 实现串口DMA+IDLE中断接收回调函数
uint32_t huart2_err_cnt = 0;
uint32_t aaa[10] = {0};
HAL_StatusTypeDef huart2_status1;
HAL_StatusTypeDef huart2_status2;uint32_t tx_lock_cnt = 0;
uint32_t tx_unlock_cnt = 0;
uint8_t rx_irq_flag = 0;void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {uart_rx_event_cnt++;huart2_status1 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);if(huart2_status1 != HAL_OK) {huart2_err_cnt++;rx_irq_flag = 1;/* 根据相应的错误类型清除相应的错误标志 */if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_ORE) != RESET) {__HAL_UART_CLEAR_OREFLAG(&JOYSTICK_HUART); //清除ORE溢出错误标志,(读SR然后读DR,这里用于清除串口错误标志)aaa[0]++;} else if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_FE) != RESET) {__HAL_UART_CLEAR_FEFLAG(&JOYSTICK_HUART); //清除FE帧错误标志aaa[1]++;} else if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_PE) != RESET) {__HAL_UART_CLEAR_PEFLAG(&JOYSTICK_HUART); //清除PE奇偶校验错误标志aaa[2]++;} else if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_NE) != RESET) {__HAL_UART_CLEAR_NEFLAG(&JOYSTICK_HUART); //清除NE噪声错误标志aaa[3]++;}huart2_status2 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);}
}
- 实现串口错误中断回调函数
uint32_t uart_error_cnt = 0; //记录是否进入串口错误中断
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {uart_error_cnt++;// 其它处理
}
- 改造HAL库串口DMA发送函数来验证问题
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{uint32_t *tmp;/* Check that a Tx process is not already ongoing */if (huart->gState == HAL_UART_STATE_READY){if ((pData == NULL) || (Size == 0U)){return HAL_ERROR;}/* Process Locked */__HAL_LOCK(huart);if(rx_irq_flag == 0) {tx_lock_cnt++;}// 省略中间的处理/* Process Unlocked */__HAL_UNLOCK(huart);if(rx_irq_flag == 0) {tx_unlock_cnt++;}/* Enable the DMA transfer for transmit request by setting the DMAT bitin the UART CR3 register */ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);return HAL_OK;}else{return HAL_BUSY;}
}
结果
下图为出错时对应的变量值
第一次测试结果:
第二次测试结果:
分析
- 根据上面的结果可以看出:程序在执行发送函数中的
__HAL_LOCK(huart)
串口加锁函数后,__HAL_UNLOCK(huart)
串口解锁函数前触发了串口IDLE中断,此时由于串口处于锁定状态,执行HAL_UARTEx_ReceiveToIdle_DMA()
函数会直接返回HAL_BUSY(即2)的状态,导致出现了错误。 - 根据
aaa[0]
的值可以判定此时尚未出现串口ORE的报错,而usart2_sr
串口状态寄存器的值显示当前出现了串口ORE错误,这个错误是由于后续又继续收到数据导致的。 - 根据
usart2_cr
串口控制寄存器1的值可以看出IDLEIE中断使能标志位现在为0,因而不会继续触发串口IDLE接收中断了,自然就不会继续执行回调函数的内容了
解决
STM32串口是支持全双工工作的,按理说收发可以做到完全互不干扰,但是串口锁定处理的这段时间确实会影响全双工的性能。程序同时高频接收和高频发送时可能会有比较高的概率出现该问题。
方法一:
屏蔽掉HAL库中关于串口锁的函数(不推荐)
方法二:
触发这种情况时添加串口解锁处理
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {uart_rx_event_cnt++;huart2_status1 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);if(huart2_status1 != HAL_OK) {huart2_err_cnt++;JOYSTICK_HUART.RxState = HAL_UART_STATE_READY;JOYSTICK_HUART.Lock = HAL_UNLOCKED; //或者调用 __HAL_UNLOCK(&JOYSTICK_HUART);huart2_status2 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);}
}