C语言CRC校验代码(CRC16)(CRC函数、CRC校验函数解析、循环冗余校验、在线CRC校验、CRC在线、CRC网站)

Dontla 2024-10-01 16:35:06 阅读 77

文章目录

直接上代码函数调用在线CRC校验网站

CRC16校验函数解析初始化CRC值主循环XOR操作(异或操作)内部循环:位处理条件判断与多项式运算疑问为什么是crc & 0x0001而不是crc & 0x1、crc & 0x01或者crc & 0x00000001?

多项式`0xA001`总结

实际计算示例初始化变量第一轮计算(对数据 `0x03`)1. XOR操作结果

2. 进行8次循环,每次检查 `crc` 的最低位,并根据该位是0还是1进行操作

第二轮计算(对数据 `0x04`)示例演示处理第一个字节(0x03)处理第二个字节(0x04)

直接上代码

函数

<code>uint16_t Calculate_CRC16(const uint8_t *data, uint16_t length)

{ -- -->

uint16_t crc = 0xFFFF;

for (uint16_t i = 0; i < length; i++)

{

crc ^= data[i]; // 直接 XOR 低字节,因为多项式是反转的

for (uint8_t j = 0; j < 8; j++)

{

if (crc & 0x0001) // 检查最低位

crc = (crc >> 1) ^ 0xA001; // 右移并应用多项式

else

crc >>= 1; // 只右移

}

}

return crc;

}

调用

int VendorB_PowerStart(serial_t *serial, char **errorMsg)

{

// 后面可能有web设置报警器喇叭静音功能,到时提供一个全局变量和设置接口,本函数就只被算法触发的时候调,本函数会读全局变量

uint8_t index = 0;

uint8_t tbuf[128]; // 局部请求缓冲区

uint8_t rbuf[128]; // Response buffer

// 构建请求数据包

tbuf[index++] = ClientInfor.devaddr; // 地址(设备地址)

tbuf[index++] = 0x10; // 功能码(对于此声光报警器控制板:把设置的数值写入指定的连续寄存器)

tbuf[index++] = 0x04; // 寄存器地址(高位)

tbuf[index++] = 0x0E; // 寄存器地址(低位)

tbuf[index++] = 0x00; // 寄存器个数(高位)

tbuf[index++] = 0x05; // 寄存器个数(低位)

tbuf[index++] = 0x0A; // 寄存器字节数(一个寄存器——高位+低位,有四个16进制数,等于两个字节)

tbuf[index++] = 0x00; // 修改数据(高位)—— 报警器状态

tbuf[index++] = 0x03; // 修改数据(低位)

tbuf[index++] = 0x00; // 修改数据(高位)—— 报警器音量

tbuf[index++] = 0x1E; // 修改数据(低位)

tbuf[index++] = 0x01; // 修改数据(高位)—— 报警器音调

tbuf[index++] = 0x01; // 修改数据(低位)

tbuf[index++] = 0x00; // 修改数据(高位)—— 报警器播放模式

tbuf[index++] = 0x01; // 修改数据(低位)

tbuf[index++] = 0x01; // 修改数据(高位)—— 警示灯闪烁模式(调试发现只支持默认模式,即 0x01 0x01)

tbuf[index++] = 0x01; // 修改数据(低位)

// 默认出厂配置

// 0x040E 00H, 03H 报警器和警示灯都打开

// 0x040F 00H, 1EH 音量为 30 级;

// 0x0410 01H, 01H 第1个文件夹的第1个语音;

// 0x0411 00H, 01H 单曲循环播放;

// 0x0412 01H, 01H 警示灯不与报警器同步,模式为爆闪模式;

// Calculate CRC16,注意CRC16先发低位再发高位

uint16_t crc = Calculate_CRC16(tbuf, index);

tbuf[index++] = crc & 0xFF; // CRC16(低位)

tbuf[index++] = (crc >> 8) & 0xFF; // CRC16(高位)

if (MODBUS_SendReceive(serial, tbuf, index, rbuf, sizeof(rbuf), errorMsg) != 0)

{

return -1;

}

// 这里可以添加对响应数据的验证逻辑

// 例如验证响应的正确性或解析错误代码等

return 0;

}

在这里插入图片描述

在这里插入图片描述

在线CRC校验网站

可用在线crc16校验网站来检验:CRC在线计算

