1. 为什么会有 CRC 校验码?
答:
数据有可能被更改,需要确认是否被更改,且不能占用太多字节,于是有了校验码。
而对一个字节(8位)一个字节的进行循环计算,从而核对数据是否被更改。
2. 修改了一定能被 CRC 校验出来吗?
答:
不是,而是一定概率可以校验出来。
奇偶校验就是属于 CRC 校验一种特例。
所以,为了更好的校验,就有了多项式。
更优的多项式,更高概率检查数据被更改。
经常提到的进行二进制触发计算 CRC,被除数,就是多项式(polynomial)。
| CCITT | CRC16 | CRC32 |
校验和位宽W | 16 | 16 | 32 |
生成多项式 | x16+x12+x5+1 | x16+x15+x2+1 | x32+x26+x23+x22+x16+ x12+x11+x10+x8+x7+x5+ x4+x2+x1+1 |
除数(多项式) | 0x1021 | 0x8005 | 0x04C11DB7 |
余数初始值 | 0xFFFF | 0x0000 | 0xFFFFFFFF |
结果异或值 | 0x0000 | 0x0000 | 0xFFFFFFFF |
3. crc16 算法代码范 表查询法,是怎样的?
1 #include <stdio.h>
2 #include <stdint.h>
3
4 #define POLY 0x1021
5
6 uint16_t crc16(unsigned char *addr, int num, uint16_t crc)
7 {
8 int i;
9
10 while (num--) {
11 crc = crc ^ (*addr++ << 8);
12 for (i = 0; i < 8; i++) {
13 if (crc & 0x8000) {
14 crc = (crc << 1) ^ POLY;
15 } else {
16 crc <<= 1;
17 }
18 }
19 crc &= 0xFFFF;
20 }
21 return crc;
22 }
23
24 int main(void)
25 {
26 uint8_t data[] = {0x02, 0x05};
27
28 printf("%04X\n", crc16(data, 2, 0xFFFF));
29
30 return 0;
31 }
4. crc16 是处理部分的逻辑是怎样的?
a、11 行操作,crc 的低 8 位不变,仅仅是 crc 的高 8 位被修改了
b、12 行到 18 行:高 8 位决定了会进行多少次与固定数字 POLY(多项式) 的异或操作。
5. 表查询法,是怎么回事呢?
问:为什么会有表查询法?
答:因为上面是一个一个 bit 位进行操作。所以在要计算的数据比较多的时候,就很影响效率。
问:表查询法是如何做到优化的?
答:
a、异或操作满足结合律、交换律。与+操作类似。
b、假设一个8位的数字,分别以 H1、 H2、 H3、 H4、 L1、 L2、 L3、 L4来表示。
V1、 V2、 V3、 V4、 V5、 V6、 V7、V8 表示 POLY(多项式)的二进制。
上下进行异或操作,高4位决定了低4位会和 POLY 进行多少次异或。
因异或操作满足结合律、交换律。上面的异或操作与下面的异或操作等价。
所以,当重复计算比较多的时候,下面的 H1 与 Vx 之间的计算结果可以保存到一个表中,后续直接查询来使用。
因 CRC 校验是一次处理一字节(8位),所以表格就有 2 的 8 次方 = 256 个。
6、查表法的范例:
1 #include <stdio.h>
2
3 #include <stdint.h>
4
5 #define POLY 0x1021
6 #define INIT 0xFFFF
7 #define FINAL 0x0000
8
9 typedef uint16_t width_t;
10
11 #define WIDTH (8 * sizeof(width_t))
12 #define TOPBIT (1 << (WIDTH - 1))
13
14 uint16_t crc16(unsigned char *addr, int num, uint16_t crc)
15 {
16 int i;
17
18 while (num--) {
19 crc = crc ^ (*addr++ << 8);
20 for (i = 0; i < 8; i++) {
21 if (crc & 0x8000) {
22 crc = (crc << 1) ^ POLY;
23 } else {
24 crc <<= 1;
25 }
26 }
27 crc &= 0xFFFF;
28 }
29 return crc;
30 }
31
32
33
34 static width_t crc_table[256];
35
36 void crc_init(void)
37 {
38 width_t reg;
39 width_t i, j;
40
41 for (i = 0; i < 256; i++) {
42 reg = i << (WIDTH - 8);
43 for(j = 0; j < 8; j++) {
44 if(reg & TOPBIT) {
45 reg = (reg << 1) ^ POLY;
46 } else {
47 reg = reg << 1;
48 }
49 }
50 crc_table[i] = reg;
51 }
52 }
53
54 width_t crc_compute(unsigned char *addr, unsigned int num)
55 {
56 unsigned int i;
57 unsigned int high;
58 width_t reg = INIT;
59
60 for (i = 0; i < num; i++) {
61 high = (reg >> (WIDTH - 8)) ^ addr[i];
62 reg = crc_table[high] ^ (reg << 8);
63 }
64
65 return (reg ^ FINAL);
66 }
67
68
69 int main(void)
70 {
71 uint8_t data[] = {0x02, 0x05};
72
73 printf("%04X\n", crc16(data, 2, 0xFFFF));
74
75 crc_init();
76 printf("%04X\n", crc_compute(data, 2));
77
78 return 0;
79 }