해당 프로토콜에 대해서 알기전에 인터페이스와 프로토콜의 정의부터 학습했다.
인터페이스는 서로 다른 두 대상이 만나 정보를 주고 받을 수 있도록 연결해주는 방식이다.
임베디드에서 인터페이스는 하드웨어적으로는 핀, 배선, 전압 레벨, 신호선 구성,
클럭 사용 방식 등을 포함할 수 있고, 소프트웨어적으로는 해당 하드웨어를 제어하기 위한 드라이버/API 구조까지 포함할 수 있다.
프로토콜은 서로 다른 두 대상이 만나 정보를 주고 받을 수 있도록 정의된 규약이다.
프로토콜은 데이터의 형식, 전송 순서, 주소, 명령, 응답, ACK/NACK, 에러 처리,
타이밍 조건 등을 정의한다.
짧게 말하면
인터페이스 = 연결되는 방식/경계
프로토콜 = 주고받는 약속/규칙
이렇게 설명할 수 있다.
통신은 직렬 통신과 병렬 통신으로 구분할 수 있다.

위 그림을 보면 하나의 선에서 비트를 하나씩 순서대로 전송한다. 이처럼 직렬 통신은 하나 또는 적은 수의 데이터 선을 통해 데이터를 1비트씩 순서대로 전송하는 방식이다.
대표적으로 UART, SPI, I2C, USB, CAN, Ethernet 등이 있다.
반대로, 병렬 통신은 아래의 그림과 같다.

병렬 통신은 여러 개의 데이터 선을 사용하여 여러 비트를 동시에 전송하는 방식이다.
예를 들어 8비트 병렬 통신은 D0~D7까지 8개의 데이터 선을 통해 한 번에 8비트를 전송한다.
겉으로 보면 병렬 통신이 한 번에 여러 비트를 보내므로 더 빠르고 효율적으로 보일 수 있다. 하지만 고속이 되거나 장치 간 거리가 길어질수록 여러 데이터 선의 신호 도착 시간이 달라지는 skew 문제가 커진다. 또한 배선 수와 핀 수가 많고, 선 사이 간섭과 EMI가 증가하며, PCB 설계와 커넥터 비용도 커진다.
반면 현대의 고속 직렬 통신은 적은 수의 선으로 매우 높은 속도의 신호를 전송하고,
차동 신호, 클럭 복원, 인코딩, SERDES 같은 기술을 사용해 고속 전송을 안정적으로 구현한다. 그래서 장거리 또는 고속 통신에서는 병렬 방식보다 고속 직렬 통신이 더 많이 사용된다.
UART(Universal Asynchronous Receiver/Transmitter)는 별도의 클럭 신호 없이 두 장치가 미리 약속한 baud rate에 맞춰 데이터를 주고받는 비동기 직렬 통신 방식이다.

물리적으로 일반적인 MCU UART 연결은 TX, RX, GND로 구성된다. 한 장치의 TX는 상대 장치의 RX에 연결하고, 상대 장치의 TX는 내 RX에 연결한다. TX와 RX가 분리되어 있으므로 양쪽이 동시에 송수신하는 full-duplex 통신이 가능하다.

