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 & 0x0001
、crc & 0x1
、crc & 0x01
、和crc & 0x00000001
都是用来检查变量crc
的最低位是否为 1。这三个表达式的功能是完全相同的,因为它们都会与crc
的最低位进行位与(AND)操作。差别只在于数值表示的方式,但这对操作结果没有影响。
具体来说:
crc & 0x0001
:这里0x0001
是一个16位的常量,表示的是最低位为1,其余位为0。使用这种格式有助于清晰地显示我们关注的是16位整数的最低位。crc & 0x01
:与上面的表达式功能相同,只是以更简洁的形式表示。crc & 0x00000001
:这是一个32位的常量,但因为crc
是uint16_t
(即16位无符号整数),所以额外的高位零没有任何作用。
通常,在编写代码时选择哪种形式取决于编程风格和可读性的考虑。例如,在处理16位数据时(我们函数中定义的
crc
变量是uint16_t
类型的,16位),使用0x0001
可以明确地表示这是一个16位操作,这对于代码的可读性和维护性是有益的。
多项式0xA001
多项式0xA001
是反转多项式,它来自标准的CRC-16多项式0x8005
。在这种情况下,因为CRC的计算从低位开始,所以使用了反转的多项式。
总结
通过这种方式,CRC算法可以检测数据中的小错误,如单个位的翻转,或者两个位的错误。每处理完一个数据字节后,CRC值会更新,最终的crc
值就是数据的CRC-16校验码。这个校验码可以用来检查数据在传输或存储过程中是否被更改过。
实际计算示例
让我们通过一个简单的示例来详细展示 <code>Calculate_CRC16 函数的工作流程。假设我们有一个简单的输入数据,该数据只包含两个字节:0x03
和 0x04
。我们将一步步计算这个数据的 CRC-16。
初始化变量
crc = 0xFFFF
:初始化CRC值为全1,即16位都是1。data = [0x03, 0x04]
:输入数据。length = 2
:数据长度为2个字节。
第一轮计算(对数据 0x03
)
1. XOR操作
crc
初始值为 0xFFFF
和数据 0x03
进行 XOR 操作。我们首先将这些数转换为二进制形式:
crc
初始值: 0xFFFF
→ 1111 1111 1111 1111
数据: 0x03
→ 0000 0000 0000 0011
进行 XOR 操作,即每一位相同则结果为 0,不同则结果为 1:
对比最右边的两位:11 XOR 11 = 00
其余位因为 0x03
的高位都是0,所以 1 XOR 0
保持不变,即为 1
结果
1111 1111 1111 1111
XOR 0000 0000 0000 0011
----------------------
1111 1111 1111 1100
→ 十六进制为 0xFFFC
2. 进行8次循环,每次检查 crc
的最低位,并根据该位是0还是1进行操作
第1次循环:
初始 crc = 0xFFFC
(二进制:1111 1111 1111 1100
)crc & 0x0001
=> 结果为 0
(最低位为0)右移 crc
=> crc = 0x7FFE
(二进制:0111 1111 1111 1110
) 第2次循环:
crc = 0x7FFE
crc & 0x0001
=> 结果为 0
右移 crc
=> crc = 0x3FFF
(二进制:0011 1111 1111 1111
) 第3次循环:
crc = 0x3FFF
crc & 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'])
我们使用了两个字节的数据(0x03
和 0x04
)进行CRC-16计算,最终的CRC结果是 0x8300
(十六进制)。以下是详细的计算步骤:
初始CRC: 0xFFFF
处理第一个字节(0x03)
XOR操作后:0xFFFC
循环操作:
0x7FFE
(右移)0x3FFF
(右移)0xBFFE
(右移后应用多项式 0xA001
)0x5FFF
(右移后应用多项式 0xA001
)0x8FFE
(右移后应用多项式 0xA001
)0x47FF
(右移后应用多项式 0xA001
)0x83FE
(右移后应用多项式 0xA001
)0x41FF
(右移后应用多项式 0xA001
)
处理第二个字节(0x04)
XOR操作后:0x41FB
循环操作:
0x80FC
(右移后应用多项式 0xA001
)0x407E
(右移)0x203F
(右移)0xB01E
(右移后应用多项式 0xA001
)0x580F
(右移后应用多项式 0xA001
)0x8C06
(右移后应用多项式 0xA001
)0x4603
(右移后应用多项式 0xA001
)0x8300
(右移后应用多项式 0xA001
)
每一步我们都进行了检查 crc
的最低位,然后决定是否右移或右移后与多项式 0xA001
进行异或操作。这些步骤一起构成了CRC计算的完整过程。
上一篇: Qt使用sqlite数据库及项目实战
下一篇: C++的类型转换
本文标签
C语言CRC校验代码(CRC16)(CRC函数、CRC校验函数解析、循环冗余校验、在线CRC校验、CRC在线、CRC网站)
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。