이전 장에서 살펴본 I2C는 센서를 여러 개 연결하기에 편리한 통신 방식이었다. 선 두 가닥만으로 여러 장치를 연결할 수 있고, 각 장치는 주소로 구분된다. 그래서 IMU, 온도 센서, OLED 디스플레이처럼 비교적 작은 데이터를 주고받는 장치에 잘 어울린다.
하지만 로봇을 만들다 보면 더 빠른 길이 필요해진다. 컬러 디스플레이에 화면을 계속 그려야 하거나, SD 카드에 주행 로그를 저장해야 하거나, 빠르게 변하는 센서 데이터를 놓치지 않고 읽어야 할 때가 있다. 이때 자주 등장하는 통신 방식이 바로 SPI다.
SPI는 I2C보다 선을 더 많이 쓰는 대신, 구조가 단순하고 빠른 데이터 전송에 유리하다. 로봇 입장에서 보면 SPI는 좁은 골목길이 아니라 여러 차선이 있는 직선 도로에 가깝다. 배선은 조금 늘어나지만, 데이터가 빠르게 지나갈 수 있다.

SPI는 언제 필요할까
센서 하나에서 숫자 몇 개만 읽는다면 I2C로도 충분한 경우가 많다. 예를 들어 IMU에서 가속도와 자이로 값을 읽거나, 작은 OLED에 짧은 문자를 표시하는 정도라면 I2C가 간단하고 편하다.
반면 화면 전체를 자주 갱신해야 하는 컬러 TFT 디스플레이를 생각해 보자. 작은 글자 하나가 아니라 수많은 픽셀의 색상 정보를 계속 보내야 한다. 이때 통신이 느리면 화면이 늦게 따라오고, 로봇 상태 표시도 답답해진다. SD 카드도 마찬가지다. 센서값, 엔코더 값, 배터리 전압, 모터 출력값을 계속 저장하려면 짧은 시간 안에 많은 데이터를 보내야 한다.
SPI는 이런 상황에서 빛을 발한다. 로봇에서는 다음과 같은 장치에서 자주 만난다.
컬러 TFT 디스플레이나 고속 OLED 디스플레이
SD 카드 모듈과 플래시 메모리
고속 ADC, DAC, 일부 IMU와 무선 모듈
라즈베리파이 또는 ESP32에 연결하는 보조 디스플레이와 센서 모듈
여기서 중요한 감각은 이것이다. SPI는 모든 장치에 무조건 써야 하는 통신이 아니라, 빠른 데이터 이동이 필요할 때 선택하는 통신이다.
SPI의 기본 구조
SPI는 보통 하나의 마스터와 하나 이상의 슬레이브로 구성된다. 최근 문서에서는 마스터를 컨트롤러, 슬레이브를 주변장치라고 부르기도 하지만, 많은 보드 설명서와 예제 코드에는 여전히 마스터·슬레이브라는 표현이 남아 있다. 이 책에서는 독자가 실제 자료를 읽을 수 있도록 두 표현을 함께 이해하는 방향으로 설명하겠다.
로봇에서 마스터 역할은 대개 아두이노, ESP32, 라즈베리파이 같은 제어 보드가 맡는다. 슬레이브 역할은 디스플레이, SD 카드 모듈, 센서 모듈 같은 주변 장치가 맡는다. 마스터는 통신의 시작과 속도를 결정하고, 슬레이브는 선택되었을 때만 응답한다.
SPI의 대표 신호선은 네 가지다.
MOSI: Master Out, Slave In의 줄임말이다. 마스터에서 슬레이브로 데이터가 나가는 선이다.MISO: Master In, Slave Out의 줄임말이다. 슬레이브에서 마스터로 데이터가 들어오는 선이다.SCK: Serial Clock의 줄임말이다. 마스터가 만드는 박자 신호다.CS: Chip Select의 줄임말이다. 어떤 슬레이브와 통신할지 고르는 선택 신호다.
물론 이 네 가닥만 있으면 되는 것은 아니다. 모든 전자 회로가 그렇듯 전원선과 GND도 필요하다. 특히 로봇에서는 여러 보드와 모듈이 함께 움직이므로 공통 GND를 잊으면 안 된다. SPI 신호는 전압을 기준으로 0과 1을 판단하기 때문에, 기준점인 GND가 서로 맞지 않으면 통신이 엉뚱하게 동작할 수 있다.
MOSI, MISO, SCK, CS를 하나씩 이해하기
SPI를 처음 보면 선 이름부터 부담스럽다. 하지만 각 선의 역할을 사람 사이의 대화로 바꾸어 생각하면 훨씬 쉽다.
SCK는 박자를 맞춰 주는 메트로놈이다. SPI에서는 마스터가 클록을 만든다. 클록이 한 번씩 움직일 때마다 데이터 비트가 한 칸씩 이동한다고 생각하면 된다. 클록이 빠르면 데이터도 빠르게 오간다. 단, 주변 장치가 감당할 수 없는 속도로 클록을 올리면 통신이 깨질 수 있다.
MOSI는 마스터가 말하는 길이다. 예를 들어 ESP32가 디스플레이에 이 색으로 픽셀을 칠해라라고 명령하거나, SD 카드에 이 데이터를 저장해라라고 보낼 때 MOSI 선을 사용한다.
MISO는 슬레이브가 대답하는 길이다. SD 카드에서 데이터를 읽거나, 센서가 측정값을 돌려줄 때 MISO 선을 사용한다. 디스플레이처럼 주로 데이터를 받기만 하는 장치에서는 MISO를 거의 쓰지 않거나 아예 연결하지 않는 경우도 있다. 하지만 SD 카드처럼 읽기와 쓰기를 모두 하는 장치는 MISO가 필요하다.
CS는 호출 버튼이다. SPI 버스에는 여러 장치가 같은 SCK, MOSI, MISO를 공유할 수 있다. 그런데 동시에 여러 장치가 말하면 데이터가 섞인다. 그래서 마스터는 통신하려는 장치의 CS만 활성화한다. 대부분의 SPI 장치는 CS가 LOW일 때 선택되고, HIGH일 때는 조용히 기다린다.
이 구조를 한 줄로 정리하면 다음과 같다.
SCK가 박자를 만들고, MOSI와 MISO가 데이터를 주고받으며, CS가 대화할 장치를 고른다.
I2C와 SPI 비교하기
I2C와 SPI는 모두 보드와 주변 장치를 연결하는 데 자주 쓰인다. 둘 중 하나가 무조건 더 좋다고 말하기는 어렵다. 목적이 다르기 때문이다. I2C는 적은 배선으로 여러 장치를 연결하기 좋고, SPI는 빠른 데이터 전송에 유리하다.
| 구분 | I2C | SPI |
|---|---|---|
| 기본 신호선 | SDA, SCL 2개 | MOSI, MISO, SCK, CS 최소 4개 |
| 장치 구분 방식 | 주소로 구분 | 장치마다 CS 핀으로 구분 |
| 배선 수 | 적다 | 장치가 늘면 CS 핀이 추가된다 |
| 속도 감각 | 센서값 읽기에는 충분한 경우가 많다 | 화면, 저장장치, 고속 센서에 유리하다 |
| 회로 특징 | 풀업 저항과 버스 용량 영향을 받는다 | 구조가 단순하지만 선이 많아진다 |
| 잘 맞는 장치 | IMU, 온도 센서, 작은 OLED | TFT 디스플레이, SD 카드, 고속 ADC |
I2C는 한 버스에 여러 장치를 달아도 주소만 다르면 된다. 그래서 선을 아끼고 싶을 때 유리하다. 하지만 같은 주소를 가진 모듈을 여러 개 연결하면 문제가 생긴다. 주소를 바꾸는 점퍼가 없으면 멀티플렉서 같은 추가 부품이 필요할 수도 있다.
SPI는 주소 개념이 없다. 대신 장치마다 CS 핀을 하나씩 따로 둔다. 장치가 하나일 때는 단순하지만, 장치가 세 개, 네 개로 늘면 제어 보드의 GPIO 핀도 그만큼 더 필요하다. 대신 어떤 장치를 선택할지 매우 명확하고, 데이터 전송도 빠르게 할 수 있다.
로봇을 만들 때는 이렇게 생각하면 좋다. 천천히 읽어도 되는 센서 여러 개는 I2C가 편하다. 빠르게 많이 보내야 하는 화면, 저장장치, 고속 센서는 SPI가 어울린다.
여러 SPI 장치를 연결하는 방법
SPI의 장점 중 하나는 SCK, MOSI, MISO를 공유하면서 여러 장치를 연결할 수 있다는 점이다. 단, 각 장치는 자기만의 CS 핀을 가져야 한다.
예를 들어 ESP32에 TFT 디스플레이와 SD 카드 모듈을 함께 연결한다고 해 보자. 두 장치는 같은 SPI 버스를 사용할 수 있다. SCK, MOSI, MISO는 함께 연결하고, 디스플레이의 CS와 SD 카드의 CS는 서로 다른 GPIO에 연결한다.
ESP32 SCK ─── TFT SCK ─── SD SCK
ESP32 MOSI ─── TFT MOSI ─── SD MOSI
ESP32 MISO ─── TFT MISO ─── SD MISO
ESP32 GPIO5 ─ TFT CS
ESP32 GPIO13 ─ SD CS
GND ─ TFT GND ─── SD GND통신할 때는 원하는 장치의 CS만 LOW로 내리고, 나머지 장치의 CS는 HIGH로 유지한다. 디스플레이에 데이터를 보낼 때 SD 카드까지 동시에 반응하면 안 되고, SD 카드에서 데이터를 읽을 때 디스플레이가 끼어들면 안 된다.
const int TFT_CS = 5;
const int SD_CS = 13;
void setup() {
pinMode(TFT_CS, OUTPUT);
pinMode(SD_CS, OUTPUT);
digitalWrite(TFT_CS, HIGH);
digitalWrite(SD_CS, HIGH);
}
void selectTFT() {
digitalWrite(SD_CS, HIGH);
digitalWrite(TFT_CS, LOW);
}
void selectSD() {
digitalWrite(TFT_CS, HIGH);
digitalWrite(SD_CS, LOW);
}
void deselectAll() {
digitalWrite(TFT_CS, HIGH);
digitalWrite(SD_CS, HIGH);
}실제 라이브러리를 사용할 때는 라이브러리가 CS 제어를 대신해 주는 경우가 많다. 그래도 원리는 알고 있어야 한다. 특히 여러 SPI 장치를 함께 쓸 때는 초기화 전에 모든 CS 핀을 출력으로 설정하고 HIGH로 올려 두는 습관이 좋다. 전원이 켜지는 순간 CS가 떠 있으면, 장치가 자신이 선택되었다고 착각할 수 있기 때문이다.
SPI 코드의 기본 모양
아두이노 계열에서는 SPI.h 라이브러리를 많이 사용한다. 장치별 라이브러리를 쓰면 내부에서 SPI 통신을 처리해 주므로 직접 SPI.transfer()를 호출하지 않아도 되는 경우가 많다. 하지만 기본 구조를 이해해 두면 문제가 생겼을 때 훨씬 빠르게 원인을 찾을 수 있다.
아래 코드는 SPI로 1바이트를 보내고 1바이트를 받는 가장 기본적인 흐름이다.
#include <SPI.h>
const int CS_PIN = 10;
void setup() {
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
SPI.begin();
}
void loop() {
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
digitalWrite(CS_PIN, LOW);
byte received = SPI.transfer(0x00);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
delay(100);
}SPISettings(1000000, MSBFIRST, SPI_MODE0)은 통신 조건을 정하는 부분이다. 첫 번째 값은 클록 속도다. 여기서는 1MHz로 설정했다. 두 번째 값은 비트를 어떤 순서로 보낼지 정한다. MSBFIRST는 가장 큰 자리 비트부터 보낸다는 뜻이다. 세 번째 값은 SPI 모드다.
SPI 모드는 클록 신호의 기준 상태와 데이터를 읽는 타이밍을 정한다. 보통 SPI_MODE0이 많이 쓰이지만, 모든 장치가 그런 것은 아니다. 어떤 센서나 메모리 칩은 다른 모드를 요구할 수 있다. 장치가 계속 이상한 값을 돌려준다면 배선만 볼 것이 아니라 SPI 모드와 속도도 확인해야 한다.
보드마다 SPI 핀이 다르다
SPI를 연결할 때 자주 하는 실수는 인터넷 예제의 핀 번호를 그대로 믿는 것이다. 아두이노 우노, 아두이노 메가, ESP32, 라즈베리파이는 SPI 핀 배치가 다르다. 같은 ESP32라도 개발 보드 종류에 따라 핀 이름과 배치가 다르게 표시될 수 있다.
아두이노 우노에서는 전통적으로 MOSI, MISO, SCK가 특정 디지털 핀에 연결되어 있고, ICSP 헤더에서도 사용할 수 있다. ESP32는 하드웨어 SPI 버스를 여러 방식으로 사용할 수 있으며, 예제에서 흔히 SCK 18, MISO 19, MOSI 23, CS 5 같은 조합을 볼 수 있다. 라즈베리파이는 GPIO 헤더에 SPI 핀이 정해져 있고, 운영체제 설정에서 SPI를 활성화해야 사용할 수 있다.
중요한 것은 암기가 아니다. 회로를 연결하기 전에 내가 쓰는 보드의 핀맵을 확인하는 습관이 중요하다. 로봇에서는 핀 하나를 잘못 꽂아도 모터, 센서, 통신이 한꺼번에 이상해 보일 수 있다. 통신 문제가 생겼을 때 가장 먼저 확인할 것은 코드보다 배선인 경우가 많다.
전압도 반드시 확인해야 한다. 라즈베리파이와 ESP32는 기본적으로 3.3V 로직을 사용한다. 아두이노 우노는 보통 5V 로직이다. 3.3V만 허용하는 센서나 SD 카드 모듈의 입력에 5V 신호를 직접 넣으면 고장 날 수 있다. 모듈에 레벨 변환 회로가 들어 있는지, 아니면 별도의 로직 레벨 변환기가 필요한지 확인해야 한다.
고속 디스플레이에서 SPI가 중요한 이유
로봇을 만들다 보면 작은 화면이 의외로 큰 도움이 된다. 시리얼 모니터를 보지 않아도 배터리 전압, 거리 센서값, 현재 모드, 모터 출력값을 바로 확인할 수 있기 때문이다. 특히 PC 없이 현장에서 로봇을 테스트할 때 디스플레이는 작은 계기판 역할을 한다.
문제는 화면 데이터가 생각보다 크다는 점이다. 흑백 OLED에 문자 몇 줄을 쓰는 정도라면 느린 통신도 큰 문제가 되지 않는다. 하지만 컬러 TFT에 그래프를 그리거나 카메라 처리 결과를 간단히 표시하려면 많은 픽셀 데이터를 보내야 한다.
이때 SPI 디스플레이를 사용하면 화면 갱신이 훨씬 부드러워진다. ESP32 같은 보드는 하드웨어 SPI를 이용해 데이터를 빠르게 밀어낼 수 있고, 일부 라이브러리는 DMA라는 기능을 사용해 CPU가 일일이 한 바이트씩 붙잡고 있지 않아도 데이터를 전송할 수 있게 해 준다. DMA는 직접 메모리 접근이라는 뜻인데, 여기서는 제어 보드가 화면 데이터를 더 효율적으로 보내는 방법 정도로 이해하면 충분하다.
로봇 상태 표시용 디스플레이를 고를 때는 해상도와 색상 수만 보지 말고 통신 방식도 확인해야 한다. 같은 크기라도 I2C 디스플레이는 배선이 간단하고, SPI 디스플레이는 빠른 갱신에 유리하다.
SD 카드 모듈과 SPI
SD 카드 모듈은 로봇 실험에서 매우 유용하다. 시리얼 모니터에 출력한 값은 전원이 꺼지면 사라지지만, SD 카드에 저장한 로그는 나중에 다시 분석할 수 있다. 예를 들어 라인트레이서가 코너에서 흔들리는 이유를 찾고 싶다면, 시간에 따른 라인 센서값과 좌우 모터 출력을 저장해 보면 된다.
SD 카드는 SPI 방식으로 연결해 사용하는 경우가 많다. 보통 MOSI, MISO, SCK, CS, 전원, GND를 연결한다. 쓰기만 하는 장치가 아니므로 MISO도 필요하다. 파일 시스템을 다루기 때문에 일반 센서보다 초기화와 전원 안정성에 민감할 수 있다.
SD 카드 모듈을 사용할 때는 몇 가지를 특히 조심해야 한다.
전원이 불안정하면 파일이 깨지거나 초기화가 실패할 수 있다.
긴 점퍼선은 고속 통신에서 오류를 만들 수 있으므로 가능한 짧게 연결한다.
3.3V 로직과 5V 로직이 맞는지 확인한다.
CS핀이 다른 SPI 장치와 겹치지 않게 한다.데이터를 너무 자주 저장하면 프로그램 흐름이 느려질 수 있으므로 저장 주기를 정한다.
로봇에서는 모든 센서값을 매 순간 저장하려고 하기보다 필요한 주기로 모아서 저장하는 편이 좋다. 예를 들어 1ms마다 저장하는 것보다 20ms 또는 50ms마다 한 줄씩 저장해도 분석에는 충분한 경우가 많다. 중요한 것은 빠르게 저장하는 것보다, 로봇의 움직임을 이해할 수 있는 형태로 안정적으로 저장하는 것이다.
SPI 속도는 높을수록 무조건 좋을까
SPI는 빠른 통신에 유리하지만, 속도를 무조건 높인다고 좋은 것은 아니다. 전선이 길거나, 브레드보드 접촉이 불안정하거나, 모듈의 품질이 좋지 않으면 높은 속도에서 데이터가 깨질 수 있다.
로봇은 책상 위의 조용한 회로보다 환경이 거칠다. 모터가 돌면서 전원에 노이즈가 생기고, 프레임이 흔들리며, 배선이 진동한다. 이런 상황에서는 통신 속도를 조금 낮추는 것이 오히려 더 안정적일 수 있다.
SPI 속도를 정할 때는 먼저 장치 데이터시트나 라이브러리 예제를 참고해 안전한 값에서 시작한다. 동작이 안정적인 것을 확인한 뒤 필요하면 조금씩 올린다. 디스플레이가 가끔 깨지거나 SD 카드 쓰기가 실패한다면 속도를 낮추고 배선을 짧게 정리해 보는 것이 좋다.
통신 속도는 자동차의 최고 속도와 비슷하다. 빠르게 달릴 수 있는 도로와 차량이 있어야 의미가 있다. 좁고 흔들리는 길에서 무리하게 속도를 올리면 목적지에 빨리 도착하는 것이 아니라 사고가 난다.
로봇에서 SPI 배선할 때의 감각
SPI 배선은 단순해 보이지만 고속 신호를 다루기 때문에 사소한 차이가 문제를 만든다. 특히 브레드보드와 긴 점퍼선을 많이 쓰는 초보 단계에서는 통신 오류가 배선 문제인지 코드 문제인지 헷갈리기 쉽다.
가장 먼저 할 일은 GND를 단단히 연결하는 것이다. 모터 전원과 보드 전원을 분리하더라도 신호를 주고받는 회로끼리는 기준 GND를 공유해야 한다. 다음으로 전선을 짧게 유지한다. SCK는 빠르게 흔들리는 클록 신호이므로 너무 길게 빼면 주변 노이즈의 영향을 받기 쉽다.
가능하다면 SPI 장치는 제어 보드 가까이에 배치한다. 로봇 프레임에서 디스플레이는 위쪽에, SD 카드 모듈은 보드 근처에 두는 식으로 배선 길이를 조절한다. 모터 전원선과 SPI 신호선을 나란히 길게 묶어 두는 것도 피하는 편이 좋다. 모터 전류가 크게 변할 때 신호선에 노이즈가 섞일 수 있기 때문이다.
SPI 통신이 불안정할 때는 다음 순서로 확인해 보자.
MOSI,MISO,SCK,CS가 서로 바뀌지 않았는지 확인한다.모든 장치의 GND가 연결되어 있는지 확인한다.
선택하지 않은 장치의
CS가 HIGH로 유지되는지 확인한다.SPI 모드와 속도가 장치에 맞는지 확인한다.
전선 길이를 줄이고, 브레드보드 접촉 불량을 의심해 본다.
5V 보드와 3.3V 모듈을 직접 연결하지 않았는지 확인한다.
이 체크리스트는 나중에 로봇이 이상하게 움직일 때도 유용하다. 통신 오류는 모터 제어 오류처럼 보일 수 있고, 센서 오류처럼 보일 수도 있다. 예를 들어 SD 카드 저장 중 오류가 나서 프로그램이 잠깐 멈추면, 로봇이 순간적으로 반응하지 않는 것처럼 보일 수 있다.
SPI 장치를 함께 쓰는 작은 로봇 예시
작은 바퀴 로봇에 ESP32, 거리 센서, TFT 디스플레이, SD 카드 모듈을 달았다고 상상해 보자. 거리 센서는 앞의 장애물까지 거리를 측정한다. 디스플레이는 현재 거리, 모터 출력, 배터리 전압을 보여 준다. SD 카드는 주행 중 센서값과 모터 명령을 저장한다.
이 로봇에서 TFT 디스플레이와 SD 카드는 같은 SPI 버스를 공유할 수 있다. ESP32는 디스플레이를 갱신할 때 TFT의 CS를 LOW로 만들고, SD 카드의 CS는 HIGH로 둔다. 로그를 저장할 때는 반대로 SD 카드의 CS를 LOW로 만들고, TFT의 CS는 HIGH로 둔다.
이 구조가 익숙해지면 로봇 회로를 더 깔끔하게 설계할 수 있다. 모든 장치에 각각 새로운 통신선을 따로 만들 필요 없이, 빠른 장치끼리는 SPI 버스를 공유하고, 느린 센서들은 I2C에 묶을 수 있다. 여기에 시리얼 통신으로 PC나 라즈베리파이와 명령을 주고받으면, 로봇 안에 작은 통신망이 만들어진다.
중요한 것은 통신 방식의 이름을 외우는 것이 아니다. 어떤 데이터가 얼마나 자주, 얼마나 많이 움직이는지 감각을 잡는 것이다. 로봇이 느리게 움직이는데 센서값이 1초에 한 번만 들어온다면 반응이 둔해진다. 반대로 화면 표시처럼 조금 늦어도 되는 일을 너무 자주 처리하면 모터 제어가 밀릴 수 있다.
SPI는 빠른 길을 제공하지만, 그 길을 언제 쓸지는 설계자가 정해야 한다.
정리
SPI는 빠른 데이터 전송이 필요한 장치를 연결할 때 매우 유용한 통신 방식이다. I2C보다 배선은 많지만, 구조가 단순하고 고속 전송에 강하다. MOSI, MISO, SCK, CS 네 신호의 역할을 이해하면 SPI 장치를 연결하고 문제를 찾는 일이 훨씬 쉬워진다.
한 버스에 여러 SPI 장치를 연결할 때는 SCK, MOSI, MISO를 공유하고, 각 장치마다 별도의 CS 핀을 둔다. 통신하려는 장치의 CS만 활성화하고 나머지는 비활성화해야 한다. 이 원리만 정확히 지켜도 대부분의 SPI 회로를 이해할 수 있다.
로봇에서는 SPI가 디스플레이, SD 카드, 고속 센서처럼 많은 데이터를 다루는 장치에서 자주 쓰인다. 다만 속도만 보고 무리하게 설정하지 말고, 배선 길이, 전원 안정성, 로직 전압, 공통 GND를 함께 확인해야 한다.
이제 로봇 안에서 데이터를 주고받는 방법을 꽤 많이 살펴보았다. 시리얼 통신은 보드와 PC 사이의 기본 대화였고, I2C는 여러 센서를 간단히 연결하는 방법이었다. SPI는 빠른 장치를 위한 넓은 도로다. 다음에는 선 없이 로봇을 조종하고 상태를 확인하는 방법, Wi-Fi와 Bluetooth로 넘어간다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.