1. 들어가면서
지난 포스팅에서는 UART TX에 대해 살펴보았습니다.
이번에는 포스팅은 RX 처리 방식과 지난 TX 처리 방식과 조합하여 Echo기능을 보여드리겠습니다.
개발자라면 누구나 그렇듯 RX 데이터는 Polling 방식으로 처리하는 경우나 드물다고 생각합니다.
첫 번째 이유는 다른 Logic을 처리하다 보면 놓치는 데이터가 다분할 것이고,
두 번째는 데이터가 들어올 때까지 마냥 기다리고 있다면 CPU 자원의 낭비이기 때문입니다.
그래서 가장 일반적인 형태는 Interrupt로 수신된 데이터를 Queue에다가 전부 저장하고,
이후에 Parsing하여 데이터를 처리하는 것이라고 생각합니다.
하지만 Stream 데이터가 계속적으로 들어오는 상황에서는 Interrupt + Queue 방식은 적합하지 않습니다.
그 이유는 계속 ISR 진입이 발생하고, 계속적인 Over Head가 발생하게 됩니다.
그래서 최근에는 Interrupt + DMA 기법들을 많이 사용합니다만, 저는 실 구현 상황에서는 사용해보지는 않았습니다....
(부끄럽네요)
이러한 RX 처리 기법에 대한 부분은 다른 포스팅에서 상세히 다뤄보고, 이번 포스팅은 단순한 RX API 활용에 대해서만 알아보겠습니다.
2. 시작합니다.
지난번 생성한 UART 프로젝트를 계속 활용하는 것이기에 동일한 내용에 대해서는 언급하지 않겠습니다.
1. 코드 구현
UART RX를 처리하기 위해 두 가지 API가 존재합니다.
-
UART_ReceiveDataBlocking
-
UART_ReceiveData
TX와 마찬가지로 두 API 모두 Interrupt에서 처리가 됩니다. (내부 API를 살펴보면, 동일한 API를 호출하고 있습니다.)
차이점은 ReceiveDataBlocking은 Timout이 발생하거나, 지정된 길이만큼의 Data를 수신하였을 때 함수가 종료되고,
ReceiveData는 Blocking은 되지 않습니다.
마찬가지로, ReceiveData 사용 시 이전에 ReceiveData 처리가 완료되지 않은 상황에서 함수를 재 호출하면 동작에 문제가 발생하게 됩니다.
-
UART_ReceiveDataBloking을 활용한 Echo 기능 (Polling 방식)
Blocking API를 사용하여, 1초 이내 1Byte를 수신하면 같은 데이터를 전송하고 수신하지 못하면 Timeout이 발생하도록 설정하였습니다.
결과는 다음과 같습니다.
정상적으로 Echo가 되는 것을 볼 수 있으며, 1초 내 데이터가 수신되지 않으면 TIMEOUT이 발생하는 것을 볼 수 있습니다.
왼쪽의 시간표시는 제가 터미널 상 TimeStamp를 찍도록 설정하였습니다.
-
UART_ReceiveData를 활용한 Echo 기능 (Interrupt 방식)
(제목 옆에다가 Interrupt 방식을 쓰면서도 고민을 했습니다... 둘 다 Interrupt 베이스로 처리가 되는데... Async/Sync가 더 맞는 거 같은데.. 좀 더 직관적인 표현을 위해 Polling Interrupt로 유지하겠습니다. API 내부를 자세하게 살펴보시면 어떤 의미인지 이해가 되실 거라고 생각합니다.)
Interrupt 처리를 위해서는 RxCallback 등록이 선행되어야 합니다.
Timer에서는 RxCallback 등록 API가 존재하였는데.. UART는 별도로 존재하지 않아, Processor Expert를 통해 등록하여야 합니다.
uart_pal1을 더블 클릭하여, Processor Expert 창을 켭니다.
아래 사진처럼 RxCallback로 사용할 함수 이름을 작성합니다. (해당 이름은 사용자가 임의로 지정합니다.)
설정이 변경되었으니, Code를 재생성합니다.
이후 UART_ReceiveData API를 통해 1byte 데이터 수신을 지정합니다.
다음으로는 Processor Expert에서 지정한 이름으로, CallBack 함수를 정의합니다.
해당 함수는 UART_ReceiveData에서 지정한 크기만큼의 데이터가 수신되었을 때도 호출되지만 다른 Error 상황에서도 호출됩니다.
그래서 각 상황에 따라 개별 로직이 필요하며 현재는 데이터 수신 Event 처리를 위한 로직만 작성하였습니다.
해당 callback에서 가장 중요한 부분은 UART_SetRxBuffer입니다. 데이터를 계속 받고 싶다면 반드시 호출해야 하는 함수입니다.
그 이유 설명하기 전 아래 Call Stack 구조를 먼저 보겠습니다.
정의한 callback 함수는 LPUART_DRV_RxIrqHandler에서 호출됩니다.
LPUART_DRV_RxIrqHandler를 보면, 빨간 네모 박스 부분이 먼저 호출됩니다.
즉, 해당 라인을 통해 callback 함수도 호출됩니다.
만약 callback 함수 내에서 UART_SetRxBuffer 함수를 호출하지 않으면 노란 네모 박스 부분이 동작하게 되고 모든 Rx 처리에 대한 동작을 중단하도록 되어있습니다.
결과는 아래와 같습니다.
3. 나갑니다.
간단하게 Rx 처리 방식에 대해 알아보았습니다.
저도 맨 처음 UART RX를 쓸 때, SetRxBuffer API에 대해 정확하게 이해하지 못하고 다른 방식으로 처리하다가 내부 API를 자세하게 보면서 잘 못쓰고 있다는 것은 알게 되었습니다.
저같이 실수하시는 분이 없기를 바라며 도움이 되는 포스팅이었으면 좋겠습니다.
다음 포스팅에서는 마찬가지로 Register 설정에 대해 좀 더 자세히 살펴보도록 하겠습니다.
'NXP' 카테고리의 다른 글
07. S32K146 - UART (1/3) using PAL TX (0) | 2021.01.01 |
---|---|
06. S32K146 - Hello World + Clocks (3/3) with LPIT (작성중) (1) | 2021.01.01 |
05. S32K146 - Hello World + Clocks (2/3) with LPIT (3) | 2020.12.20 |
04. S32K146 - Hello World + Clocks (1/3) (1) | 2020.12.20 |
03. S32K146 - Clock 설정 (2) | 2020.09.08 |