CRC-8校验

1.数据校验

减少错误:在数据传输过程中,可能会因为各种原因(比如网络问题、硬件故障等)导致数据发生错误。数据校验可以帮助我们发现并纠正这些错误。例如,假设我们正在发送一个包含用户信息的数据包,如果在传输过程中某些位发生了改变,那么接收方接收到的就可能是错误的用户信息。但是,如果我们在发送数据包时加入了校验码,那么接收方就可以通过这个校验码来判断数据包是否在传输过程中发生了改变,从而避免使用错误的数据。

保证数据一致性:在进行数据复制或迁移时,我们需要确保源数据和目标数据的一致性。数据校验可以帮助我们达到这个目标。例如,假设我们正在将一个数据库从一个服务器迁移到另一个服务器,如果在迁移过程中由于网络问题或其他原因导致某些数据丢失或发生改变,那么目标服务器上的数据库就可能与源服务器上的数据库不一致。但是,如果我们在迁移数据时使用了数据校验,那么我们就可以发现这些问题,并在数据到达目标服务器之前进行纠正。

所以,本次介绍的CRC-8校验,就是一种数据校验的算法。

2.CRC-8校验

CRC-8(Cyclic Redundancy Check,循环冗余校验)是一种基于多项式的校验算法,通常用于检测数据传输中的错误。CRC 算法通过生成多项式除法来计算冗余检查值,这个值附加在数据后面传输。接收方在接收数据时使用相同的 CRC 算法进行计算,然后将计算得到的冗余检查值与接收到的数据中附加的值进行比较,以检测是否有错误发生。

以下是 CRC-8 校验算法的基本步骤:

1.选择生成多项式: 选择一个固定的多项式作为生成多项式。对于 CRC-8,通常使用的是二进制值 0b100000111,对应的十六进制为 0x07。

2.初始化 CRC 寄存器: 初始化一个称为 CRC 寄存器的变量,初始值为0。

3.处理每个字节: 遍历要计算的数据,对每个字节进行如下操作:

将 CRC 寄存器的最高位和当前字节的最高位进行异或操作。
将 CRC 寄存器左移一位。
如果异或操作的结果为1,则与生成多项式进行异或操作。
4.处理完所有字节: 当所有字节都被处理完毕后,CRC 寄存器中的值即为计算得到的冗余检查值。

5.附加 CRC 值: 将计算得到的 CRC 值附加在数据后面,形成最终的传输数据。

CRC-8 的优点在于它能够检测一定数量的错误,但并不适用于纠正错误。选择合适的生成多项式非常关键,不同的生成多项式可能适用于不同的应用场景。CRC 算法广泛用于通信协议、存储系统和其他数据传输领域,以提高数据传输的可靠性。

(这个是详细的循环冗余校验的计算)

3.代码实现

以下是一个简单的实现:

(0x80 是十六进制表示,对应二进制是 10000000,crc & 0x80 的结果就是 crc 的二进制表示中最高位是否为1。0x80u表示无符号)

// 定义CRC-8的生成多项式
#define CRC_POLYNOMIAL_8 0x0C

// CRC-8校验函数
uint8 crc_8(uint8 crc, uint8* pdata, uint32 len) {
    // 遍历需要进行CRC校验的数据
    for (uint32 i = 0; i < len; i++) {
        // 将当前数据与CRC值进行异或操作
        crc ^= pdata[i];
        // 对每一位进行处理
        for (uint8 j = 0; j < 8; j++) {
            // 判断最高位是否为1
            if ((crc & 0x80u) > 0) {
                // 如果最高位为1,那么将CRC值左移一位,然后与生成多项式进行异或操作
                crc = ((uint8)(crc << 1u)) ^ CRC_POLYNOMIAL_8;
            } else {
                // 如果最高位不为1,那么直接将CRC值左移一位
                crc <<= 1u;
            }
        }
    }
    // 返回CRC校验码
    return crc;
}

在CRC校验中,我们将数据看作是一个二进制多项式的系数,然后将这个多项式除以一个预定的生成多项式,得到的余数就是我们的CRC校验码。在这个过程中,我们使用的是模2除法,也就是异或操作

在这段代码中,我们首先将CRC值与当前的数据进行异或操作,然后对每一位进行处理。这个处理过程实际上就是模拟了多项式的除法过程。

当最高位为1时,我们将CRC值左移一位,然后与生成多项式进行异或操作。这相当于在模2除法中,当被除数的最高位为1时,我们将被除数左移一位,然后与除数进行异或操作,得到新的被除数。

当最高位不为1时,我们直接将CRC值左移一位。这相当于在模2除法中,当被除数的最高位不为1时,我们直接将被除数左移一位,得到新的被除数。

这个过程会一直重复,直到处理完所有的数据。最后,返回的CRC校验码就是我们得到的余数。

这个算法的效率并不高,因为它使用了两层循环。在实际应用中,通常会使用查表法来提高效率。

4.进阶代码(查表法):

查表法的基本思想是预先计算出所有可能的CRC校验码,并将它们存储在一个表中。然后,在进行CRC校验时,直接从表中查找对应的校验码,从而避免了重复的计算。

1.生成 CRC 表: 在程序的初始化阶段,通过遍历所有可能的输入值,计算它们对应的 CRC 校验码,并将结果存储在一个查表中。这个表通常是一个固定大小的数组,每个数组元素存储一个输入值对应的 CRC 校验码。

2.查表计算 CRC: 在实际运行时,当需要计算 CRC 校验码时,直接查表获取输入值对应的 CRC 校验码。这是一个常数时间的查找操作,因此非常高效。

