본문 바로가기
자동차 소프트웨어

CAN 신호 타입 완벽 정리: Periodic vs Event vs Change vs Write

by 버그없는토마토 2026. 1. 30.

CAN 신호 타입

CAN 신호 타입 완벽 정리: Periodic vs Event vs Change vs Write

Periodic? Event? vs Change? vs Write?

 

들어가며

먼저 CANDB를 보시면 다양한 메시지 타입이 있습니다.

이게 여러가지죠

[P] Periodic
[E] On Event
[PE] Periodic and On Event
[C] On Change
[EC] On Event and On Change
[EW] On Event and On Write

다양하기도 합니다...

이 신호 타입들이 각각 무슨 의미인지 그리고 어떻게 쓰이는지 에 대해 알아보도록 할게요


 

일단 자동차 CAN 버스의 속도는 최대 1 Mbps (메가비트/초) 정도죠

1 Mbps = 1,000,000 비트/초

한 개의 CAN 메시지:
- 크기: 64 비트 (8바이트)
- 시간: 64 / 1,000,000 = 0.064ms = 64마이크로초

네트워크 오버헤드 포함하면:
- 실제 처리량: ~700-800 kbps

결론:
한 초에 최대 약 10,000 ~ 15,000개의 메시지만 전송 가능!

그런데 자동차에는 수천 개의 신호가 있습니다.

엔진 신호:
- RPM, 온도, 압력, 산소 센서, ... (100+)

변속기 신호:
- 기어, 입력 속도, 출력 속도, ... (50+)

바디 신호:
- 도어, 윈도우, 조명, 와이퍼, ... (100+)

배터리 신호:
- SOC, 전압, 전류, 온도, ... (20+)

샤시 신호:
- 속도, 가속도, 조향각, ... (50+)

... 총 1,000+ 신호!

모든 신호를 계속 보낼 수 없다. 그래서 신호 타입이 존재하게 됩니다

"이 신호는 언제 보낼 것인가?"

이 글에서는 6가지 CAN 신호 타입과 각각의 특징, 사용 시기, 실무 예시를 정리해봅시다


1. CAN 신호란?

1.1 CAN 메시지 vs CAN 신호

CAN 메시지:
┌──────────────────────────────────┐
│ ID: 0x100                        │
│ DLC: 8 바이트                    │
│ Data: 12 34 56 78 9A BC DE F0   │
└──────────────────────────────────┘

CAN 신호:
CAN 메시지 안의 각 정보

Data: 12 34 56 78 9A BC DE F0
       ││ ││ ││ ││ ││ ││ ││ ││
       ││ ││ ││ ││ ││ ││ ││ └─ 신호 8: 배터리 SOC (0%)
       ││ ││ ││ ││ ││ ││ └──── 신호 7: 배터리 전압
       ││ ││ ││ ││ ││ └─────── 신호 6: 온도
       ││ ││ ││ ││ └────────── 신호 5: 압력
       ││ ││ ││ └───────────── 신호 4: 속도
       ││ ││ └──────────────── 신호 3: 기어
       ││ └─────────────────── 신호 2: RPM (하위)
       └────────────────────── 신호 1: RPM (상위)

한 메시지에 여러 신호가 포함됨!

1.2 신호의 주기성

신호의 특징:

[1] 항상 필요함
    ├─ RPM (엔진 속도)
    ├─ 속도 (차량 속도)
    └─ 온도 (엔진 온도)
    → Periodic (계속 보냄)

[2] 필요할 때만
    ├─ 에러/경고
    ├─ 비상상황
    └─ 중요 이벤트
    → On Event (발생 시에만)

[3] 변할 때만
    ├─ 도어 열림/닫힘
    ├─ 조명 ON/OFF
    └─ 기어 변화
    → On Change (변화 시만)

[4] 효율과 안정성의 결합
    ├─ 중요 신호 (브레이크, 스티어링)
    └─ 안전 신호
    → Periodic + Event / Periodic + Change

2. [P] Periodic (주기적)

2.1 정의

Periodic = 일정 주기로 계속 전송하는 신호

┌──────┐
│ 신호 │ (Periodic 100ms)
└──────┘
  │
  ├─── 0ms:    전송 ✓
  ├─── 100ms:  전송 ✓
  ├─── 200ms:  전송 ✓
  ├─── 300ms:  전송 ✓
  └─── ... 계속 반복

수신자는 항상 최신 데이터를 가짐

2.2 장단점

