개요
통신 구현시 프로토콜의 패킷 구성이 명령어 별로 사이즈가 다른 경우, 정상적으로 데이터를 수신받기 위해서는 추가적인 처리가 필요하다.
HAL Library에서 기본적으로 제공하는 HAL_USART_Receive_IT or DMA() 메서드의 동작은 설정한 버퍼 사이즈만큼 데이터가 수신되면 인터럽트가 발생하는 방식이다.
따라서 해당 메서드를 사용하여 가변길이의 데이터를 파싱하기 위해선 반드시 1byte씩 데이터를 수신받아 내부 추가 처리를해야한다.
HAL_UARTEX_ReceivetoIdle_IT or DMA() 메서드를 사용하면 데이터를 수신받다가 IDLE 상태가 되면 인터럽트가 발생하는 방식이다.
따라서 해당 메서드와 링버퍼를 사용하면 가변 패킷을 수신받아도 추가 파싱 처리 없이 데이터를 수신받을 수 있다.
아래는 위의 내용에 대한 설명이다.
기존 HAL_UART_Receive_DMA() 방식
HAL_UART_Receive_DMA(&huart1, RxBuf, RxBuf_SIZE);
- 이 방식에서는 RxBuf_SIZE만큼 데이터가 채워져야 인터럽트가 발생합니다.
- 만약 데이터가 1~2바이트씩만 들어오면, RxBuf_SIZE가 다 차기 전까지는 인터럽트가 발생하지 않음.
- 즉, **"버퍼가 다 찼을 때만 인터럽트 발생"**하는 방식.
HAL_UARTEx_ReceiveToIdle_DMA() 방식
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RxBuf, RxBuf_SIZE);
- 이 방식에서는 데이터가 수신되다가 "Idle 상태"가 감지되면 즉시 인터럽트가 발생함.
- 즉, RxBuf_SIZE만큼 채워지지 않아도 연속된 데이터 수신이 멈추면(Idle 상태 감지) 바로 인터럽트 호출.
- **"데이터가 들어오다가 멈추면 인터럽트 발생"**하는 방식.
Idle 상태(Idle Line Detection)란?
UART에서 Idle 상태란 연속된 데이터 수신이 멈춘 경우를 의미해요.
- MCU는 UART 라인에서 일정 시간 동안 추가 데이터가 들어오지 않으면 "Idle"로 판단.
- **즉, "더 이상 데이터가 안 들어온다!"**라고 감지되면 HAL_UARTEx_RxEventCallback()이 호출됨.
언제 HAL_UARTEx_ReceiveToIdle_DMA()를 사용하면 좋을까?
이 방식을 사용하면 가변 길이 패킷을 처리하는 경우에 매우 유용합니다.
- 예를 들어, 패킷이 길이가 일정하지 않고, 데이터가 중간중간 멈추면서 들어올 때
- HAL_UART_Receive_DMA()는 고정 크기의 데이터만 받을 수 있기 때문에 비효율적
- 반면, HAL_UARTEx_ReceiveToIdle_DMA()는 데이터가 끊기면 자동으로 처리할 수 있어 더 유연함.
HAL_UARTEx_ReceiveToIdle_DMA()의 동작 흐름
- DMA를 통해 UART 데이터를 수신하다가...
- 데이터가 연속적으로 들어오다가 멈춘 순간(Idle 상태 감지)
- 즉시 HAL_UARTEx_RxEventCallback() 인터럽트 발생
- 콜백 내에서 현재까지 수신된 데이터 크기(Size)를 기반으로 데이터 처리
- 다시 HAL_UARTEx_ReceiveToIdle_DMA()를 호출하여 다음 데이터를 받을 준비
기존 방식과 Idle 방식 비교
방식동작 방식언제 인터럽트 발생?장점단점
방식 | 동작 방식 | 인터럽트 발생 조건 | 장점 | 단점 |
HAL_UART_Receive_DMA() | 고정된 크기만큼 수신 | RxBuf_SIZE만큼 다 채워졌을 때 | 일정한 데이터 블록 처리에 적합 | 가변 길이 데이터 처리 어려움 |
HAL_UARTEx_ReceiveToIdle_DMA() | Idle 감지 시 즉시 인터럽트 | 데이터가 끊겼을 때(Idle) | 가변 길이 데이터 처리 가능 | 너무 짧은 데이터 수신 시 자주 인터럽트 발생 가능 |
사용 예제 코드
#define RxBuf_SIZE 10
#define MainBuf_SIZE 20
uint8_t RxBuf[RxBuf_SIZE];
uint8_t MainBuf[MainBuf_SIZE];
int main()
{
....
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RxBuf, RxBuf_SIZE);
}
uint16_t curPos=0;
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if( huart->Instance==USART1)
{
if(curPos+Size>MainBuf_SIZE)
{
int numofdata = MainBuf_SIZE - curPos;
memcpy((uint8_t *)MainBuf+curPos,RxBuf,numofdata);
curPos =0;
memcpy((uint8_t *)MainBuf,(uint8_t *)RxBuf + numofdata,(Size-numofdata));
curPos = Size - numofdata;
}
else
{
memcpy((uint8_t *)MainBuf+curPos,RxBuf,Size);
curPos=(curPos+Size)%MainBuf_SIZE;
}
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RxBuf, RxBuf_SIZE);
}
}
결론
✔ UART에서 가변 길이 패킷을 처리해야 한다면 HAL_UARTEx_ReceiveToIdle_DMA()가 훨씬 효율적
✔ 데이터가 수신되다가 멈추면 자동으로 인터럽트가 발생하여 즉시 처리 가능
✔ 기존 HAL_UART_Receive_DMA()는 고정 크기 데이터 수신에는 좋지만, 가변 패킷 처리에는 비효율적
'SW 개발 공부 > STM32' 카테고리의 다른 글
[STM32] Vector Table & SP & PC (0) | 2025.02.28 |
---|---|
[Stm32 Project]BootLoader 구현 (3) | 2024.11.13 |
[stm32 chip 메모리구조#2] 메모리 디버깅 방법 (1) | 2024.05.27 |
ISR 내부 루틴의 간소화 필요성 (1) | 2024.02.14 |
stm32에서 printf() 함수 사용하기 (0) | 2024.02.07 |