CRC-16/MODBUS的多项式为:x16+x15+x2+1(8005),宽度为16。运算时,首先将一个16位的寄存器预置为11111111 11111111,然后连续把数据帧中的每个字节中的8位与该寄存器的当前值进行运算。仅仅每个字节的8位数据位参与生成CRC。
在生成CRC时,每个字节的8位与寄存器中的内容进行异或,然后将结果向低位位移,高位则用0补充,最低位(LSB)移出并检测,如果是1,该寄存器就与一个预设的固定值(0A001H)进行一次异或运算,如果低位为0,不作任何处理。
上述处理重复进行,直到执行完了8次位移操作,当最后一位移完以后,下一个8位字节与寄存器当前的值进行异或运算,同样进行上述的另一轮8次位移异或才做,当数据帧中的所有字节都做了处理,生成最终值就是CRC值。
生成CRC的流程为:
1. 预置一个16位寄存器位FFFFH,称之为CRC寄存器。
2. 把数据帧中第一个字节的8位与CRC寄存器中的低字节进行异或运算,结果存回CRC寄存器。
3. 将CRC寄存器向右移1位,最高位以0填充,最低位移出并监测。
4. 如果最低位为0:重复第3步(下一次移位),如果最低位为1:将CRC寄存器与一个预设的固定值(0A001H)进行异或运算。
5. 重复第3步和第4步直到8次位移,这样就处理完了一个完整的8位。
6. 重复第2步到第5步来处理下一个字节,知道处理完校验位前所有的数据。
7. 最终CRC寄存器得值就是CRC的值。
C++代码实现
1 #include <stdio.h> 2 #include<iostream> 3 #include<string> 4 #include<sstream> 5 using namespace std; 6 7 unsigned short getCrc16(unsigned char *arr, int len); 8 9 string intToHexStr(int data); 10 11 int main() 12 { 13 unsigned char arr[] = {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00}; 14 int len = 10; 15 int crcInt; 16 crcInt = getCrc16(arr, len); 17 printf( "crc16 int is: %d\n", crcInt ); 18 string crcStr; 19 crcStr = intToHexStr(crcInt); 20 cout << "crc16 hex is: " << crcStr.c_str() <<endl; 21 リターン 0 ; 22 } 23 24 / * * 25 *进行CRC-6 / MODBUS计算 26 * / 27の符号なしショート getCrc16(符号なしのchar *のARR、INT LEN) 28 { 29 符号なしの短い斧、LSB。 30 int型I、J。 31 AX = 0xFFFFの。 32 のために(i = 0 ; iがLEN <; iは++ ) 33 { 34 AX ^ = ARRの[I]。 35 のための(J = 0 ; J < 8。、J ++ ) 36 { 37 [ LSB = AX&0x0001に、 38である AX = AX >> 1。; 39 IF(!LSB = 0 ) 40 { 41である AX ^ = 0xA001 ; 42である } 43れる } 44れます } 45 リターンAX; 46である } 47 48 / * * 49 * INT 16進文字列データ型に変換 50 * / 51 string intToHexStr(int data) 52 { 53 /*** 将int类型数据转换成16进制字符串 ***/ 54 string hexStr; 55 ostringstream temp; 56 temp<<hex<<data; 57 hexStr = temp.str(); 58 59 /*** 将小写转大写 ***/ 60 int i; 61 int len = hexStr.size(); 62 for (i = 0; i < len; i++) { 63 hexStr[i] = toupper(hexStr[i]); 64 } 65 66 /*** 保证将字符串长度为4 ***/ 67 while (hexStr.size() < 4) { 68 hexStr = "0" + hexStr; 69 } 70 71 /*** 返回 ***/ 72 return hexStr; 73 }
JAVA代码实现
1 package com.yanwu.demo.utils; 2 3 /** 4 * @author <a herf="mailto:[email protected]">yanwu</a> 5 * @date 2019-08-26 14:22. 6 * <p> 7 * description: 8 * 本方法使用CRC-16/MODBUS算法 9 */ 10 public class Crc16Util { 11 private static final Integer TWO = 2; 12 private static final Integer HEX = 16; 13 private static final String NUL = ""; 14 private static final String SPACE = " "; 15 private static final String ASCII = "US-ASCII"; 16 17 /** 18 * 根据报文byte数组,获取CRC-16 16进制字符串 19 * 48 4C 01 00 01 00 00 05 00 00 >> 0xE647 20 * 21 * @param data 22 * @return 23 */ 24 public static String getCrc16HexStr(String data) { 25 return intToHexStr(getCrc16(data)); 26 } 27 28 /** 29 * 根据报文byte数组,获取CRC-16 int值 30 * 48 4C 01 00 01 00 00 05 00 00 >> 58951 31 * 32 * @param data 报文数组 33 * @return CRC值(10进制) 34 */ 35 public static int getCrc16(String data) { 36 data = data.replaceAll(SPACE, NUL); 37 return getCrc16(getByteArr(data)); 38 } 39 40 /** 41 *パケットのバイト配列、CRC-16 16を得ることが16進数の文字列 42は、 * {0x48、0x4C、0x01で、0x00を、0x01の、0x00を、0x00に、0x05を、0x00に、0x00の} >> 0xE647 43である * 44れる * @param データ 45 * @return 46である * / 47 パブリック 静的文字列getCrc16HexStr(バイト[]データ){ 48 リターンintToHexStr(getCrc16(データ)); 49 } 50 51れる / ** 52れる *バイトアレイパケットに応じて、CRC-16、INT値取得 53 {0x48 *、0x4C、0x01で、0x00を、0x01の、0x00を、0x00に、0x05を、0x00に、0x00の} >> 58951 54であります * 55 * @paramのデータ・パケット・アレイ 56は、 * @return CRC値(10進) 57がある * / 58 パブリック 静的 INT getCrc16(バイト[]データ){ 59 // -----予め設定されたCRCは、初期値レジスタ0xFFFFである 60 INT CRC = 0xFFFFのは、 61であり 、バイト ByteLen = 8 ; 62である ため(バイトB:データ){ 63である // -----サイクル、CRCレジスタの各バイトを、各データフレームの下位ワードはXOR部 64 CRC ^ =((INT)B&0x00FFに); 65 用 (int j = 0; j < byteLen; j++) { 66 // ----- 将寄存器右移1位,最高位自动补0 67 if ((crc & 0x0001) != 0) { 68 // ----- 如果右移出来的位不为0,将寄存器与固定值 0xA001 异或运算 69 crc >>= 1; 70 crc ^= 0xA001; 71 } else { 72 // ----- 如果右移出来的位为0,不做处理,进行下一次右移,知道处理完整个字节的8位 73 crc >>= 1; 74 } 75 } 76 } 77 // ----- 最终寄存器得值就是CRC的值,返回 78 return crc; 79 } 80 81 /** 82 * 将16进制字符串转换为16进制Byte数组 83 * 48 4C 01 00 01 00 00 05 00 00 >> {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00} 84 * 85 * @param str 报文字符串 86 * @return 报文数组 87 */ 88 public static byte[] getByteArr(String str) { 89 str = str.replaceAll(SPACE, NUL); 90 int strLen = str.length(); 91 byte[] result = new byte[strLen / TWO]; 92 // ----- 两位一个字节 93 for (int i = 0; i < strLen; i += TWO) { 94 String temp = str.substring(i, i + TWO); 95 result[i / TWO] = (byte) Integer.parseInt(temp, HEX); 96 } 97 return result; 98 } 99 100 /** 101 * 将CRC-16值转换成16进制字符串,且保持最小长度为4位 102 * <p> 103 * 58951 >> E647 104 * 105 * @param data 106 * @return 107 */ 108 private static String intToHexStr(int data) { 109 String crcStr = Integer.toHexString(data).toUpperCase(); 110 int size = 4 - crcStr.length(); 111 StringBuilder builder = new StringBuilder(); 112 // ---- 长度不够 4 位高位自动补0 113 while (size > 0) { 114 builder.append("0"); 115 size-- ; 1 16 } 117 戻りbuilder.append(crcStr).toString(); 118 } 119 120 / ** 121 * 16バイナリ出力長、テストコードのメソッド使用C ++ CRCチェックに供給され、 122 * 123 *の@param STR 124 * / 125 プライベート 静的 ボイドprintHexStr(文字列STR){ 126 のString [] =分割str.split(SPACE) 127 のStringBuilderビルダー= 新しい新規のStringBuilder(); 128 builder.append( "unsigned char型のARR [] = {" )。 129 のためには、(int型 ; iはsplit.length <I ++は、I = 0 ){ 130 builder.append( "0X" ).append(スプリット[i])とします。 131 であれば(私はsplit.lengthを< - 1 ){ 132 builder.append( "" )。 133 } 134 } 135 builder.append( "};" ); 136 のSystem.out.println(builder.toString())。 137 のSystem.out.println( "int型LEN =" + split.length + ";" ); 140 /** 141 * 测试CRC获取 142 * 143 * @param args 144 */ 145 public static void main(String[] args) throws Exception { 146 String str = "48 4C 01 00 01 00 00 05 00 00"; 147 // ----- 输出16进制数组给 C++ 测试使用 148 Crc16Util.printHexStr(str); 149 // ----- 获取CRC-16的值 150 System.out.println("crc16 int is: " + Crc16Util.getCrc16(str)); 151 System.out.println("crc16 hex is: " + Crc16Util.getCrc16HexStr(str));152 } 153 154 }