장점:
-> 예측 가능 (수신자가 타이밍 알 수 있음)
-> 데이터 신선도 확인 (주기 이상 안 오면 에러)
-> 구현 간단 (타이머로 주기 관리)
-> 시간 동기화 용이

단점:
!! 불필요한 대역폭 사용
  (값이 안 바뀌어도 계속 보냄)
!! CAN 버스 혼잡 가능
!! 에너지 낭비 (항상 활성화)

2.3 사용 사례

자동차에서 Periodic 신호:

엔진 신호:
- Engine RPM        (주기: 10ms)
- Engine Temperature (주기: 100ms)
- Fuel Pressure     (주기: 50ms)

구동 신호:
- Vehicle Speed     (주기: 20ms)
- Acceleration      (주기: 50ms)
- Steering Angle    (주기: 100ms)

배터리 신호:
- Battery Voltage   (주기: 100ms)
- Battery Current   (주기: 100ms)
- Battery SOC       (주기: 500ms)

왜 Periodic?
→ 항상 최신 정보 필요
→ 다른 시스템이 계속 모니터링
→ 신선한 데이터 중요

2.4 코드 예시

// Periodic 신호 전송
void SendPeriodicSignals() {
    // 100ms 주기 타이머에서 호출

    uint16_t rpm = GetEngineRPM();
    uint8_t temp = GetEngineTemp();
    uint8_t speed = GetVehicleSpeed();

    // CAN 메시지 구성
    uint8_t can_data[8];
    can_data[0] = (rpm >> 8) & 0xFF;     // RPM 상위
    can_data[1] = rpm & 0xFF;            // RPM 하위
    can_data[2] = temp;                  // 온도
    can_data[3] = speed;                 // 속도

    // 항상 전송 (주기 타이머마다)
    SendCANMessage(0x100, can_data, 8);
}

void Init_PeriodicTimer() {
    // 100ms 주기 타이머 설정
    SetupTimer(100, SendPeriodicSignals);
}

3. [E] On Event (이벤트 발생 시)

3.1 정의

On Event = 특정 이벤트 발생할 때만 전송

┌──────────────┐
│ 에러 신호    │ (On Event)
└──────────────┘
  │
  ├─── 이벤트 발생 없음
  ├─── 이벤트 발생! → 전송 ✓
  ├─── 이벤트 해결 → 해결 신호 전송 ✓
  └─── 이벤트 발생! → 전송 ✓

전송 시기가 불규칙함

3.2 장단점

장점:
-> 대역폭 효율적 (필요할 때만)
-> CAN 버스 덜 혼잡
-> 에너지 절약 (대기 중 비활성)
-> 중요한 순간에 즉시 알림

단점:
!! 응답 지연 가능
!! 예측 불가능한 트래픽
!! 누락 위험 (메시지 손실 시)
!! 수신자가 타이밍 모름

3.3 사용 사례

자동차에서 On Event 신호:

에러/경고:
- Battery Low Warning
- Engine Temperature High
- Fault Detection

비상 상황:
- Airbag Deployment
- ABS Activation
- Stability Control

예외 상황:
- Door Ajar
- Seatbelt Unbuckled
- Fuel Low

왜 On Event?
→ 자주 발생하지 않음
→ 발생할 때만 중요
→ 즉시 알림 필요

3.4 코드 예시

// On Event 신호 전송
void OnBatteryError() {
    // 배터리 오류 감지!

    uint8_t can_data[8] = {0};
    can_data[0] = 0x01;  // 배터리 오류 코드

    // 이벤트 발생 시에만 전송
    SendCANMessage(0x200, can_data, 8);

    printf("Battery error event sent!\n");
}

void OnTemperatureHigh() {
    // 과열 감지!

    uint8_t can_data[8] = {0};
    can_data[0] = 0x02;  // 과열 코드
    can_data[1] = GetEngineTemp();

    // 이벤트 발생 시에만 전송
    SendCANMessage(0x200, can_data, 8);

    printf("Temperature high event!\n");
}

// 에러 감지 ISR
void ErrorDetectionISR() {
    if (IsBatteryError()) {
        OnBatteryError();
    }

    if (IsTemperatureHigh()) {
        OnTemperatureHigh();
    }
}

4. [PE] Periodic and On Event (주기 + 이벤트)

4.1 정의

PE = 주기적으로 전송 + 이벤트 시 즉시 전송

┌──────────────────┐
│ 중요 신호 (PE)   │
└──────────────────┘
  │
  ├─── 0ms:    주기 전송 ✓
  ├─── 50ms:   주기 전송 ✓
  ├─── 80ms:   이벤트 발생! → 즉시 전송 ✓
  ├─── 100ms:  주기 전송 ✓
  ├─── 150ms:  이벤트! → 즉시 전송 ✓
  └─── 200ms:  주기 전송 ✓

