CAN — 차량이 쓰는 버스
UART·SPI·I²C 는 누가 누구에게 보내는지를 정한다. CAN 은 다르다 — 아무도 주소를 갖지 않고, 메시지에 ID 가 붙는다. 관심 있는 노드가 골라서 받는다.
그리고 CAN 의 설계 전체가 "차 안에서 잡음을 견디며 끊기지 않는 것" 에 맞춰져 있다.
차동 신호 — 잡음을 이기는 방식
CAN_H ──── 3.5V ──── 우성(0) 일 때
CAN_L ──── 1.5V ──── 전압 차 = 2V
CAN_H ──── 2.5V ──── 열성(1) 일 때
CAN_L ──── 2.5V ──── 전압 차 = 0V
두 선의 차이로 값을 읽는다. 외부 잡음이 들어오면 두 선에 똑같이 실리므로 차이는 그대로다 — 잡음이 상쇄된다. 이것이 차동 신호의 핵심이고, 엔진룸처럼 잡음이 심한 곳에서도 통신이 되는 이유다.
잡음 +1V 가 두 선에 실리면
CAN_H 3.5V → 4.5V
CAN_L 1.5V → 2.5V
차이 2V → 2V ← 변하지 않는다
종단 저항 120Ω
노드 ──┬──────────────────────────┬── 노드
[120Ω] [120Ω]
└── 버스 양 끝에만 ──────────┘
종단 저항은 선 끝에서 신호가 반사되는 것을 막는다. 없으면 반사파가 원래 신호와 겹쳐 파형이 깨진다.
증상별 진단 (전원 끄고 CAN_H↔CAN_L 저항을 잰다)
60Ω 근처 ✓ 정상 (120Ω 둘이 병렬)
120Ω ✗ 한쪽만 있다 — 통신이 되다 안 되다 한다
40Ω 이하 ✗ 종단이 셋 이상 — 중간 노드에 달린 것을 떼야 한다
수 kΩ ✗ 종단이 없다
"짧은 선에서는 되는데 길게 하면 안 된다" 의 원인이 대개 종단이다. 그리고 종단은 양 끝 두 개뿐이어야 한다 — 중간 노드에 달면 전류가 과다해 신호 진폭이 준다.
프레임 구조