使用查表法的优势在于它在牺牲一定的存储空间的前提下,大大减少了 CRC 计算的时间复杂度。这对于嵌入式系统等资源有限的环境来说尤为重要。在应用中,选择合适的 CRC 多项式和生成 CRC 表的方式是关键,以满足具体应用场景的需求。

#include <iostream>
#include <iomanip>

// CRC-8 多项式
#define CRC8_POLY 0x07

// 生成 CRC8 表格
void generateCRC8Table(uint8_t crc8Table[256]) {
    for (int i = 0; i < 256; ++i) {
        uint8_t crc = static_cast<uint8_t>(i);
        for (int j = 0; j < 8; ++j) {
            // 判断最高位是否为1
            if (crc & 0x80) {
                // 如果最高位为1,则左移一位并与多项式异或
                crc = (crc << 1) ^ CRC8_POLY;
            } else {
                // 如果最高位为0,则直接左移一位
                crc = (crc << 1);
            }
        }
        // 存储计算得到的 CRC8 值
        crc8Table[i] = crc;
    }
}

int main() {
    // 初始化 CRC8 表格
    uint8_t crc8Table[256];
    generateCRC8Table(crc8Table);

    // 打印生成的 CRC8 表格
    for (int i = 0; i < 16; ++i) {
        for (int j = 0; j < 16; ++j) {
            // 按照格式输出 CRC8 表格的值
            std::cout << std::setw(4) << static_cast<int>(crc8Table[i * 16 + j]);
        }
        std::cout << std::endl;
    }

    return 0;
}

   0   7  14   9  28  27  18  21  56  63  54  49  36  35  42  45

 112 119 126 121 108 107  98 101  72  79  70  65  84  83  90  93

 224 231 238 233 252 251 242 245 216 223 214 209 196 195 202 205

 144 151 158 153 140 139 130 133 168 175 166 161 180 179 186 189

 199 192 201 206 219 220 213 210 255 248 241 246 227 228 237 234

 183 176 185 190 171 172 165 162 143 136 129 134 147 148 157 154

  39  32  41  46  59  60  53  50  31  24  17  22   3   4  13  10

  87  80  89  94  75  76  69  66 111 104  97 102 115 116 125 122

 137 142 135 128 149 146 155 156 177 182 191 184 173 170 163 164

 249 254 247 240 229 226 235 236 193 198 207 200 221 218 211 212

 105 110 103  96 117 114 123 124  81  86  95  88  77  74  67  68

  25  30  23  16   5   2  11  12  33  38  47  40  61  58  51  52

  78  73  64  71  82  85  92  91 118 113 120 127 106 109 100  99

  62  57  48  55  34  37  44  43   6   1   8  15  26  29  20  19

 174 169 160 167 178 181 188 187 150 145 152 159 138 141 132 131

 222 217 208 215 194 197 204 203 230 225 232 239 250 253 244 243

1.定义 CRC-8 多项式: CRC-8 多项式是一个8位的二进制数,用于生成 CRC 校验码。在这里,CRC-8 多项式是 0x07。

2.初始化 CRC8 表格: 创建一个包含 256 个元素的数组,用于存储计算得到的 CRC8 值。数组的索引表示输入的字节值,数组的元素表示计算得到的 CRC8 值。

3.计算 CRC8 值:为什么是 256 个元素呢?因为一个字节有 8 位,所以有 2^8 = 256 种可能的字节值,从 0 到 255。因此,CRC8 表格的大小为 256。

那么接收方的代码如下:

#include <iostream>
#include <cstdint>

// CRC8 表格
static constexpr unsigned char CRC8_Table[] = {
  // ... 生成的 CRC8 表格 ...
};

// 计算 CRC8 校验码
uint8_t calculateCRC8(const uint8_t* data, size_t length) {
    uint8_t crc = 0;
    for (size_t i = 0; i < length; ++i) {
        crc = CRC8_Table[crc ^ data[i]];
    }
    return crc;
}

int main() {
    // 接收到的数据
    uint8_t received_data[] = { /* 接收到的数据内容 */ };
    size_t data_length = sizeof(received_data);

    // 接收到的 CRC 校验码
    uint8_t received_crc = received_data[data_length - 1];

    // 计算接收到的数据的 CRC 校验码(除去最后一个字节,即除去 CRC 校验码本身)
    uint8_t calculated_crc = calculateCRC8(received_data, data_length - 1);

    // 检测 CRC 校验结果
    if (received_crc == calculated_crc) {
        std::cout << "CRC 校验通过" << std::endl;
    } else {
        std::cout << "CRC 校验失败" << std::endl;
    }

    return 0;
}

uint8_t crc = 0;:首先初始化 CRC 校验码为 0。

for (size_t i = 0; i < length; ++i):然后,通过循环遍历数据数组的每个字节。

crc = CRC8_Table[crc ^ data[i]];:在循环中,通过异或运算 ^ 将当前 CRC 校验码 crc 与当前数据字节 data[i] 进行异或操作。

然后,使用 CRC8 表格 CRC8_Table 查找新的 CRC 值,将结果赋给 crc。这个步骤重复进行,直到遍历完所有数据。

return crc;:最终返回计算得到的 CRC8 校验码。

发送方:

// 伪代码示例
uint8_t data[] = { /* 数据内容 */ };
size_t data_length = sizeof(data);

// 计算 CRC 校验码
uint8_t crc = calculateCRC8(data, data_length);

// 将 CRC 校验码附加到数据帧中
uint8_t frame[data_length + 1];
memcpy(frame, data, data_length);
frame[data_length] = crc;

// 将带有 CRC 校验码的数据帧发送出去
sendFrame(frame, data_length + 1);

calculateCRC8 函数用于计算数据的 CRC 校验码,然后将这个校验码附加到数据帧的末尾。最终,带有 CRC 校验码的数据帧通过 sendFrame 函数发送。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值