<code>01 10 04 0E 00 05 0A 00 03 00 1E 01 01 00 01 01 01

将发送数据(CRC校验码之前的数据)粘贴进去计算,结果与我们代码计算结果一致:

在这里插入图片描述

CRC16校验函数解析

<code>uint16_t Calculate_CRC16(const uint8_t *data, uint16_t length)

{ -- -->

uint16_t crc = 0xFFFF;

for (uint16_t i = 0; i < length; i++)

{

crc ^= data[i]; // 直接 XOR 低字节,因为多项式是反转的

for (uint8_t j = 0; j < 8; j++)

{

if (crc & 0x0001) // 检查最低位

crc = (crc >> 1) ^ 0xA001; // 右移并应用多项式

else

crc >>= 1; // 只右移

}

}

return crc;

}

这段C语言代码是实现了CRC-16(循环冗余校验)的一个常见版本。CRC-16是一种检测数据错误的方法,广泛用于通信和存储系统中。让我们逐步解释这段代码的关键部分:

初始化CRC值

uint16_t crc = 0xFFFF;

这行代码初始化了CRC的值为0xFFFF。这是开始计算前的标准初始值,用于确保CRC的高位在开始计算前就已经设置好了。

主循环

for (uint16_t i = 0; i < length; i++)

这个循环负责遍历输入的数据数组data,其中length是数组中数据的个数。每次循环处理一个字节。

XOR操作(异或操作)

crc ^= data[i];

在这一行中,当前的CRC值与数据的当前字节进行异或运算(XOR)。异或运算是一种基础的位操作,用于CRC计算中以整合当前字节的数据。

内部循环:位处理

for (uint8_t j = 0; j < 8; j++)

此循环对CRC值的每一位进行处理,总共循环8次,因为每个字节有8位。

条件判断与多项式运算

if (crc & 0x0001)

crc = (crc >> 1) ^ 0xA001;

else

crc >>= 1;

这里是CRC计算的核心部分。首先检查CRC的最低位(通过与0x0001进行AND运算)。如果最低位是1(表示当前的CRC是奇数),则CRC值先向右移一位,然后与多项式0xA001进行异或运算。如果最低位是0,则CRC值只向右移一位。

疑问
为什么是crc & 0x0001而不是crc & 0x1、crc & 0x01或者crc & 0x00000001?

在C语言中,表达式 crc & 0x0001crc & 0x1crc & 0x01、和 crc & 0x00000001 都是用来检查变量 crc 的最低位是否为 1。这三个表达式的功能是完全相同的,因为它们都会与 crc 的最低位进行位与(AND)操作。差别只在于数值表示的方式,但这对操作结果没有影响。

具体来说:

crc & 0x0001:这里 0x0001 是一个16位的常量,表示的是最低位为1,其余位为0。使用这种格式有助于清晰地显示我们关注的是16位整数的最低位。crc & 0x01:与上面的表达式功能相同,只是以更简洁的形式表示。crc & 0x00000001:这是一个32位的常量,但因为 crcuint16_t(即16位无符号整数),所以额外的高位零没有任何作用。

通常,在编写代码时选择哪种形式取决于编程风格和可读性的考虑。例如,在处理16位数据时(我们函数中定义的crc变量是uint16_t类型的,16位),使用 0x0001 可以明确地表示这是一个16位操作,这对于代码的可读性和维护性是有益的。

多项式0xA001

多项式0xA001是反转多项式,它来自标准的CRC-16多项式0x8005。在这种情况下,因为CRC的计算从低位开始,所以使用了反转的多项式。

总结

通过这种方式,CRC算法可以检测数据中的小错误,如单个位的翻转,或者两个位的错误。每处理完一个数据字节后,CRC值会更新,最终的crc值就是数据的CRC-16校验码。这个校验码可以用来检查数据在传输或存储过程中是否被更改过。

实际计算示例

在这里插入图片描述

让我们通过一个简单的示例来详细展示 <code>Calculate_CRC16 函数的工作流程。假设我们有一个简单的输入数据,该数据只包含两个字节:0x030x04。我们将一步步计算这个数据的 CRC-16。

初始化变量

crc = 0xFFFF:初始化CRC值为全1,即16位都是1。data = [0x03, 0x04]:输入数据。length = 2:数据长度为2个字节。

