STM32 HAL_LOCK問題

25034 ワード

STM32 HAL_LOCK問題
STM32 HAL_LOCK問題
STM 32を使用する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()はデータ割り込みを受信した後に1回しか呼び出されません.HAL_UART_Receive_IT()は最後まで実行されず、次の受信割り込みを起動できませんでした.そのため、今後HAL_UART_RxCpltCallback()は呼び出される機会がありません.
誰がロックしたの?
送信関数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を送信受信するので、受信に影響されます.
簡単な解決策__HAL_LOCK(huart) defineを空の関数にすればいい.簡単で、乱暴だ.
より完璧な方法で、割り込み関数を修正します.
HALの割り込み関数インタフェースを使わないで、自分で1段書きます
/**
 * 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が発生する確率が高い.
通信の問題はみな大きな問題だから、根本的に問題を解決しなければならない.