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

/**
  * @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发生的概率更高.

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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值