使用 c/python 实现 CRC-16/MODBUS 校验算法
使用 c/python 实现 CRC-16/MODBUS 校验算法
·
关于 crc校验 REFIN REFOUT 之类的基础知识就不讲解了,网上有很多。我就直接贴代码了。
下面是满足 CRC 生成多项式为 CRC-16:‘x16+x15+x2+1’ 的 CRC-16-MODBUS 算法代码。
python 代码如下:
# CRC-16-MODBUS
def calculate_crc16(data: bytes) -> int:
# 初始化crc为0xFFFF
crc = 0xFFFF
# 循环处理每个数据字节
for byte in data:
# 将每个数据字节与crc进行异或操作
crc ^= byte
# 对crc的每一位进行处理
for _ in range(8):
# 如果最低位为1,则右移一位并执行异或0xA001操作(即0x8005按位颠倒后的结果)
if crc & 0x0001:
crc = (crc >> 1) ^ 0xA001
# 如果最低位为0,则仅将crc右移一位
else:
crc = crc >> 1
# 返回最终的crc值
return crc
if __name__ == '__main__':
# 测试数据
test_data = bytes.fromhex("31 32 33")
# test_data = bytes("Hello, world!", encoding='utf-8')
# 计算CRC-16校验码
crc16 = calculate_crc16(test_data)
# 输出校验码值
print(f'CRC-16校验码值为: 0x{crc16:04X}')
c 代码如下:
// CRC-16-MODBUS
#include <stdio.h>
#include <stdint.h>
uint16_t calculate_crc16(const uint8_t *data, size_t len) {
// printf("%d\n",len);
// 初始化crc为0xFFFF
uint16_t crc = 0xFFFF;
// 循环处理每个数据字节
for (size_t i = 0; i < len; i++) {
// 将每个数据字节与crc进行异或操作
crc ^= data[i];
// 对crc的每一位进行处理:如果最低位为1,则右移一位并执行异或0xA001操作(即0x8005按位颠倒后的结果)
for (int j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
}
// 如果最低位为0,则仅将crc右移一位
else {
crc = crc >> 1;
}
}
}
return crc;
}
int main() {
// 测试数据
unsigned char data[] = {0x31, 0x32, 0x33};
size_t len = sizeof(data);
// 计算CRC-16校验码
uint16_t crc16 = calculate_crc16(data, len);
// 输出结果
// printf("Data: %s\n", data);
printf("CRC-16: 0x%04X\n", crc16);
return 0;
}
比较疑惑的点是 CRC-16-MODBUS 算法是需要在计算前将原始数据 data 和运算完成后得到的 CRC 进行反转的,上述代码未能体现这些流程,而是在运算时使用反转后的 crc 值及对 crc 的低位操作,maybe 有异曲同工之妙,学艺不精没有搞懂。下面给出实实在在的按流程走的代码,得到的结果是一样的。
c 代码如下:
// CRC-16-MODBUS
#include <stdio.h>
#include <stdint.h>
// 反转一个8位无符号整数的字节序,即高低字节互换
void InvertUint8(unsigned char *invertBuf)
{
// 将8位无符号整数指针强制转换为16位无符号整数指针
unsigned short *srcBuf = (unsigned short *)invertBuf;
int i;
unsigned char tmp[4]={0};
for(i=0;i< 8;i++)
{
if(srcBuf[0]& (1 << i))
tmp[0]|=1<<(7-i);
}
invertBuf[0] = tmp[0];
}
// 反转一个16位无符号整数的字节序,即高低字节互换
void InvertUint16(unsigned short *invertBuf)
{
// 将16位无符号整数指针强制转换为16位无符号整数指针
unsigned short *srcBuf = (unsigned short *)invertBuf;
int i;
unsigned short tmp[4]={0};
for(i=0;i< 16;i++)
{
if(srcBuf[0]& (1 << i))
tmp[0]|=1<<(15 - i);
}
invertBuf[0] = tmp[0];
}
// 计算给定数据的CRC-16校验码
uint16_t calculate_crc16(const uint8_t *data, size_t len)
{
uint16_t crc = 0xFFFF;
unsigned char invertData = 0;
// 循环处理每个字节
while (len--)
{
invertData = *(data++);
InvertUint8(&invertData); // 反转字节序
crc ^= (invertData << 8); // 异或运算
for(int i = 0;i < 8;i++)
{
if(crc & 0x8000) // 判断高位是否为1
crc = (crc << 1) ^ 0x8005; // 左移1位并与多项式0x8005进行异或运算
else
crc = crc << 1; // 左移1位
}
}
InvertUint16(&crc); // 反转字节序
return (crc);
}
int main()
{
unsigned char data[] = {0x31, 0x32, 0x33}; // 待计算CRC-16的数据
size_t len = sizeof(data); // 数据长度
uint16_t crc16 = calculate_crc16(data, len); // 计算CRC-16校验码
// 输出CRC-16校验码
printf("CRC-16: 0x%04X\n", crc16);
return 0;
}
参考文章:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献2条内容
所有评论(0)