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 해준다.
'Firmware > stm32' 카테고리의 다른 글
STM32 IAP (Ymodem 프로토콜) Bootloader - 1 (1) | 2020.06.29 |
---|---|
FSMC HAL_SRAM_Write_16b Bug 문제 (0) | 2020.06.13 |
OV5642_LWIP_RTOS Project -3(최종 구현 및 디버깅) (0) | 2020.05.31 |
OV5642_LWIP_RTOS Project -2(Camera API) (0) | 2020.04.24 |
OV5642_LWIP_RTOS Project -2(Camera Interface 구현 ) (1) | 2020.04.23 |