1. ISO 11898 기반 CAN 버스 통신 프로토콜 도입 배경 및 개요
현대 임베디드 시스템 및 차량용 마이크로컨트롤러 환경에서는 다수의 ECU(Electronic Control Unit) 간의 실시간 데이터 교환이 필수적입니다. 기존의 점대점(Point-to-Point) 배선 방식은 차량 내 하네스(Harness)의 무게와 복잡도를 대폭 증가시켜 생산 비용 상승 및 신뢰성 저하 문제를 유발합니다.
Bosch가 개발하고 ISO 11898로 표준화된 CAN(Controller Area Network) 프로토콜은 이러한 하네스 복잡도를 해결하기 위해 등장한 2선식(Twisted Pair) 멀티마스터 직렬 통신 규격입니다. 각 노드는 물리적 주소가 아닌 11비트(Standard) 또는 29비트(Extended) 식별자(Identifier) 기반으로 메시지를 식별합니다. 본 포스팅에서는 CAN 프로토콜의 표준화 역사, 프레임 구조, CSMA/CA 메커니즘, 그리고 최신 CAN FD 표준으로의 발전 과정을 다룹니다.
2. CAN 프로토콜 핵심 요약 및 표준 스펙 정리
- 통신 방식 및 토폴로지: 2선식 Differential Signal(CAN_H, CAN_L) 기반 버스 토폴로지 및 CSMA/CA 비트 단위 중재 방식 사용.
- 표준 규격 비교: ISO 11898-1(Data Link Layer), ISO 11898-2(Physical Layer High-Speed up to 1Mbps), ISO 11898-1:2015(CAN FD up to 8Mbps, Data 64 Bytes).
- 핵심 데이터 프레임: Identifier(11비트/29비트), DLC(4비트), Data Payload(0~8 Bytes), CRC(15비트).
3. CAN 통신 아키텍처 상세 분석 및 ISO 11898 기술 규격
3.1 CAN 통신 물리 계층 및 차동 전압(Differential Voltage) 특성
CAN 통신은 외부 전자기 간섭(EMI)을 방지하기 위해 CAN_H 및 CAN_L 2선 차동 신호 방식을 채택합니다. 논리 상태는 Dominant(0)와 Recessive(1)로 구분됩니다.
| 논리 상태 (Logic State) | 버스 상태 (Bus State) | CAN_H 전압 (Typical) | CAN_L 전압 (Typical) | 차동 전압 Vdiff (VH−VL) |
| Logic 0 | Dominant | 3.5V | 1.5V | 2.0V |
| Logic 1 | Recessive | 2.5V | 2.5V | 0.0V |
3.2 CSMA/CA 기반 Bitwise Arbitration 메커니즘
버스 상에서 두 개 이상의 노드가 동시에 프레임 전송을 시작하면 비트 단위 중재(Bitwise Arbitration)가 진행됩니다. 송신 노드는 자신의 ID 비트를 1비트씩 출력하는 동시에 버스의 실제 전송 상태를 모니터링합니다.
/* Pseudocode: Bitwise Arbitration Logic in Controller Transceiver */
void can_transmit_frame(CAN_Frame *frame) {
uint32_t id = frame->id;
for (int i = 10; i >= 0; i--) {
uint8_t bit_to_send = (id >> i) & 0x01;
drive_can_bus(bit_to_send);
/* Read back bus state to handle collision */
uint8_t bus_state = read_can_bus();
if (bit_to_send == RECESSIVE_1 && bus_state == DOMINANT_0) {
/* Arbitration Lost: Switch immediately to Receiver Mode */
set_can_mode(CAN_MODE_RECEIVER);
return;
}
}
/* Arbitration Won: Continue transmitting Control and Data fields */
}
특정 노드가 Recessive (1) 비트를 송신하고 있는 동안 다른 노드가 Dominant (0) 비트를 전송하면, 버스 레벨은 Dominant (0) 상태로 고정됩니다. Recessive를 송신하던 노드는 자신이 전송한 비트와 버스의 비트가 불일치함을 감지하고 즉시 송신을 중단(Arbitration Lost)하며 수신 모드로 전환됩니다.
3.3 CAN 2.0B 데이터 프레임 필드 구조
ISO 11898-1 데이터 프레임은 아래와 같은 비트 필드로 구성됩니다.
+-----+-------------------+-----+-----+------+------------------+---------+-----+-----+
| SOF | Identifier (ID) | RTR | IDE | DLC | Data Payload | CRC | ACK | EOF |
| 1b | 11b (Std) / 29b | 1b | 1b | 4b | 0 - 8 Bytes | 15b | 2b | 7b |
+-----+-------------------+-----+-----+------+------------------+---------+-----+-----+
- SOF (Start of Frame): Dominant 1비트로 프레임의 시작을 동기화합니다.
- Arbitration Field: Identifier 비트와 RTR(Remote Transmission Request) 비트로 구성됩니다.
- Control Field: IDE(Identifier Extension) 비트, r0(예약 비트), 그리고 데이터 길이를 나타내는 DLC(Data Length Code, 4비트)로 구성됩니다.
- Data Field: 실제 전달되는 데이터 페이로드(0 ~ 8 Bytes)입니다.
- CRC Field: 15비트 CRC 체크섬과 1비트 CRC Delimiter로 구성됩니다.
- ACK Field: ACK Slot(1비트) 및 ACK Delimiter(1비트)로 구성되며, 수신 노드가 Dominant 비트로 덮어써 수신 성공을 알립니다.
- EOF (End of Frame): 연속된 7비트 Recessive 상태로 프레임 종료를 전달합니다.
3.4 Linux SocketCAN 기반 수신 C 코드 예제
Linux OS 환경에서 SocketCAN API를 활용하여 CAN 데이터 프레임을 읽어오는 표준 코드 구현법입니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main(void) {
int s;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
/* Create CAN Raw Socket */
if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Socket creation failed");
return 1;
}
/* Specify CAN interface can0 */
strcpy(ifr.ifr_name, "can0");
ioctl(s, SIOCGIFINDEX, &ifr);
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/* Bind socket to CAN interface */
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Bind failed");
close(s);
return 1;
}
/* Read CAN frame */
int nbytes = read(s, &frame, sizeof(struct can_frame));
if (nbytes < 0) {
perror("Read failed");
close(s);
return 1;
}
printf("Received CAN ID: 0x%03X, DLC: %d\n", frame.can_id, frame.can_dlc);
for (int i = 0; i < frame.can_dlc; i++) {
printf("Data[%d]: 0x%02X\n", i, frame.data[i]);
}
close(s);
return 0;
}
4. CAN 버스 실무 구축 및 Linux candump 디버깅 팁
- 하드웨어 종단 저항 (Termination Resistor) 검증: CAN 버스 양단 끝 노드에는 반드시 $120\Omega$ 종단 저항이 병렬로 배치되어야 합니다. 멀티미터로 CAN_H와 CAN_L 사이의 전원 off 상태 저항을 측정했을 때 합성 저항 값이 약 $60\Omega$이 나오는지 검증하세요.
- SocketCAN 명령어 활용 방법: Linux 개발 환경(Raspberry Pi, Jetson, NXP i.MX 등)에서는 can-utils 패키지를 설치하여 트래픽을 모니터링할 수 있습니다.
# Configure Bitrate to 500kbps and bring up can0 interface
sudo ip link set can0 type can bitrate 500000
sudo ip link set up can0
# Display real-time CAN traffic on terminal
candump can0
# Send Standard CAN Data Frame (ID: 0x123, Data: DE AD BE EF)
cansend can0 123#DEADBEEF
5. CAN 통신 구축 시 트러블슈팅 및 흔히 하는 실수
| 오류 현상 (Issue) | 원인 (Root Cause) | 해결 방법 (Resolution) |
| Bus Off State 진단 발생 | Transmit Error Counter(TEC)가 255를 초과함 (물리선 단선, GND 미연결 또는 비트레이트 불일치) | CAN_H/CAN_L 배선 상태, $120\Omega$ 종단 저항 측정($60\Omega$ 확인), 모든 노드의 Bit Timing 설정 일치 여부 점검 |
| ACK Error 연속 발생 | 네트워크 상에 메시지를 수신받아 ACK 비트를 덮어쓸 다른 노드가 없음 (단일 노드 테스트 시 발생) | 최소 2개 이상의 노드를 버스에 연결하거나, CAN 트랜시버/컨트롤러의 Loopback 모드 활성화 후 테스트 |
| Stuff Error 발생 | 송신 데이터에 동일한 논리 비트가 연속 5개 이상일 때 Bit Stuffing 실패 (클럭 오차 범위 초과) | 각 노드의 MCU 시스템 클럭 오차(Oscillator Tolerance) 확인 및 CAN Bit Timing(SJW, BS1, BS2) 레지스터 재설정 |
6. ISO 11898 표준 CAN 프로토콜 분석 마무리
CAN 프로토콜은 차동 신호 기반의 높은 내노이즈 특성과 CSMA/CA 비트 단위 중재 알고리즘을 통해 뛰어난 신뢰성을 보장하는 기술입니다. 본 가이드에서 제시한 차동 전압 규격, 데이터 프레임 필드 분석, SocketCAN C 구현 및 $60\Omega$ 종단 저항 검증을 바탕으로 안정적인 임베디드 통신 시스템을 구축하기 바랍니다.