CRC16详解和Java实现

2024-02-28 21:12
文章标签 java 实现 详解 crc16

本文主要是介绍CRC16详解和Java实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1 CRC介绍

2 CRC16 分类

3 计算步骤

4 代码实现


1 CRC介绍

CRC校验(循环冗余校验)

CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。

常见CRC参数模型如下:

CRC算法名称

多项式公式

宽度

多项式

初始值

结果异或值

输入反转

输出反转

CRC-4/ITU

x4 + x + 1

4

03

00

00

true

true

CRC-5/EPC

x5 + x3 + 1

5

09

09

00

false

false

CRC-5/ITU

x5 + x4 + x2 + 1

5

15

00

00

true

true

CRC-5/USB

x5 + x2 + 1

5

05

1F

1F

true

true

CRC-6/ITU

x6 + x + 1

6

03

00

00

true

true

CRC-7/MMC

x7 + x3 + 1

7

09

00

00

false

false

CRC-8

x8 + x2 + x + 1

8

07

00

00

false

false

CRC-8/ITU

x8 + x2 + x + 1

8

07

00

55

false

false

CRC-8/ROHC

x8 + x2 + x + 1

8

07

FF

00

true

true

CRC-8/MAXIM

x8 + x5 + x4 + 1

8

31

00

00

true

true

CRC-16/IBM

x16 + x15 + x2 + 1

16

8005

0000

0000

true

true

CRC-16/MAXIM

x16 + x15 + x2 + 1

16

8005

0000

FFFF

true

true

CRC-16/USB

x16 + x15 + x2 + 1

16

8005

FFFF

FFFF

true

true

CRC-16/MODBUS

x16 + x15 + x2 + 1

16

8005

FFFF

0000

true

true

CRC-16/CCITT

x16 + x12 + x5 + 1

16

1021

0000

0000

true

true

CRC-16/CCITT-FALSE

x16 + x12 + x5 + 1

16

1021

FFFF

0000

false

false

CRC-16/X25

x16 + x12 + x5 + 1

16

1021

FFFF

FFFF

true

true

CRC-16/XMODEM

x16 + x12 + x5 + 1

16

1021

0000

0000

false

false

CRC-16/DNP

x16 + x13 + x12 + x11 + x10 + x8 + x6 + x5 + x2 + 1

16

3D65

0000

FFFF

true

true

CRC-32

x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1

32

04C11DB7

FFFFFFFF

FFFFFFFF

true

true

CRC-32/MPEG-2

x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1

32

04C11DB7

FFFFFFFF

00000000

false

false

CRC算法参数模型解释:

可参考在线工具加深理解。

  • WIDTH:宽度,即CRC比特数,可分为CRC-4/5/67/8/16/32等。
  • POLY:多项式,以16进制表示。例如:x16 + x15 + x2 + 1即是0x8005,忽略了最高位的"1"。
  • INIT:这是算法开始时寄存器(crc)的初始化预置值,十六进制表示。
  • XOROUT:计算结果与此参数异或后得到最终的CRC值。
  • REFIN:待测数据的每个字节是否按位反转,True或False。
  • REFOUT:在计算后之后,异或输出之前,整个数据是否按位反转,True或False。

一般确定一个算法会指出:CRC16  x16 + x15 + x2 + 1,其他有默认值。

2 CRC16 分类

CRC16_CCITT:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或。
CRC16_CCITT_FALSE:多项式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或。
CRC16_XMODEM:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或。
CRC16_X25:多项式x16+x12+x5+1(0x1021),初始值0xffff,低位在前,高位在后,结果与0xFFFF异或。
CRC16_MODBUS:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或。
CRC16_IBM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或。
CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或。
CRC16_USB:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或。
CRC16_DNP:多项式x16+x13+x12+x11+x10+x8+x6+x5+x2+1(0x3D65),初始值0x0000,低位在前,高位在后,结果与0xFF异或。

3 计算步骤

(1)、预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器;
(2)、把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果放于CRC寄存器,高八位数据不变;
(3)、把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位;
(4)、如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或;
(5)、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
(6)、重复步骤2到步骤5,进行通讯信息帧下一个字节的处理;
(7)、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换;
(8)、最后得到的CRC寄存器内容即为:CRC码。

4 代码实现

