1. 程式人生 > >CRC16校驗原理及實現

CRC16校驗原理及實現

 CRC碼由傳送端計算,放置於傳送資訊報文的尾部。接收資訊的裝置再重新計算接收到資訊報文的CRC,比較計算得到的CRC是否與接收到的相符,如果兩者不相符,則表明出錯。
 校驗碼的計算多項式為(X16 + X15 + X2 + 1)。具體CRC16碼的計算方法是:
        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)進行異或;(Modbus)
        5.重複步驟3和4,直到右移8次,這樣整個8位資料全部進行了處理;
        6.重複步驟2到步驟5,進行通訊資訊幀下一個位元組的處理;
        7.將該通訊資訊幀所有位元組按上述步驟計算完成後,得到的16位CRC暫存器的高、低位元組進行交換;
        8.最後得到的CRC暫存器內容即為:CRC碼。
具體程式碼類如下:
public class Crcheck {
    private static final int POLYNOMIAL = /*0xA053;*//*0x0589*/0xA001;
    /*    02 05 00 03 FF 00 的不同crc計算值:
    CRC-16 0x127C
    CRC-16 (Modbus)    0x097C 對應的多項式為 0xA001
    CRC-16 (Sick)  0xE2F0
    CRC-CCITT (XModem) 0xF2B8
    CRC-CCITT (0xFFFF) 0xFCA8
    CRC-CCITT (0x1D0F) 0xC386
    CRC-CCITT (Kermit) 0xA63E
CRC-DNP 0x6E28*/ private static final int PRESET_VALUE = 0xFFFF; public static String getHexString(int i){ String hexString = String.format("%04x",i); return hexString; } /** * @param data byte[] * @return 低位在前,高位在後的16進位制字串 */ public static String getCrc16String(byte
[] data){ StringBuilder builder = new StringBuilder(); String hex = getHexString(getCrc16(data)); builder.append(hex.substring(2)); builder.append(hex.substring(0,2)); return builder.toString(); } /** * @param data String * @return 低位在前,高位在後的16進位制字串 */ public static String getCrc16String(String data){ StringBuilder builder = new StringBuilder(); String hex = getHexString(getCrc16(data)); builder.append(hex.substring(2)); builder.append(hex.substring(0,2)); return builder.toString(); } /** * @param data byte陣列 * @return CRC16校驗得到的十進位制int */ public static int getCrc16(byte[] data){ System.out.println("\nCRC 16 calculation progress:\n"); int current_crc_value = PRESET_VALUE; for (int i = 0; i < data.length; i++){ current_crc_value ^= data[i] & 0xFF; for (int j = 0; j < 8; j++ ) { if ((current_crc_value & 1) != 0) { current_crc_value = (current_crc_value >>> 1) ^ POLYNOMIAL; } else{ current_crc_value = current_crc_value >>> 1; } } } return current_crc_value & 0xFFFF; } /** * @param str 16進位制字串 * @return CRC16校驗得到的十進位制int */ public static int getCrc16(String str){ byte[] data = hexStringToBytes(str); System.out.println("\nCRC 16 calculation progress:\n"); int current_crc_value = PRESET_VALUE; for (int i = 0; i < data.length; i++){ current_crc_value ^= data[i] & 0xFF; for (int j = 0; j < 8; j++ ) { if ((current_crc_value & 1) != 0) { current_crc_value = (current_crc_value >>> 1) ^ POLYNOMIAL; } else{ current_crc_value = current_crc_value >>> 1; } } } return current_crc_value & 0xFFFF; } /** * byte[] 轉 16進位制字元 * * @param b * @return */ public static String printHexString(byte[] b) { StringBuffer returnValue = new StringBuffer(); for (int i = 0; i < b.length; i++) { String hex = Integer.toHexString(b[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } if (i % 2 == 0) { returnValue.append(hex.toUpperCase() + ""); } else { returnValue.append(hex.toUpperCase() + " "); } } return returnValue.toString(); } /** * 16進位制字串轉byte[] * * @param hexString * @return */ public static byte[] hexStringToBytes(String hexString) { if (hexString == null || hexString.equals("")) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d; } private static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } }