1 CRC校验原理
使用CRC校验的作用在于多机通讯时保证数据发送与接收的一致性
CRC计算主要步骤:
- 1.选取对应的多项式算子,展开获得CRC除数(例:x4 + x + 1 二进制x为2则算子为1011(提取系数) 省略最高位为0x03)
- 3.在原始数据末端加0,加0的个数取决于最高次(例:x4 + x + 1 则添加0000至原始数据末尾)
- 4.从左往右,数据与算子按位异或,最终余数即为CRC校验结果,填充至原始数据加0处
CRC详细关键步骤:
1.初始化CRC寄存器
预留一个寄存器(通常为16位或根据CRC版本不同而有所变化,如CRC-8、CRC-16、CRC-32等),并将其初始化为全1(如0xFFFF对于CRC-16)或特定的初始值(根据CRC算法的具体实现而定)。
2. 处理数据
将要发送的数据的第一个字节(或指定长度的数据块)与CRC寄存器的低几位进行异或操作,高几位保持不变(具体位数取决于CRC版本)。
将得到的CRC寄存器的值向右移动一位,最高位用0填充,并检查移出的位(即原来的最低位)。
3. 模2除法
如果移出的位为0,则重复上一步的右移操作。
如果移出的位为1,则将CRC寄存器与预定义的生成多项式进行异或操作。生成多项式是CRC算法中的一个关键参数,用于检测数据中的错误。
重复上述右移和异或操作,直到处理了数据中的所有字节。
4. 交换CRC寄存器的高低字节(可选)
在某些CRC算法中,需要将最终CRC寄存器的高低字节进行交换,以得到最终的CRC校验码。这一步骤取决于CRC算法的具体实现和接收端的期望格式。
5. 附加CRC校验码到数据帧
将计算得到的CRC校验码附加到原始数据帧的末尾,形成一个新的数据帧进行发送。
6. 接收端校验
接收端在接收到数据后,使用相同的生成多项式对包括CRC校验码在内的整个数据帧进行模2除法运算。
如果运算结果为0(即没有余数),则认为数据在传输过程中没有发生错误;否则,认为数据存在错误
计算细节可参考以下链接:
链接: [CRC校验]手算与直观演示
CRC-16-CCITT-FALSE C语言基础实现
链接: C语言在线编译工具
链接: CRC校验在线计算工具
#include <stdio.h>
#include <stdint.h>
#define Poly 0x1021//1.确定多项式算子 省略最高位的1
uint16_t crc16_ccitt(uint8_t *data, size_t len) {
uint16_t crc = 0xFFFF; // 1.初始化CRC寄存器 初始值
uint8_t i;
while (len--) {
crc ^= (uint16_t)(*data++ << 8); // 2.从左到右运算预处理 将数据左移8位后异或到CRC的高8位
for (i = 0; i < 8; i++) {
if (crc & 0x8000) { // 检查最高位(第16位)
crc = (crc << 1) ^ Poly; // 3. 先左移在异或是因为算子省略了最高位 左移一位,如果最高位是1,则异或多项式
} else {
crc = crc << 1;
}
}
}
return crc; //4.最终CRC校验结果
}
int main() {
uint8_t data[] = {0x12, 0x56, 0x78,0x34};
size_t len = sizeof(data) / sizeof(data[0]);
uint16_t crc = crc16_ccitt(data, len);
// 如果需要交换高低字节(例如,某些协议要求)
// 注意:这个步骤是在CRC计算之后进行的
uint16_t swapped_crc = (crc >> 8) | (crc << 8);
printf("CRC-16 (without swapping): 0x%04X\n", crc);
printf("CRC-16 (with swapping): 0x%04X\n", swapped_crc);
return 0;
}
代码验证:
CRC-32 查表法C语言基础实现
#include <stdio.h>
#include <stdint.h>
// CRC-32表
uint32_t crc_table[256];
// 初始化CRC表
void init_crc_table() {
for (uint32_t i = 0; i < 256; i++) {
uint32_t crc = i;
for (uint8_t j = 0; j < 8; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0xEDB88320; // 多项式0x04C11DB7的逆
} else {
crc >>= 1;
}
}
crc_table[i] = crc;
}
}
// 使用查表法计算CRC-32
uint32_t crc32(uint8_t *buf, size_t len) {
uint32_t crc = 0xFFFFFFFF; // 初始CRC值
while (len--) {
crc = crc_table[(crc ^ *buf++) & 0xFF] ^ (crc >> 8);
}
return ~crc; // 返回最终的CRC值(按惯例取反)
}
int main() {
// 初始化CRC表
init_crc_table();
// 测试字符串
char test_str[] = "Hello, World!";
// 计算CRC-32
uint32_t crc = crc32((uint8_t*)test_str, sizeof(test_str) - 1);
// 输出结果
printf("CRC-32 of '%s' is 0x%08X\n", test_str, crc);
return 0;
}
代码验证:
2 EB配置以及接口应用
配置步骤:
1.选择是否需要输入参数无效故障诊断
2.对应的CRC标准实现方式
1.选择是否需要输入参数无效故障诊断
2.对应的CRC标准实现方式
接口应用步骤
1.直接调用对应接口
接口名 | 传入参数 | 说明 | 返回参数 | 说明 |
---|---|---|---|---|
Crc_CalculateCRC8 | Crc_DataRefType Crc_DataPtr, uint32 Crc_Length, uint8 Crc_StartValue8, boolean Crc_IsFirstCall | 数据buffer,数据长度,自定义初始值,设置初始值调用方式 (0 使用默认值^自定义值 1使用默认值) | FUNC(uint8, CRC_CODE) | 返回CRC校验值 |
Crc_CalculateCRC16 | 同上 | 同上 | 同上 | 同上 |
Crc_CalculateCRC32 | 同上 | 同上 | 同上 | 同上 |
Crc_CalculateCRC8H2F | 同上 | 同上 | 同上 | 同上 |
Crc_CalculateCRC32P4 | 同上 | 同上 | 同上 | 同上 |
3 总结
本文为博主个人学习总结记录,如有不正,欢迎指正