[SOF] [ID 11비트] [RTR] [IDE] [r0] [DLC 4비트] [데이터 0~8바이트] [CRC 15비트] [ACK] [EOF]
1 11 1 1 1 4 0~64 15+1 1+1 7
| 필드 | 뜻 |
|---|---|
| SOF | 프레임 시작 (우성 1비트) |
| ID | 메시지 식별자. 11비트(표준) 또는 29비트(확장) |
| RTR | 원격 전송 요구 — "이 ID 의 데이터를 보내 달라" |
| DLC | 데이터 길이 (0~8바이트) |
| CRC | 15비트 순환 중복 검사 |
| ACK | 받은 노드가 하나라도 있으면 우성으로 당긴다 |
데이터가 최대 8바이트라는 점이 CAN 의 큰 제약이다. 그래서 긴 데이터는 여러 프레임으로 쪼개는 상위 프로토콜(ISO-TP)이 필요하다. CAN FD 는 이를 64바이트로 늘렸다.
중재 — 주소가 없는데 충돌은 어떻게
버스는 와이어드 AND — 0(우성)이 1(열성)을 이긴다
노드 A ID = 0b00010011000
노드 B ID = 0b00010100000
^ 여기서 A 는 0, B 는 1 을 보낸다
선은 0 이 되고, B 는 "내가 1 을 보냈는데 0 이 읽힌다" 를 알아챈다
→ B 가 즉시 물러난다. A 는 아무 방해 없이 계속 보낸다
ID 가 작을수록 우선순위가 높다. 그리고 진 쪽은 데이터가 깨지지 않고 그냥 물러나 다음 기회에 다시 보낸다 — 비파괴 중재(non-destructive arbitration) 다.
이더넷의 충돌 감지와 비교하면 차이가 분명하다.
이더넷 충돌하면 양쪽 다 버리고 무작위 시간 뒤 재시도 → 대역폭 낭비
CAN 충돌하면 우선순위 높은 쪽이 그대로 진행 → 낭비 0
그래서 CAN 은 최악 지연을 계산할 수 있다. 브레이크 신호에 낮은 ID 를 주면 "아무리 바빠도 이만큼 안에 전달된다" 를 보장할 수 있어, 안전 시스템에 쓸 수 있다.
// ID 배정이 곧 우선순위 설계다
#define ID_BRAKE 0x010 // 가장 급하다
#define ID_ENGINE 0x0A0
#define ID_DASHBOARD 0x300
#define ID_DIAGNOSTIC 0x7DF // 급하지 않다
비트 스터핑
같은 값이 5개 연속되면 → 반대 값 1비트를 강제로 끼워 넣는다
원본 11111 0000 0
스터핑 11111 0 0000 1
^ ^ 삽입된 비트
클럭선이 없으므로 수신 쪽이 엣지를 보고 동기를 유지해야 한다. 같은 값이 계속되면 엣지가 없어 시계가 어긋나므로, 강제로 변화를 만들어 준다.
부작용이 있다 — 프레임 길이가 데이터에 따라 달라진다. 최악의 경우 표준 프레임이 약 20% 길어져, 정확한 최악 지연 계산에는 이것을 반영해야 한다.
비트 타이밍 — 모든 노드가 같아야 한다
한 비트 = 동기 세그먼트 + 전파 세그먼트 + 위상 세그먼트1 + 위상 세그먼트2
// 500kbps @ 42MHz APB1
// 42MHz / (프리스케일러 × (1 + BS1 + BS2)) = 500kHz
CAN1->BTR = (5 << 0) // 프리스케일러 - 1 = 5 → /6
| (11 << 16) // BS1 = 12 tq
| (1 << 20); // BS2 = 2 tq
// 42M / 6 / (1+12+2) = 466.7k ← 안 맞는다. 조합을 다시 계산해야 한다
비트 타이밍이 한 노드만 달라도 그 노드는 통신에 참여할 수 없다. 그리고 증상이 고약하다 — 오류 프레임을 계속 뿌려 버스 전체를 마비시킨다.
온라인 계산기나 벤더 도구로 조합을 구하는 것이 실무이고, 샘플 포인트를 75~87.5% 에 두는 것이 권장된다.
실제로 값을 골라 보기 — 왜 아무 클럭이나 안 되나
500kbps 를 만들려면 BRP(분주비)가 정수여야 한다. 42MHz 에서 총 tq 수를 바꿔 가며 계산하면 선택지가 생각보다 적다는 것이 드러난다.
BRP = f_APB / (bitrate × 총_tq)
| 총 tq | BRP 계산 | 정수인가 |
|---|---|---|
| 14 | 42MHz / (500k × 14) = 6 | ✓ |
| 16 | 42MHz / (500k × 16) = 5.25 | ✗ |
| 20 | 42MHz / (500k × 20) = 4.2 | ✗ |
42MHz 에서 500kbps 는 총 14 tq 조합만 가능하다. 그래서 CAN 을 쓸 보드는 APB 클럭을 먼저 CAN 이 떨어지는 값으로 고르는 것이 순서다 — 클럭을 정해 놓고 CAN 을 맞추려 하면 막힌다.
총 14 tq 를 세그먼트로 나누면 샘플 포인트가 정해진다.
SYNC_SEG(1) + TSEG1(11) + TSEG2(2) = 14 tq
샘플 포인트 = (1 + 11) / 14 = 85.7% ← CiA 권고 87.5% 에 근접 ✓
왜 샘플 포인트를 뒤쪽(75~87.5%)에 두나 — CAN 은 한 비트 시간 안에 신호가 버스 양 끝을 왕복해야 중재와 ACK 가 성립한다(중재 중에는 여러 노드가 동시에 송신한다). 전파 지연과 트랜시버 지연이 앞부분을 잡아먹으므로, 신호가 안정된 뒤쪽에서 읽어야 한다. 그래서 버스가 길수록(전파 지연이 클수록) 샘플 포인트를 더 뒤로 민다.
함정 — 노드마다 샘플 포인트가 크게 다르면 어떤 노드는 읽고 어떤 노드는 못 읽는 상태가 되어 에러 프레임이 산발적으로 뜬다. "한 노드만 바꿨는데 버스 전체가 불안정" 의 원인이 대개 비트 타이밍 불일치다. 모든 노드의 총 tq·샘플 포인트를 표로 관리해야 한다.
오류 처리 — 스스로 버스에서 빠진다
CAN 은 각 노드가 오류 카운터를 갖는다.
송신 오류 카운터(TEC) · 수신 오류 카운터(REC)
TEC < 128 Error Active 정상. 오류를 적극적으로 알린다
TEC ≥ 128 Error Passive 조용해진다. 오류 알림을 약하게
TEC ≥ 256 Bus Off ★ 버스에서 완전히 분리된다
버스 오프는 CAN 의 중요한 안전 장치다. 오류가 쌓이면 먼저 조용해지고(Error Passive), 그래도 계속되면 스스로 버스에서 빠진다 — 고장 난 노드 하나가 계속 오류를 뿌려 버스 전체를 망치는 것을 막기 위해서다.
// Bus Off 를 감지하고 복구하는 것은 애플리케이션 책임이다
if (CAN1->ESR & CAN_ESR_BOFF) {
// 자동 복구를 켜 두었다면 128 × 11 연속 열성 비트를 관측한 뒤 복귀한다
// 안 켰다면 직접 초기화해야 한다
can_reinit();
boff_count++; // 이 카운터를 로그로 남긴다
}
Bus Off 가 반복되면 배선·종단·비트 타이밍 문제다. 소프트웨어로 계속 복구만 하면 근본 원인을 못 본다. 그래서 발생 횟수를 세어 남기는 것이 중요하다.
필터 — 관심 있는 것만 받는다
// 하드웨어 필터를 안 걸면 모든 프레임에 인터럽트가 걸린다
// 차량 버스는 초당 수천 프레임이라 CPU 가 그것만 하다 끝난다
// 마스크 모드 — ID 의 특정 비트만 검사
CAN1->sFilterRegister[0].FR1 = (0x100 << 5); // 원하는 ID
CAN1->sFilterRegister[0].FR2 = (0x700 << 5); // 마스크: 상위 3비트만 검사
// → 0x100~0x1FF 를 전부 받는다
필터를 하드웨어에 맡기는 것이 필수다. 소프트웨어로 걸러도 되지만, 인터럽트 진입 자체가 이미 비용이다.
상위 프로토콜
CAN 은 "8바이트를 잡음 없이 전달" 까지만 한다. 그 위에 규칙이 필요하다.
| 무엇을 얹나 | |
|---|---|
| ISO-TP (ISO 15765-2) | 8바이트를 넘는 데이터를 여러 프레임으로 분할·재조립 |
| OBD-II / UDS | 진단 명령 체계. 정비소 스캐너가 쓰는 것 |
| CANopen | 산업 장비용 객체 사전·상태 기계 |
| J1939 | 상용차·중장비 표준 |
// ISO-TP 의 분할 — 첫 프레임에 전체 길이를 담는다
// [0x1n nn] 첫 프레임 (n nn = 12비트 길이)
// [0x2n] 연속 프레임 (n = 순번 0~15 순환)
실무 진단 순서
① 전원 끄고 CAN_H↔CAN_L 저항 60Ω 근처여야 한다
② 전원 켜고 두 선의 전압 유휴 시 둘 다 약 2.5V
③ 오실로스코프로 차동 파형 진폭 2V, 사각형인가
④ 노드 하나만 남기고 송신 시도 ACK 가 없어 오류가 나는 것이 정상
⑤ 두 노드로 늘려 통신 확인
⑥ 오류 카운터·Bus Off 횟수를 로그로
④가 유용하다. CAN 은 ACK 를 받아야 성공인데, 혼자면 아무도 ACK 를 안 주므로 오류가 나는 것이 정상이다. 이걸 모르면 "혼자 테스트하는데 왜 안 되나" 로 시간을 버린다.
# 리눅스 SocketCAN 으로 확인하면 훨씬 빠르다
ip link set can0 up type can bitrate 500000
candump can0 # 흐르는 프레임 관찰
cansend can0 123#DEADBEEF # 보내 보기
ip -details -statistics link show can0 # 오류 카운터·Bus Off 횟수
CAN FD
| Classical CAN | CAN FD | |
|---|---|---|
| 데이터 | 8바이트 | 64바이트 |
| 속도 | 1Mbps | 데이터 구간만 5~8Mbps |
| CRC | 15비트 | 17/21비트 |
중재 구간은 느리게, 데이터 구간만 빠르게 하는 것이 FD 의 발상이다. 중재는 모든 노드가 동시에 판단해야 하므로 전파 시간에 묶여 있지만, 데이터는 승자 혼자 보내므로 빠르게 갈 수 있다.
한눈에 정리
- 차동 신호 — 잡음이 두 선에 똑같이 실려 차이는 그대로다. 그래서 엔진룸에서도 된다
- 종단 120Ω 을 양 끝에만. 전원 끄고 재서 60Ω 근처면 정상
- 주소가 없고 메시지에 ID 가 붙는다 — 관심 있는 노드가 골라 받는다
- ID 가 작을수록 우선순위가 높고, 진 쪽은 데이터를 잃지 않는다(비파괴 중재) → 최악 지연 계산이 가능하다
- **비트 스터핑**으로 동기를 유지하지만 프레임 길이가 데이터에 따라 변한다
- 비트 타이밍이 한 노드만 달라도 버스 전체가 마비된다
- Bus Off 는 고장난 노드가 스스로 빠지는 안전 장치. 반복되면 하드웨어 문제이므로 횟수를 로그로 남긴다
- 하드웨어 필터는 필수 — 차량 버스는 초당 수천 프레임이다
- 혼자 테스트하면 ACK 가 없어 오류가 나는 것이 정상
- 데이터 8바이트 한계 때문에 ISO-TP·UDS·CANopen 같은 상위 프로토콜이 필요하다
- CAN FD 는 중재는 느리게 데이터만 빠르게 — 중재가 전파 시간에 묶여 있기 때문
꼬리질문 대비
- "주소가 없는데 충돌은 어떻게 해결하나?" → ID 로 비트 단위 중재. dominant(0)가 recessive(1)를 이겨 낮은 ID 가 우선, 진 쪽은 자동 재시도
- "42MHz 에서 500kbps 를 만들 때 총 tq 를 아무거나 못 쓰는 이유는?" → BRP 가 정수여야 한다 — 14 tq 만 BRP=6 으로 떨어진다
- "샘플 포인트를 왜 뒤쪽(87.5%)에 두나?" → 중재·ACK 때문에 한 비트 안에 신호가 버스를 왕복해야 한다. 안정된 뒤 읽어야 한다
- "노드 하나를 바꿨더니 버스 전체가 불안정하다면?" → 비트 타이밍(총 tq·샘플 포인트) 불일치를 먼저 본다
출처 — ISO 11898-1(CAN 데이터 링크 계층)·ISO 11898-2(고속 물리 계층·종단) · Bosch, CAN Specification 2.0 · ISO 15765-2(ISO-TP) · STMicroelectronics, RM0090 STM32F4 Reference Manual §32 bxCAN(BTR·ESR·필터) · Linux SocketCAN 문서