public class CRC16Demo {/*** CRC16_CCITT:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或* 0x8408是0x1021按位颠倒后的结果。** @param buffer* @return*/public static int CRC16_CCITT(byte[] buffer) {int wCRCin = 0x0000;int wCPoly = 0x8408;for (byte b : buffer) {wCRCin ^= ((int) b & 0x00ff);for (int j = 0; j < 8; j++) {if ((wCRCin & 0x0001) != 0) {wCRCin >>= 1;wCRCin ^= wCPoly;} else {wCRCin >>= 1;}}}
//        wCRCin=(wCRCin<<8)|(wCRCin>>8);
//        wCRCin &= 0xffff;return wCRCin ^= 0x0000;}/*** CRC-CCITT (0xFFFF)* CRC16_CCITT_FALSE:多项式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或** @param buffer* @return*/public static int CRC16_CCITT_FALSE(byte[] buffer) {int wCRCin = 0xffff;int wCPoly = 0x1021;for (byte b : buffer) {for (int i = 0; i < 8; i++) {boolean bit = ((b >> (7 - i) & 1) == 1);boolean c15 = ((wCRCin >> 15 & 1) == 1);wCRCin <<= 1;if (c15 ^ bit)wCRCin ^= wCPoly;}}wCRCin &= 0xffff;return wCRCin ^= 0x0000;}/*** CRC-CCITT (XModem)* CRC16_XMODEM:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或** @param buffer* @return*/public static int CRC16_XMODEM(byte[] buffer) {int wCRCin = 0x0000; // initial value 65535int wCPoly = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)for (byte b : buffer) {for (int i = 0; i < 8; i++) {boolean bit = ((b >> (7 - i) & 1) == 1);boolean c15 = ((wCRCin >> 15 & 1) == 1);wCRCin <<= 1;if (c15 ^ bit)wCRCin ^= wCPoly;}}wCRCin &= 0xffff;return wCRCin ^= 0x0000;}/*** CRC16_X25:多项式x16+x12+x5+1(0x1021),初始值0xffff,低位在前,高位在后,结果与0xFFFF异或* 0x8408是0x1021按位颠倒后的结果。** @param buffer* @return*/public static int CRC16_X25(byte[] buffer) {int wCRCin = 0xffff;int wCPoly = 0x8408;for (byte b : buffer) {wCRCin ^= ((int) b & 0x00ff);for (int j = 0; j < 8; j++) {if ((wCRCin & 0x0001) != 0) {wCRCin >>= 1;wCRCin ^= wCPoly;} else {wCRCin >>= 1;}}}return wCRCin ^= 0xffff;}/*** CRC-16 (Modbus)* CRC16_MODBUS:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或* 0xA001是0x8005按位颠倒后的结果** @param buffer* @return*/public static int CRC16_MODBUS(byte[] buffer) {int wCRCin = 0xffff;int POLYNOMIAL = 0xa001;for (byte b : buffer) {wCRCin ^= ((int) b & 0x00ff);for (int j = 0; j < 8; j++) {if ((wCRCin & 0x0001) != 0) {wCRCin >>= 1;wCRCin ^= POLYNOMIAL;} else {wCRCin >>= 1;}}}return wCRCin ^= 0x0000;}/*** CRC-16* CRC16_IBM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或* 0xA001是0x8005按位颠倒后的结果** @param buffer* @return*/public static int CRC16_IBM(byte[] buffer) {int wCRCin = 0x0000;int wCPoly = 0xa001;for (byte b : buffer) {wCRCin ^= ((int) b & 0x00ff);for (int j = 0; j < 8; j++) {if ((wCRCin & 0x0001) != 0) {wCRCin >>= 1;wCRCin ^= wCPoly;} else {wCRCin >>= 1;}}}return wCRCin ^= 0x0000;}/*** CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或* 0xA001是0x8005按位颠倒后的结果** @param buffer* @return*/public static int CRC16_MAXIM(byte[] buffer) {int wCRCin = 0x0000;int wCPoly = 0xa001;for (byte b : buffer) {wCRCin ^= ((int) b & 0x00ff);for (int j = 0; j < 8; j++) {if ((wCRCin & 0x0001) != 0) {wCRCin >>= 1;wCRCin ^= wCPoly;} else {wCRCin >>= 1;}}}return wCRCin ^= 0xffff;}/*** CRC16_USB:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或* 0xA001是0x8005按位颠倒后的结果** @param buffer* @return*/public static int CRC16_USB(byte[] buffer) {int wCRCin = 0xFFFF;int wCPoly = 0xa001;for (byte b : buffer) {wCRCin ^= ((int) b & 0x00ff);for (int j = 0; j < 8; j++) {if ((wCRCin & 0x0001) != 0) {wCRCin >>= 1;wCRCin ^= wCPoly;} else {wCRCin >>= 1;}}}return wCRCin ^= 0xffff;}/*** CRC16_DNP:多项式x16+x13+x12+x11+x10+x8+x6+x5+x2+1(0x3D65),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或* 0xA6BC是0x3D65按位颠倒后的结果** @param buffer* @return*/public static int CRC16_DNP(byte[] buffer) {int wCRCin = 0x0000;int wCPoly = 0xA6BC;for (byte b : buffer) {wCRCin ^= ((int) b & 0x00ff);for (int j = 0; j < 8; j++) {if ((wCRCin & 0x0001) != 0) {wCRCin >>= 1;wCRCin ^= wCPoly;} else {wCRCin >>= 1;}}}return wCRCin ^= 0xffff;}public static void main(String[] args) {String dataStr = "0809060010040303";byte[] data = hexStringToByteArray(dataStr.replaceAll(" ", ""));int res = CRC16_IBM(data);System.out.println(Integer.toHexString(res));}public static byte[] hexStringToByteArray(String s) {int len = s.length();byte[] data = new byte[len / 2];for (int i = 0; i < len; i += 2) {data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)+ Character.digit(s.charAt(i + 1), 16));}return data;}

这篇关于CRC16详解和Java实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/756630

相关文章

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Java中的数组与集合基本用法详解

《Java中的数组与集合基本用法详解》本文介绍了Java数组和集合框架的基础知识,数组部分涵盖了一维、二维及多维数组的声明、初始化、访问与遍历方法,以及Arrays类的常用操作,对Java数组与集合相... 目录一、Java数组基础1.1 数组结构概述1.2 一维数组1.2.1 声明与初始化1.2.2 访问

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

Java 方法重载Overload常见误区及注意事项

《Java方法重载Overload常见误区及注意事项》Java方法重载允许同一类中同名方法通过参数类型、数量、顺序差异实现功能扩展,提升代码灵活性,核心条件为参数列表不同,不涉及返回类型、访问修饰符... 目录Java 方法重载(Overload)详解一、方法重载的核心条件二、构成方法重载的具体情况三、不构

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构