SPI에서 클럭의 유휴 상태와 데이터 샘플링 시점을 정하는 두 설정.
정의
-
CPOL (Clock Polarity) — — 클럭이 쉴 때의 레벨
- CPOL = 0 → 유휴 시 Low
- CPOL = 1 → 유휴 시 High
-
CPHA (Clock Phase) — — 어느 에지에서 데이터를 읽나
- CPHA = 0 → 첫 번째 에지에서 샘플링
- CPHA = 1 → 두 번째 에지에서 샘플링
네 가지 모드
| 모드 | CPOL | CPHA | 샘플링 에지 | 데이터 변경 에지 |
|---|---|---|---|---|
| 0 | 0 | 0 | 상승 | 하강 |
| 1 | 0 | 1 | 하강 | 상승 |
| 2 | 1 | 0 | 하강 | 상승 |
| 3 | 1 | 1 | 상승 | 하강 |
모드 0과 모드 3이 가장 널리 쓰인다.
파형으로 이해하기
모드 0 (CPOL=0, CPHA=0)
SCLK ___┌─┐_┌─┐_┌─┐___ 유휴 Low
MOSI ─X───X───X───X── 데이터는 하강 에지에서 바뀌고
↑ ↑ ↑ 상승 에지에서 읽힌다
샘플링
모드 3 (CPOL=1, CPHA=1)
SCLK ‾‾‾└─┘‾└─┘‾└─┘‾‾‾ 유휴 High
MOSI ─X───X───X───X──
↑ ↑ ↑
샘플링 (상승 에지)
핵심 원리 — 왜 이렇게 나누나
- 데이터가 안정된 시점에 읽어야 한다
- 데이터를 바꾸는 에지와 읽는 에지가 반대여야 한다
한쪽 에지에서 출력 → 다른 쪽 에지에서 입력
CPHA가 이 "어느 쪽을 먼저 쓸지"를 정한다.
CPHA = 0 : 첫 에지에서 읽는다
-
첫 비트는 클럭이 시작되기 전에 이미 나와 있어야 한다
-
CS가 내려가는 순간 첫 비트를 출력한다 ★ CPHA = 1 : 첫 에지에서 출력, 두 번째 에지에서 읽는다
-
클럭 에지마다 규칙적이라 구현이 단순하다
★ CPHA=0의 특성이 중요하다 — 첫 비트가 CS 하강에 맞춰 나오므로, CS를 바이트마다 토글해야 하는 슬레이브가 있다. 연속 전송에서 CS를 계속 내려 둔 채로 보내면 동작하지 않는다.
모드가 틀리면 어떻게 되나
- CPHA만 틀림 — → 한 비트씩 밀려서 읽힌다 (0x5A → 0xB4 같은 시프트)
- CPOL만 틀림 — → 반전된 에지에서 읽어 값이 깨진다
- (CPHA=1이면 우연히 동작하는 경우도 있다)
- 둘 다 틀림 → 완전히 엉뚱한 값
"SPI로 읽으면 0xFF나 0x00만 나온다" → 슬레이브 미응답(전원·CS·배선) 또는 모드 불일치를 의심한다.
"읽은 값이 예상값을 1비트 시프트한 것 같다" → CPHA가 틀렸을 가능성이 높다.
설정 방법
hspi.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL = 0
hspi.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA = 0
// → 모드 0
슬레이브 데이터시트의 타이밍 다이어그램에서 확인한다. "SCK idle low, data sampled on rising edge" 라고 쓰여 있으면 모드 0이다.
흔한 장치들의 모드
모드 0 : 대부분의 센서, SD 카드, 많은 SPI 플래시 모드 3 : 일부 플래시, 일부 디스플레이 모드 1/2 : 드물다
여러 슬레이브가 서로 다른 모드를 요구하면 슬레이브를 바꿀 때마다 SPI 설정을 재구성해야 한다. 하드웨어 SPI 인스턴스를 나누는 방법도 있다.
로직 애널라이저로 판별하기
- ① 클럭이 쉴 때의 레벨을 본다 → CPOL
- ② CS 하강 직후 데이터가 이미 나와 있는가
- 있다 → CPHA = 0
- 클럭 첫 에지에서 나온다 → CPHA = 1
대부분의 로직 애널라이저 소프트웨어가 모드를 지정하면 자동 디코딩해 준다. 모드를 바꿔 가며 시도해 보면 어느 것이 의미 있는 값을 내는지 알 수 있다.
비트 순서도 함께 확인
hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; // 대부분 MSB 먼저
드물게 LSB 먼저인 장치가 있다. 값이 비트 반전된 것처럼 보이면 이것을 의심한다.