SPI
고친 사람 github-actions[bot]
SPI 는 한 기판 위의 프로세서가 주변 칩과 데이터를 빠르게 주고받게 해 주는 통신 규칙입니다. 프로세서가 박자 신호를 보내면 한 박자마다 양쪽이 한 비트씩 동시에 건넵니다. 플래시 메모리나 화면처럼 데이터를 많이 나르는 칩을 붙일 때 주로 씁니다. 자바에서 말하는 SPI 는 이름만 같은 다른 개념입니다.
쉽고 빠른 이해
SPI 는 프로세서가 선 몇 가닥으로 주변 칩과 데이터를 주고받는 방법입니다. 예를 들어 작은 보드의 프로세서가 켜질 때 플래시 메모리 칩에서 프로그램을 읽어 옵니다.
두 칩이 비트를 주고받으려면 언제 다음 비트를 읽을지 서로 맞아야 합니다. SPI 에서는 프로세서가 박자 신호를 직접 보내서 이 박자를 맞춥니다. 칩을 부르는 주소나 잘 받았다는 답 같은 절차가 없어 단순하고 빠릅니다.
어떻게 도는가:
- 프로세서가 말을 걸 칩의 선택선 전압을 낮춥니다
- 박자를 한 번 칠 때마다 프로세서와 칩이 한 비트씩 서로 건넵니다
- 다 주고받으면 선택선 전압을 다시 높입니다
대가도 있습니다. 칩이 하나 늘 때마다 선택선을 한 가닥씩 더 깔아야 합니다. 칩이 제대로 받았는지 알려 주는 절차가 없습니다. 선을 길게 끌 수 없어 한 기판 안에서만 씁니다.
상세
SPI(Serial Peripheral Interface, 직렬 주변 장치 인터페이스)는 프로세서와 주변 칩이 짧은 거리에서 데이터를 주고받는 규칙입니다. 주변 칩은 센서, 메모리, 화면처럼 프로세서 곁에 붙어 일을 거드는 집적 회로입니다. 흔히 칩이라고 부릅니다.
SPI 는 직렬 통신입니다. 직렬 통신은 비트를 한 줄로 세워 하나씩 차례로 보내는 방식입니다. 여러 비트를 나란히 보내는 방식보다 선이 적게 듭니다.
이 절은 먼저 SPI 를 이루는 선과 두 역할을 봅니다. 그다음 한 박자에 한 비트씩 맞바꾸는 구조와 레지스터 하나를 읽는 한 왕복을 따라갑니다. 끝으로 이웃 방식과 견주고 운영체제에서 SPI 가 어떻게 보이는지 봅니다.
컨트롤러와 주변 장치
SPI 로 이어진 칩은 두 역할로 나뉩니다. 대화를 여는 쪽이 컨트롤러입니다. 불려서 답하는 쪽이 주변 장치입니다. 대개 프로세서가 컨트롤러입니다.
박자 신호도 컨트롤러가 만듭니다. 이 박자 신호를 클록이라고 합니다. 클록은 일정한 간격으로 오르내리는 신호입니다. 주변 장치는 이 박자를 보고 언제 비트를 읽을지 압니다. 그래서 두 칩이 통신 속도를 미리 약속해 두지 않아도 됩니다.
주변 장치는 먼저 말을 걸지 못합니다. 컨트롤러가 클록을 칠 때만 비트를 내보냅니다.
주변 장치가 알릴 일이 생기면 곤란해집니다. 컨트롤러가 부르기 전에는 말할 방법이 없기 때문입니다. 그래서 많은 칩이 SPI 선과 별도로 인터럽트 선을 하나 둡니다. 칩이 이 선의 전압을 바꾸면 컨트롤러가 알아채고 SPI 로 값을 읽어 갑니다.
예전 문서와 데이터시트는 컨트롤러를 마스터, 주변 장치를 슬레이브라고 부릅니다. 데이터시트는 칩 제조사가 내는 설명서입니다. 이름만 다르고 같은 역할입니다.
네 가닥의 선
SPI 는 신호선 네 가닥을 씁니다. 아래 표는 선마다 무엇을 싣고 누가 그 선의 전압을 바꾸는지 보입니다.
| 선 | 싣는 것 | 전압을 바꾸는 쪽 |
|---|---|---|
| SCLK(Serial Clock, 직렬 클록) | 박자 | 컨트롤러 |
| MOSI(Master Out Slave In, 마스터 출력 슬레이브 입력) | 컨트롤러가 보내는 비트 | 컨트롤러 |
| MISO(Master In Slave Out, 마스터 입력 슬레이브 출력) | 주변 장치가 보내는 비트 | 주변 장치 |
| CS(Chip Select, 칩 선택) | 지금 말을 거는 칩 | 컨트롤러 |
MOSI 와 MISO 는 옛 이름 마스터·슬레이브에서 온 줄임말입니다. 요즘은 COPI(Controller Out Peripheral In)와 CIPO(Controller In Peripheral Out)로 적는 문서도 있습니다. 가리키는 선은 같습니다.
보내는 선과 받는 선이 따로 있다는 점이 SPI 의 뼈대입니다. 두 방향이 서로 다른 선을 쓰니 한꺼번에 오갈 수 있습니다.
CS 는 대개 낮은 전압일 때 「선택됨」을 뜻합니다. 그래서 칩을 고른다는 말을 CS 를 「내린다」고도 합니다. CS 를 SS(Slave Select)라고 적는 데이터시트도 있습니다.
칩마다 한 가닥씩 까는 칩 선택선
컨트롤러 하나에 주변 장치 여럿을 붙일 수 있습니다. SCLK·MOSI·MISO 세 가닥은 모든 칩이 함께 씁니다. 이렇게 여러 칩이 함께 쓰는 선 묶음을 버스라고 합니다. CS 만 칩마다 따로 한 가닥씩 깝니다.
flowchart TD
C["컨트롤러"]
C --- SH["함께 쓰는 세 가닥 · SCLK · MOSI · MISO"]
SH --- P1["주변 장치 1"]
SH --- P2["주변 장치 2"]
SH --- P3["주변 장치 3"]
C -->|"CS"| P1
C -->|"CS"| P2
C -->|"CS"| P3
그림에서 CS 라고 적힌 세 선이 칩마다 깐 선택선입니다. 컨트롤러는 말을 걸 칩의 CS 하나만 내립니다. CS 가 내려간 칩만 SCLK 와 MOSI 를 듣고 MISO 로 답합니다.
선택되지 않은 칩은 대개 MISO 에서 손을 뗍니다. 전기적으로 선에서 떨어진 것처럼 구는 것입니다. 이런 상태를 하이 임피던스라고 합니다. 덕분에 세 칩이 MISO 한 가닥을 같이 써도 신호가 섞이지 않습니다.
대가는 선 수입니다. 칩이 n 개면 선은 공용 셋에 CS n 가닥을 더해 3 + n 가닥이 듭니다. 프로세서의 핀이 모자라면 칩을 더 붙이기 어렵습니다.
한 박자에 한 비트씩 맞바꾸는 시프트 레지스터
SPI 가 비트를 주고받는 속을 들여다보면 시프트 레지스터가 나옵니다. 시프트 레지스터는 비트 여러 개를 한 줄로 담는 저장소입니다. 비트 하나가 한 칸을 차지합니다.
시프트 레지스터는 박자마다 담긴 비트를 한 칸씩 밀어냅니다. 밀려 나간 비트 하나는 밖으로 나갑니다. 반대쪽 끝으로는 새 비트 하나가 들어옵니다.
컨트롤러와 주변 장치는 저마다 8비트짜리 시프트 레지스터를 갖습니다. 두 레지스터는 MOSI 와 MISO 로 이어져 고리를 이룹니다.
flowchart TD
subgraph C["컨트롤러"]
CR["시프트 레지스터 · 8비트"]
end
subgraph P["주변 장치"]
PR["시프트 레지스터 · 8비트"]
end
CR -->|"MOSI · 밀려 나간 비트"| PR
PR -->|"MISO · 밀려 나간 비트"| CR
클록이 한 번 뛰면 양쪽 레지스터가 동시에 한 칸씩 밉니다. 컨트롤러에서 밀려 나간 비트는 MOSI 를 타고 주변 장치로 들어갑니다. 같은 순간 주변 장치에서 밀려 나간 비트는 MISO 를 타고 컨트롤러로 들어갑니다.
여덟 번 뛰고 나면 두 레지스터의 내용이 서로 뒤바뀌어 있습니다. 컨트롤러가 담았던 바이트는 주변 장치에, 주변 장치가 담았던 바이트는 컨트롤러에 있습니다. SPI 의 한 바이트 전송은 이렇게 맞바꾸기입니다.
그래서 SPI 는 보내기와 받기가 늘 함께 일어납니다. 두 방향이 동시에 오가는 이런 통신을 전이중 통신이라고 합니다.
읽으려면 무언가를 보내야 하는 까닭
맞바꾸기 구조에는 뜻밖의 결과가 하나 따릅니다. 컨트롤러가 값을 받고 싶어도 클록을 치지 않으면 비트가 안 옵니다. 클록을 치면 MOSI 로도 비트가 나갑니다.
그래서 컨트롤러는 읽기만 하고 싶을 때도 바이트를 보냅니다. 주변 장치가 무시할 값을 채워 보냅니다. 이렇게 채워 보내는 바이트를 흔히 더미 바이트라고 부릅니다. 0 이나 모든 비트가 1 인 값을 흔히 씁니다.
쓰기만 할 때는 거꾸로입니다. 컨트롤러는 MISO 로 들어온 바이트를 그냥 버립니다.
레지스터 하나를 읽는 한 왕복
이 소절은 컨트롤러가 센서 칩에서 값 하나를 읽는 한 번을 따라갑니다. 센서 칩 안에는 측정값이나 설정값을 담아 두는 저장소가 여럿 있습니다. 이 저장소를 레지스터라고 하고 저장소마다 번호가 붙습니다.
sequenceDiagram
participant C as 컨트롤러
participant P as 주변 장치
C->>P: CS 를 내린다
C->>P: 첫 바이트 · 읽기 명령과 레지스터 번호
P-->>C: 같은 여덟 박자 동안 · 뜻 없는 바이트
C->>P: 둘째 바이트 · 더미 바이트
P-->>C: 같은 여덟 박자 동안 · 레지스터 값
C->>P: CS 를 올린다
컨트롤러가 CS 를 내려 칩을 고릅니다. 첫 바이트로 「어느 레지스터를 읽겠다」는 명령을 보냅니다. 그 여덟 박자 동안 주변 장치도 MISO 로 바이트 하나를 내보냅니다.
주변 장치는 아직 명령을 다 듣지 못했습니다. 그래서 첫 바이트 동안 돌아온 값에는 뜻이 없습니다. 컨트롤러는 이 바이트를 버립니다.
첫 바이트를 다 들은 주변 장치는 명령이 가리킨 레지스터의 값을 자기 시프트 레지스터에 옮겨 담습니다. 둘째 바이트에서 컨트롤러는 더미 바이트를 보냅니다. 그 여덟 박자 동안 옮겨 담은 값이 MISO 로 밀려 나갑니다. 컨트롤러가 CS 를 올리면 한 번의 대화가 끝납니다.
명령 바이트의 모양은 칩마다 다릅니다. 어느 비트가 읽기와 쓰기를 가르는지, 레지스터 번호를 몇 비트로 적는지는 그 칩의 데이터시트가 정합니다. SPI 자체는 비트를 맞바꾸는 방법까지만 정합니다.
박자를 맞추는 네 가지 모드
주변 장치가 비트를 읽는 때는 클록이 바뀌는 순간입니다. 클록이 낮은 전압에서 높은 전압으로 올라가는 순간이 있습니다. 거꾸로 높은 전압에서 내려가는 순간도 있습니다. 칩마다 어느 순간에 읽는지가 다릅니다.
이 차이를 두 값으로 적습니다. 첫째 값 CPOL(Clock Polarity, 클록 극성)은 쉴 때 클록이 낮은지 높은지를 정합니다. CPOL 이 0 이면 쉴 때 낮고, 1 이면 쉴 때 높습니다.
둘째 값 CPHA(Clock Phase, 클록 위상)는 비트를 읽는 순간을 정합니다. CS 가 내려간 뒤 클록이 처음 바뀌는 순간에 읽으면 0, 두 번째 바뀌는 순간에 읽으면 1 입니다. 쉴 때 낮은 클록은 처음에 올라가고 두 번째에 내려갑니다. 쉴 때 높은 클록은 그 반대입니다.
두 값이 각각 0 과 1 이라 조합은 넷입니다. 이 넷을 모드 0~3 이라고 부릅니다. 아래 표의 마지막 열은 두 값을 합쳐 읽은 것입니다. 모드 0 이면 쉴 때 낮으니 처음 바뀌는 순간이 올라갈 때이고, CPHA 가 0 이라 그때 읽습니다.
| 모드 | CPOL | CPHA | 쉴 때 클록 | 비트를 읽는 순간 |
|---|---|---|---|---|
| 0 | 0 | 0 | 낮음 | 올라갈 때 |
| 1 | 0 | 1 | 낮음 | 내려갈 때 |
| 2 | 1 | 0 | 높음 | 내려갈 때 |
| 3 | 1 | 1 | 높음 | 올라갈 때 |
컨트롤러는 붙일 칩의 모드에 맞춰 클록을 칩니다. 모드가 어긋나면 비트를 엉뚱한 순간에 읽어 값이 틀어집니다. 오류가 났다는 신호는 오지 않습니다.
비트를 보내는 순서도 맞춰야 합니다. 대개 바이트에서 가장 큰 값을 맡은 비트인 최상위 비트를 먼저 보냅니다. 거꾸로 보내는 칩도 있어 이 또한 데이터시트에서 확인합니다.
주소도 확인 응답도 없는 단순함
SPI 에는 칩을 부르는 주소가 없습니다. 누구와 말할지는 CS 선이 정합니다. 그래서 대화를 시작할 때 주소를 보내는 절차가 없습니다.
잘 받았다는 확인 응답도 없습니다. 컨트롤러는 칩이 바이트를 받았는지 모릅니다. 칩이 빠져 있어도 컨트롤러는 클록을 쳐서 MISO 에서 무언가를 읽습니다. 이때 읽히는 값은 대개 모든 비트가 0 이거나 1 인 바이트입니다.
오류를 검사하는 절차도 없습니다. 선에 잡음이 끼어 비트 하나가 뒤집혀도 SPI 는 알아채지 못합니다. 이 검사가 필요한 칩은 데이터 뒤에 CRC(Cyclic Redundancy Check, 순환 중복 검사) 같은 검사값을 스스로 덧붙입니다.
SPI 에는 정식 표준 문서도 없습니다. 여러 제조사가 비슷한 방식을 따라 쓴 결과로 굳은 사실상 표준입니다. 그래서 모드, 한 번에 보내는 비트 수, 명령 모양은 칩마다 데이터시트를 봐야 합니다.
대신 주고받는 비트가 전부 데이터입니다. 절차에 쓰이는 비트가 없으니 같은 박자로 더 많은 데이터를 나릅니다.
I2C·UART 와 견줌
기판 위에서 SPI 와 자주 견주는 방식이 둘 있습니다. 하나는 I2C(Inter-Integrated Circuit, 집적 회로 간 통신)입니다. 선 두 가닥에 여러 칩을 붙이고 주소로 상대를 부르는 방식입니다.
다른 하나는 UART(Universal Asynchronous Receiver-Transmitter, 범용 비동기 송수신기)입니다. 클록 선 없이 두 장치가 속도를 미리 맞춰 두고 비트를 보내는 방식입니다. 셋 다 직렬 통신입니다. 아래 표는 셋이 선을 몇 가닥 쓰고 상대를 어떻게 고르는지 견줍니다.
| SPI | I2C | UART | |
|---|---|---|---|
| 선 | 공용 셋 + 칩마다 CS 하나 | 둘 | 둘(보내는 선·받는 선) |
| 상대를 고르는 법 | CS 선 | 주소 | 상대가 하나뿐 |
| 박자 | 컨트롤러가 클록을 보낸다 | 컨트롤러가 클록을 보낸다 | 클록 없이 속도를 미리 맞춘다 |
| 두 방향 동시 | 된다 | 안 된다 | 된다 |
| 확인 응답 | 없다 | 바이트마다 있다 | 없다 |
SPI 는 데이터를 많이 나르는 칩에 맞습니다. 절차 비트가 없고 클록을 높게 올릴 수 있기 때문입니다. 화면, 플래시 메모리, SD 카드(Secure Digital card), ADC(Analog-to-Digital Converter, 아날로그-디지털 변환기)가 SPI 로 붙는 흔한 칩입니다. ADC 는 전압 같은 아날로그 값을 숫자로 바꾸는 칩입니다.
칩이 많고 핀이 모자라면 흔히 I2C 를 씁니다. 칩이 늘어도 선이 두 가닥으로 그대로이기 때문입니다.
기판 밖 먼 장치와 이을 때는 SPI 를 쓰지 않습니다. 선이 길어지면 박자와 비트가 어긋나기 쉽습니다. 그런 거리는 CAN(Controller Area Network, 컨트롤러 영역 네트워크) 같은 다른 방식이 맡습니다.
데이터선을 한 방향으로 모아 쓰는 듀얼 SPI 와 쿼드 SPI
보통 SPI 에서 비트를 나르는 데이터선은 MOSI 와 MISO 두 가닥입니다. 두 가닥이 방향을 하나씩 맡으니, 컨트롤러가 읽을 때 쓸모 있는 비트는 한 박자에 MISO 로 오는 한 비트뿐입니다.
플래시 메모리처럼 읽을 데이터가 아주 많은 칩은 이 두 가닥을 같은 방향으로 함께 씁니다. 그러면 한 박자에 두 비트가 한쪽으로 지나가고, 이 방식을 듀얼 SPI라고 부릅니다. 데이터선을 두 가닥 더 깔아 네 가닥을 한 방향으로 쓰면 쿼드 SPI입니다. 같은 클록에서 읽는 속도가 두 배, 네 배가 됩니다.
대신 이 방식은 두 방향을 동시에 쓰지 않습니다. 모든 데이터선이 한 번에 한 방향으로만 비트를 나릅니다. 이렇게 번갈아 한 방향씩 쓰는 통신을 반이중 통신이라고 합니다.
많은 컴퓨터가 켜질 때 처음 도는 펌웨어를 SPI 로 붙은 플래시 칩에 담아 둡니다. 프로세서는 켜지자마자 이 칩에서 펌웨어를 읽어 옵니다.
운영체제에서 보이는 모습
운영체제는 SPI 를 다루는 디바이스 드라이버를 대개 둘로 나눕니다. 한쪽은 프로세서 안의 SPI 컨트롤러 하드웨어를 다뤄 클록을 치고 바이트를 맞바꿉니다. 다른 쪽은 붙은 칩 하나를 맡아 그 칩의 명령과 레지스터가 무슨 뜻인지 압니다.
이렇게 나누면 같은 센서 드라이버를 다른 SPI 컨트롤러가 달린 보드에서도 쓸 수 있습니다.
리눅스 커널에는 칩 드라이버 없이 사용자 프로그램이 SPI 를 직접 쓰게 해 주는 spidev 드라이버가 있습니다. spidev 는 SPI 를 /dev/spidev0.0 같은 장치 파일로 내놓습니다. 장치 파일은 프로그램이 보통 파일처럼 열어 장치와 주고받는 통로입니다.
파일 이름의 두 숫자는 어느 칩과 말할지를 가리킵니다. 앞 숫자는 버스 번호입니다. 프로세서에 SPI 컨트롤러가 여럿이면 컨트롤러마다 버스가 하나씩 생겨 번호가 붙습니다. 뒤 숫자는 그 버스에서 몇 번째 CS 선인지입니다.
프로그램은 이 파일을 열어 전송을 요청합니다. 전송 한 번에 보낼 바이트를 넘기면 같은 개수의 받은 바이트가 돌아옵니다. 앞에서 본 맞바꾸기가 프로그램에서도 이렇게 드러납니다.
이름이 같은 자바의 SPI
백엔드 개발자가 SPI 라는 이름을 먼저 만나는 곳은 대개 자바입니다. 자바의 SPI 는 서비스 제공자 인터페이스(Service Provider Interface)입니다. 라이브러리가 정한 인터페이스를 다른 쪽이 구현해 끼워 넣게 하는 방식입니다.
두 SPI 는 줄임말만 같고 이어지는 데가 없습니다. 이 문서의 SPI 는 칩 사이의 통신 규칙입니다.
관련 항목
SPI 를 이루는 신호선과 핀
SCLK · MOSI · MISO · 칩 선택 · 클록 · GPIO
SPI 가 비트를 주고받는 방식
직렬 통신 · 시프트 레지스터 · 전이중 · 반이중 · 클록 극성 · 클록 위상 · 최상위 비트 · 하이 임피던스
SPI 와 같은 역할을 두고 겨루는 직렬 버스
I2C · I3C · UART · CAN · RS-485 · 1-Wire
SPI 를 넓히거나 달리 잇는 방식
듀얼 SPI · 쿼드 SPI · 옥탈 SPI · 데이지 체인
SPI 로 붙는 칩
플래시 메모리 · SD 카드 · ADC · DAC · EEPROM · LCD · 센서 · 집적 회로
SPI 를 다루는 운영체제 구성 요소
디바이스 드라이버 · 리눅스 커널 · spidev · 장치 파일 · ioctl · 장치 트리 · 인터럽트 · DMA
SPI 로 붙은 칩이 담거나 따르는 정보
SPI 가 속하는 상위 분류
버스 · 임베디드 · 마이크로컨트롤러 · 컴퓨터 구조 · 디지털 회로
SPI 와 이름이 겹치는 자바 개념
서비스 제공자 인터페이스 · ServiceLoader · JDBC · 플러그인 아키텍처
다른 이름: Serial Peripheral Interface · 직렬 주변 장치 인터페이스 · SPI 버스