마이크로컨트롤러(MCU)를 사용하다 보면, 서로 다른 장치 간에 데이터를 주고받는 일이 자주 필요합니다. 이를 위한 대표적인 방식으로 UART, I²C, 그리고 SPI (Serial Peripheral Interface)가 있습니다.
이 중 SPI는 높은 속도와 단순한 구조 덕분에 널리 사용됩니다. 일반적으로는 MCU가 Master, 전용 IC가 Slave로 동작하며, Master가 클록 신호를 생성하고 Slave는 해당 클록에 맞춰 데이터를 송수신합니다. 전용 IC는 내부 하드웨어 로직을 통해 안정적으로 SPI를 처리할 수 있습니다.
하지만 두 개의 MCU가 SPI를 통해 직접 통신하려고 하면 이야기가 달라집니다. 각 MCU는 SPI 통신뿐만 아니라 자체적으로 수행해야 할 고유 작업이 있기 때문에, 기존의 SPI 메커니즘만으로는 안정적이고 유연한 데이터 교환을 보장하기 어렵습니다.
이번 글에서는 이러한 문제를 어떻게 해결했는지, 그리고 우리가 실제로 구현한 방법을 소개합니다.
UART는 왜 안될까?
“그냥 UART 쓰면 되지 않을까?”라고 생각할 수 있습니다.
물론 UART는 간단하고 널리 지원되지만, 우리의 경우 각 장치의 UART 포트가 모두 이미 다른 기능에 할당되어 있었습니다. 따라서 UART는 선택지가 될 수 없었고, 결국 SPI를 통한 MCU 간 통신을 설계할 수밖에 없었습니다.
안정적인 SPI 통신을 위한 조건
새로운 방식을 설계할 때 반드시 충족해야 하는 요구사항은 다음과 같았습니다:
- 각 장치의 고유 작업에 방해가 없어야 한다.
- 블로킹 없는(non-blocking) 통신이어야 한다. (특정 데이터를 기다리며 멈춰 있으면 안 됨)
- 가능한 한 빠른 속도로 데이터를 주고받아야 한다.
- 데이터 손실이 발생해서는 안 된다.
우리가 선택한 방식
이 요구사항을 충족하기 위해 다음과 같은 구조를 채택했습니다:
- 2개의 SPI 포트를 사용한다.
- 각 SPI는 Half Duplex 모드로 설정한다.
- Master 포트 → 송신 전용
- Slave 포트 → 수신 전용
- 데이터 전송은 DMA(Direct Memory Access)를 활용하여 CPU 개입을 최소화한다.
이 방식은 MCU 리소스를 절약하면서도 빠르고 안정적인 데이터 교환을 가능하게 합니다.
상세 구현
아래 그림은 실제 구현 개념도를 보여줍니다.

우리는 통신의 안정성을 위해 각 SPI 채널마다 NSS와 nCS 두 개의 제어 신호를 사용했습니다.
- NSS(하드웨어 제어): 장치 간 SPI 타이밍 동기를 맞추는 용도
- nCS(소프트웨어 제어): DMA 방식에서 데이터 프레임의 시작과 끝을 구분하는 용도
Master 측
- nCS를 GPIO 출력으로 설정
- 데이터 전송 전에 nCS를 Low로 내림
- 전송 완료 후 (DMA Tx Complete 인터럽트에서) nCS를 High로 복귀
Slave 측
- nCS를 GPIO 입력 + EXTI 인터럽트로 설정
- Falling Edge 발생 시 → Rx Start 플래그 설정
- Rising Edge 발생 시 → Rx Complete로 인식하고, DMA 버퍼에 저장된 데이터를 애플리케이션 버퍼로 복사
이러한 구조 덕분에 통신은 블로킹 없이, 동기화된 상태로 빠르고 안정적으로 이루어질 수 있습니다.
테스트 결과
실제 테스트 환경에서는 상당히 많은 데이터를 동시에 주고받아도 문제없이 동작했습니다. 구체적으로는:
- UART 포트 7개
- GPIO 제어 신호 24개
- 센서 데이터 18개 스트림
을 동시에 처리했음에도 불구하고 실시간으로 안정적인 데이터 전송이 가능했습니다. 데이터 손실도 전혀 발생하지 않았습니다.
결론
두 개의 MCU가 SPI로 통신하는 것은 단순히 MCU와 전용 IC 간 통신을 구현하는 것과는 다른 도전 과제를 안고 있습니다. 하지만, 듀얼 SPI 채널, DMA 기반 데이터 전송, 그리고 추가적인 제어 신호 설계를 통해, 우리는 빠르고, 비블로킹이며, 데이터 손실 없는 MCU 간 통신을 구현할 수 있었습니다.
이 방식은 다수의 데이터 스트림을 동시에 처리해야 하는 복잡한 임베디드 시스템에서도 안정적으로 사용할 수 있는 방법임을 검증했습니다.
0개의 댓글