RTUフレーム & ASCIIフレーム的差错チェック-Modbus通信プロトコル要点第5部分

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

RTUフレーム & ASCIIフレーム的差错チェック-Modbus通信プロトコル要点第5部分

前面已经分析过RTUフレーム和ASCIIフレーム的报文区别,细心的朋友应该会发现在两种不同的报文传输モード下都有一个共同的组成部分——差错チェック。这个差错チェック在RTUモード和ASCIIモード下也ではない不相同的。

看下面的对比:RTUモード的差错Verification:

RTUフレーム & ASCIIフレーム的差错チェック-Modbus通信プロトコル要点第5部分イラスト

ASCIIモード的差错Verification:

RTUフレーム & ASCIIフレーム的差错チェック-Modbus通信プロトコル要点第5部分イラスト1

总而言之,RTUモード下用的是CRC check,ASCIIモード下用的是LRC verification。

下面会説明这两者不同的チェック方式是怎么実装的。

1、RTUモード的CRC check

在 RTU モード中基于循环冗余チェック ( CRC - - Cyclical RedundancyChecking) 算法的エラー检验域。CRC 域检验整个报文的内容。不管报文有パリティなし,均执行此检验。CRC 包含由2つ 8 位バイト组成的一个 16 位值,作为报文的最后的域附加在报文之后。計算后,首先附加低バイト数,然后是ハイバイト。CRCハイバイト为报文送信的最后一个子节。附加在报文后面的 CRC 的值由送信デバイス計算。受信デバイス在受信报文时重新計算 CRC 的值,并将計算结果于实际受信到的 CRC 值相比较。もし2つ值不相等,则为エラー。这样的操作是可以防止データ在传送过成发生エラー,导致最后出现異常な現象。

(1)CRC生成的原理

CRC 的生成过程中, 每个 8–位文字与レジスタ中的值异或。然后结果向最低有效位 (LSB) 方向移動(Shift) 1 位,而最高有效位 (MSB) 位置充零。 然后提取并チェック LSB:もし LSB 为 1, 则レジスタ中的值与一个固定的预置值异或;もし LSB 为 0, 则不进行异或操作。

这个过程将重复直到执行完 8 次移位。完成最后一次(No. 8 次)移位及相关操作后,下一个 8位バイト与レジスタ的当前值异或,然后又同上面説明过的一样重复 8 次。当すべての报文中子节都运算之后得到的レジスタ中的最终值,就是 CRC。

(2)CRC的生成过程


1. 将一个 16 位レジスタ装入十六进制 FFFF (全 1). 将之称作 CRC register.
2. 将报文的第一个 8 位バイト与 16 位 CRC レジスタ的低バイト数异或,结果置于 CRC register.
3. 将 CRC レジスタ右移 1 位 (向 LSB 方向), MSB 充零. 提取并检测 LSB.
4. (もし LSB 为 0): 重复步骤 3 (另一次移位).(もし LSB 为 1): 对 CRC レジスタ异或多項式值 0xA001 (1010 0000 0000 0001).
5. 重复步骤 3 和 4,直到完成 8 次移位。当做完此操作后,将完成对 8 位バイト完全に。操作。
6. 对报文中的下一バイト単位。重复步骤 2 to 5,继续此操作直至すべての报文被処理完毕。
7. CRC レジスタ中的最终内容为 CRC 值.
8. 当放置 CRC 值于报文时,高低バイト数必须交換。

流程图如下:

RTUフレーム & ASCIIフレーム的差错チェック-Modbus通信プロトコル要点第5部分イラスト2

(3)CRC check生成的算法実装CRC的計算一般是用查表法実装,这种方法的优点是計算速度快,缺点是占用内存リソース多。CRC的ハイバイト表如下:


static unsigned char auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
} ;

CRC的低バイト数表如下:


static char auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};

CRC的生成函数如下:


unsigned short CRC16 ( puchMsg, usDataLen ) /* 函数以 unsigned short タイプ戻る CRC */
{
    unsigned char *puchMsg ; /* 用于計算 CRC 的报文 */
    unsigned short usDataLen ; /* 报文中的Bytesの数 */
    unsigned char uchCRCHi = 0xFF ; /* CRC 的ハイバイト初期化 */
    unsigned char uchCRCLo = 0xFF ; /* CRC 的低バイト数初期化 */
    unsigned uIndex ; /* CRC 查询表索引 */

    while (usDataLen--) /* 完成整个报文バッファ */
    {
            uIndex = uchCRCLo ^ *puchMsgg++ ; /* result CRC */
            uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex} ;
            uchCRCHi = auchCRCLo[uIndex] ;
    }

    return (uchCRCHi << 8 | uchCRCLo) ;
}

(4)CRC check码追加到报文中当 16 位 CRC (2 个 8 位バイト) 在报文中传送时,低位バイト首先送信,然后是高位バイト。例如, もし CRC 值为十六进制 1241 (0001 0010 0100 0001),如下:

RTUフレーム & ASCIIフレーム的差错チェック-Modbus通信プロトコル要点第5部分イラスト3

2、ASCIIモード的LRC verification

(1)LRC的生成原理纵向冗余チェック(LRC)为一バイト単位。,含有 8 位二进制值。LRC 由送信デバイス計算,并附加 LRC 到报文。受信デバイス在受信文时計算 LRC, 并将計算的结果与在 LRC 受信到的实际值相比较,もし2つ值不相等,则结果为错。LRC 的計算, 对报文中的すべての的连续 8 位バイト相加,忽略任何进位,然后求出其二进制补码。LRC 为一个 8 位域,那么每个会导致值大于 255 新的相加只是简单的将域的值在零”回绕”。因为ない第 9 位,进位被自动放弃。

(2)LRC的生成过程


1.不包括起始”冒号”和结束 CRLF 的报文中的すべてのバイト相加到一个 8 位域,故此进位被丢弃。
2.从 FF (全 1)十六进制中减去域的最终值,产生 1 的补码(二进制反码)。
3.加 1 产生二进制补码.

(3)LRC verification生成的算法


static unsigned char LRC(auchMsg, usDataLen) /* 函数戻る unsigned char タイプ的 LRC common error troubleshooting*/
{
    unsigned char *auchMsg ; /* 要計算 LRC 的报文*/
    unsigned short usDataLen ; /* 报文的Bytesの数 */
    unsigned char uchLRC = 0 ; /* LRC 初期化 */

    while (usDataLen--) /* 完成整个报文バッファ */
        uchLRC += *auchMsg++ ; /* バッファバイト相加,无进位 */

    return ((unsigned char)(-((char)uchLRC))) ; /* 戻る二进制补码 */
}

(4)将LRC verification码追加到报文中当 8 位 LRC (2 个 ASCII characters) 在报文中传送时,高位文字首先送信,然后是低位文字。例如,もし LRC 值为十六进制 61 (0110 0001),如下:

RTUフレーム & ASCIIフレーム的差错チェック-Modbus通信プロトコル要点第5部分イラスト4
Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.