【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发生的概率更高.
通讯的问题都是大问题,所以要从根本上去解决问题.