본문 바로가기

Firmware/stm32

stm32 uart interrupt 멈추는 현상( RXNEIE disable, overrun)

UART RX Interrupt를 받기 위한 코드는 다음과 같다

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_UART_Transmit(&huart1, &rx_data, rx_data, 100);
  }
  /* USER CODE END 3 */
}

Main에서 HAL_UART_Receive_IT 함수를 호출하여 Interrupt가 Enable이 된다.

 

그리고 CallBack 함수에서는 다음과 같이 처리후 다시 HAL_UART_Receive_IT를 호출하여 enable 해준다.

 

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		/*
		 *  데이터 들어 왔을 시  코드 처리
		 */

	}

	HAL_UART_Receive_IT(huart, &rx_data, 1); //문제 발생하는 코드
}

 

문제는 Callback 함수에서 HAL_UART_Receive_IT를 호출 하면서 Disable 되어 있던  RXNEIE레지스터를 enable 해야 하지만 enable 되지 않고 return 된다.

 

원인은 main에서 호출하는 HAL_UART_Transmit 내부의 _HAL_LOCK(huart) 에서 LOCK을 걸었을 때 Receive Interrupt가 발생하면UART Register를 Control 하지 못하고  return 하게 발생한 것이다.

 

 즉 아래와 같은 상황이면 동작 중간에 UART Interrupt가 꺼진다.

 

HAL_UART_Transmit을 실행하는 도중에 Interrupt가 발생

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  uint32_t tickstart = 0U;

  /* 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);
    
    /*
     * __HAL_LOCK 코드가 실행 되고 아래 코드가 실행 되는 상황에서 Interrupt가 발생.
     */

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Init tickstart for timeout managment */
    tickstart = HAL_GetTick();


 

그런데 위 코드와 같이 하필 HAL_LOCK(UART) 가 걸려 있을 때 RX interrupt가 발생 .

 

CallBack 함수 실행 후 마지막 코드인 HAL_UART_Receive_IT를 실행

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);  //__HAL_LOCK으로 인해 더이상 진입하지 못하고 HAL_BUSY return 한다.
    

}

#define __HAL_LOCK(__HANDLE__)                                           \
                                do{                                        \
                                    if((__HANDLE__)->Lock == HAL_LOCKED)   \
                                    {                                      \
                                       return HAL_BUSY;                    \
                                    }                                      \
                                    else                                   \
                                    {                                      \
                                       (__HANDLE__)->Lock = HAL_LOCKED;    \
                                    }                                      \
                                  }while (0U)
                                  
                        

HAL_UART_Receive_IT를 실행하였지만 함수 안에 있는 HAL_LOCK(huart)으로 인해 더이상 진입하지 못하고 

return HAL_BUSY가 실행 된다.

 

위 문제를 해결 하기 위해선 HAL_UART_Transmit 코드를 호출 할 때 Interrupt가 발생하지 않도록 해줘야한다.

 

Main 함수 일부 

  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  

    /* USER CODE BEGIN 3 */
	  HAL_NVIC_DisableIRQ(USART1_IRQn); //Rx Callback 함수 Disable
	  HAL_UART_Transmit(&huart1, &rx_data, rx_data, 100);
	  HAL_NVIC_EnableIRQ(USART1_IRQn);  //Rx callback 함수 enable
  }
  /*

 

 

위와 같이 DeadLock 현상을 피하기 위해 Transmit에 진입하기 전 UART IRQ를 Disable 해주고 

Transmit이 끝나면 다시 Enable 해준다.