정기적 + 필요 시 추가 전송

4.2 장단점

장점:
-> 안정성 높음 (주기적 업데이트)
-> 긴급 대응 가능 (이벤트 즉시)
-> 예측 가능 + 유연성
-> 안전 신호에 최적

단점:
!! 구현 복잡 (타이머 + 이벤트)
!! 대역폭 더 많이 사용
!! 관리 어려움

4.3 사용 사례

자동차에서 PE 신호:

안전 관련:
- Brake Pressure    (주기 20ms + 급제동 시 즉시)
- Steering Angle    (주기 50ms + 조향 변화 시)
- Accelerator       (주기 50ms + 급가속 시)

중요 시스템:
- Engine RPM        (주기 10ms + 과회전 감지 시)
- Transmission Gear (주기 100ms + 기어 변화 시)
- Battery Status    (주기 100ms + 비정상 시)

왜 PE?
→ 안정성과 응답성 모두 필요
→ 안전 크리티컬 신호
→ 정상/비정상 모두 감시 필요

4.4 코드 예시

// PE 신호: 주기 + 이벤트
uint8_t brake_pressure_last = 0;

void SendBrakePressure() {
    uint8_t pressure = GetBrakePressure();

    uint8_t can_data[8] = {0};
    can_data[0] = pressure;

    // 1. 주기적 전송 (20ms마다)
    SendCANMessage(0x150, can_data, 8);

    // 2. 이벤트 전송 확인
    if (IsHardBraking(pressure)) {
        // 급제동 감지!
        can_data[1] = 0xFF;  // 긴급 플래그
        SendCANMessage(0x150, can_data, 8);
        printf("Hard braking event!\n");
    }

    brake_pressure_last = pressure;
}

void Init_BrakePE() {
    // 20ms 주기 타이머
    SetupTimer(20, SendBrakePressure);
}

5. [C] On Change (변화 시)

5.1 정의

On Change = 값이 변했을 때만 전송

┌──────────┐
│ 상태신호 │ (On Change)
└──────────┘
  │
  ├─── 값: 0 (안 보냄)
  ├─── 값: 0 (안 보냄)
  ├─── 값: 1 (변함!) → 전송 ✓
  ├─── 값: 1 (같음, 안 보냄)
  ├─── 값: 1 (같음, 안 보냄)
  ├─── 값: 0 (변함!) → 전송 ✓
  └─── 값: 0 (같음, 안 보냄)

변화가 있을 때만 전송

5.2 장단점

장점:
-> 매우 효율적 (변화 없으면 안 보냄)
-> 대역폭 최소
-> 변화 감지 명확
->저전력

단점:
!! 변화 감지 어려움 (노이즈)
!! 누락 위험 높음
!! 수신자의 마지막 상태 불명확
!! 디버깅 어려움

5.3 사용 사례

자동차에서 On Change 신호:

상태 정보:
- Door Status (Open/Closed)
- Window Status (Up/Down)
- Light Status (On/Off)
- Gear Position (P/R/N/D)

스위치:
- Wiper Activated
- Headlight Activated
- Turn Signal

모드:
- Cruise Control (On/Off)
- Parking Mode (On/Off)

왜 On Change?
→ 자주 변하지 않음
→ 변했다는 것이 중요
→ 상태 정보

5.4 코드 예시

// On Change 신호
static uint8_t door_status_last = 0;

void SendDoorStatus() {
    uint8_t door_status = GetDoorStatus();

    // 변화 감지?
    if (door_status != door_status_last) {
        // 변했다! 전송
        uint8_t can_data[8] = {0};
        can_data[0] = door_status;

        SendCANMessage(0x300, can_data, 8);
        printf("Door status changed to: %d\n", door_status);

        door_status_last = door_status;
    }
    // 변화 없으면 아무것도 안 함
}

void MainLoop() {
    // 10ms마다 확인 (변화 감지하기 위해)
    SendDoorStatus();
}

6. [EC] On Event and On Change (이벤트 + 변화)

6.1 정의

EC = 이벤트 발생 시 또는 값이 변했을 때 전송

┌──────────────┐
│ 복합 신호    │ (On Event + On Change)
└──────────────┘
  │
  ├─── 값: 0 (안 보냄)
  ├─── 이벤트 발생! → 전송 ✓
  ├─── 값: 1 (변함!) → 전송 ✓
  ├─── 값: 2 (변함!) → 전송 ✓
  ├─── 이벤트 발생! → 전송 ✓
  └─── 값: 1 (변함!) → 전송 ✓

