目录

一、CRC 校验的基本原理

1 核心概念定义

a.原始数据(D):

b.生成多项式(G):

c.CRC 校验码(R):

2 3 步运算流程

步骤 1:给原始数据 “补零”

步骤 2:算校验码(用 “异或除法”)

步骤 3:接收端查错

示例:

二、常见的 CRC 标准(生成多项式)

三、CRC 的检错能力

四、CRC 的实现方式

1.硬件实现:

2.软件实现:

软件实现关键:查表法(提升效率)

代码实现:

代码关键说明

五、CRC 与其他校验技术的对比

总结


(Cyclic Redundancy Check,循环冗余校验)是一种广泛应用于数据传输和存储领域的差错检测技术,通过对数据进行多项式运算生成固定长度的校验码,从而判断数据在传输或存储过程中是否发生错误。其核心优势是检错能力强、计算效率高,常见于网络通信(如以太网、USB)、存储设备(如硬盘、光盘)、串口通信等场景。

一、CRC 校验的基本原理

CRC 的本质是基于多项式除法的数学运算,将数据视为二进制多项式,通过与一个预设的 “生成多项式” 进行模 2 除法(即异或运算,无进位 / 借位,相同的相减得0,不同的相减得1),得到的余数即为 “CRC 校验码”。整个过程分为 3 个核心步骤:

1 核心概念定义

在理解过程前,需明确 3 个关键要素:

a.原始数据(D)

待校验的数据,设为 n 位二进制数,对应多项式为 D(x) = dₙ₋₁xⁿ⁻¹ + dₙ₋₂xⁿ⁻² + ... + d₁x¹ + d₀x⁰(例如数据1011对应多项式x³ + x¹ + x⁰)。

b.生成多项式(G)

预先规定的校验规则,由通信双方或标准协议(如 CRC-32、CRC-16)统一确定,设为 r+1 位二进制数(r 为校验码长度),对应多项式为 G(x) = gᵣxʳ + gᵣ₋₁xʳ⁻¹ + ... + g₁x¹ + g₀x⁰(例如生成多项式1011(r=3)对应x³ + x¹ + x⁰)。

注:校验码长度 r,即最终生成的 CRC 校验码的二进制位数(如 CRC-32 的 r=32,校验码是 32 位;CRC-16 的 r=16,校验码是 16 位)。

生成多项式的二进制表示:生成多项式是一个 “系数仅为 0 或 1” 的多项式(因为数据是二进制),其二进制位数 = 多项式的最高次项次数 + 1。例如:生成多项式G(x) = x³ + 0 * x^2 + x¹ + x⁰(对应二进制1011),最高次项是(次数为 3),所以二进制位数是3 + 1 = 4

校验码几位(r),生成多项式就比它多 1 位(r+1 位)

c.CRC 校验码(R)

原始数据经过运算后生成的 r 位二进制余数,最终传输 / 存储的数据为 “原始数据 + CRC 校验码”(共 n+r 位)。

2 3 步运算流程

步骤 1:给原始数据 “补零”

先看生成多项式的长度(比如生成多项式是 4 位二进制数,那它的次数 r 就是 3)。在原始数据的末尾,补上 r 个 0(比如 r=3 就补 3 个 0),得到一个更长的数据(原始长度 + r 位)。目的:给后面要生成的校验码 “预留位置”。

步骤 2:算校验码(用 “异或除法”)

用补零后的新数据,和生成多项式做 “只异或、不进位的除法”:

  • 从新数据的最高位开始,每次取和生成多项式一样长的位数,和生成多项式做异或(相同为 0,不同为 1)。

  • 把得到的结果,拼上下一位数据,重复上面的异或操作,直到把新数据的所有位都算完。

  • 最后剩下的、长度为 r 的结果,就是 CRC 校验码(不够 r 位就在前面补 0)。

步骤 3:接收端查错

接收端收到 “原始数据 + 校验码” 后,用同样的生成多项式再做一次上面的 “异或除法”:

  • 如果最后余数是 0,说明数据没出错;

  • 如果余数不是 0,说明数据传错了(需要重传)。

简单说就是:补零留位置→算校验码→收端查余数判对错。

示例:

这里数据比特位的循环移位操作,即为“循环”二字的来历

二、常见的 CRC 标准(生成多项式)

不同场景下的 CRC 校验有固定的标准,核心差异在于生成多项式的选择。下表列出了工业界常用的 CRC 标准:

三、CRC 的检错能力

CRC 的检错能力由生成多项式决定,其核心优势是能检测绝大多数常见错误,具体能力如下:

  1. 检测所有 1 位错误:无论数据长度如何,CRC 可 100% 检测出仅 1 位二进制位出错的情况。

  2. 检测所有 2 位错误:只要生成多项式包含x+1因子(多数标准多项式均满足,如 CRC-32),可 100% 检测出 2 位错误。

  3. 检测奇数位错误:若生成多项式包含x+1因子,可 100% 检测出任意奇数位错误。

  4. 检测突发错误

    1. 对于长度≤r 的突发错误(连续 r 位内的错误),可 100% 检测。

    2. 对于长度 = r+1 的突发错误,检测概率为1 - 1/2^(r-1)(如 CRC-32 的检测概率约为 99.999999%)。

    3. 对于长度 > r+1 的突发错误,检测概率为1 - 1/2^r(CRC-32 约为 99.9999998%)。

注意:CRC 是差错检测技术,而非差错纠正技术(无法修复错误);同时,它不能检测 “数据篡改”(攻击者可故意修改数据和 CRC 码,使余数仍为 0),需结合加密(如 HMAC)实现完整性校验。