第一轮计算(对数据 0x03

1. XOR操作

crc 初始值为 0xFFFF 和数据 0x03 进行 XOR 操作。我们首先将这些数转换为二进制形式:

crc 初始值: 0xFFFF1111 1111 1111 1111数据: 0x030000 0000 0000 0011

进行 XOR 操作,即每一位相同则结果为 0,不同则结果为 1:

对比最右边的两位:11 XOR 11 = 00其余位因为 0x03 的高位都是0,所以 1 XOR 0 保持不变,即为 1

结果

1111 1111 1111 1111XOR 0000 0000 0000 0011----------------------1111 1111 1111 1100 → 十六进制为 0xFFFC

2. 进行8次循环,每次检查 crc 的最低位,并根据该位是0还是1进行操作

第1次循环

初始 crc = 0xFFFC(二进制:1111 1111 1111 1100crc & 0x0001 => 结果为 0(最低位为0)右移 crc => crc = 0x7FFE(二进制:0111 1111 1111 1110第2次循环

crc = 0x7FFEcrc & 0x0001 => 结果为 0右移 crc => crc = 0x3FFF(二进制:0011 1111 1111 1111第3次循环

crc = 0x3FFFcrc & 0x0001 => 结果为 1右移并异或多项式 crc = (crc >> 1) ^ 0xA001 => crc = 0xD000(二进制:1101 0000 0000 0000) 继续上述循环至第8次,根据是否与1相与来决定是否应用多项式。

处理结束后的 crc(假设完成上述所有循环后的值)。

第二轮计算(对数据 0x04

进行类似的处理,从XOR开始,直至八次循环。

这个过程会根据数据和每次循环的情况而改变。每一步都检查最低位,并根据这个最低位决定是否应用多项式 0xA001,或者只是简单地右移。

示例演示

接下来,我将编写一个Python程序来模拟这个计算过程,并显示每步的详细结果。这样你就可以清晰地看到每一步的变化。

def calculate_crc16_demo(data):

crc = 0xFFFF

steps = []

for byte in data:

crc ^= byte

steps.append(f"After XOR with { byte:02X}: { crc:04X}")

for _ in range(8):

if crc & 0x0001:

crc = (crc >> 1) ^ 0xA001

else:

crc >>= 1

steps.append(f"Step after shift/XOR: { crc:04X}")

return crc, steps

# Example data: [0x03, 0x04]

data_example = [0x03, 0x04]

final_crc, calculation_steps = calculate_crc16_demo(data_example)

final_crc, calculation_steps

运行结果:

结果

(33536,

['After XOR with 03: FFFC',

'Step after shift/XOR: 7FFE',

'Step after shift/XOR: 3FFF',

'Step after shift/XOR: BFFE',

'Step after shift/XOR: 5FFF',

'Step after shift/XOR: 8FFE',

'Step after shift/XOR: 47FF',

'Step after shift/XOR: 83FE',

'Step after shift/XOR: 41FF',

'After XOR with 04: 41FB',

'Step after shift/XOR: 80FC',

'Step after shift/XOR: 407E',

'Step after shift/XOR: 203F',

'Step after shift/XOR: B01E',

'Step after shift/XOR: 580F',

'Step after shift/XOR: 8C06',

'Step after shift/XOR: 4603',

'Step after shift/XOR: 8300'])

我们使用了两个字节的数据(0x030x04)进行CRC-16计算,最终的CRC结果是 0x8300(十六进制)。以下是详细的计算步骤:

初始CRC: 0xFFFF

处理第一个字节(0x03)

XOR操作后0xFFFC循环操作

0x7FFE(右移)0x3FFF(右移)0xBFFE(右移后应用多项式 0xA0010x5FFF(右移后应用多项式 0xA0010x8FFE(右移后应用多项式 0xA0010x47FF(右移后应用多项式 0xA0010x83FE(右移后应用多项式 0xA0010x41FF(右移后应用多项式 0xA001

处理第二个字节(0x04)

XOR操作后0x41FB循环操作

0x80FC(右移后应用多项式 0xA0010x407E(右移)0x203F(右移)0xB01E(右移后应用多项式 0xA0010x580F(右移后应用多项式 0xA0010x8C06(右移后应用多项式 0xA0010x4603(右移后应用多项式 0xA0010x8300(右移后应用多项式 0xA001

每一步我们都进行了检查 crc 的最低位,然后决定是否右移或右移后与多项式 0xA001 进行异或操作。这些步骤一起构成了CRC计算的完整过程。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。