STM32|STM32 HAL_LOCK问题

【STM32|STM32 HAL_LOCK问题】STM32 HAL_LOCK问题
STM32 HAL_LOCK问题 在使用STM32的HAL库开发时候,在使用UART和CAN的使用,偶尔会碰到突然不再接收数据的情况.调试发现,信号有的,但是就是软件不再进入接收中断了.
通过调试,最后定位到问题点在于__HAL_LOCK()这个函数里.
以下用uart为例子,剖析这个问题.
典型的uart接收数据例子 uart配置后,最后调用一下 HAL_UART_Receive_IT()

HAL_UART_Receive_IT(&huart1, (u8 *)RxBuffer, 1);

然后每次收到数据后,HAL_UART_RxCpltCallback()会被调用.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart == &huart1) { uart1ReceivedBuffer=RxBuffer; //处理接收到的数据 HAL_UART_Receive_IT(&huart1,&RxBuffer,1); //启动下一次接收 } }

接收到数据后,读取数据,然后再启动下一次的接收.
逻辑上看,一点问题都没有.
但是实际使用中,特别是uart全双工,数据量大的时候,突然会发现HAL_UART_RxCpltCallback()不再被调用了,然后接收就断了.
为什么出出现这情况?
__HAL_LOCK()做了什么? 先来看看HAL_UART_Receive_IT()的源代码:
/** * @briefReceives an amount of data in non blocking mode. * @noteWhen UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01), *the received data is handled as a set of u16. In this case, Size must indicate the number *of u16 available through pData. * @paramhuart Pointer to a UART_HandleTypeDef structure that contains *the configuration information for the specified UART module. * @parampData Pointer to data buffer (u8 or u16 data elements). * @paramSizeAmount of data elements (u8 or u16) to be received. * @retval HAL status */ HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { /* Check that a Rx process is not already ongoing */ if (huart->RxState == HAL_UART_STATE_READY) { if ((pData =https://www.it610.com/article/= NULL) || (Size == 0U)) { return HAL_ERROR; }/* Process Locked */ __HAL_LOCK(huart); huart->pRxBuffPtr = pData; huart->RxXferSize = Size; huart->RxXferCount = Size; huart->ErrorCode = HAL_UART_ERROR_NONE; huart->RxState = HAL_UART_STATE_BUSY_RX; /* Process Unlocked */ __HAL_UNLOCK(huart); /* Enable the UART Parity Error Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_PE); /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ __HAL_UART_ENABLE_IT(huart, UART_IT_ERR); /* Enable the UART Data Register not empty Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); return HAL_OK; } else { return HAL_BUSY; } }

调试发现,程序运行到__HAL_UNLOCK(huart); 之后就退出了HAL_UART_RxCpltCallback()!
所以我们看看__HAL_UNLOCK(huart); 里面到底干了什么:
#if (USE_RTOS == 1U) /* Reserved for future use */ #error "USE_RTOS should be 0 in the current HAL release" #else #define __HAL_LOCK(__HANDLE__)\ do{\ if((__HANDLE__)->Lock == HAL_LOCKED)\ {\ return HAL_BUSY; \ }\ else\ {\ (__HANDLE__)->Lock = HAL_LOCKED; \ }\ }while (0U) #define __HAL_UNLOCK(__HANDLE__)\ do{\ (__HANDLE__)->Lock = HAL_UNLOCKED; \ }while (0U) #endif /* USE_RTOS */

单步跟踪发现return HAL_BUSY; 这句代码被运行了, HAL_UART_Receive_IT()并没有执行到后面就直接退出了!!!
所有的异常就是因为这个我们意料之外的退出造成的.
HAL_UART_RxCpltCallback()只会在接收到数据中断后被调用一次,HAL_UART_Receive_IT()没有执行到最后,没能启动下一次接收中断,所以往后HAL_UART_RxCpltCallback()都没机会再被调用!
谁locked了huart? 发送函数locked了huart
/** * @briefSends an amount of data in non blocking mode. * @noteWhen UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01), *the sent data is handled as a set of u16. In this case, Size must indicate the number *of u16 provided through pData. * @paramhuart Pointer to a UART_HandleTypeDef structure that contains *the configuration information for the specified UART module. * @parampData Pointer to data buffer (u8 or u16 data elements). * @paramSizeAmount of data elements (u8 or u16) to be sent * @retval HAL status */ HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { /* Check that a Tx process is not already ongoing */ if (huart->gState == HAL_UART_STATE_READY) { if ((pData =https://www.it610.com/article/= NULL) || (Size == 0U)) { return HAL_ERROR; }/* Process Locked */ __HAL_LOCK(huart); huart->pTxBuffPtr = pData; huart->TxXferSize = Size; huart->TxXferCount = Size; huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; /* Process Unlocked */ __HAL_UNLOCK(huart); /* Enable the UART Transmit data register empty Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_TXE); return HAL_OK; } else { return HAL_BUSY; } }

发送数据期间,HAL_UART_Transmit_IT()__HAL_LOCK(huart);
而发送和接收公用同一个huart,所以,接收被影响了.
全双工的uart,硬生生的被HAL库弄成了半双工!
简单的解决办法 把__HAL_LOCK(huart)define成空函数就行了.
简单,粗暴.
更完美的方法,修改中断函数 不用HAL的中断函数接口,自己写一段
/** * Uart common interrupt process. This need add to uart ISR. * * @param serial serial device */ static void uart_isr(UART_HandleTypeDef *UartHandle,uint8_t type) { uint32_t isrflags= READ_REG(UartHandle->Instance->SR); uint32_t cr1its= READ_REG(UartHandle->Instance->CR1); uint8_t rec; UartHandle->pRxBuffPtr = &gUartValue[type]; if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)) { rec=(uint8_t)(UartHandle->Instance->DR & (uint8_t)0x00FF); ; rt_hw_serial_isr(UartHandle,rec); //新的接收函数,也可以调用hal的接收回调函数. __HAL_UART_CLEAR_FLAG(UartHandle, UART_FLAG_RXNE); //关键!!!!这里启动了下一次接收 } /* UART in mode Transmitter ------------------------------------------------*/ if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)) { /* Check that a Tx process is ongoing */ if (UartHandle->gState == HAL_UART_STATE_BUSY_TX) { uint16_t *tmp; if (UartHandle->Init.WordLength == UART_WORDLENGTH_9B) { tmp = (uint16_t *) UartHandle->pTxBuffPtr; UartHandle->Instance->DR = (uint16_t)(*tmp & (uint16_t)0x01FF); if (UartHandle->Init.Parity == UART_PARITY_NONE) { UartHandle->pTxBuffPtr += 2U; } else { UartHandle->pTxBuffPtr += 1U; } } else { UartHandle->Instance->DR = (uint8_t)(*UartHandle->pTxBuffPtr++ & (uint8_t)0x00FF); }if (--UartHandle->TxXferCount == 0U) { /* Disable the UART Transmit Complete Interrupt */ __HAL_UART_DISABLE_IT(UartHandle, UART_IT_TXE); /* Enable the UART Transmit Complete Interrupt */ __HAL_UART_ENABLE_IT(UartHandle, UART_IT_TC); } } } /* UART in mode Transmitter end --------------------------------------------*/ if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET)) { /* Disable the UART Transmit Complete Interrupt */ __HAL_UART_DISABLE_IT(UartHandle, UART_IT_TC); /* Tx process is ended, restore huart->gState to Ready */ UartHandle->gState = HAL_UART_STATE_READY; /*这里可以添加发送完成中断回调函数*/ } }

其实这里面绝大部分代码和void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)一样的.
这个函数就是要取代HAL_UART_IRQHandler,然后再自己实现接收和发送.
这里解决了,其他问题都好解决了!
中断入口里面修改成:
void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */ uart_isr(&huart1,1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ }

这才是一般习惯的uart工作模式!
CAN也有这一样的问题,而且CAN发生的概率更高.
通讯的问题都是大问题,所以要从根本上去解决问题.

    推荐阅读