為確保消息數據的完整性,除了驗證消息CRC之外,建議實現檢查串行端口(UART)成幀錯誤的代碼。如果接收消息中的CRC與接收設備計算的CRC不匹配,則應忽略該消息。下面的C語言代碼片段顯示了如何使用逐位移位和異或運算來計算Modbus消息CRC。使用消息幀中的每個字節計算CRC,除了包含CRC本身的最后兩個字節。
參考:https://www.cnblogs.com/skullboyer/p/8342167.html
一、CRC16校驗碼的使用
現選擇最常用的CRC-16校驗,說明它的使用方法。
根據Modbus協議,常規485通訊的信息發送形式如下:
地址 功能碼 數據信息 校驗碼
1byte 1byte nbyte 2byte
CRC校驗是前面幾段數據內容的校驗值,為一個16位數據,發送時,低8位在前,高8為最后。
例如:信息字段代碼為: 1011001,校驗字段為:1010。
發送方:發出的傳輸字段為: 1 0 1 1 0 0 1 1 0 10
信息字段 校驗字段
接收方:使用相同的計算方法計算出信息字段的校驗碼,對比接收到的實際校驗碼,如果相等及信息正確,不相等則信息錯誤;或者將接受到的所有信息除多項式,如果能夠除盡,則信息正確。
三、CRC16常見幾個標准的算法
CRC16常見的標准有以下幾種,被用在各個規范中,其算法原理基本一致,就是在數據的輸入和輸出有所差異,下邊把這些標准的差異列出,並給出C語言的算法實現。
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),初始值0x0000,低位在前,高位在后,結果與0xFFFF異或
CRC16_MODBUS:多項式x16+x15+x5+1(0x8005),初始值0xFFFF,低位在前,高位在后,結果與0x0000異或
CRC16_IBM:多項式x16+x15+x5+1(0x8005),初始值0x0000,低位在前,高位在后,結果與0x0000異或
CRC16_MAXIM:多項式x16+x15+x5+1(0x8005),初始值0x0000,低位在前,高位在后,結果與0xFFFF異或
CRC16_USB:多項式x16+x15+x5+1(0x8005),初始值0xFFFF,低位在前,高位在后,結果與0xFFFF異或
四、CRC16的算法原理及程序
1.根據CRC16的標准選擇初值CRCIn的值。
2.將數據的第一個字節與CRCIn高8位異或。
3.判斷最高位,若該位為 0 左移一位,若為 1 左移一位再與多項式Hex碼異或。
4.重復3直至8位全部移位計算結束。
5.重復將所有輸入數據操作完成以上步驟,所得16位數即16位CRC校驗碼。
根據算法原理與標准要求就能簡單的寫出具體程序:
二、CRC16校驗碼計算方法
常用查表法和計算法。計算方法一般都是:
(1)、預置1個16位的寄存器值0xFFFF,稱此寄存器為CRC寄存器;
(2)、把第一個8位二進制數據(既通訊信息幀的第一個字節)與16位的CRC寄存器的低
8位相異或,把結果放於CRC寄存器,高八位數據不變;
(3)、把CRC寄存器的內容右移一位(朝高位)用0填補最高位,並檢查右移后的移出位;
(4)、如果移出位為0:重復第3步(再次右移一位);如果移出位為1,CRC寄存器與一多
項式(A001)進行異或;
(5)、重復步驟3和4,直到右移8次,這樣整個8位數據全部進行了處理;
(6)、重復步驟2到步驟5,進行通訊信息幀下一個字節的處理;
(7)、將該通訊信息幀所有字節按上述步驟計算完成后,得到的16位CRC寄存器的高、低
字節進行交換;
(8)、最后得到的CRC寄存器內容即為:CRC碼。
以上計算步驟中的多項式A001是8005按位顛倒后的結果。
unsigned int CRC16_2(unsigned char *buf, int len)
{
unsigned int crc = 0xFFFF;
for (int pos = 0; pos < len; pos++)
{
crc ^= (unsigned int)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) // Loop over each bit
{
if ((crc & 0x0001) != 0) // If the LSB is set
{
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
{
crc >>= 1; // Just shift right
}
}
}
//高低字節轉換
crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
return crc;
}