C++与JAVA代码实现CRC-16/MODBUS算法

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     return 0;
22 }
23 
24 /**
25  * 进行CRC-6/MODBUS计算
26  */
27 unsigned short getCrc16(unsigned char *arr, int len)
28 {
29     unsigned short ax,lsb;
30     int i,j;
31     ax = 0xFFFF;
32     for (i = 0; i < len; i++) 
33     {
34         ax ^= arr[i];
35         for (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     return 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      * 根据报文byte数组,获取CRC-16 16进制字符串
 42      * {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00} >> 0xE647
 43      *
 44      * @param data
 45      * @return
 46      */
 47     public static String getCrc16HexStr(byte[] data) {
 48         return intToHexStr(getCrc16(data));
 49     }
 50 
 51     /**
 52      * 根据报文byte数组,获取CRC-16 int值
 53      * {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00} >> 58951
 54      *
 55      * @param data 报文数组
 56      * @return CRC值(10进制)
 57      */
 58     public static int getCrc16(byte[] data) {
 59         // ----- 预置一个CRC寄存器,初始值为0xFFFF
 60         int crc = 0xFFFF;
 61         byte byteLen = 8;
 62         for (byte b : data) {
 63             // ----- 循环,将每数据帧中的每个字节与CRC寄存器中的低字节进行异或运算
 64             crc ^= ((int) b & 0x00FF);
 65             for (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--;
116         }
117         return builder.append(crcStr).toString();
118     }
119 
120     /**
121      * 输出16进制与长度, 提供给 C++ CRC校验方法 测试 代码使用
122      *
123      * @param str
124      */
125     private static void printHexStr(String str) {
126         String[] split = str.split(SPACE);
127         StringBuilder builder = new StringBuilder();
128         builder.append("    unsigned char arr[] = {");
129         for (int i = 0; i < split.length; i++) {
130             builder.append("0x").append(split[i]);
131             if (i < 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 + ";");
138     }
139 
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 }

猜你喜欢

转载自www.cnblogs.com/yanwu0527/p/11424730.html
今日推荐