STM32 HAL_LOCK问题

Vanna ·
更新时间:2024-09-20
· 740 次阅读

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()的源代码:

/** * @brief Receives an amount of data in non blocking mode. * @note When 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. * @param huart Pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @param pData Pointer to data buffer (u8 or u16 data elements). * @param Size Amount 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 == 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

/** * @brief Sends an amount of data in non blocking mode. * @note When 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. * @param huart Pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @param pData Pointer to data buffer (u8 or u16 data elements). * @param Size Amount 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 == 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发生的概率更高.

通讯的问题都是大问题,所以要从根本上去解决问题.


作者:m0_47132384



LOCK

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章