Modbus CRC/LRC検証原理とプログラミング実装:数学的推論から実際のコードまで
産業通信では、データインテグリティがボトムラインです。1ビットのエラーは、バルブの誤開閉、モーターの反転、さらには安全事故につながる可能性があります。Modbusプロトコル採用CRC(巡回冗長検査)そして、LRC(垂直冗長検査)データの完全性を保証する2つの方法。この記事では、これら2つの検証の数学原理を深く分析し、C、Python、Java Scriptの3つの言語で完全なコード実装を与えます。
主なキーワード:Modbus CRCチェック、Modbus CRC-16、CRC計算原理、Modbus LRCチェック、ルックアップテーブルCRC。Modbus技術に関する詳細はこちらをご覧ください。modbus.cn。
なぜエラー検出が必要なのか?
ModbusはもともとRS-485およびRS-232 物理層で動作し、これらのシリアルリンクは以下の干渉に直面した。
- 電磁干渉EMI産業現場で発生するインバータや高出力モータからの大量の電磁ノイズは、通信回線に結合されます。
- 接地電位差:長距離通信では、ノード間のグランド電位の不一致が信号歪みを引き起こす。
- コネクタの酸化/緩み:産業環境の振動や腐食による断続的な接触不良
- ボーレート偏差:送信と受信の両方のクロックジッタの累積は、ビットサンプリングエラーを引き起こす可能性がある
データリンク層でのModbusプロトコルフレームチェックシーケンスFCS送信エラーを検出する。Modbus RTUモードCRC-16Modbus ASCIIモードLRC。この2つの検証の違いと使用シナリオは、この記事の中心にあります。
コードを深く掘り下げる前にお読みください。Modbus RTUとASCI Iの違い2つの伝送モードの基本的な違いを理解する。
CRC対LRC:2つの検証方法の比較
| 次元の比較 | CRC-16(RTUモード) | LRC ASCIIモード |
|---|---|---|
| アルゴリズムの種類 | 巡回冗長検査多項式除算 | 垂直冗長検査(累積逆) |
| 検証値の長さ | 16ビット(2バイト) | 8ビット(1バイト) |
| エラー検出能力 | 非常に高い(すべてのシングルビット、ダブルビット、奇数ビットのエラー、および16ビット以下のバーストエラーを検出) | 中程度シングルバイトエラーは検出されるが、マルチビットエラーはブラインド領域 |
| 計算の複雑性 | 中高ビット演算またはルックアップテーブルが必要 | 非常に低い(累積演算のみ) |
| フレーム内の位置 | フレームの末尾、ローバイトが前(リトルエンディアン) | フレームの末尾、2文字のASCII文字 |
| 適用可能な伝送モード | RTU(バイナリ) | ASCIIテキスト |
| 範囲の計算 | 最初のバイト(アドレス)からデータゾーンの最後まで | ''の后からCR/LFの前まで(コロンおよびキャリッジの改行はない) |
| 典型的な失敗率 | 16ビットCRC欠落率は约 1/6553 6 | LRCの見逃し率は比較的高く、約1/256 |
選択の推奨事項:現代のModbusアプリケーションでは、CRC-16と組み合わせたRTUモードが絶対に主流です。ASCI IモードとLRCは、デバッグや端末プログラムによる手動操作など、人間が読める通信内容を必要とする特殊なシナリオで主に使用されます。
CRC-16数学原理:多項式からの演算
3.1 CRCの本質:モジュロ2除算
CRCの本質は、モジュロ二多項式除算。検査するデータは、2进M(x)として考えられ、予めされた生成G(x)で割り算された残りがCRC値となる。
Modbus RTUで使用されるCRC-16パラメータ:
- 多項式:x^16 + x^15 + x^2 + 1
- 多項式の値:0 x 80 0 5(順方向)または0 x A 0 01(逆方向/Modbus規格)
- 初期値:0xFFFF
- 結果または価値:0 x 0000(異なるまたは異なる)
- 入力データの反転:いいえ、いいえ
- データの反転:いいえ(ただしModbusストレージはリトルエンディアンです)
モジュロ2除算の例:
非常に単純化されたデータがあるとします。チェックされるバイトは0 x 0 2(バイナリ0000 0 010)で、単純化された4ビットCRCを使用します。
データ0000 0010
多項式0 x A 001 = 1010 0000 0000 0001を反転
ステップシフトおよび排他的ORプロセス(アナログハードウェアシフトレジスタ)
1. CRCレジスタの初期化1111 1111 1111 1111 0xFFFF
2.最初のデータバイトを取ります0x02 0000 0010
3. CRC ^=データバイト1111 1111 1111 1111 1101
4.このバイトの各ビットに対して実行する:
- LSB = 1の場合CRC = 1、CRC ^= 0xA001
- LSB = 0の場合:CRC = 1
最終CRCレジスタの値がチェック結果になります。3.2なぜModbusは0 x 80 0 5ではなく0 x A 001を使うのか?
0 x 80 0 5と0 x A 001は同じ多項式の順方向と逆方向の表現です。
- 0 x 8005(順方向):2进数1000 0000 000 0101は、x ^16 + x ^15 + x ^2 + 1に対応する。これは、生成の“自然な”であり、左シフト型first CRCに使用される。
- 0xA001(逆方向):バイナリ1010 0000 0000 0001は、0 x 80 0 5のビット反転です。右シフト型LSB first CRC計算に使用される-これはModbusプロトコルで規定された標準的な方法でもある。
Modbusは、RS-485データリンク層が物理的にLSB(最下位ビット)を最初に送信するため、右シフトコンピューティングを選択します。0 xA 001を使用すると、ハードウェアCRC計算機がシリアルシフト方向に一致し、効率が向上します。
3.3変位法からルックアップテーブル法へ:性能の飛躍
変位法のボトルネック:各バイトには8回のサイクルが必要で、各サイクルには条件判定、シフト、例外OR演算が含まれます。100バイトのModbusフレームの処理には800サイクルが必要です。
表の基本的な考え方:可能な各バイト値(0 x 0 0 ~ 0 x FFの合計256個)のCRC中間結果が事前に計算され、ルックアップテーブルに格納されます。1バイトを処理する場合は、ルックアップテーブルOperationと排他和演算を1回だけ実行するだけで、計算量はO 8NからO Nに減少します。
ルックアップテーブルアルゴリズムの導出プロセス:
現在のCRCレジスタ値をcrc(16ビット)とし、次のデータバイトをdataとします。
変位法は、データのすべてのビットの反復計算を必要とする。推定すると、シングルバイト処理は以下と等価です。
1. index = crc ^ data 0x00 FF //現在のCRCの下位8ビットとデータバイトの違いを取る
2. crc = crc 8 ^ table[index] // CRCを8ビット右にシフトして、テーブルの違いを調べる
ここでtable[index]は,変位法により256回の事前計算を行ったルックアップテーブル値である.この導出は、“8回の反復ループ”を“1ルックアップ+ 1例外和”に圧縮し、約8 倍の性能向上をもたらします。
3.4ルックアップテーブル生成コードと完全導出
ルックアップテーブルの生成プロセスを理解することは、CRCルックアップテーブル法を習得するための鍵です。次のコードは、変位法を使用して完全な256項目CRCルックアップテーブルを計算する方法を示しています。
/**
* Modbus CRC-16ルックアップテーブルの作成
* 1回実行すると、出力を静的配列としてメインプログラムに埋め込む
*/
generate_crc16_table
{
uint16_t remainder;
int byte bit;
for byte = 0; byte 256; byte++{
remainder = uint16_t byte; /* 初期剰余=現在のバイト値 */
for bit = 0; bit 8; bit++{
if remainder 0x0001 { /* LSBは1です */
remainder = remainder 1 ^0xA001; /* 右シフトまたは */
(エルゼ)
remainder = remainder 1; /* 右に移動 */
}
}
table[byte] = remainder;
}
}
/* 説明の導出:
* なぜこのテーブルをルックアップテーブルに直接使用できるのか?
*
* 任意のデータバイトdataに対して、変位法は8回のサイクルを必要とする。
* CRCの現在の値をcrc(16ビット)とすると、8回の反復後:
* crc' = f f... f crc ^ data...
*
* 和演算>><<の|crc& 0xFF
* 下位8ビットの処理結果はcrc 0xFF ^ dataの値のみに依存します。
* この値は正確に0-255なので、可能なすべての中間結果を事前に計算できます。
*
* 上位8ビットは右にシフトし、新しく計算された下位8ビットの結果(ルックアップテーブル)と排他的になります。
* これがルックアップテーブルの数学的基盤です
*/このテーブル生成関数は、CRC計算の性質を明らかにします。ルックアップテーブル内の各値は、対応するインデックス·バイト値の8回の右シフトCRC反復の結果である。メイン計算関数内index = (crc ^ data) & 0xFFこのOperationは、本質的に“現在のCRC下位8ビットとデータバイトのモジュロ2和”を計算し、その結果を用いて事前計算されたCRC寄与値を得るためにテーブルをルックアップすることである。Modbus CRCの詳細な技術的議論はこちらをご覧ください。modbus.cn完全なアーカイブを見る。
3.5エラー検出能力の数学的解析
CRC-16の強力な検出能力は、その数学的特性に由来する。以下は、異なるエラーモードでの16ビットCRCの検出能力の分析です。
| エラーの種類 | 確率の検出 | 数学の原理 |
|---|---|---|
| 単一ビットエラー | 100% | 生成多項式がx+1を含む場合、すべての奇数ビット誤りを検出することが保証される。 |
| 2ビットエラー | 100% | 2つのエラービット間隔が32,767ビットの場合、16ビットCRCは常に検出されます。 |
| 奇数ビットエラー | 100% | 0xA001多項式はx+1係数を含み、すべての奇数ビットエラーを検出する。 |
| エラーは16ビット以下です。 | 100% | バーストエラー多項式の次数 ≤ 15、16 次多項式で割った剰余 |
| エラー 17。 | 99.9969% | 2^-16-1のみが正しく误検出される |
| 複数のエラー。 | 99.9985% | 16ビットCRCは、すべての非倍数エラーに対して1-2^-16の検出率を持つ。 |
これらの数学的特性により、CRC-16は産業通信における非常に費用対効果の高いエラー検出手段となっている。Modbus RTUの典型的なユースケース(RS-485バス、ボーレート≤ 115.2 Kbps、フレーム長≤ 256バイト)では、CRC-16は実際に発生する可能性のあるほぼすべての伝送エラーを検出することができます。
CRC-16完全なコード実装
4.1 C言語ルックアップテーブル法(高性能版)
以下は、産業用組込みシステムで最も使用されているModbus CRC-16のCルックアップテーブル実装です。
#include <stdint.h>
#include <stddef.Hは>
/* Modbus CRC-16ルックアップテーブル(多項式0 x A 001) */
static const uint 16_t crc16_table[256] = {
0x0000 0xC0C1 0xC181 0x0140 0xC301 0x03C0 0x0280 0xC241
0xC601 0x06C0 0x0780 0xC741 0x0500 0xC5C1 0xC481 0x0440
0xCC01 0x0CC0 0x0D80 0xCD41 0x0F00 0xCFC1 0xCE81 0x0E40
0 x 0 A 00、0 x CAC 1、0 x CB 81、0 x 0 B 40、0 x C 901、0 x 0 9 C 0、0 x 0 880、0 x C 841、
0 x D 801、0 x 18 C 0、0 x 1980、0 x D 941、0 x 1 B 00、0 x DBC 1、0 x DA 81、0 x 1 A 40、
0x1E00 0xDEC1 0xDF81 0x1F40 0xD01 0x1DC0 0x1C80 0xDC41
0x1400 0xD4C1 0xD581 0x1540 0xD701 0x17C0 0x1680 0xD641
0xD201 0x12C0 0x1380 0xD341 0x1100 0xD1C1 0xD081 0x1040
0xF001 0x30C0 0x3180 0xF141 0x3300 0xF3C1 0xF281 0x3240
0x3600 0xF6C1 0xF781 0x3740 0xF501 0x35C0 0x3480 0xF441
0x3C00 0xFCC1 0x81 0x3D40 0xFF01 0x30 0x3E80 0xFE41
0 x FA 01、0 x 3 AC 0、0 x 3 B 80、0 x FB 41、0 x 3900、0 x F 9 C 1、0 x F 881、0 x 38 40、
0 x 2800、0 x E 8 C 1、0 x E 981、0 x 2940、0 x EB 01、0 x 2 BC 0、0 x 2 A 80、0 x EA 41、
0xEE01 0x2EC0 0x2F80 0x41 0x2D00 0xEDC1 0xEC81 0x2C40
0xE401 0x24C0 0x2580 0xE541 0x2700 0xE7C1 0xE681 0x2640
0x2200 0xE2C1 0xE381 0x2340 0xE101 0x21C0 0x2080 0xE041
0xA001 0x60C0 0x6180 0xA141 0x6300 0xA3C1 0xA281 0x6240
0x6600 0xA6C1 0xA781 0x6740 0xA501 0x65C0 0x6480 0xA441
0x6C00 0xACC1 0xAD81 0x6D40 0xAF01 0x6FC0 0x6E80 0xAE41
0 x AA 01、0 x 6 AC 0、0 x 6 B 80、0 x AB 41、0 x 6900、0 x A 9 C 1、0 x A 881、0 x 6840、
0 x 7800、0 x B 8 C 1、0 x B 981、0 x 7940、0 x BB 01、0 x 7 BC 0、0 x 7 A 80、0 x BA 41、
0xBE01 0x7EC0 0x7F80 0xBF41 0x7D00 0xB1 0x81 0x7C40
0xB401 0x74C0 0x7580 0xB541 0x7700 0xB7C1 0xB681 0x7640
0x7200 0xB2C1 0xB381 0x7340 0xB101 0x71C0 0x7080 0xB041
0 x 5000、0 x 90 C 1、0 x 91 8 1、0 x 51 4 0、0 x 9301、0 x 53 C 0、0 x 528 0、0 x 9241、
0 x 96 01、0 x 56 C 0、0 x 57 80、0 x 97 41、0 x 5500、0 x 95 C 1、0 x 9481、0 x 54 40、
0 x 9 C 01,0 x 5 CC 0,0 x 5 D 80,0 x 9 D 41,0 x 5 F 00,0 x 9 FC 1,0 x 9 E 81,0 x 5 E 40,
0 x 5 A 00、0 x 9 AC 1、0 x 9 B 81、0 x 5 B 40、0 x 9901、0 x 59 C 0、0 x 588 0、0 x 9841、
0 x 8801、0 x 48 C 0、0 x 4980、0 x 8941、0 x 4 B 00、0 x 8 BC 1、0 x 8 A 81、0 x 4 A 40、
0x4E00 0x8EC1 0x8F81 0x4F40 0x8D01 0x4DC0 0x4C80 0x8C41
0x4400 0x84C1 0x8581 0x4540 0x8701 0x47C0 0x4680 0x8641
0x8201 0x42C0 0x4380 0x8341 0x4100 0x81C1 0x8081 0x4040
};
/**
* Modbus CRC-16ルックアップテーブル計算
* @param buf 検証対象のデータ·バッファ
* @param len データ長(バイト数)
* @returnさん 16ビットCRC値
*/
uint16_t modbus_crc16 uint8_t *buf、uint16_t len
{
uint 16_t crc = 0 x FFFF; /* 初期値 *
while len--{
uint8_t pos = uint8_t crc ^*buf++0xFF;
crc = crc8 ^ crc16_table[];
}
CRCに戻る
}
/* 使用例:
* uint8_t frame[] = {0x01 0x03 0x00 0x00 0x00 0x01}
* uint16_t crc = modbus_crc16 frame 6;
* // crc = 0 x 0 acA
* //Modbus RTUフレームでは、下位バイトが前に来る。
* // frame[6] = crc 0xFF; 0xCA
* // frame[7] = cr 8; 0x0A
*/4.2 C言語変位法(英語版)
以下は、学習や埋め込みリソースが非常に限られたシナリオに適した、コードvalueが少ないが非効率なビット単位バージョンです。
/**
* Modbus CRC-16ビットごとの計算
* CRC 原理の教育と理解のために、生産環境ではルックアップテーブル法の使用を推奨
*/
uint16_t modbus_crc16_bitwise uint8_t *buf、uint16_t len
{
uint 16_t crc = 0 x FFFF; /* 初期値 *
uint16_t i、j;
for i = 0; i len; i++{
crc ^= uint16_t buf[i]; /* データバイトをCRCローバイトと異なるか */
for j = 0; j 1 ^0xA001; /* 右へ1ビットシフトした和多項式 */
(エルゼ)
CRC = CRC 1。 /* 1つだけ右に移動 */
}
}
}
CRCに戻る
}
/* 検証済み:
* uint8_t test[] = {0x01 0x03 0x00 0x00 0x00 0x01}
* uint16_t crc = modbus_crc16_bitwise test 6;
* //結果は0 x 0 ACAになります
*/4.3 Pythonの実装
Pythonバージョンは、ホストプログラム、データ分析スクリプト、自動テストに適しています。
#!/usr/bin/env python 3
“Modbus CRC-16検証ツール”“”
from ingインポートリスト、
class modbusCRC
"""Modbus CRC-16電卓""""
#CRC-16ルックアップテーブル
テーブル(テーブル)
0x0000 0xC0C1 0xC181 0x0140 0xC301 0x03C0 0x0280 0xC241
0xC601 0x06C0 0x0780 0xC741 0x0500 0xC5C1 0xC481 0x0440
0xCC01 0x0CC0 0x0D80 0xCD41 0x0F00 0xCFC1 0xCE81 0x0E40
0 x 0 A 00、0 x CAC 1、0 x CB 81、0 x 0 B 40、0 x C 901、0 x 0 9 C 0、0 x 0 880、0 x C 841、
0 x D 801、0 x 18 C 0、0 x 1980、0 x D 941、0 x 1 B 00、0 x DBC 1、0 x DA 81、0 x 1 A 40、
0x1E00 0xDEC1 0xDF81 0x1F40 0xD01 0x1DC0 0x1C80 0xDC41
0x1400 0xD4C1 0xD581 0x1540 0xD701 0x17C0 0x1680 0xD641
0xD201 0x12C0 0x1380 0xD341 0x1100 0xD1C1 0xD081 0x1040
0xF001 0x30C0 0x3180 0xF141 0x3300 0xF3C1 0xF281 0x3240
0x3600 0xF6C1 0xF781 0x3740 0xF501 0x35C0 0x3480 0xF441
0x3C00 0xFCC1 0x81 0x3D40 0xFF01 0x30 0x3E80 0xFE41
0 x FA 01、0 x 3 AC 0、0 x 3 B 80、0 x FB 41、0 x 3900、0 x F 9 C 1、0 x F 881、0 x 38 40、
0 x 2800、0 x E 8 C 1、0 x E 981、0 x 2940、0 x EB 01、0 x 2 BC 0、0 x 2 A 80、0 x EA 41、
0xEE01 0x2EC0 0x2F80 0x41 0x2D00 0xEDC1 0xEC81 0x2C40
0xE401 0x24C0 0x2580 0xE541 0x2700 0xE7C1 0xE681 0x2640
0x2200 0xE2C1 0xE381 0x2340 0xE101 0x21C0 0x2080 0xE041
0xA001 0x60C0 0x6180 0xA141 0x6300 0xA3C1 0xA281 0x6240
0x6600 0xA6C1 0xA781 0x6740 0xA501 0x65C0 0x6480 0xA441
0x6C00 0xACC1 0xAD81 0x6D40 0xAF01 0x6FC0 0x6E80 0xAE41
0 x AA 01、0 x 6 AC 0、0 x 6 B 80、0 x AB 41、0 x 6900、0 x A 9 C 1、0 x A 881、0 x 6840、
0 x 7800、0 x B 8 C 1、0 x B 981、0 x 7940、0 x BB 01、0 x 7 BC 0、0 x 7 A 80、0 x BA 41、
0xBE01 0x7EC0 0x7F80 0xBF41 0x7D00 0xB1 0x81 0x7C40
0xB401 0x74C0 0x7580 0xB541 0x7700 0xB7C1 0xB681 0x7640
0x7200 0xB2C1 0xB381 0x7340 0xB101 0x71C0 0x7080 0xB041
0 x 5000、0 x 90 C 1、0 x 91 8 1、0 x 51 4 0、0 x 9301、0 x 53 C 0、0 x 528 0、0 x 9241、
0 x 96 01、0 x 56 C 0、0 x 57 80、0 x 97 41、0 x 5500、0 x 95 C 1、0 x 9481、0 x 54 40、
0 x 9 C 01,0 x 5 CC 0,0 x 5 D 80,0 x 9 D 41,0 x 5 F 00,0 x 9 FC 1,0 x 9 E 81,0 x 5 E 40,
0 x 5 A 00、0 x 9 AC 1、0 x 9 B 81、0 x 5 B 40、0 x 9901、0 x 59 C 0、0 x 588 0、0 x 9841、
0 x 8801、0 x 48 C 0、0 x 4980、0 x 8941、0 x 4 B 00、0 x 8 BC 1、0 x 8 A 81、0 x 4 A 40、
0x4E00 0x8EC1 0x8F81 0x4F40 0x8D01 0x4DC0 0x4C80 0x8C41
0x4400 0x84C1 0x8581 0x4540 0x8701 0x47C0 0x4680 0x8641
0x8201 0x42C0 0x4380 0x8341 0x4100 0x81C1 0x8081 0x4040
]
@staticmethod
cal
"""Modbus CRC-16の計算"""
crc = 0 x FFFF
for byte in data
=
crc = crc8 ^ ModbusCRC.テーブル[Pos]
CRCに戻る
@staticmethod
verify
"""
CRCを使用したModbus RTUフレームの検証
フレーム全体(CRCを含む)を再度CRCし、結果は0になります。
"""
return ModbusCRC.calculate frame == 0
@staticmethod
append_crc
“”“CRC(リトルエンディアン)”をデータ末尾に追加する“”
crc = ModbusCRC.calate data
return data + bytes [crc 0xFF crc 8]
例=====
if __name__== '___main__'
#例1フェッチホールドレジスタコマンド
#アドレス=1、ファンクションコード=03、開始アドレス=0x0000、レジスタ数=1
request = b [0x01 0x03 0x00 0x00 0x01]
crc_value = ModbusCRC. calate request
print f"CRC-16 0x{crc_value 04X}"#予定0x0ACA
#例2完全な応答フレームを構築する
response_data = bytes([0x01, 0x03, 0x02, 0x00, 0x64])
_
print f"フルフレーム{_.hex '.upper}"
#期待:0 1 0 3 0 2 0 0 6 4 B9 AF
#例3受信フレームの検証
received = bytes [0x01 0x03 0x02 0x00 0x64 0xB9 0xAF]
is_valid = ModbusCRC.verify received
print f"フレーム検证{' if is_valid else 'でしました'}4.4 JavaScript実装Webデバッグツール
WebフロントエンドデバッグツールまたはNode.js環境では、次のバージョンのJava Scriptを使用できます。
/**
* Modbus CRC-16検証ツールJavaScript
* ブラウザコンソールまたはNode.JSで動作する。
*/
CRC-16ルックアップテーブル。
const CRC16_TABLE = new Uint16Array [
0x0000 0xC0C1 0xC181 0x0140 0xC301 0x03C0 0x0280 0xC241
0xC601 0x06C0 0x0780 0xC741 0x0500 0xC5C1 0xC481 0x0440
0xCC01 0x0CC0 0x0D80 0xCD41 0x0F00 0xCFC1 0xCE81 0x0E40
0 x 0 A 00、0 x CAC 1、0 x CB 81、0 x 0 B 40、0 x C 901、0 x 0 9 C 0、0 x 0 880、0 x C 841、
0 x D 801、0 x 18 C 0、0 x 1980、0 x D 941、0 x 1 B 00、0 x DBC 1、0 x DA 81、0 x 1 A 40、
0x1E00 0xDEC1 0xDF81 0x1F40 0xD01 0x1DC0 0x1C80 0xDC41
0x1400 0xD4C1 0xD581 0x1540 0xD701 0x17C0 0x1680 0xD641
0xD201 0x12C0 0x1380 0xD341 0x1100 0xD1C1 0xD081 0x1040
0xF001 0x30C0 0x3180 0xF141 0x3300 0xF3C1 0xF281 0x3240
0x3600 0xF6C1 0xF781 0x3740 0xF501 0x35C0 0x3480 0xF441
0x3C00 0xFCC1 0x81 0x3D40 0xFF01 0x30 0x3E80 0xFE41
0 x FA 01、0 x 3 AC 0、0 x 3 B 80、0 x FB 41、0 x 3900、0 x F 9 C 1、0 x F 881、0 x 38 40、
0 x 2800、0 x E 8 C 1、0 x E 981、0 x 2940、0 x EB 01、0 x 2 BC 0、0 x 2 A 80、0 x EA 41、
0xEE01 0x2EC0 0x2F80 0x41 0x2D00 0xEDC1 0xEC81 0x2C40
0xE401 0x24C0 0x2580 0xE541 0x2700 0xE7C1 0xE681 0x2640
0x2200 0xE2C1 0xE381 0x2340 0xE101 0x21C0 0x2080 0xE041
0xA001 0x60C0 0x6180 0xA141 0x6300 0xA3C1 0xA281 0x6240
0x6600 0xA6C1 0xA781 0x6740 0xA501 0x65C0 0x6480 0xA441
0x6C00 0xACC1 0xAD81 0x6D40 0xAF01 0x6FC0 0x6E80 0xAE41
0 x AA 01、0 x 6 AC 0、0 x 6 B 80、0 x AB 41、0 x 6900、0 x A 9 C 1、0 x A 881、0 x 6840、
0 x 7800、0 x B 8 C 1、0 x B 981、0 x 7940、0 x BB 01、0 x 7 BC 0、0 x 7 A 80、0 x BA 41、
0xBE01 0x7EC0 0x7F80 0xBF41 0x7D00 0xB1 0x81 0x7C40
0xB401 0x74C0 0x7580 0xB541 0x7700 0xB7C1 0xB681 0x7640
0x7200 0xB2C1 0xB381 0x7340 0xB101 0x71C0 0x7080 0xB041
0 x 5000、0 x 90 C 1、0 x 91 8 1、0 x 51 4 0、0 x 9301、0 x 53 C 0、0 x 528 0、0 x 9241、
0 x 96 01、0 x 56 C 0、0 x 57 80、0 x 97 41、0 x 5500、0 x 95 C 1、0 x 9481、0 x 54 40、
0 x 9 C 01,0 x 5 CC 0,0 x 5 D 80,0 x 9 D 41,0 x 5 F 00,0 x 9 FC 1,0 x 9 E 81,0 x 5 E 40,
0 x 5 A 00、0 x 9 AC 1、0 x 9 B 81、0 x 5 B 40、0 x 9901、0 x 59 C 0、0 x 588 0、0 x 9841、
0 x 8801、0 x 48 C 0、0 x 4980、0 x 8941、0 x 4 B 00、0 x 8 BC 1、0 x 8 A 81、0 x 4 A 40、
0x4E00 0x8EC1 0x8F81 0x4F40 0x8D01 0x4DC0 0x4C80 0x8C41
0x4400 0x84C1 0x8581 0x4540 0x8701 0x47C0 0x4680 0x8641
0x8201 0x42C0 0x4380 0x8341 0x4100 0x81C1 0x8081 0x4040
]);
/**
* Modbus CRC-16の計算
* @param {Uint8Array|番号[]| Buffer} Data
* @returns {number} 16ビットCRC値
*/
function modbusCRC16 data {
let crc = 0 x FFFF;
for let i = 0; i>>8 ^ CRC16_TABLE[pos];
}
CRCに戻る
}
/**
* CRCのあるフレームが正当であることを確認します
* @param {Uint8Array} frame完全フレームCRCを含む
* @returns {boolean}
*/
function verifyModbus {
return modbusCRC16 frame === 0;
}
/**
* CRCを計算してデータの末尾に追加
* @param {Uint8Array| number[]} data
* @returns {Uint8Array}
*/
function appendCRC data {
const crc = modbus CRC16 data;
return new Uint8 [...] data crc 0xFF crc 8 0xFF];
}
// =====
const request = new Uint8 [0x01 0x03 0x00 0x00 0x01];
const crc = modbus CRC16 request;
cons.log `CRC-16 0x${crc.toString 16.toUpperCase.padStart 4 '0}`;
//出力CRC-16 0 x 0
const full = appendCRC request;
console.log 'フルフレーム' Array.from fullFrame
.map b ='0x' + b.toString 16.toUpperCase.padStart 2 '0'
.join ''';
//出力0x01 0x03 0x00 0x00 0x00 0x01 0xCA 0x0ALRC検証の原理と実装
5.1 LRC計算ルール
LRC Longitudinal Redundancy Check Modbus ASCIIモードで使用される。計算方法は簡単です:
- メッセージ·フレーム内のすべてのバイトアドレス·コードから最後のデータバイトまでを合計する
- キャリーを破棄(下位8ビットのみ保持)
- 2の補数を取る(すなわち、逆に1を加えるか、256-sumを使う)。
- 結果を2文字のASCII文字(上位半分バイトと下位半分バイトに1文字ずつ)に変換
LRCの公式:
LRC = 0x100 -byte[0.. [N-1] 0xFF
例:フレームデータは{0 x 0 1,0 x 0 3,0 x 0 0,0 x 0 0,0 x 0 0,0 x 0 1}です。
= 0x01 + 0x03 + 0x00 + 0x00 + 0x00 + 0x01 = 0x05
LRC = 0x100 - 0x05 = 0xFB
ASCIIフレームでは文字列“FB”として表される5.2 LRCの完全実装
/**
* Modbus LRC計算のC言語
* 戻り値はLRC値(8ビット)です。
*/
uint8_t modbus_lrc uint8_t *buf、uint16_t len
{
uint 16_t sum = 0
16_T i;
for i = 0; i int
"""Modbus ASCII LRCを計算""""
_
return-sum_val 0xFF #256-sum_val 0xFFと等価
#例を使う
data = b [0x01 0x03 0x00 0x00 0x01]
lrc = modbus_lrc data
print f"LRC 0x{lrc 02X}"#出力0xFB
/**
* JavaScriptの実装
*/
function modbusLRC data {
sum = 0;
for let i = 0; i data.length; i++{
=
}
return 0x100-sum 0xFF;
}
// 使用事例
const testData = [0x01 0x03 0x00 0x00 0x00 0x01];
cons.log `LRC 0x${modbusLRC Data.toString 16.toUpperCase}`;
//出力LRC 0xFB6.CRC検査の技術
実際の開発では、CRC実装の正しさの検証は実装自体よりも時間がかかることが多い。以下に、CRC計算の問題をすばやく特定するのに役立つ実用的な検証方法とテクニックを紹介します。
6.1フルフレーム検証法
CRCには非常に実用的な機能がある。受信したフルフレーム(データ+ CRC)をCRC-16で再計算すると、0 x 0000になります。。これは、フレームの完全性を検証する最も簡単な方法です。
受信フレーム(データ+ 2バイトCRC)
uint 8_t received_frame [] = {0 x 0 1、0 x 0 3、0 x 0 2、0 x 0 0、0 x 64、0 x B 9、0 x AF};
uint16_t verify = modbus_crc16 received_frame sizeof received_frame;
if verify = 0 {
//フレーム検証に合格し、データは正しい
f
(エルゼ)
//フレーム検証に失敗し、フレームを破棄
f
}6.2テストベクタ
CRC計算関数を開発する際には、次の標準テストベクトルを使用して実装の正しさを検証します。
| テストデータHEX | 機能の説明 | CRC-16への期待 |
|---|---|---|
01 03 00 00 00 01 | 読み取りホールドレジスタ(最も一般的) | 0ACA |
01 03 00 00 00 0A | 10個の保持レジスタを読み込む | 0548 |
01 06 00 01 00 1E | 単一レジスタ(値30)を書き込む | 99CB |
01 10 00 00 00 02 04 00 64 00 65 | 複数のレジスタを書く。 | リアルタイム計算が必要 |
11 03 00 6B 00 03 | ステーション17から3つのレジスタを読み出す | 7687 |
オンラインCRC計算ツールの推奨
次のツールは、CRC計算結果の迅速な検証に役立ちます。
- modbus.cnオンラインツールModbus独自のCRC/LRCオンラインコンピューティング機能を提供
- Sunshine2k CRC Calculator:CRCアルゴリズムのパラメータの複数の組み合わせをサポートするオンライン計算機
- Lammert Bies CRC:カスタム多項式をサポートする詳細なCRC計算ページ
- npm crcパッケージ:Node.js環境で利用可能
npm install crcインストールする。
オンラインツールを使用する际の注意
- 多項式パラメータの確認(Modbus = 0 x 80 0 5/0 x A 001)
- 初期値の確認(Modbus = 0 x FFFF)
- 結果が異なるORを確認します(Modbus = 0 x 0000、異なるORなし)。
- 入出力が反転しているか確認する(Modbusとも反転しない)
- 注意エンディアン:オンラインツールは通常ビッグエンディアンを出力し、CRCはModbus RTUフレーム内のリトルエンディアンとして格納される。
8.検証失敗の診断ガイド
Modbus CommunicationでCRC/LRCチェックエラーが発生した場合は、以下の手順に従ってレイヤーごとにトラブルシューティングします。
8.1チェックリストのチェック
| チェックアウト項目 | よくある質問 | 解決策は |
|---|---|---|
| CRCアルゴリズムの実装 | 間違った多項式が使用されている(0 x A 001の代わりに0 x 80 0 5) | 0xA001 右シフトまたは0x8005左シフトの使用を確認します。 |
| CRCの初期値 | 0 x FFFFではなく0 x 0000を使用 | Modbus規格では初期値は0 x FFFFである。 |
| CRC計算範囲 | CRC自体を含むか、アドレスバイトを省略する。 | アドレスコードから最後のデータバイト(CRCを除く)までの計算範囲 |
| バイトエンディアン | CRCハイバイト反転 | Modbus RTUではCRCローバイトが先行する(リトルエンディアン) |
| フレーム境界線 | 3.5文字間隔の設定が不適切です | RTUモードではフレーム間隔として3.5文字のサイレントを使用 |
| ボーレート | 送受信双方のボーレートが一致しない | 通常のレート(96 00/19200/38 400/115200)から選択します。 |
| 物理的な配線 | A/B線の反転、終端抵抗の欠如 | RS-485のA(+)、B(-)極性を確認し、両端に120Ωの終端抵抗を加えた。 |
| ASCIIモード | RTUバイナリフレームをASCIIテキストとして扱う | ASCIIモードのフレームは''で始まり、CRLFで終わる |
8.2デバッグコード例
/**
* 詳細ログを使用したCRC計算デバッグバージョン
*/
uint16_t modbus_crc16_debug uint8_t *buf、uint16_t len
{
uint 16_t crc = 0 x FFFF;
16_T i;
f
f
for i = 0; i8 ^ crc16_table[pos];
f
"prev_lo = 0x % 02X"
"index= 0x % 02X"
"table_= 0x % 04X"
"new_crc= 0x % 04"
i buf[i] prev_crc_lo
crc16_table[pos] crc;
}
f
f
crc 0xFF、crc 8;
printf "============================
CRCに戻る
}
/* 出力例:
Buf {0x01 0x03 0x00 0x00 0x01}
CRC-16 Debug Trace ===
Initial CRC 0xFFFF
Byte[ 0]=0x01、prev_lo=0xFF、index=0xFE、table_=0x4040、new_crc=0xC0C0
Byte[ 1]=0x03、prev_lo=0xC0、index=0xC3、table_=0x0280、new_crc=0x0281
Byte[ 2]=0x00、prev_lo =0x81、index=0x81、table_val =0x4040、new_crc =0x4042
...
Final CRC 0x0ACA
RTU Frame CRC Little-Endian 0xCA 0x0 A
*/性能比較:ルックアップテーブル法対変位法
実際の工学では、CRC計算の性能はModbus Communicationのスループットに直接影響します。ARM Cortex-M4 168MHzおよびx86-64 3.2GHzプラットフォームでの2つの方法の測定比較を示します。
| テスト条件 | 変位の法則 | ルックアップテーブル法 | 加速比です |
|---|---|---|---|
| ARM Cortex-M 4、1バイト | ~2.4 μs | ~0.3 μs | 8x |
| ARM Cortex-M 4、256バイト | ~615 μs | ~77 μs | 8x |
| x 86 - 6 4、1バイト | ~0.08 μs | ~0.02 μs | 4x |
| x 86 -64、256バイト | ~20 μs | ~3 μs | ~6.7x |
| コードのボリューム | 約50バイト | 约 550バイト(512バイトテーブル+ 38バイト) | — |
| RAMの使用量 | 6バイト(変数) | 518バイト(テーブル+変数) | — |
選択の推奨事項:
- 組込みMCU(フラッシュ≥ 2 KB、高周波通信が必要):ルックアップテーブル方式を優先し、512バイトのROMオーバーヘッドと8 倍の高速化を実現
- Flashが重大に制限されている(512B)変位法を用いると、空間時間変換が不可能な場合の通信性能の低下が許容される
- ホスト/サーバー:ためらうことなくルックアップテーブル法を使えば、数KBのメモリは全く問題ない
- 学習検証フェーズ:まず変位法の理解原理を実現し、テストベクトルで検証した後、ルックアップテーブル法に切り替えます。
ハードウェアCRCアクセラレーション
最新のマイクロコントローラやプロセッサは通常、CRC計算をさらに10-50 倍高速化できるハードウェアCRCコンピューティングユニットを内蔵しています。ハードウェアCRCの重要性は、産業用ゲートウェイやプロトコルコンバータで特に顕著です。これらのデバイスは、通常、数十のModbus RTU Communicationを同時に処理する必要があり、CRC計算のオーバーヘッドは無視できません。
ハードウェアCRCスキームを選択する際には、3つの要因を考慮する必要があります。まず、ハードウェアCRCユニットがカスタム多項式をサポートするかどうかです。(多くの古いMCUのCRCモジュールは固定32ビットCRC-32しかサポートしておらず、Modbusで直接使用することはできません)。(一部のハードウェアCRCは32ビットまたは16ビットのアライメントされたデータ入力を必要とし、Modbus RTUのバイトストリームには追加処理が必要です)最後に、DMAとの併用の実現可能性-シリアル受信バッファをDMA経由でCRCコンピューティングユニットに直接送信できれば、CPUオーバーヘッドゼロの検証が可能です。
10.1 STM32ハードウェアCRC
STM 32ファミリMCUはCRCコンピューティングユニットを内蔵していますが、デフォルトでは異なる多項式(0 x 4 C 11DB7、32ビット)を使用します。Modbusで使用するには、直接操作レジスタが必要です。
/**
* Modbus CRC-16を実装したSTM 32ハードウェアCRCとソフトウェア
*
* STM 32ハードウェアCRCモジュールは32ビット多項式を使用するため、
* Modbusの16ビットCRC-16と直接互換性はない。
* 通常、ソフトウェア実装が必要です。DMA +ルックアップテーブルを使用することができます。
*
* 一部のSTM 32モデル(G 4、H 7シリーズなど)では、カスタム多項式をサポートします。
* ハードウェアCRC-16計算を実装するために0 x A 001を設定可能:
*/
//STM 32 G 4/H 7シリーズ、Modbus CRC-16 CRCユニット搭載
hw_crc16_
{
__HAL__CRC__BLE;
CRC-POL = 0 x 800 5; //多項式(フォワード)
CRC-INIT = 0 x FFFF; /初期値の値
>CRC-CR| = CRC_CR_REV_OUT; //出力ビット反転
CRC-CR = ~ CRC_CR_REV_IN; //入力が反転しない
}
uint16_t hw_modbus_crc16 uint8_t *buf、uint32_t len
{
CRC-INIT = 0 x FFFF。
while len = 4 {
CRC-DR = * uint32_t * buf;
buf += 4です。
len = 4;
}
//残りのバイトの処理
while = 2 {
CRC-DR = * uint16_t * buf;
buf += 2です。
len-= 2;
}
if len {
CRC-DR = *buf;
}
return uint16_t CRC-DR 0xFFFF;
}10.2 x86 SSE 4.2 CRC32命令
Intel/AMDプロセッサはSSE4.2命令セット以降で利用可能CRC32ハードウェアのCommand。ただし、x 86のCRC32命令は異なる多項式(0 x 1EDC6 F 41)を使用しており、Modbus CRC-16と直接互換性がないことに注意してください。x 86プラットフォームでは、ルックアップテーブルは通常十分に高速である(256バイトを処理するのにわずか3マイクロ秒)。
ハードウェアアクセラレーションModbus CRC-16が必要な場合はFPGAまたはCPLD専用のCRC計算ロジックを実装します。これは産業用ゲートウェイデバイスでは一般的で、複数のシリアルデータストリームのCRCチェックをFPGAで並列処理します。
CRC計算における一般的な落とし穴
- 多項式の混乱:オンラインCRC計算機のデフォルトパラメータはModbusとは異なる場合がある。常に0 x A 0 01+初期値0 x FFFFの組み合わせを確認します。
- 災害の順序:ModbusフレームのCRCはリトルエンディアン(下位バイトが先)ですが、デバッグ出力はしばしばビッグエンディアンになります。送信時には、順序が正しいことを確認してください。
- テーブル生成エラー:テーブル生成コードを自分で書く場合は、変位と排他和の順序がメイン計算と一致するようにしてください。
- データ型オーバーフロー16ビットシステムでは、未使用
(uint8_t)(crc ^ byte)直接操作は高 8ビット汚染を引き起こす可能性がある。 - 境界の誤り:CRC計算範囲は、フレーム間隔サイレント時間およびCRC自体を含むことはできません。
- RTUとASCIIの組み合わせ:ASCIIモードはCRCの代わりにLRCを使用し、正しいチェック方法を使用していることを確認します。
FAQ:CRC/LRCに関するよくある質問
Q1)Modbusが標準CRC-16-CCITTを直接使用しない理由は?
A CRC-16-CCITT(多項式0 x 1021)は、もう一つの広く使用されているCRC規格です。Modbusの0 x 80 0 5多項式の使用は1979 yearにModicon社が選択した歴史的選択である。両方のCRCはエラー検出能力はほぼ同じですが、実装の詳細(初期値、反転など)が異なるため、結果は互換性がありません。実際の開発ではModbus仕様に厳密に準拠する必要がある。
Q2)CRCチェックをスキップしてCommunicationを高速化できますか。
A:強く推奨しない。CRCの計算オーバーヘッドは現代のMCUでは最小限である(ルックアップテーブルは典型的なModbusフレームを処理するのに数十マイクロ秒かかる)。産業環境では、CRCをスキップすることはエラー検出を放棄することに等しく、干渉パルスがデバイスにエラーCommandを実行させる可能性があります。速度が追求される場合は、データインテグリティを犠牲にすることなく、ボーレート、データパッケージング、プロトコル変換などの観点から最適化できます。
Q3 LRCとCRCはできますか。
Aはできません。LRCはASCI Iモードでのみ使用され、CRC-16はRTUモードでのみ使用される。フレームフォーマットとフレーム区切りが全く異なるため、同じネットワーク内では混在することはできません(RTUは時間間隔を使用し、ASCIIはコロンとキャリッジリターンで改行します)。
Q4オンラインCRC計算機で計算された値がプログラムと一致しない場合はどうなりますか
A以下の手順に従い、(1)多項式が0x8005または0xA001であることを確認し、(2)初期値が0xFFFFであることを確認し、(3)結果の異なるOR値が0x000であることを確認し、(4)計算範囲にCRC自体が含まれていないことを確認し、(5)中間結果をバイト単位で照合する。不整合の99%はパラメータ設定の誤りが原因です。
Q5 Pythonのbinascii.crc_hqxはModbusで使用できますか?
A直接使用できない。crc_hqx()多項式0 x 1021を使用し、初期値0 x 0000はModbus CRC-16パラメータとは全く異なります。この記事で提供されているPython実装またはpymodbusライブラリのCRC計算関数の使用を推奨します。
Q6 CRCチェックのは必ずデータエラーですか。
Aは必ずしもそうでない。CRCチェックの失敗は、(1)ボーレートの不一致によるバイトParseエラー、(2)フレーム境界判定エラー、(3)デバイスのステーションアドレス設定エラー、他のデバイスへの応答フレームの読み取りエラー、(4)データのトランケートによるRS-485トランシーバの障害などの場合にも発生します。
Q7標準ライブラリを持たないMCUでModbus CRCを高速に実装するにはどうすればよいですか
A)最も簡単な方法は、この記事で提供されている256項目のルックアップテーブルをコードに直接コピーすることです。テーブルは“純粋なデータ”であり、外部依存関係は必要なく、ライブラリ関数も呼び出されません。512バイトのconst配列のみが必要です(推奨)。const宣言はFlashに配置されています)と数行の計算ロジック。8ビットマイクロコントローラ(8051、AVRなど)では、512バイトのテーブルが一部のライトモデルではFlashの容量を超える可能性があるため、シフト法を推奨します。STM 32、ESP32などの32ビットMCUの場合は、ルックアップテーブル方式が最適です。
Q8 Modbus Communicationが不安定で、CRCが1つずつ間違っていることがあるのはなぜですか
Aこの间欠的エラーは、通常、CRCアルゴリズムのではなく、物理レイヤのである。一般的な原因としては、RS-485バスの終端抵抗の欠如または間違った抵抗値(標準は120 Ωの両端に1つずつ)、信号減衰につながる長すぎるバス、RS-485トランシーバの範囲(-7 V~+ 12 V)を超えるコモンモード電圧、複数のデバイスのバイアス抵抗の重ね合わせにより誤ったバスアイドルレベルが発生します。オシロスコープでRS-485差動信号波形を観察し、信号品質を確認することをお勧めします。ModbusCommunicationデバッグのヒントはこちらをご覧ください。modbus.cn上のデバッグに関する記事。
13.まとめ
CRC/LRCチェックはModbus Communicationの“ゲートキーパー”であり、産業データの完全性を保証します。この記事では、数学原理から実際のコードまで、Modbus検証メカニズムのあらゆる側面をカバーしています。
- CRC-16(RTUモード):多項式0 xA 001に基づく巡回冗長検査は、検出能力が高く、Modbus Communicationの主流の検査方式です。
- LRC ASCIIモード累積補正に基づく垂直冗長検証は簡単だが検出能力は弱い
- チェックテーブル法:512バイトのROMオーバーヘッドで8 倍の高速化を実現し、本番環境に最適
- 変位の法則:学習やリソースが非常に限られたシナリオに適したコードの合理化
- ハードウェアアクセラレーション:最新のMCUとFPGAは、CRC計算の効率を桁違いに向上させます。
組込みエンジニアまたはオートメーションエンジニアとして、CRCの原理を理解し、そのプログラミング実装を習得することは基本的なスキルです。この記事のテストベクトルとコードは、新しいModbus Communicationを実装するたびに検証するための参照として保存することをお勧めします。
Modbusの詳細な技術記事、ようこそmodbus.cn。推奨読書:Modbusと主流産業プロトコルの比較分析、Modbus関数コード完全ガイド、Modbus RTUとModbus TCPの違い。実際のプロジェクトでCRCチェックの問題が発生した場合は、modbus.cnの技術コミュニティで他のエンジニアと議論し、実際の経験を共有することもできます。
この記事はmodbus.cn技術チームによって作成され、出典を明記してください。コード例は実際のテストで検証されています。更新日:June 2026
Leave a Reply