프로그래밍/작은 메모

Modbus와 CRC16, CRC32 데이터 유효성 검사

satnurn 2025. 10. 17. 16:51
반응형

1. Modbus

# "Modbus"라는 이름의 유래

Modbus는 “Modicon Bus”의 줄임말

  • Modicon = 1970년대 초 Modicon사(현재 슈나이더 일렉트릭) 가 만든 PLC(Programmable Logic Controller) 브랜드
  • Bus = 여러 장치가 데이터를 주고받는 통신선

 

Modbus = Modicon이 만든 산업용 통신 버스 프로토콜으로 탄생함.

1979년에 공개되어 지금까지도 전 세계 산업 현장에서 가장 널리 쓰이는 오픈 프로토콜 중 하나임.

 

 

 

 

 


 

 

 

# Modbus 구성

Modbus는 “CRC 다항식” 하나로 구분되지않음.

대신 다음 3가지 구성 요소로 정의.

구성 요소설명

 

통신 구조 (프레임 포맷) [Slave Address][Function Code][Data][CRC16] 같은 구조
기능 코드(Function Code) 0x03: Holding Register 읽기, 0x06: Write Single Register 등
오류 검출 방식 (CRC16) CRC 다항식 0xA001 사용 (MSB일 경우, 0x8005)

즉, Modbus는 단순히 CRC만의 이름이 아니라,
“데이터 구조 + 명령 규칙 + CRC 검증 방식”을 모두 포함한 프로토콜 전체 이름.

 

 

 

 

 


 

 

 

 # 왜 CRC도 “Modbus CRC”라고 부를까?

  • Modbus 프로토콜에서 사용하는 CRC16 알고리즘의 다항식(0xA001)
    다른 표준(CRC-CCITT, CRC-USB 등)과 다르기 때문

CRC 이름 다항식 용도
CRC16-CCITT 0x1021 통신 규약, XMODEM, PPP 등
CRC16-IBM / ANSI / MODBUS 0x8005 / 0xA001  Modbus 통신
CRC32 (Ethernet) 0x04C11DB7 이더넷, ZIP 등

즉, “Modbus CRC”는

“Modbus 프로토콜에서 사용하는 CRC16의 변형”
→ 다항식 0xA001 사용
→ 비트 순서 LSB-first

 

 

 

 

 

 

2. CRC16과 CRC32

 # CRC란?

 CRC는 “순환 중복 검사(Cyclic Redundancy Check)”의 약자, 데이터 유효성 검사
데이터를 다항식(polynomial) 형태로 보고, 특정 생성 다항식으로 나눈 나머지(remainder) 를 체크값으로 사용하는 방식

 - 말은 어렵지만 간단하게 한 비트씩 시프트하면서 1이면 이면 XOR 0이면 시프트 연산만 처리, 반복해서 CRC 값을 구함.

XOR보다 훨씬 오류 검출 능력이 좋음.

 

 

 

 

 

 


 

 

# CRC-16 예시 (C++)

가장 흔히 쓰이는 CRC-16-IBM (CRC-16-ANSI) 방식 예시

  • 다항식: 0xA001 (LSB), 0x8005 (MSB)
  • 초기값: 0xFFFF
 
#include <iostream>
#include <vector>

uint16_t crc16(const std::vector<uint8_t>& data) {
    uint16_t crc = 0xFFFF;
    for (uint8_t byte : data) {
        crc ^= byte;
        for (int i = 0; i < 8; i++) {
            if (crc & 1)
                crc = (crc >> 1) ^ 0xA001;  // 다항식 XOR
            else
                crc >>= 1;
        }
    }
    return crc;
}

int main() {
    std::vector<uint8_t> data = {0x12, 0x34, 0x56};
    uint16_t crc = crc16(data);
    std::cout << "CRC16: 0x" << std::hex << crc << std::endl;
    return 0;
}

 

출력 예시

CRC16: 0x4b37

 

 

 

 


 

 

 

 # CRC-32 예시 (C++)

CRC-32는 이더넷, ZIP, PNG, gzip 등에서 가장 흔히 쓰이는 방식

  • 다항식: 0xEDB88320 (LSB), 0x04C11DB7 (MSB)
  • 초기값: 0xFFFFFFFF
 
#include <iostream>
#include <vector>

uint32_t crc32(const std::vector<uint8_t>& data) {
    uint32_t crc = 0xFFFFFFFF;
    for (uint8_t byte : data) {
        crc ^= byte;
        for (int i = 0; i < 8; i++) {
            if (crc & 1)
                crc = (crc >> 1) ^ 0xEDB88320;
            else
                crc >>= 1;
        }
    }
    return ~crc;  // 보통 마지막에 보수(~) 처리
}

int main() {
    std::vector<uint8_t> data = {0x12, 0x34, 0x56};
    uint32_t crc = crc32(data);
    std::cout << "CRC32: 0x" << std::hex << crc << std::endl;
    return 0;
}

 

출력 예시

 
CRC32: 0x8f7c1cb0

 

 

 

 

 

 


 

 

 

# CRC vs XOR 체크섬 비교


 

항목 XOR 체크썸 CRC16 CRC32
연산 속도 빠름 보통 약간 느림
검출 능력 약함 (2비트 오류 검출 불가) 좋음 (1~2비트, 버스트 오류 검출) 매우 좋음
코드 복잡도 매우 단순 보통 조금 복잡
사용 예 간단한 센서, UART 산업용 프로토콜(Modbus) 네트워크, 파일 포맷

 

 

 

 

 # 참고

  • UART, RS485 같은 통신에서는 CRC16 (Modbus 방식)
  • 파일 검증, 네트워크에서는 CRC32
  • MCU나 센서용 가벼운 체크에는 XOR

 

 

 

* 송신측에서 보내는 데이터 crc 값과 수신측에서 계산된 crc 값이 동일해야 데이터 유효성이 검증됨.

 

 

 

반응형