Embedded/STM32

[STM32] USB-to-TTL 모듈 없이 GPS 데이터 PC로 확인하기 (UART DMA Bridge)

변화의 물결1 2026. 3. 23. 00:34

 

안녕하세요.

 

 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 기능을 잘 활용하면 간단하게 데이터 브리지를 만들 수 있습니다.

 

 

감사합니다.

반응형