UART 데이터 프레임은 보통 idle 상태에서 시작된다. 일반적인 MCU UART에서 idle 상태의 TX 라인은 High이며, 전송을 시작할 때 Start bit인 Low를 보낸다. 이후 설정된 data bits를 LSB first로 전송하고, 설정에 따라 parity bit를 추가할 수 있다. 마지막으로 Stop bit인 High를 전송하여 한 프레임의 끝을 나타낸다.
수신기는 RX 라인이 High 상태에서 Low로 떨어지는 Start bit를 감지하면, 미리 설정된 baud rate에 맞춰 data bit를 샘플링한다. parity가 설정되어 있으면 parity 검사를 수행하고, Stop bit가 High인지 확인하여 프레임이 정상적으로 끝났는지 판단한다.
연속적인 바이트 데이터를 전송할 때는 각 바이트마다 Start bit, Data bits, 선택적인 Parity bit, Stop bit로 구성된 프레임이 반복된다. Stop bit 이후 긴 idle 시간이 없어도 되며, 바로 다음 Start bit가 이어질 수 있다. UART의 경우 LSB bit부터 데이터를 전송한다.
MCU는 UART baud rate를 만들 때 내부 peripheral clock으로 나눈다.
UART clock / 분주값 = baud rate
만약, 분주값을 나눴을 때 소수점이 나오고 분주 레지스터가 정수만 받는 구조라면 반올림 혹은 버림을 하게 된다.
예를 들어, 80MHz의 UART clock을 가진 MCU가 목표 baud를 115200을 가질 때,
80,000,000 / 115,200 = 694.4444...
따라서, 반올림 혹은 버림을 통해 분주 레지스터에 694의 분주값이 저장된다.
그래서 실제 baud rate는
80,000,000 / 694 = 115273.7 baud
즉, 약 73 baud의 오차가 발생하게 된다.
여기서 오버샘플링 개념을 추가하면 UART는 baud rate 자체가 아니라
baud rate × oversampling에 해당하는 내부 샘플링 클럭을 만든다.
oversampling 16일 때,
USARTDIV = 80,000,000 / (16 × 115,200) = 43.40277... 이다.
일반적인 MCU의 baud rate register는 이 값을 정수부와 소수부로 나누어 저장한다.
예를 들어 소수부를 1/16 단위로 표현한다면 43.40277...은 약 43 + 6/16 = 43.375로 근사된다.
따라서 실제 baud rate는
80,000,000 / (16 × 43.375) = 약 115,273.8 baud
가 되고,
목표 115,200 baud 대비 약 73.8 baud, 약 0.064%의 오차가 발생한다.
UART 통신에서는 분주 오차뿐만 아니라 송신 장치와 수신 장치의 클럭 소스 오차도 함께 고려해야 한다. 예를 들어 내부 RC oscillator를 사용하는 경우 온도나 전압에 따라 실제 clock이 달라질 수 있고, 이 차이가 실제 baud rate 차이로 이어질 수 있다.
또한 배선 길이, 노이즈, GND 기준 차이, 전압 레벨 불일치 등은 baud rate 자체를 바꾸지는 않지만 신호 품질을 떨어뜨려 수신 오류를 만들 수 있다.
따라서 UART를 안정적으로 사용하려면 분주 오차와 클럭 소스 오차를 허용 범위 안에 두고, 신호 품질도 함께 확보해야 한다.
I2C(Inter-Integrated Circuit)은 Master의 SCL 클럭 신호에 맞춰 데이터를 주고 받는 동기 시리얼 통신이다. 일반적으로 하나의 Master가 SCL선과 SDA선으로 여러 디바이스(Slave)와 1:N으로 통신한다. SCL 클럭 신호에 맞춰 SDA선에 데이터를 올리고 샘플링하며 데이터를 전송하며 하나의 데이터 선을 이용하기 때문에 반이중 통신(half duplex)의 형태를 가진다.

물리적으로 SDA선과 SCL선에 pull up 저항을 달아, 항상 HIGH 상태로 유지하고 Master와 Slave가 필요 시에 당겨서 사용하는 Open drain 방식의 회로를 구성하고 있다. Slave에 CS(Chip Select)핀이 존재하지 않고 Master가 모든 Slave에게 주소를 보내고 주소에 맞는 Slave가 수신을 진행한다.
추가적으로, I2C에서는 속도에 따른 모드를 지원한다.
| I2C 모드 | 최대 속도 |
|---|---|
| Standard-mode | 100 kbit/s |
| Fast-mode | 400 kbit/s |
| Fast-mode Plus | 1 Mbit/s |
| High-speed mode | 3.4 Mbit/s |
| Ultra Fast-mode | 5 Mbit/s |
속도에 따라, SCL 클럭 주기가 만들어지고 데이터 전송 속도가 결정된다. 대체로 Standard mode와 Fast mode를 많이 사용하고, 이는 SCL 클럭 속도와 전기적 조건에 따라 나눈다.