이벤트 또는 변화 감지 시 전송

6.2 사용 사례

자동차에서 EC 신호:

배터리 신호:
- Battery Voltage (값 변화 + 비정상 이벤트)
- Battery Temperature (값 변화 + 과열 이벤트)

엔진 신호:
- Oil Pressure (값 변화 + 저압 경고)
- Coolant Temperature (값 변화 + 과열 경고)

시스템 신호:
- DPF Status (상태 변화 + 재생 이벤트)
- Transmission Health (상태 변화 + 오류 이벤트)

왜 EC?
→ 정상 변화도 중요 (모니터링)
→ 이상 상황도 중요 (경고)
→ 둘 다 감지 필요

6.3 코드 예시

static uint8_t battery_voltage_last = 0;
static uint8_t battery_error_sent = 0;

void SendBatteryStatus() {
    uint8_t voltage = GetBatteryVoltage();  // 0~255 범위
    uint8_t is_error = IsBatteryError();

    uint8_t should_send = 0;

    // 1. 값이 변했는가?
    if (voltage != battery_voltage_last) {
        should_send = 1;
        battery_voltage_last = voltage;
    }

    // 2. 에러 이벤트 발생했는가?
    if (is_error && !battery_error_sent) {
        should_send = 1;
        battery_error_sent = 1;
    } else if (!is_error && battery_error_sent) {
        should_send = 1;
        battery_error_sent = 0;
    }

    // 전송
    if (should_send) {
        uint8_t can_data[8] = {0};
        can_data[0] = voltage;
        can_data[1] = is_error ? 0xFF : 0x00;

        SendCANMessage(0x400, can_data, 8);
        printf("Battery status: V=%d, Error=%d\n", voltage, is_error);
    }
}

7. [EW] On Event and On Write (이벤트 + 쓰기)

7.1 정의

EW = 이벤트 발생 또는 외부에서 값을 쓸 때 전송

┌──────────────────────┐
│ 제어/진단 신호 (EW)  │
└──────────────────────┘
  │
  ├─── 내부 값: 0
  ├─── 외부에서 값 쓰기! → 전송 ✓
  ├─── 이벤트 발생! → 전송 ✓
  ├─── 내부 값: 1
  └─── 외부에서 값 쓰기! → 전송 ✓

외부 제어 + 이벤트 감지

7.2 사용 사례

자동차에서 EW 신호:

진단/제어:
- Diagnostic Command (진단 도구에서 쓰기)
- Test Mode (테스트 중 제어)
- Calibration Value (캘리브레이션)

소프트웨어 업데이트:
- OTA Update Command
- Bootloader Mode
- Firmware Version

특별 기능:
- Service Mode (정비 중 제어)
- Engineering Mode

왜 EW?
→ 외부 명령 실행 필요 (진단 도구)
→ 이벤트도 감시 (상태 변화)
→ 명시적 제어 + 모니터링

7.3 코드 예시

static uint8_t diag_command_last = 0;

void SendDiagnosticStatus() {
    uint8_t diag_command = GetDiagnosticCommand();
    uint8_t diag_event = GetDiagnosticEvent();

    uint8_t should_send = 0;

    // 1. 진단 명령이 써졌는가? (외부 제어)
    if (diag_command != diag_command_last) {
        should_send = 1;
        diag_command_last = diag_command;
        printf("Diagnostic command received: %d\n", diag_command);
    }

    // 2. 진단 이벤트 발생?
    if (diag_event) {
        should_send = 1;
    }

    // 전송
    if (should_send) {
        uint8_t can_data[8] = {0};
        can_data[0] = diag_command;
        can_data[1] = diag_event;

        SendCANMessage(0x700, can_data, 8);
    }
}

// 외부에서 진단 명령 쓰기 (진단 도구)
void WriteDiagnosticCommand(uint8_t cmd) {
    SetDiagnosticCommand(cmd);
    // → SendDiagnosticStatus()에서 감지하여 전송
}

8. 신호 타입 비교 및 선택

8.1 비교표

신호 타입  주기  이벤트  변화  외부제어  대역폭  응답성  구현도
────────────────────────────────────────────────────────────
P        ✓    ✗      ✗    ✗      높      낮    낮
E        ✗    ✓      ✗    ✗      낮      높    낮
C        ✗    ✗      ✓    ✗      최저    중    중
PE       ✓    ✓      ✗    ✗      중~높  높    높
EC       ✗    ✓      ✓    ✗      중      높    중
EW       ✗    ✓      ✗    ✓      낮      높    높