四、CRC 的实现方式

CRC 的计算可通过硬件软件实现,两种方式各有优劣:

1.硬件实现:

采用移位寄存器、异或门组成的专用电路。

优势:并行 / 串行计算,速度极快(纳秒级)、不占用 CPU 资源;

劣势:灵活性低(需匹配固定生成多项式)、成本高 适用场景:高速通信(如以太网、PCIe)、存储控制器

2.软件实现:

通过算法模拟模 2 除法,分为 “逐位计算” 和 “查表法”。

优势:灵活性高(可切换任意多项式)、成本低(无额外硬件);

劣势:速度较慢(依赖 CPU 性能)。

适用场景:

低速通信(如串口)、软件校验(如文件 CRC 校验工具)。

软件实现关键:查表法(提升效率)

“逐位计算” 需逐位处理数据,效率低;查表法通过预计算 “所有可能的 8 位数据对应的 CRC 值”(256 个值,称为 CRC 表),每次处理 1 字节数据,将效率提升 8 倍,是软件实现的主流方式:

  1. 初始化 CRC 寄存器为全 1 或全 0(根据标准定,如 CRC-32 初始为 0xFFFFFFFF)。

  2. 取 1 字节数据与 CRC 寄存器的低 8 位异或。

  3. 以异或结果为索引,查询预生成的 CRC 表,得到对应值,与 CRC 寄存器的高 24 位拼接,更新 CRC 寄存器。

  4. 重复步骤 2-3,直到处理完所有数据。

  5. 对最终 CRC 寄存器值进行 “异或后处理”(如 CRC-32 需与 0xFFFFFFFF 异或),得到最终 CRC 码。

代码实现:

#include <stdio.h>
#include <stdint.h>

// 声明CRC-32表(256个32位值)
static uint32_t crc32_table[256];

// 生成CRC-32表(仅需初始化一次)
void crc32_init() {
    uint32_t crc;
    for (int i = 0; i < 256; i++) {
        crc = i;  // 从8位数据i开始计算
        // 对每个位进行处理(模拟模2除法)
        for (int j = 0; j < 8; j++) {
            // 若最高位为1,则与多项式异或;否则左移
            if (crc & 1) {
                crc = (crc >> 1) ^ 0xEDB88320;  // 多项式0xEDB88320(反射形式)
            } else {
                crc >>= 1;
            }
        }
        crc32_table[i] = crc;  // 保存计算结果到表中
    }
}

// 查表法计算CRC-32
uint32_t crc32_calc(const uint8_t *data, size_t len) {
    uint32_t crc = 0xFFFFFFFF;  // 初始值
    for (size_t i = 0; i < len; i++) {
        // 核心操作:当前CRC低8位与数据字节异或,作为表的索引,更新CRC
        crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ data[i]];
    }
    return crc ^ 0xFFFFFFFF;  // 结果异或处理
}

// 测试函数(验证标准数据)
int main() {
    crc32_init();  // 初始化CRC表

    // 标准测试数据"123456789",其CRC-32标准结果为0xCBF43926
    const uint8_t test_data[] = "123456789";
    size_t test_len = sizeof(test_data) - 1;  // 减去字符串结束符'\0'

    uint32_t result = crc32_calc(test_data, test_len);
    printf("CRC-32结果: 0x%08X\n", result);  // 应输出0xCBF43926

    return 0;
}

代码关键说明

  1. CRC 表生成(crc32_init函数):对 0~255 的每个 8 位数据,模拟逐位模 2 除法,计算其对应的 CRC 值并存入表中。这一步只需执行一次(程序启动时初始化)。

  2. 查表计算(crc32_calc函数)

    1. 初始 CRC 值设为0xFFFFFFFF(标准规定)。

    2. 对每个输入字节:① 取当前 CRC 的低 8 位(crc & 0xFF)与输入字节异或,得到表的索引(0~255)。② 用索引查 CRC 表,得到对应的值,与当前 CRC 右移 8 位后的值异或,更新 CRC。

    3. 所有字节处理完后,将结果与0xFFFFFFFF异或,得到最终 CRC 值。

  3. 测试验证:字符串"123456789"是 CRC 校验的标准测试数据,其 CRC-32 结果固定为0xCBF43926,可用于验证代码正确性。

五、CRC 与其他校验技术的对比

除 CRC 外,常见的差错检测技术还有奇偶校验、校验和(Checksum),三者的差异如下:

校验技术 校验码长度 检错能力 计算效率 典型应用
奇偶校验 1 位 仅检测 1 位错误,无法检测 2 位及以上错误 极高 简单通信(如 ASCII 传输)、内存校验
校验和(如 IP 校验和) 16 位 / 32 位 检测 1-2 位错误概率高,对突发错误检测能力弱 较高(加法运算) IP 协议、UDP 协议、文件传输(如 FTP)
CRC 8 位 / 16 位 / 32 位 / 64 位 检错能力最强,尤其擅长突发错误 中高(硬件快,软件依赖查表法) 以太网、USB、存储设备、压缩文件

总结

CRC 校验是一种基于多项式运算的高效差错检测技术,凭借强检错能力、灵活的实现方式,成为数据传输和存储领域的 “标配”。在实际应用中,需根据场景选择合适的 CRC 标准(如以太网用 CRC-32,串口用 CRC-16),并注意其 “仅检错、不纠错、不防篡改” 的局限性 —— 如需完整性和安全性,需结合加密算法使用。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