Hej,
witam wszystkich forumowiczów, dawno mnie tu nie było
Chciałbym się z wami podzielić tym czego dowiedziałem się na temat MODBUS RTU.
Tworzę sobie mały układ, który będzie Serwerem MODBUS i przy okazji musiałem trochę wgryźć się w temat:)
Może komuś się przyda, a po drugie może ktoś zweryfikuje czy nie piszę głupot:)
Przepraszam za nie uporządkowany wygląd postu ale miałem ładnie poukładane, to jak wkleiłem treść, wszystko się porozjeżdżało
Jest na to jakiś sposób??
Wszystko co tu opisałem jest na podstawie :
strony
http://www.modbus.org MODBUS APPLICATION PROTOCOL SPECIFICATION V1.1b3
oraz
https://www.modbustools.com/modbus.html Założenie : serwer MODBUS RTU nie musi posiadać obsługi wszystkich rozkazów MODBUS wymienionych
w standardzie.
/*****************************************************************************************************************
Opis budowy ramki MODBUS RTU
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Każdą wiadomość rozpoczyna i kończy tzw. cisza na łączu. Jest to czas bezczynności,
trwający 3,5 x czas pojedynczego znaku.
Odstęp pomiędzy wysyłanymi znakami nie może być dłuższy niż 1,5 x czas pojedynczego znaku.
Czas ten jest zależny od ustawienia transmisji:
- BAUDRATE
- ilość bitów danych danych (założenie - 8 bitów)
- czy jest bit parzystości (założenie - brak)
- ile jest bitów stopu (założenie - 1 bit)
Najdłuższa ramka UART 12 bitów:
| START | Bit 1 | Bit 2 | Bit 3 | Bit 4 | Bit 5 | Bit 6 | Bit 7 | Bit 8 | parzystość | STOP | STOP |
przy założeniu założeniach podanych wyżej, długość ramki wynosi: 10 bitów
| START | Bit 1 | Bit 2 | Bit 3 | Bit 4 | Bit 5 | Bit 6 | Bit 7 | Bit 8 | STOP |
obliczenie czasu trwania jednej ramki UART:
t = (1/BAUDRATE)[s]
t3,5 = 3,5 * t = 3,5 * (1/BAUDRATE)
Przykładowo dla BAUDRATE = 9600 i długości ramki 10 bitów:
t = (1/9600) = 1041,6667 mikro sekundy
t1,5 = 1,5 * 1041,6667 = 1562,5 mikro sekundy
t3,5 = 3,5 * 1041,6667 = 3645,8333 mikro sekundy
Maksymalna długość ramki MODBUS RTU dla RS232 / RS485 to: 256 bajtów
- 1 bajt adres SLAVE
- 1 bajt kod funkcji (rozkaz)
- 252 bajty dane
- 2 bajty suma kontrolna CRC
Co reprezentują przesyłane wartości w polu "DANE" zależy od konkretnej funkcji, która ma być wykonana.
Np. adres początkowy, liczbę zmiennych, offsets, dane do zapisu rejestrów itp.
Dane mogą być 8 bitowe lub 16 bitowe.
Dane 16 bitowe dzielone są na bajty i wysyłane w kolejności: Hi Lo
Suma CRC jest 16 bitowa i wysyłana w kolejności: Lo Hi
przykład ramki:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Ramki MODBUS RTU podzielone są na 3 rodzaje:
- zapytanie, {adres slave, kod fukcji, dane, CRC}
- odpowiedź, {adres slave, kod fukcji, dane, CRC}
- odpowiedź na "wyjątek" - zwraca się kod błędu {adres slave, kod funkcji wyjątku, kod błędu }
kod funkcji wyjątku = 0x80 + kod funkcji MODBUS np. 0x80 + 0x03 = 0x83
kod błędu - opisane poniżej
Kiedy klient wysyła zapytanie do serwera, to oczekuje poprawnej odpowiedzi.
Mogą być 4 odpowiedzi:
1. Jeśli serwer otrzyma zapytanie bez błędu komunikacji, to zwraca poprawną odpowiedź
2. Jeśli serwer nie otrzyma zapytania z powodu błędu komunikacji, to nie zwraca żadnej odpowiedzi
Klient stwierdzi, że został przekroczony TIME-OUT.
3. Jeśli serwer otrzyma zapytanie ale sumy CRC nie będą się zgadzać, to nie zwraca żadnej odpowiedzi
Klient stwierdzi, że został przekroczony TIME-OUT.
4. jeśli serwer otrzyma zapytanie bez błędu komunikacji, ale nie może go obsłużyć, to zwróci
odpowiedź z kodem błędu.
Kody błędów:
0x01 ILLEGAL FUNCTION serwer nie obsługuje kodu funkcji zawartego w zapytaniu
0x02 ILLEGAL DATA ADDRESS adres danych otrzymany w zapytaniu jest nie poprawny lub podany
adres z ilością rejestrów do odczytu jest nie poprawny.
Np. serwer ma 100 rejestrów (adresy od 0 do 99), a w zapytaniu
podajemy adres 101 lub podajemy adres 90 a ilość rejestrów
do odczytu 20.
0x03 ILLEGAL DATA VALUE dana (dane) zawarte w zapytaniu nie są dopuszczalne dla serwera
Np. przekroczona wartość jakiejś nastawy
0x04 SERVER DEVICE FAILURE Wystąpił nieodwracalny błąd, gdy serwer próbował wykonać
żądaną akcję
0x05 ACKNOWLEDGE opis str. 48 dokumentu źródłowego
0x06 SERVER DEVICE BUSY opis str. 48 dokumentu źródłowego
0x08 MEMORY PARITY ERROR opis str. 48 dokumentu źródłowego
0x0A GATEWAY PATH UNAVAILABLE opis str. 49 dokumentu źródłowego
0x0B GATEWAY TARGET DEVICE opis str. 49 dokumentu źródłowego
FAILED TO RESPOND
Typy danych w MODBUS (Teoria)
W protokole MODBUS zdefiniowano 2 rodzaje danych
- cewki
- rejestry
Każdy rodzaj danych ma przypisane zakresy adresów.
UWAGA: TO JAKIE DANE I W JAKICH ADRESACH BĘDĄ WYKOŻYSTANE DANE OKREŚLONE JEST PRZEZ TWÓRCĘ "SLAVE"
Cewki - reprezentują bity - mogą reprezentować stany fizyczne wejść, wyjść
Rejestry - 16 bitowe - mogą być tylko odczytywane lub odczytywane i zapisywane.
Jakie mają znaczenie to zależy od twórcy
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
TAK NAPRAWDĘ TO OD NAS ZALEŻY JAK SOBIE ZORGANIZUJEMY PAMIĘĆ, ALE MUSIMY SIĘ TRZYMAĆ ZASAD
I PRZEDEWSZYSTKIM OPISAĆ M A P Ę P A M I Ę C I, CZYLI OKREŚLIĆ CO ZA CO ODPOWAIDA
A TO WSZYSTJKO ZALEŻY OD TEGO CO DANY UKŁAD MA ROBIĆ.
Opisy kodów funkcji MODBUS - nie wszystkie są wymienione
Kody funkcji mogą przyjmować wartość od 1 do 127
Kody funkcji dzielimy na:
- Public Function Codes - kody funkcji publiczne zakres 1 - 64 i 111 - 127
- User-Defined Function Codes - definiowane przez użytkownika zakres 65 - 72 i 100 - 110
- Reserved Function Codes - kody zastrzeżone
KODY PUBLICZNE - najbardziej przydatne (moim zdaniem) a i tak nie wszystkie się wykorzystuje
0x01 Read Coils - odczyt wyjść bitowych
0x02 Read Discrete Inputs - odczyt wejść bitowych
0x03 Read Holding Registers - odczyt rejestrów z podanego zakresu
0x04 Read Input Register - odczyt rejestrów wejściowych
0x05 Write Single Coil - zapis pojedynczego bitu
0x06 Write Single Register - zapis pojedynczego rejestru
0x07 Read Exception status - odczyt statusu wyjątku urządzenia SLAVE
0x08 Diagnostic - test diagnostyczny
0x0F Write Multiple Coils - zapis bitów z podanego zakresu
0x10 Write Multiple Registers - zapis rejestrów z podanego zakresu
0x11 Report Server ID - identyfikacja urządzenia SLAVE
0x17 Read/Write Multiple Registers
Pozostałe kody w tabeli na stronie 11 podanego wcześniej dokumentu.
Przykłady jak wyglądają ramki dla konkretnych kodów funkcji: źródło
https://www.modbustools.com/modbus.html Przykład kod funkcji 0x03
Zapytanie z MASTER: HEX
Slave Address 01
Function 03
Starting Address Hi 00
Starting Address Lo 00
Quantity of Registers Hi 00
Quantity of Registers Lo 02
Error Check Lo C4
Error Check Hi 0B
Total Bytes 8
Wyliczenie adresu początkowego : Adres, który chcemy odczytać - Adres pierwszego rejestru
czyli 4001 - 4001 = 0 dlatego :
Starting Address Hi 0x00
Starting Address Lo 0x00
jeśli chcielibyśmy czytać od adresu 4020 to robimy obliczenie : 4020 - 4001 = 19 (0x13) i wtedy byłoby:
Starting Address Hi 0x00
Starting Address Lo 0x13
Odpowiedź od SLAVE: HEX
Slave Address 01
Function 03
Byte Count 04
Data Hi 00
Data Lo 06
Data Hi 00
Data Lo 05
Error Check Lo DA
Error Check Hi 31
Total Bytes 9
Przykład kod funkcji 0x16
Zapytanie z MASTER: HEX
Slave Address 11
Function 10
Starting Address Hi 00
Starting Address Lo 01
Quantity of Registers Hi 00
Quantity of Registers Lo 02
Byte Count 04
Data Hi 00
Data Lo 0A
Data Hi 01
Data Lo 02
Error Check Lo C6
Error Check Hi F0
Total Bytes 13
Wyliczanie sumy CRC
Sumę CRC wylicza się z:
- Adresu SLAVE
- kodu funkcji
- danych
np: HEX
Slave Address 11
Function 10
Starting Address Hi 00
Starting Address Lo 01
Quantity of Registers Hi 00
Quantity of Registers Lo 02
Byte Count 04
Data Hi 00
Data Lo 0A
Data Hi 01
Data Lo 02
Wynikiem obliczeń jest :
HEX
Error Check Lo C6
Error Check Hi F0
******************************************************************************************************************/
i jeszcze funkcja do obliczania CRC na podstawie
http://www.modbus.orgwynik 16 bitowy - przy czym starszy bajt to Lo CRC a młodszy bajt to Hi CRC
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.