8.2 선택 기준

신호를 선택할 때:

1단계: 신호의 특성 파악
  └─ 항상 필요? → P (Periodic)
  └─ 가끔 필요? → E (Event)
  └─ 변할 때? → C (Change)

2단계: 안정성 요구
  └─ 높음 → P 포함 (Periodic 포함)
  └─ 중간 → PE, EC 고려
  └─ 낮음 → E, C 가능

3단계: 대역폭 고려
  └─ 여유 있음 → PE (안정성 우선)
  └─ 부족 → C, E (효율성 우선)
  └─ 부족함 → E or C만 (최소)

4단계: 외부 제어 필요?
  └─ 필요 → EW
  └─ 불필요 → 제외

9. 실무 시나리오

9.1 엔진 제어 신호

엔진 RPM:
- 특성: 항상 필요, 중요
- 안정성: 높음
- 선택: Periodic (10ms)
  → 엔진 제어에 항상 필요

엔진 온도:
- 특성: 항상 필요, 변화 느림
- 안정성: 높음 + 경고 필요
- 선택: Periodic + On Event (100ms + 과열 시)
  → PE (주기 100ms + 과열 경고)

엔진 오류:
- 특성: 가끔, 중요
- 안정성: 높음
- 선택: On Event
  → E (오류 발생 시에만)

9.2 도어 제어 신호

도어 잠금:
- 특성: 상태 정보, 자주 안 바뀜
- 안정성: 중간
- 선택: On Change
  → C (잠금/해제 시만)

도어 열림:
- 특성: 상태 정보 + 경고 필요
- 안정성: 높음
- 선택: Periodic + On Change
  → PC (주기 100ms + 변화 시)
  또는 PE (주기 + 열린 경고)

9.3 배터리 신호

배터리 전압:
- 특성: 항상 필요 + 변화도 중요
- 안정성: 높음
- 선택: Periodic + On Change
  → PC (주기 100ms + 변화 시)

배터리 오류:
- 특성: 이벤트 + 값 변화도 중요
- 안정성: 높음
- 선택: On Event + On Change
  → EC (오류 발생 + 전압 변화)

10. 자주 하는 실수

10.1 신호 타입 선택 오류

!! 잘못된 예:
// 브레이크 압력을 On Change로 설정
// → 주기적 모니터링 불가능
// → 급제동 감지 지연 가능

-> 올바른 예:
// 브레이크 압력: Periodic + On Event
// → 안정적 모니터링 + 즉각 응답

10.2 주기 설정 오류

!! 잘못된 예:
// 엔진 RPM을 1000ms (1초) 주기로 설정
// → 엔진 제어 지연, 응답 불가능

-> 올바른 예:
// 엔진 RPM: 10ms 주기
// → 빠른 응답, 안정적 제어

10.3 이벤트 조건 모호

!! 잘못된 예:
if (temperature > 100) {
    // 과열 이벤트
    // 100도 정확히일 때는?
    // 노이즈 때문에 반복 전송?
}

-> 올바른 예:
#define TEMP_THRESHOLD 100
#define TEMP_HYSTERESIS 5

if (temperature > TEMP_THRESHOLD && !overheating_flag) {
    // 과열 시작
    overheating_flag = 1;
    SendEvent();
} else if (temperature < (TEMP_THRESHOLD - TEMP_HYSTERESIS) && overheating_flag) {
    // 과열 해제
    overheating_flag = 0;
    SendEvent();
}

11. 핵심 정리

CAN 신호 타입:

[P] Periodic
  └─ 항상 필요한 신호
  └─ 고정 주기로 계속 전송
  └─ 예: RPM, 온도, 속도

[E] On Event
  └─ 이벤트 발생 시만
  └─ 경고, 오류, 비상
  └─ 예: 에러 코드, 경고음

[PE] Periodic + Event
  └─ 안정성 + 응답성
  └─ 중요 신호
  └─ 예: 브레이크, 스티어링

[C] On Change
  └─ 상태 정보
  └─ 변화 없으면 미전송
  └─ 예: 도어, 조명, 기어

[EC] Event + Change
  └─ 이벤트 + 값 변화
  └─ 예: 배터리, 센서

[EW] Event + Write
  └─ 이벤트 + 외부 제어
  └─ 예: 진단, 제어

선택 기준:
- 신호의 중요도
- 변화 빈도
- 안정성 요구
- 대역폭 제약
- 외부 제어 여부