안녕하세요.
GPS 모듈을 테스트하려는데 시리얼 변환기(USB to TTL)가 없어서, 고민한 적이 있을 겁니다.
STM32 보드 하나만 있다면, 두 개의 UART 포트를 연결해 데이터를 PC로 중계해 주는 브리지(Bridge)를 만들 수 있습니다.
대단한 것은 아니고, 그냥 받은 데이터를 전달하는 것이고, DMA를 통해서 간단하게 필요할 때 사용할 수 있을 것 같아서 남겨봅니다.
CPU 부하를 최소화하면서 가변적인 GPS 문장을 실시간으로 전송하는 UART DMA Idle Line Detection를 사용해 보았습니다.
1. UART DMA + IDLE Detection
일반적인 DMA 수신은 버퍼가 꽉 차야 인터럽트가 발생합니다. 하지만 GPS 문장은 길이가 제각각입니다.
STM32의 ReceiveToIdle 기능을 사용하면, 데이터 수신 중 문장이 끝나서 통신 라인이 잠시 쉬는 상태(IDLE)가 되는 순간을 감지해 즉시 콜백을 호출합니다. 덕분에 데이터가 들어오는 즉시 PC로 넘겨줄 수 있습니다.
2. STM32CubeMX (.ioc) 설정
UART1은 GPS와, UART2는 PC(ST-Link 가상 포트)와 연결됩니다.

1) UART1 설정 (GPS 수신 측)
- Baud Rate: 9600 (대부분의 GPS 기본값)
- DMA Settings: UART1_RX 추가
* Mode: Circular (끊임없이 들어오는 데이터를 놓치지 않기 위해 필수)
* Memory Address Increment: 체크
- NVIC Settings: USART1 global interrupt Enabled (콜백 실행을 위해 필수)
2) UART2 설정 (PC 전송 측)
- Baud Rate: 9600 (PC 터미널 설정과 동일하게)
- DMA Settings: UART2_TX 추가
* Mode: Normal (전송 명령이 있을 때만 동작)
* Memory Address Increment: 체크
- NVIC Settings: USART2 global interrupt Enabled
3. 코드 구현 (main.c)
핵심은 수신된 크기(Size)만큼만 정확히 송신하고, 송신부의 상태를 체크하여 시스템이 멈추지 않게 하는 것입니다.
/* USER CODE BEGIN PV */
// volatile을 사용하여 DMA가 메모리에 직접 접근함을 컴파일러에게 알립니다.
volatile uint8_t DMA_data[256];
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
// 수신 시작: UART1에서 데이터를 받아 Idle 상태가 되면 콜백 호출
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *)DMA_data, 256);
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART1)
{
/* 1. UART2 송신 상태 체크 */
/* 무한 루프(while) 대기 대신 if문을 사용하여 전송 가능할 때만 송신합니다.
이전 데이터가 아직 전송 중(BUSY)이라면 이번 데이터는 버리고
시스템이 멈추는(Deadlock) 현상을 방지합니다. */
if (huart2.gState == HAL_UART_STATE_READY)
{
// (uint8_t *) 캐스팅으로 volatile 관련 경고 방지
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)DMA_data, Size);
}
/* 2. 다시 UART1 수신 상태로 재설정 */
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *)DMA_data, 256);
}
}
/* USER CODE END 4 */

4. 주의사항 및 한계점
이 방식은 급하게 데이터를 확인할 때는 매우 유용하지만, 정식 프로젝트에 적용하기엔 몇 가지 한계가 있습니다.
단순히 코드를 짰을 때 시스템이 먹통이 된다면 다음 두 가지를 확인해야 합니다.
1) UART2 인터럽트 미활성화: 송신 인터럽트가 꺼져 있으면 전송 완료 후 huart2.gState가 READY로 바뀌지 않습니다. 이 상태에서 상태 체크 루프를 돌리면 시스템이 무한 대기에 빠집니다.
2) 콜백 내 무한 대기: while문을 써서 전송 완료를 기다리면 GPS 데이터가 쏟아지는 속도를 따라가지 못해 수신 에러가 발생하거나 시스템이 멈춥니다. 반드시 if문으로 상태를 체크하거나 더블 버퍼링을 고려해야 합니다.
5. 더 완벽한 대안: 더블 버퍼링(Double Buffering)
데이터 유실 없는 완벽한 시스템을 원하신다면 더블 버퍼링 구조를 사용할 필요가 있습니다.
방법: 수신 전용 버퍼와 송신용 복사 버퍼를 별도로 운영합니다.
로직: DMA가 수신 버퍼에 데이터를 채우는 동안, CPU는 이미 완료된 데이터를 송신 버퍼로 memcpy 하여 따로 전송합니다. 이렇게 하면 수신과 송신이 서로의 상태에 영향을 주지 않고 독립적으로 동작할 수 있습니다.
위와 같이 간단하게 설정과 코드를 넣으면 시리얼 변환 모듈이 없어도 STM32의 DMA 기능을 잘 활용하면 간단하게 데이터 브리지를 만들 수 있습니다.
감사합니다.
'Embedded > STM32' 카테고리의 다른 글
| [STM32] NUCLEO-STM32C092RC 보드 확인해 보기 (0) | 2026.03.08 |
|---|---|
| [STM32] STM32G4 ADC 정밀도 높이는 캘리브레이션(Calibration) 함수 사용해 보기 (1) | 2026.03.03 |
| [STM32] UART DMA Circular 모드와 Ring Buffer 이용해 수신해 보기 (0) | 2026.02.22 |
| STM32 MCU에서 비트 필드(Bit Field) 구조체 사용해 보기 (1) | 2025.11.12 |
| NUCLEO-C031C6 리뷰 - 저가 저전력 STM32C0 시리즈 보드 (0) | 2025.10.26 |