임베디드 학습 노트 목차

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Ω      ✗ 종단이 없다

"짧은 선에서는 되는데 길게 하면 안 된다" 의 원인이 대개 종단이다. 그리고 종단은 양 끝 두 개뿐이어야 한다 — 중간 노드에 달면 전류가 과다해 신호 진폭이 준다.

프레임 구조

CAN 표준 프레임 — SOF, 11비트 ID, RTR, 제어, 데이터, CRC, ACK, EOF 순서.
CAN-Bus frame in base format — CC BY-SA 3.0 (Wikimedia Commons)

[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바이트)
CRC15비트 순환 중복 검사
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)
총 tqBRP 계산정수인가
1442MHz / (500k × 14) = 6
1642MHz / (500k × 16) = 5.25
2042MHz / (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 CANCAN FD
데이터8바이트64바이트
속도1Mbps데이터 구간만 5~8Mbps
CRC15비트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 문서

I²C — 선 두 개로 여럿을 다룬다LIN — 한 가닥으로 싸게, 느리게