I2C는 SDA 선을 통해 Master-to-Slave 또는 Slave-to-Master 방향으로 데이터를 전송한다. 버스가 idle 상태일 때 SDA와 SCL은 pull-up 저항에 의해 High 상태를 유지한다.
통신은 Master가 START condition을 생성하면서 시작된다. START condition은 SCL이 High인 동안 SDA가 High에서 Low로 변하는 조건이다.
START 이후 Master는 Slave address와 R/W bit를 전송하고, 해당 주소와 일치하는 Slave가 ACK를 응답한다. 이후 START와 STOP 사이에서 여러 데이터 바이트를 연속으로 전송할 수 있으며, 이를 burst read/write 또는 multi-byte transfer라고 부를 수 있다.
각 8비트 전송 뒤에는 9번째 SCL 클럭에서 ACK/NACK 비트가 전송된다. ACK는 데이터를 수신한 장치가 SDA를 Low로 당겨 생성한다. Master가 Slave로 데이터를 쓰는 경우에는 Slave가 ACK를 생성하고, Master가 Slave로부터 데이터를 읽는 경우에는 Master가 ACK 또는 마지막 바이트에서 NACK를 생성한다.
데이터 전송이 완료되면 Master가 STOP condition을 생성한다. STOP condition은 SCL이 High인 동안 SDA가 Low에서 High로 변하는 조건이며, 이후 버스는 다시 idle 상태가 된다.
또한 UART가 일반적으로 LSB first로 데이터를 전송하는 것과 달리, I2C는 MSB first로 데이터를 전송한다.
I2C에서 SCL은 클럭선으로 Master가 Start condition을 만든 뒤, I2C 규격에서 요구하는 최소 setup/hold timing을 만족한 다음 SCL 클럭을 생성한다. 이때, 데이터 전송 중에는 송신 측이 SCL Low 구간에서 SDA 값을 다음 비트 값으로 설정하고, SCL High 구간에서는 SDA 값을 안정적으로 유지한다. 수신 측은 SCL High 구간에서 SDA 레벨을 샘플링하여 비트를 읽는다.
SPI(Serial Peripheral Interface)는 Master의 SCLK 클럭 신호에 맞춰 데이터를 주고 받는 동기 시리얼 통신이다. I2C와 달리 주소 대신 CS(Chip Select)핀으로 디바이스를 선택하고 MOSI(Master Out Slave In) 선과 MISO(Master In Slave Out)선을 통해서 두 장치가 데이터를 주고 받을 수 있는 Full Duplex 통신의 형태를 가진다.
SPI의 경우 일반적인 MCU SPI 구현에서는 SCLK, MOSI, CS가 push-pull 출력으로 구동되고, 선택된 Slave의 MISO도 push-pull로 구동되는 경우가 많다. Master가 CS와 SCLK를 활성화하여 Slave에게 데이터를 전송할 수 있고 Slave는 반대로 Master의 핀 활성화 시점에 맞춰 데이터를 전송할 수 있다. 만약, Slave가 주도로 데이터를 전송하려면 Slave가 Master에게 GPIO Interrupt를 통해 IRQ 신호를 전송하고 Master는 이를 확인한 후 CS와 SCLK를 활성화하여 데이터를 전송하는 방식을 사용할 수도 있다.
SPI의 경우에도 모드가 존재한다. 보통 CPOL(Clock Polarity)와 CPHA(Clock Phase)의 조합으로 결정되고 4가지의 모드를 설정할 수 있다.
CPOL의 경우 SCLK가 idle일 때의 레벨을 결정한다.
CPOL 0 = SCLK idle Low
CPOL 1 = SCLK idle High
그리고, CPHA의 경우 데이터를 어느 클럭 edge에서 샘플링할지 결정한다.
CPHA 0 = 첫 번째 edge에서 샘플링
CPHA 1 = 두 번째 edge에서 샘플링
여기서 첫 번째/두 번째 edge는 CS가 활성화된 뒤 나타나는 SCLK edge 기준이다.
| SPI Mode | CPOL | CPHA | SCLK Idle | 샘플링 Edge |
|---|---|---|---|---|
| Mode 0 | 0 | 0 | Low | 첫 번째 edge, Rising |
| Mode 1 | 0 | 1 | Low | 두 번째 edge, Falling |
| Mode 2 | 1 | 0 | High | 첫 번째 edge, Falling |
| Mode 3 | 1 | 1 | High | 두 번째 edge, Rising |
따라서, Master와 Slave의 동일한 CPOL/CPHA 타이밍으로 맞아야 한다
Mode 0을 기준으로 데이터 전송 타이밍을 보면, Master가 CS를 Low로 당겨 Slave를 선택한 뒤 SCLK 클럭을 발생시킨다. Mode 0에서는 SCLK rising edge에서 수신 측이 데이터를 샘플링하고, falling edge에서 송신 측이 다음 비트로 데이터를 변경한다.
CPHA = 0인 경우 첫 번째 edge에서 바로 첫 비트를 샘플링하므로, 첫 번째 비트는 CS가 활성화된 뒤 첫 SCLK edge 전에 이미 데이터 라인에 준비되어 있어야 한다. 즉 Master는 MOSI의 첫 비트를, Slave는 MISO의 첫 비트를 첫 SCLK edge 전에 유효하게 만들어 setup time을 만족해야 한다. 그렇지 않으면 수신 측이 의도하지 않은 비트를 샘플링할 수 있다.