zoukankan      html  css  js  c++  java
  • C++与JAVA代码实现CRC-16/MODBUS算法,且与 http://www.ip33.com/crc.html 进行结果验证

    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
    ", 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 import org.apache.commons.lang3.RandomUtils;
      4 import org.apache.commons.lang3.StringUtils;
      5 
      6 import java.io.BufferedReader;
      7 import java.io.InputStreamReader;
      8 import java.net.URL;
      9 import java.net.URLConnection;
     10 
     11 /**
     12  * @author <a herf="mailto:yanwu0527@163.com">yanwu</a>
     13  * @date 2019-08-26 14:22.
     14  * <p>
     15  * description:
     16  * 本方法使用CRC-16/MODBUS算法
     17  */
     18 @SuppressWarnings("all")
     19 public class Crc16Util {
     20     private static final Integer ONE = 1;
     21     private static final Integer TWO = 2;
     22     private static final Integer HEX = 16;
     23     private static final String NUL = "";
     24     private static final String SPACE = " ";
     25     private static final String ASCII = "US-ASCII";
     26 
     27     /**
     28      * 根据报文byte数组,获取CRC-16 16进制字符串<p>
     29      * 48 4C 01 00 01 00 00 05 00 00 >> 0xE647
     30      *
     31      * @param data 报文数组
     32      * @return CRC值(16进制)
     33      */
     34     public static String getCrc16HexStr(String data) {
     35         return crcToHexStr(getCrc16(data));
     36     }
     37 
     38     /**
     39      * 根据报文byte数组,获取CRC-16 int值<p>
     40      * 48 4C 01 00 01 00 00 05 00 00 >> 58951
     41      *
     42      * @param data 报文数组
     43      * @return CRC值(10进制)
     44      */
     45     public static int getCrc16(String data) {
     46         if (StringUtils.isBlank(data)) {
     47             // ----- 校验:报文字符串不能为空,否则抛异常
     48             throw new RuntimeException("The string cannot be empty!");
     49         }
     50         return getCrc16(hexStrToByteArr(data));
     51     }
     52 
     53     /**
     54      * 根据报文byte数组,获取CRC-16 16进制字符串<p>
     55      * {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00} >> 0xE647
     56      *
     57      * @param data 报文数组
     58      * @return CRC值(16进制)
     59      */
     60     public static String getCrc16HexStr(byte[] data) {
     61         return crcToHexStr(getCrc16(data));
     62     }
     63 
     64     /**
     65      * 根据报文byte数组,获取CRC-16 int值<p>
     66      * {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00} >> 58951
     67      *
     68      * @param data 报文数组
     69      * @return CRC值(10进制)
     70      */
     71     public static int getCrc16(byte[] data) {
     72         if (data.length == 0) {
     73             // ----- 校验:报文数组不能为空,否则抛异常
     74             throw new RuntimeException("The array cannot be empty!");
     75         }
     76         // ----- 预置一个CRC寄存器,初始值为0xFFFF
     77         int crc = 0xFFFF;
     78         byte byteLen;
     79         boolean flag;
     80         for (byte item : data) {
     81             // ----- 循环,将每数据帧中的每个字节与CRC寄存器中的低字节进行异或运算
     82             crc ^= ((int) item & 0x00FF);
     83             byteLen = 8;
     84             while (byteLen > 0) {
     85                 // ----- 判断寄存器最后一位是 1:[true: 1; false: 0]
     86                 flag = (crc & ONE) == ONE;
     87                 // ----- 将寄存器右移1位,最高位自动补0
     88                 crc >>= 1;
     89                 if (flag) {
     90                     // ----- 如果右移出来的位是 1:将寄存器与固定值 0xA001 异或运算
     91                     // ----- 如果右移出来的位是 0:不做处理,进行下一次右移
     92                     // ----- 直到处理完整个字节的8位
     93                     crc ^= 0xA001;
     94                 }
     95                 byteLen--;
     96             }
     97         }
     98         // ----- 最终寄存器得值就是CRC的值,返回
     99         return crc;
    100     }
    101 
    102     /**
    103      * 将16进制字符串转换为16进制Byte数组<p>
    104      * 48 4C 01 00 01 00 00 05 00 00 >> {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00}
    105      *
    106      * @param str 报文字符串
    107      * @return 报文数组
    108      */
    109     private static byte[] hexStrToByteArr(String str) {
    110         str = str.replaceAll(SPACE, NUL);
    111         int strLen = str.length();
    112         if ((strLen & ONE) == ONE) {
    113             // ----- 报文字符串必须是以一个字节为单位(两位为一个字节),所以当去除所有空格后的报文长度为单数时说明报文错误
    114             throw new RuntimeException("Incorrect message format!");
    115         }
    116         byte[] result = new byte[strLen / TWO];
    117         // ----- 两位一个字节
    118         for (int i = 0; i < strLen; i += TWO) {
    119             String temp = str.substring(i, i + TWO);
    120             result[i / TWO] = (byte) Integer.parseInt(temp, HEX);
    121         }
    122         return result;
    123     }
    124 
    125     /**
    126      * 将CRC-16值转换成16进制字符串,且保持最小长度为4位<p>
    127      * 58951 >> E647
    128      *
    129      * @param data CRC值(10进制)
    130      * @return CRC值(16进制)
    131      */
    132     private static String crcToHexStr(int data) {
    133         String crcStr = Integer.toHexString(data).toUpperCase();
    134         int size = 4 - crcStr.length();
    135         StringBuilder builder = new StringBuilder();
    136         // ---- 长度不够 4 位高位自动补0
    137         while (size > 0) {
    138             builder.append("0");
    139             size--;
    140         }
    141         return builder.append(crcStr).toString();
    142     }
    143 
    144     /**
    145      * 输出16进制与长度, 提供给 C++ CRC校验方法 测试 代码使用
    146      *
    147      * @param str 16进制字符串
    148      */
    149     private static void printHexStr(String str) {
    150         String[] split = str.split(SPACE);
    151         StringBuilder builder = new StringBuilder();
    152         builder.append("    unsigned char arr[] = {");
    153         for (int i = 0; i < split.length; i++) {
    154             builder.append("0x").append(split[i]);
    155             if (i < split.length - 1) {
    156                 builder.append(", ");
    157             }
    158         }
    159         builder.append("};");
    160         System.out.println(builder.toString());
    161         System.out.println("    int len = " + split.length + ";");
    162     }
    163 
    164     /**
    165      * 测试CRC获取
    166      *
    167      * @param args
    168      */
    169     public static void main(String[] args) throws Exception {
    170         String str = "48 4C 01 00 01 00 00 05 00 00";
    171         // ----- 输出16进制数组给 C++ 测试使用
    172         Crc16Util.printHexStr(str);
    173         // ----- 获取CRC-16的值
    174         System.out.println("crc16 int is: " + Crc16Util.getCrc16(str));
    175         System.out.println("crc16 hex is: " + Crc16Util.getCrc16HexStr(str));
    176         // ----- 与 http://www.ip33.com/crc.html 进行结果验证
    177         check();
    178     }
    179 
    180     private static void check() {
    181         char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    182         // ----- 校验的次数
    183         int size = Integer.MAX_VALUE;
    184         int len = 0, min = 10, max = 500;
    185         StringBuilder thisData = new StringBuilder();
    186         StringBuilder httpData = new StringBuilder();
    187         while (size > 0) {
    188             thisData.delete(0, thisData.length());
    189             httpData.delete(0, httpData.length());
    190             len = RandomUtils.nextInt(min, max);
    191             while (len > 0) {
    192                 char h = chars[RandomUtils.nextInt(0, 16)];
    193                 char l = chars[RandomUtils.nextInt(0, 16)];
    194                 httpData.append(h).append(l);
    195                 thisData.append(h).append(l);
    196                 if (len != 1) {
    197                     httpData.append("+");
    198                     thisData.append(SPACE);
    199                 }
    200                 len--;
    201             }
    202             String thisCrc = getCrc16HexStr(thisData.toString());
    203             String httpCrc = getCrcByUrl(httpData.toString());
    204             System.out.println("this: " + thisCrc + " -> " + thisData.toString());
    205             System.out.println("http: " + httpCrc + " -> " + httpData.toString());
    206             if (!thisCrc.equals(httpCrc)) {
    207                 throw new RuntimeException("ERROR!!!");
    208             }
    209             size--;
    210         }
    211     }
    212 
    213     public static String getCrcByUrl(String data) {
    214         BufferedReader in = null;
    215         String result = "";
    216         try {
    217             String path = "http://api.ip33.com/crc/c?width=16&poly=8005&init=FFFF&xor=0000&refin=true&refout=true&data=" + data;
    218             URL realUrl = new URL(path);
    219             URLConnection connection = realUrl.openConnection();
    220             connection.setRequestProperty("accept", "*/*");
    221             connection.setRequestProperty("connection", "Keep-Alive");
    222             connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
    223             connection.connect();
    224             in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
    225             String line;
    226             while ((line = in.readLine()) != null) {
    227                 result += line;
    228             }
    229             System.out.println("responce   -> " + result);
    230             int index = result.indexOf(""hex": "") + 8;
    231             return result.substring(index, index + 4);
    232         } catch (Exception e) {
    233             e.printStackTrace();
    234         } finally {
    235             try {
    236                 if (in != null) {
    237                     in.close();
    238                 }
    239             } catch (Exception e2) {
    240                 e2.printStackTrace();
    241             }
    242         }
    243         return null;
    244     }
    245 
    246 }
  • 相关阅读:
    查看JVM使用的默认的垃圾收集器
    生产环境mysql的参数设置不一样,好好的程序,又出错
    伤秦姝行
    《道德经》全文——马王堆出土帛书版
    100篇锻炼口才表达能力的绕口令
    《道德经》部分
    40篇英语短文搞定3500个单词
    python浮点数与整数间的转化
    理解微积分
    matlab判断某个变量是否存在
  • 原文地址:https://www.cnblogs.com/yanwu0527/p/11424730.html
Copyright © 2011-2022 走看看