Das Problem ist weit verbreitet, als Sie denken.
Mit Modbus Poll lesen Sie die gesamte aktive Leistung eines Stromzählers in der Registrierung 40001 bis 40002 und geben `42C8 00 00` zurück. Sie sind zuversichtlich, IEEE 754 float32 - 100.0 kWh, vernünftig. Das gleiche Programm, wechseln Sie zu einem anderen inländischen Stromzähler, lesen Sie die gleichen zwei Register, geben Sie `00 00 42 C8` zurück, buchstabieren Sie 0.0, PLC direkt auf dem Touchscreen zeichnet Ihnen eine gerade Linie. Sie haben nicht falsch geschrieben, nicht falsch CRC berechnet, nur umgekehrt durchsetzt.
Dies ist der Kernpunkt der Modbus-Multi - Register-Datenanalyse. Modbus-Protokoll - Spezifikation V1.1b Abschnitt 4.2 "Data Encoding" sagt sehr offen: * * "Modbus verwendet eine 'big-endian' Repräsentation für Adressen und Datenelemente. Dies bedeutet, dass, wenn eine numerische Menge größer als ein einzelnes Byte übertragen wird, das signifikanteste Byte zuerst gesendet. Modbus verwendet die High-End - Reihenfolge - die hohen Bytes voran - beim Übertragen von 16 - Bit-Daten in einem einzigen Register. Die Spezifikation sagt jedoch nicht, wie 32 - Bit oder 64 - Bit über mehrere Register angeordnet werden. Die Reihenfolge zweier aufeinanderfolgender Register, die Reihenfolge von zwei Bytes innerhalb jedes Registers, ist dem Gerätehersteller selbst zu entscheiden.
Das klingt wie eine Schlampe, aber die Modicon-Ingenieure im Jahr 1979 mussten sich nicht um 32 - Bit-Flowerkommazahlen kümmern - die 16 - Bit-Ganzzahlen waren zu einem Luxus. Nach vierzig Jahren müssen wir uns selber tragen.
Vier Zeilen, vier Zeilen klar.
Angenommen, Sie lesen das Hold-Register, das von der Adresse 0 (Modbus-Adresse 40001) beginnt, und fordern 2 Register. Der Datenbereich in der von der Station zurückgegebenen Nachricht lautet:
41 DB 85 1FDies ist eine ursprüngliche Sequenz von Bytes für IEEE 754 Single-Precise - Gleitkomma-Zahlen. Der wahre Wert beträgt 27.440271... (ca. 27,44). Nun sehen Sie, wie die vier verschiedenen Byte-Sequenzen geschrieben werden:
ABCD (Big-End - Sequenz, Modbus-Standard - Anordnung)
Der Wert des Registers 40001 ist `0x41DB` und der Wert des Registers 40002 ist `0x851F`. 32 Punkte in der Reihenfolge:
寄存器 40001(高16位) 寄存器 40002(低16位)
+--------+--------+ +--------+--------+
| 0x41 | 0xDB | | 0x85 | 0x1F |
+--------+--------+ +--------+--------+
A B C DSpeicheransicht (niedrig bis hohe Adresse): `41DB85 1F`
→ float32 = **27.44**
Dies ist das Standardverhalten der Modicon-SPS-Serie von Schneider (M340, M580, M241 usw.). Schneider, der Vater des Modbus-Protokolls, folgte immer dieser Standard-Anordnung - 16 - Bit für kleine Registeradressen und 16 - Bit für große Registeradressen. Du liest zwei Wörter mit READ _ VAR zurück und buchstabierst sie direkt richtig.
DCBA (kleine Sequenz, vollständig umgekehrt)
寄存器 40001(但内容反转了) 寄存器 40002
+--------+--------+ +--------+--------+
| 0x1F | 0x85 | | 0xDB | 0x41 |
+--------+--------+ +--------+--------+Speicheransicht: `1F85DB41`
→ float32 = * * 1.5468 × 10 − 28 * * - ein extremer Wert nahe Null, der im Grunde 0,0 oder Überflussfehler darstellt.
Einige inländische Modbus-Module (insbesondere einige auf 51 - Chip-Computer oder frühen ARM-Nacklauf - Geräte) verwenden diese Art und Weise. Weil ihre interne CPU ist klein, direkt memcpy Dinge, ist im Wesentlichen "mütig umwandeln". Ein intelligentes Stromzähler einer Marke in Peking ist ein typischer Fall, und Sie müssen die Byte-Umkehrung manuell durchführen, um sie zurückzulesen.
CDAB
寄存器 40002 的内容放前面 寄存器 40001 的内容放后面
+--------+--------+ +--------+--------+
| 0x85 | 0x1F | | 0x41 | 0xDB |
+--------+--------+ +--------+--------+Speicheransicht: `85 1F 41DB`
→ float32 = **-1.096 × 10−33**
Dies ist das tatsächliche Verhalten von Siemens S7-1200 / S7-1500 Modbus TCP-Servern. Siemens PLC-interne REAL-Variable nimmt 4 Bytes, in den DB-Block ist das Standard-IEEE - 754 - Format, aber MB_SERVER-Befehl - Block in der Aufrechterhaltung des Registers an den Modbus-Client ausgesetzt, * zuerst niedrig Adress-Register niedrig Byte, dann hohe Byte, dann hohe Adress-Register niedrig Byte, dann hohe Byte *. Das ist gleichbedeutend mit dem Austausch von zwei Wörtern von Schneider ABCD. Sie lesen die Gleitkommazahlen der Siemens PLC mit Modbus Poll, wenn Sie nicht die Float in den Display-Einstellungen in den "CDAB" - Modus wechseln, muss es eine astronomische Zahl sein.
libmodbus bietet vier Funktionen für diese vier Reihenfolge: `modbus _ get _ float _ abcd ()`, `modbus _ get _ float _ dcba ()`, `modbus _ get _ float _ badc ()`, `modbus _ get _ float _ cdab ()`.
BADC (Byte-Switch, Innenumkehrung pro WORD)
寄存器 40001(内部字节交换) 寄存器 40002(内部字节交换)
+--------+--------+ +--------+--------+
| 0xDB | 0x41 | | 0x1F | 0x85 |
+--------+--------+ +--------+--------+Speicher-Ansichten: `DB41 1F85`
→ float32 = ** -1.457 × 1017**
Die Delta DVP-Serie, Mitsubishi FX3U / FX5U und ein Teil der HSBC-Serie (H3u, H5u) verwenden bei der Übertragung von 32 - Bit-Daten über Modbus RTUs tatsächlich eine BADC-Anordnung. Warum nicht? Weil die internen Datenspeicher dieser japanischen / Stand-System - SPS selbst ein "kleines Ende in einem Wort" ist - ein einzelnes 16 - Bit-WORD - internes Low-Byte vor. In der Modbus-Transportschicht wird das hohe Byte des WORDS (Modbus-Anforderung) zuerst ausgegeben, so dass das WORDS-Interieur einmal umgekehrt ist. Zwischen den beiden Wörtern ist die große Reihenfolge von Modbus beibehalten. Das Ergebnis ist also BADC: jedes WORD-interne Byte umgekehrt, wobei die Reihenfolge ABCD zwischen den Wörtern beibehalten wird.
Eine weniger strenge Analogie: Schneider ist eine ganze Kiste Puzzles in der Reihenfolge für Sie (ABCD), Siemens ist die linke Hälfte zuerst wechseln und Ihnen zu geben (CDAB), Delta / Mitsubishi / Huichuan ist das Innere jeder Hälfte zuerst umdrehen und Ihnen zu geben in der Reihenfolge (BADC). Was DCBA angeht, so ist es das Gegenteil von der Box.
Datentypen sind viel versteckter als Byte-Sequenzen
dieselbe 4 - Byte-Sequenz `41DB85 1F`, auf unterschiedliche Datentypen aufgeklärt:
| Aufgeklärt | Ergebnisse |
|---|---|
| float32 (IEEE 754) | 27.44 |
| uint32 | 1,104,626,975 |
| int32 | 1,104,626,975 |
| Zwei uint16 Splicing | [16859, 34079] |
Betrachten Sie diesen Unterschied, float32 von 27,44 und uint32 von 1,1 Milliarden, ein Unterschied von acht Größenordnungen. Wenn Sie auf Ihrem SCADA eine Temperaturmessung von 1104626975.0 ° C sehen, müssen Sie sich nicht beeilen, den Sensor zu wechseln - prüfen Sie, ob float32 mit uint32 aufgelöst wurde.
Dies ist in der Praxis versteckter als das Byte-Sequenz - Problem, weil * * Sie die richtige Zahl bekommen, aber die Art und Weise, wie Sie es interpretieren, falsch ist *. Die Byteordnung ist falsch, zumindest die Zahlen sind chaotisch, man kann es auf einen Blick sehen; der Typ ist falsch, manchmal ist es wirklich verwirrt.
Nehmen Sie ein Beispiel vor Ort. Der Wechselrichter eines Photovoltaik-Kraftwerks überträgt die tägliche Stromerzeugung über Modbus TCP, und das Register-Zusammenstellungsdokument schreibt "40001 - 40002: tägliche Stromerzeugung, 32 - Bit". Der Ingenieur analysierte mit int32 und leste eine positive Zahl zurück, der Wert sah ziemlich vernünftig aus und lief einen Monat lang auf der Energieverbrauchsplattform. Bis die finanzielle Abrechnung entdeckte, dass 30% fehlt, eine Überprüfung zu wissen - der Hersteller ist tatsächlich * * uint32 * *, wenn die hohe Position als Symbol Bit behandelt wird, solange die Stromerzeugung übersteigt 2,147,483,647 Wh (2,15 GWh), wird der Wert der Auflösung negativ.
Wenn Sie also mit einem Multi-Register - Datenpunkt konfrontiert sind, müssen Sie sicherstellen, dass es nicht eine Variable ist, sondern drei:
1. Was ist der Datentyp (float32 / int32 / uint32 / int64 / uint64 / double64 / string)? 2. Was ist eine von vier (eine von vier)? 3. Gibt es einen Skalierungsaufwand? Ist das z.B. der ursprüngliche Wert von X 10 oder X 100?
Der dritte Punkt wird oft übersehen. Viele Modbus-Instrumente, um Register zu sparen und Gleitkommaübertragung zu vermeiden, multiplizieren den Gleitkommawert mit 10 oder 100 und konvertieren ihn in int32. Wenn wir beispielsweise int32 = 2744 lesen, beträgt die tatsächliche Temperatur 27,44 ° C (÷ 100), der Druck 274,4 kPa (÷ 10). Dieser Skalierungsfaktor ist nur im Gerätehandbuch geschrieben und ist auf der Protokollnachricht völlig nicht zu sehen.
Das tatsächliche Verhalten der Mainstream-Anbieter
Dieser Abschnitt ist der praktischste Teil dieses Artikels. Die folgenden Daten stammen aus tatsächlichen Debugging-Dokumenten und Community-Bestätigungen und stammen nicht aus dem Produkthandbuch.
Modicon M340 / M580 / M241
Erfinder des Modbus-Protokolls, der sich am "Standard" verhält. Die Modbus TCP / RTU-Kommunikation von QUANTUM, M340, M580, M241 und 32 - Bit-Daten (REAL, DINT) sind standardmäßig in der * * ABCD * * - Anordnung. Das `% MW` Register wird mit READ _ VAR gelesen, wobei die niedrigste Adresse 16 Bits hoch und die höhere Adresse 16 Bits niedrig gespeichert wird.
Besondere Anmerkung: In Unity Pro / EcoStruxure Control Expert von Schneider gibt es eine Übertragungsschicht für den internen Speicher von `% MD` (Doppel-Wort) und `% MF` (Floating-Doppel - Wort) mit Modbus-Register - Zuordnung. MD0 entspricht `%MW0` und `%MW1`, wobei `%MW0` die hohe und `%MW1` die niedrige Position ist - genau ABCD.
Schlussfolgerung: Schneider PLC Modbus Slave Station, direkt nach oben drücken ABCD zu analysieren.
Siemens S7-1200 / S7-1500 (MB_SERVER)
Dies ist der größte. Siemens ruft den Befehl `MB _ SERVER` im TIA-Portal als Modbus TCP-Server auf, wobei die REAL-Variablen (4 - Byte) in den beiden Aufbewahrungsregistern * * CDAB * * angeordnet sind.
Prinzip: Der DB-Block des Siemens S7-1200/1500 ist standardmäßig "optimiert für den Blockzugriff" und hat eine undurchsichtige interne Byte-Anordnung. Selbst wenn Sie die Optimierung deaktivieren (auf Standardzugriff eingestellt), ist die Byte-Reihenfolge von REAL-Variablen in der DB gering (von Intel x86 - Prozessoren bestimmt). Wenn `MB_SERVER` die vier Bytes dieses REALs in das Modbus-Haltregister zugeordnet, füllt es sich in der Reihenfolge des Bytes: die kleineren Register-Bytes 0 und 1 (das ist der niedrigste Teil von REAL), die großen Register-Bytes 2 und 3 (der hohe Teil).
Die eigentliche Wirkung ist CDAB. Sie müssen in der Anzeige-Einstellung von Modbus Poll auf "CDAB" schalten, um die richtige Gleitkomma-Zahl zu sehen.
Wenn Sie einen Modbus-Client (`MB _ CLIENT`) mit einer Siemens PLC verwenden, um einen Modbus-Slaven von Drittanbietern zu lesen, müssen Sie auch beachten - die Anordnung der beiden Wörter, die Sie zurücklesen, hängt von der Byteordnung des Slaves ab, und der REAL-Speicher in Ihrem Siemens ist eine andere Sache. In diesem Fall ist die Befehl `READ _ BIG` / `READ _ LITTLE` / `WRITE _ BIG` / `WRITE _ LITTLE` von Botox V16 und höher viel zuverlässiger als manuelle SWAP-Verlagerung.
Delta DVP-Serie
Delta DVP-ES2 / EX2 / SV2 und andere Serien, wenn Modbus RTU-Slavenstationen, 32 - Bit-Fließkomma - Zahlen (F-Register) in Modbus-Halt - Registers zugeordnet sind, die Anordnung ist * * BADC *.
Dies hat damit zu tun, wie die internen Gleitkommazahlen von Delta gespeichert werden - die 32 - Bit-Daten der Delta PLC folgen der Regel „low-bit - low-address" (small-end), während Modbus-RTU - Frames jedes WORD im Big-end senden müssen. Das interne WORD-Byte wurde einmal umgekehrt, aber die interne PLC-Reihenfolge zwischen den beiden WORDS wurde beibehalten und schließlich zu BADC wurde.
Wenn Sie in WPLSoft D0 = F100.0 (Fließkommazahlen) anzeigen, lesen Sie 40001 ~ 40002 in Modbus Poll, wählen Sie Display "BADC", Sie sehen 100.0. Die anderen drei Auswahlpläne sind verwirrend.
Mitsubishi FX3U / FX5U (über Modbus-Kommunikationsmodul)
Bei der Installation des FX3U - 485ADP-MB - Kommunikationsmoduls als Modbus-RTU - Master - oder Slave-Station ist die 32 - Bit-Daten - Anordnung auch * * BADC *. Wie die Logik von Delta - die interne Architektur der japanischen PLC ist klein.
Der FX5U-Körper verfügt über eine Modbus-RTU - Funktion (über RS - 485 - Anschluss), wenn Sie mit ADPRW-Befehl kommunizieren, müssen Sie die 32 - Bit-Daten mit MOV-Befehl manuell schreiben. Die beiden Register D100, D101 aus dem MODBUS lesen, die BADC-Anordnung der Rohdaten gespeichert sind, müssen zuerst die Bytes jedes Registers mit der SWAP-Anweisung ausgetauscht werden, und dann die hohe und niedrige Bit-Stappe - Anweisung zum endgültigen 32 - Bit-Wert kombiniert werden.
Ich habe mehr als ein Projekt gesehen - Mitsubishi PLC liest die Frequenz des Schneider-Frequenzumrichter, umgekehrt liest und nicht SWAP, zwei Tage Debugging und schließlich entdeckt, dass es ein Byte-Sequenz - Topf ist.
Omron CP1H / CP1L
OMRON CP1 - Serie über CP1W-CIF11 (RS - 485 Optionsplatte) Modbus-RTU - Slave-Stationen, halten die Registers mit DM-Zone - Zuordnung. Die Anordnung von 32 - Bit-Daten in zwei DM-Wörtern hängt davon ab, wie Sie programmieren.
Die CP1 - Serie selbst ist eine Wortadressarchitektur und hat keinen nativen Doppel-Wort - Datentyp. Die Gleitkommazahlen werden mit zwei DM-Wörtern geschrieben, und die Reihenfolge wird vollständig von Ihrem Trapezoid-Diagramm bestimmt. Die meisten Standardimplementierungen folgen jedoch der * * ABCD * * - Anordnung - was mit der CP1 - Serie zusammenhängt, die aus der frühen SYSMAC-Architektur entstanden ist. Es wird empfohlen, beim Debuggen einen bekannten Wert zu verwenden (z. B. Schwimmbarkomma 1.0 = `3F800000` in zwei DM-Wörter zu schreiben).
inländische PLC (Huichuan, Buko)
Die HSBC H3u / H5u-Serie macht Modbus-RTU - Slave-Stationen, 32 - Bit-Daten Standard * * BADC * * Anordnung, mit Delta übereinstimmt. Bei der AM600 - Serie von HSBC (basierend auf der CODESYS-Plattform) ist das jedoch anders - der interne Real-Nummer - Speicher von CODESYS ist eine Standard-IEEE - 754 - Large-End - Struktur, und die Register-Anordnung, die Modbus TCP Server ausgesetzt, ist standardmäßig * * ABCD *.
Die HSBC Easy-Serie ist eine vereinfachte Version von H5u für die Steuerung von kleinen Geräten, und die Gleitkomma-Anordnung der Modbus RTU-Sklavenstation ist ebenfalls * * BADC *.
Kinco gibt es einige Modelle, die * * CDAB * * gehen, wie z. B. die K2 - Serie. Dies ist ähnlich wie bei Siemens, da die Baureihe K2 / K5 auf Software-Ebene die Touchscreen-Programmierlogik von Siemens übernommen hat.
Zusammenfassung einer Quick-Look - Tabelle:
| Hersteller / Gerät | Modbus 32 - Bit-Anordnung | Bemerkungen |
|---|---|---|
| Schneider M340 / M580 / M241 | ABCD | Modbus Original-Standard |
| Siemens S7 - 1200 / 1500 | CDAB | MB_SERVER Befehlblockverhalten |
| Delta DVP-Serie | BADC | Intrabyte-Switch |
| Mitsubishi FX3U / FX5U | BADC | Nach dem SWAP |
| Omron CP1H / CP1L | ABCD (meist implementiert) | Abhängig von Trapezoid |
| HMICHUAN H3u / H5u | BADC | Konsistent mit Delta |
| HMICHUAN AM600 (CODESYS) | ABCD | CODESYS Plattform |
| Die K2 - Serie | CDAB | Teilweise Modelle |
| Typische inländische Stromzähler | DCBA oder ABCD | müssen einzeln getestet werden |
Hinweis: Diese Tabelle ist eine gemessene Referenz und kein offizielles Versprechen. Verschiedene Modelle des gleichen Herstellers, verschiedene Firmware-Versionen, kann das Byte-Sequenz - Verhalten unterschiedlich sein. Bevor Sie in die Produktionsumgebung einführen, müssen Sie die Überprüfung durchführen.
Umschaltungen von Modbus Poll
Modbus Poll bietet in der Anzeige-Einstellung eine Funktion zum Umschalten der Datenformate. Nach der Auswahl des Datenausstellungsbereichs mit der rechten Maustaste → Format, können Sie sehen:
Signed - 有符号 16 位整数
Unsigned - 无符号 16 位整数
Hex - 十六进制
Binary - 二进制
32-bit Signed - 32 位有符号整数
32-bit Unsigned - 32 位无符号整数
32-bit Float - 32 位浮点数
64-bit Float - 64 位双精度浮点数Wenn Sie 32 - Bit Float wählen, wird rechts die Option Bytes-Sequenz angezeigt:
Float Big-endian (ABCD) - 大端
Float Little-endian (DCBA) - 小端
Float Big-endian byte swap (BADC) - 大端字节交换
Float Little-endian byte swap (CDAB) - 小端字节交换Sie müssen sich nicht merken, welche Sequenz von welchem Hersteller verwendet wird. Beim Debuggen werden alle vier Reihenfolge geschnitten, um zu sehen, welcher Wert angemessen ist.读一个你确定量程范围的已知测点(比如环境温度在 20~30℃ 之间),四种序里只有一种会落在合理范围。
64 - Bit-Datenverarbeitung
Akkumulationsleistung, Gesamtlaufzeit, Akkumulationsvolumen des Datenverkehrs - diese Szenarien müssen oft mit 4 Registeren (8 Byte) dargestellt werden, die Byte-Sequenz - Probleme sind komplexer. Weil Sie mit zwei verschachtelten Anordnungsschichten arbeiten:
Schicht 1: Inneres Byte-Sequenz für jedes 32 - Bit-DWORD (wie im vorherigen 32 - Bit-Szenario, vier Möglichkeiten) Schicht 2: Die Reihenfolge zwischen zwei 32 - Bit-DWORDs (Ist das Register 1 - 2 ein hoher oder ein niedriger 32 - Bit-DWORD?)
Nehmen wir an, Sie lesen 4 Register und geben die ursprüngliche Byte-Sequenz zurück:
41 DB 85 1F 42 C8 00 00Wenn Sie "zuerst 32 Bits hoch, dann 32 Bits niedrig, 32 Bits nach ABCD" - dies ist die regelmäßigste Anordnung:
高32位 (ABCD): 41 DB 85 1F → float32 = 27.44
低32位 (ABCD): 42 C8 00 00 → float32 = 100.0Hinweis: Dies ist die typische Konstruktion eines Totalisers. Die hohe 32 - Bit-Speicherung des großen Teils, die niedrige 32 - Bit-Speicherung des dezimalen Teils oder die hohe 32 - Bit-Speicherung des Ganzzahlteils, die niedrige 32 - Bit-Speicherung des dezimalen Teils. Wie man das Gerät kombiniert, sieht man im Handbuch an.
Ein häufigeres Szenario ist die kumulative uint64. Die vier Register sind abcd angeordnet, von niedrigem nach höchstem:
寄存器1 (MSW-High): A B → 字节0-1
寄存器2 (MSW-Low): C D → 字节2-3 } 组成高32位
寄存器3 (LSW-High): E F → 字节4-5
寄存器4 (LSW-Low): G H → 字节6-7 } 组成低32位uint64: `(uint64_t) (reg1 < < 48)| reg2 < < 32| Reg3 < < 16| reg4) `, aber diese Formel gilt nur dann, wenn die CPU selbst ein Big-End - Sequenzierungs ist. Sie schreiben C-Code auf x86 (small-end), müssen umgekehrt.
Erfahrung: Beim Verarbeiten von 64 - Bit-Daten, nicht zuerst verwickeln Sie die Byte-Sequenz, lesen Sie zuerst den ursprünglichen Wert von 4 Registern mit Hexadezimal, klar, ob der Accumulator wächst, beobachten Sie die Richtung des Wachstums - wenn der Accumulator jedes Mal zunimmt, sehen Sie, ob die Veränderung vom niedrigen Adress-Register oder vom hohen Adress-Register beginnt, wissen Sie, dass die hohen und niedrigen Bit angeordnet sind.
libmodbus bietet keine direkte 64 - Bit-Konvertierungsfunktion an, so dass Sie es selbst buchstabieren müssen:
uint16_t regs[4];
modbus_read_registers(ctx, addr, 4, regs);
// 假设 ABCD 排列
uint64_t value = ((uint64_t)regs[0] << 48) |
((uint64_t)regs[1] << 32) |
((uint64_t)regs[2] << 16) |
((uint64_t)regs[3]);Das ist aber nur eine von vier Regeln. Wenn Ihr Gerät eine BADC-Anordnung ist, müssen Sie zuerst die Bytes jedes Registers austauschen; wenn es CDAB ist, müssen Sie zuerst die Positionen des Registerpaares austauschen.
Die Speicherung der String
Geräte-Seriennummern, Firmware-Versionsnummern Diese ASCII-Zeichenfolgen werden in Modbus-Registern gespeichert. Es gibt zwei gängige Modus:
* * Modus 1: 2 Zeichen pro Register (Standardpraktiken) * *
Jedes 16 - Bit-Register enthält zwei Byte, jeweils ein ASCII-Zeichen. Zum Beispiel die Seriennummer "SN20240001" (10 Zeichen), benötigt 5 Register:
寄存器1: 0x53 0x4E → 'S' 'N'
寄存器2: 0x32 0x30 → '2' '0'
寄存器3: 0x32 0x34 → '2' '4'
寄存器4: 0x30 0x30 → '0' '0'
寄存器5: 0x30 0x31 → '0' '1'direkt nach dem Byte lesen, um den ASCII-Wert zu Zeichen zu nehmen, brauchen Sie keine Sorge um die Bytefolge - ASCII-Zeichen `A` ist `0x41`, unabhängig davon, ob das große Ende klein.
* * Modus 2: Jedes Register nur niedrige Byte gültig * *
Einige Gerätehersteller faul, jedes Register verwendet nur die niedrigsten 8 - Bit ein Zeichen, die höhere 8 - Bit ist 0x00. Das gleiche "SN20240001" benötigt 10 Register:
寄存器1: 0x00 0x53 → 空 'S'
寄存器2: 0x00 0x4E → 空 'N'
...Diese Methode verschwendet die Hälfte des Registers, aber die Analyse ist einfach - einfach nur das niedrigste Byte pro Register.
Tipp: Das Byte-Sequenz - Problem der Zeichenkette gibt es meistens nicht, da ASCII eine Single-Byte - Kodierung ist. Das Problem ist, wenn Sie mit UTF - 16 oder einer anderen Multibyte-Codierung arbeiten - aber das ist nicht mehr der Fall des Modbus-Protokolls, es ist reine Kodierungsoption auf der Anwendungsschicht.
Testmethodik
Nicht raten, sprechen Sie mit Daten. Hier ist ein Standard-Fehlerreihenfolge - Prozess:
* * Erster Schritt: Lesen Sie die ursprüngliche Hexadezimalform * *
Mit Modbus Poll schneiden Sie das Display-Format in Hex und notieren Sie den ursprünglichen Registerwert. Unabhängig von der Reihenfolge, sehen Sie zuerst die nackten Daten.
读 40001-40002 返回: 41 DB 85 1F* * Schritt 2: Finden Sie einen Messpunkt, den Sie wirklich kennen, um den bekannten Wert zurückzuziehen. Aktuelle Temperatur beträgt 27,54 ° C. Wenn Sie diesen Wert auf dem mitgelieferten Display des Geräts gelesen haben, notieren Sie ihn. Dann schauen Sie, welche der vier Reihenfolge 27,44 buchstabiert wird, das ist die richtige Antwort.
Finden Sie einen Messpunkt, den Sie wirklich kennen. Aktuelle Temperatur beträgt 27,54 ° C. Wenn Sie diesen Wert auf dem mitgelieferten Display des Geräts gelesen haben, notieren Sie ihn. Dann schauen Sie, welche der vier Reihenfolge 27,44 buchstabiert wird, das ist die richtige Antwort.
Bessere Ansatz - Schreiben Sie einen bekannten Wert. Schreiben Sie `0x3F80000000` (dies ist die Standard-IEEE - 754 - Darstellung für float32 = 1.0) mit dem Funktionscode 16 (Schreiben mehrerer Registers) in das Registerpaar und lesen Sie es dann zurück. Sehen Sie sich die ursprüngliche Hexadezimalzahl an:
- Wenn es `3F80 00 00` → ABCD ist - Wenn die Ausgabe `00 00 3F80` → DCBA - Wenn es `80 3F0000` → BADC ist - Wenn die Ausgabe `00 00 80 3F` → CDAB
* * Schritt 3: Pakete mit Wireshark greifen * *
Der Modbus-TCP - Port ist 502. Filtern Sie `tcp.port == 502` in Wireshark, klicken Sie mit der rechten Taste → Decode As → Modbus / TCP. In der Antwortnachricht, die Sie sehen, ist der Datenbereich die Original-Byte - Sequenz. Vergleichen Sie diese Reihenfolge mit Ihrem Analyseergebnis:
Wireshark 报文:
Modbus/TCP
Transaction Identifier: 1
Protocol Identifier: 0
Length: 7
Unit Identifier: 1
Function Code: 3 (Read Holding Registers)
Byte Count: 4
Register 0 (40001): 0x41db
Register 1 (40002): 0x851fDie Registerwerte, die Wireshark zeigt, sind das Ergebnis der großen Auflösung. Wenn Ihre SPS ein BADC ist, dann sind die `0x41db` und `0x851f`, die wireshark zeigt, nicht mehr die ursprünglichen Byte-Sequenzen - die ursprüngliche Byte-Sequenzen sollten `DB41 1F85` sein.
Aus diesem Grund sollten Sie beim Debuggen von Byte-Sequenz - Problemen eher den hexadezimalen Dump von Wireshark ansehen, als dass er Ihnen hilft, gute Registerwerte zu analysieren. Erweitern Sie das Feld "Daten" in den Nachrichtendetails und sehen Sie das Original-Byte.
* Schritt 4: Online-Byte - Rechner * *
Das Konzept ist einfach - Sie geben vier Hexadezimal-Bytes ein, und es gibt Ihnen die vier Reihenfolge der Werte float32, int32, uint32 und zeigt auf einen Blick, welche die richtige Antwort ist.
Wenn es nicht vorhanden ist, schreiben Sie eine selbst auch nicht kompliziert:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void print_float(uint8_t *bytes) {
float f;
memcpy(&f, bytes, 4);
printf("float32: %fn", f);
}
int main() {
uint8_t data[4] = {0x41, 0xDB, 0x85, 0x1F};
uint8_t abcd[4] = {data[0], data[1], data[2], data[3]};
uint8_t dcba[4] = {data[3], data[2], data[1], data[0]};
uint8_t badc[4] = {data[1], data[0], data[3], data[2]};
uint8_t cdab[4] = {data[2], data[3], data[0], data[1]};
printf("ABCD: "); print_float(abcd);
printf("DCBA: "); print_float(dcba);
printf("BADC: "); print_float(badc);
printf("CDAB: "); print_float(cdab);
return 0;
}Anhang: Modbus CRC - 16 Tabellen-Suche - C-Sprache Implementierung
Byte-Sequenz - Problem ist oft mit der CRC-Prüfungs - Byte-Sequenz vermischt - nicht, dass der Algorithmus ist ein Problem, sondern * * CRC-Ergebnis zwei Byte in der Nachricht, wer zuerst sendet, wer später sendet * *, verschiedene Dokumente sind anders geschrieben.
Die Modbus-Spezifikation ist eindeutig: CRC - 16 * Low-Byte - First-Send * *. Das bedeutet, dass das Ergebnis der CRC-Berechnung `0x1234` ist, so dass die letzten beiden Bytes in der Nachricht `34 12` sind und nicht `12 34`.
Die folgende Tabellensuchmethode gibt direkt Modbus-konforme Ergebnisse zurück - das niedrigste Byte steht vor und kann direkt am Ende der Nachricht hinzugefügt werden.
#include <stdint.h>
/* CRC-16 Modbus 查找表 */
static const uint16_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,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 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, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 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,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
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,返回符合 Modbus 帧格式的结果
* 低字节在前(低地址),可直接追加到报文末尾
*/
uint16_t crc16_modbus(const uint8_t *data, uint16_t len)
{
uint8_t crc_hi = 0xFF;
uint8_t crc_lo = 0xFF;
uint8_t idx;
while (len--) {
idx = crc_hi ^ *data++;
crc_hi = crc_lo ^ (uint8_t)(crc16_table[idx] >> 8);
crc_lo = (uint8_t)(crc16_table[idx] & 0xFF);
}
/*
* 返回 16 位 CRC 值
* 使用时追加到 Modbus RTU 帧末尾:
* frame[len] = crc & 0xFF; // 低字节在前
* frame[len+1] = (crc >> 8) & 0xFF; // 高字节在后
*/
return ((uint16_t)crc_lo << 8) | crc_hi;
}Hier ist ein bemerkenswertes Detail: Wenn Sie sich einige andere CRC - 16 - Implementierungen anschauen, gibt es einige, die hohe Bytes vor und einige, die niedrige Bytes vor geben. Der Unterschied besteht in der `return` - - `(crc_lo < < 8)| crc _ hi `und `(crc _ hi < < 8)| crc _ lo ist das Gegenteil. Die Modbus-Spezifikation besagt, dass das CRC-Feld "low byte first" ist, also gibt die obige Implementierung eine uint16_t zurück, aber Sie wissen, dass die unteren 8 Bits die niedrigen Bytes des CRC sind und die oberen 8 Bits die hohen Bytes des CRC. Schreiben Sie zuerst `(crc & 0xFF)` und dann `((crc > 8) & 0xFF)`.
Wenn Sie `return (crc _ hi < < 8) verwenden| crc_lo `, dann muss die Reihenfolge umgekehrt sein - das ist einer der Gründe, warum viele Debugger "CRC-Checks sind immer falsch" finden.
Probleme noch mal reden. Wenn Sie das nächste Mal eine falsche Byte-Sequenz auftreten, nehmen Sie diese vier Bytes direkt durch ABCD / DCBA / BADC / CDAB, schneller als Sie das Herstellerhandbuch durchblättern.
Antwort veröffentlichen