Kanał - ATNEL tech-forum
Wszystkie działy
Najnowsze wątki

KURS HOME ASSISTANT

Chcesz zautomatyzować swój dom bez skomplikowanego kodowania?
Zastanawiasz się nad wyborem sprzętu, oprogramowania i aplikacji?
Od czego zacząć przygodę z HA w 2025? Co będzie najlepsze na start?

Nasz kurs Home Assistant nauczy Cię krok po kroku, jak łatwo zautomatyzować swój dom i oszczędzić na rachunkach za prąd i ogrzewanie. Bez chmur, bez zbędnych abonamentów. Twoja przygoda z Home Assistant zaczyna się tutaj!

↓↓↓

    Szanujemy Twoją prywatność. Możesz wypisać się w dowolnym momencie.




    Teraz jest 31 mar 2025, o 14:00


    Strefa czasowa: UTC + 1





    Utwórz nowy wątek Odpowiedz w wątku  [ Posty: 1 ] 
    Autor Wiadomość
     Tytuł: Modbus RTU
    PostNapisane: 26 sty 2015, o 11:19 
    Offline
    Użytkownik

    Dołączył(a): 09 cze 2013
    Posty: 34
    Pomógł: 0

    Modbus RTU – implementacja Atmega

    Jakiś czas temu w pracy miałem potrzebę skomunikowania mikrokontrolera z panelem operatorskim firmy Proface. Wybór padł na protokół modbus RTU. Jako, że czytając na forum, nie znalazłem dobrze opisanej implementacji tego protokołu na Atmedze, a jest to czasami potrzebne to postanowiłem podzielić się moimi doświadczeniami.
    Całość postanowiłem opisać na przykładzie, bez zbędnej teorii, wszystko można doczytać (polecam plik PDF: http://www.modbus.org/docs/Modbus_Appli ... _V1_1b.pdf). Mój procesorek to Atmega64, natomiast panel operatorski to Proface GP4106G1D.
    Na początek, zapraszam do obejrzenia załączonego filmiku, żeby było jasne o czym jest mowa ;)
    http://youtu.be/4LVJcYCdRjU
    Przede wszystkim, dosyć szybko udało mi się zrozumieć o co chodzi, dzięki temu, że korzystałem z manuala od chińskiego serva, w którym zawarte są tylko konkretne informacje (fragment owego manuala, w którym opisany jest modus zamieszczam w załączniku).
    Modbus opiera się na modelu master-slave. W moim przypadku Proface to master, Atmega – slave.
    Na samym początku należy skonfigurować moduł UART, u mnie jest ustawiony na 8 bitów danych, bez parzystości, jeden bit stopu i prędkość 115200 bps. To samo ustawiamy w panelu, dodatkowo w masterze jest jeszcze parametr o nazwie Timeout, oznacza on czas jaki master będzie oczekiwał na odpowiedź po zadaniu pytania. U mnie jest to ustawione na 3 sekundy (tak było ustawione domyślnie). Dodatkowo jeżeli komunikacja będzie opierała się na standardzie RS-485 trzeba przewidzieć pin przełączający na nadawanie i odbiór, oczywiście będzie potrzebny też scalak MAX-485 lub inny, u mnie jest to 75176B texas inst. Oto przykładowy schemat:
    Obrazek
    Przejdźmy do samej obsługi ramki. Przykładowo na panelu wstawiamy dwie zmienne 16-bitowe, które mają być wysyłane do slavea. W tym momencie na nóżce RXD mikrokontrolera będzie dochodziła ramka danych, która w kodzie HEX wygląda tak:

    01 03 02 00 00 02 C5 B3

    Tak powinna wyglądać odpowiedź:

    01 0304 00 B1 1F 40 A3 D4

    Przejdźmy do opisu pierwszej ramki:

    01 - adress slave
    03 - kod funkcji
    02 00 - adress początkowy pod który trafią dane od slavea
    00 02 - ilość 16-bitowych słów, które master chce otrzymać
    C5 B3 - suma kontrolna

    Opis Odpowiedzi

    01 - adress slave
    03 - kod funkcji
    04 - ilość bajtów jakie slave wysyła do mastera
    00 B1 - zawartość pierwszego słowa
    1F 40 - zawartość drugiego słowa
    A3 D4 - suma kontrolna

    Drugi bajt zawsze oznacza kod funkcji. W powyższym przykładzie jest to 0x03 Access multiple words. Pozostałe funkcję są opisane w PDF dotyczącym modbusa, do którego link podałem wcześniej. W moim kodzie obsłużyłem 3 funkcję (0x03, 0x01, 0x0F) ponieważ więcej nie miałem potrzeby.
    Zabierzmy się do pisania kodu:

    Składnia: [ Pobierz ] [ Ukryj ] [ Zaznacz wszystko ]
    język c
    Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.

    Inicjalizacji UART nie będę opisywał (jest tak samo napisane jak w bluebooku inicjalizacja RS-485). Natomiast wyjaśnię obsługę otrzymywania ramki. Już nie pamiętam dlaczego ale za pomocą bufora okrężnego nie udało mi się tego obsłużyć, więc rozwiązałem to w inny sposób. Po prostu zdefiniowałem tablicę odbiorczą na rozmiar 11 bajtów ( 11 ponieważ rozmiar ramki w moim przypadku nie przekroczy 11 bajtów) i za każdym razem gdy zostaną odebrane wszystkie bajty z ramki, co jest sprawdzane w warunku if(UART_RxIndex==data_length) to zerowany jest indeks tablicy UART_RxIndeks i ustawiana flaga recieve_done, która powoduje uruchomienie fragmentu kodu gdzie jest wysyłana odpowiedź. Odpowiedź jest wykonywana w funkcji modbus();.
    Zanim przejdziemy do opisu odpowiedzi na zapytanie mastera, wyjaśnię jeszcze co się dzieje w przerwaniu Compare Match, na wszelki wypadek napisałem jeszcze watchdoga. Jeśli flaga recieve_done nie jest ustawiona przez dłużej niż 250 ms to inicjalizuje parametry odbioru danych od nowa. Na filmiku jest widoczne jak przerywam komunikację poprzez odłączenie wtyczki i transmisja wstaje za każdym razem. Przy tych ustawieniach nie zdarzyło mi się aby komunikacja się zgubiła.
    Teraz należało by odpowiedzieć na pytanie mastera. Wykonuję to w funkcji modbus():

    Składnia: [ Pobierz ] [ Ukryj ] [ Zaznacz wszystko ]
    język c
    Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.

    Myśle, że komentarze w kodzie wyjaśniają większość wątpliwości. Wyjaśnienia wymaga na pewno obliczanie sumy kontrolnej czyli
    CRC = crc_chk(Modbus_TxBuf, data_to_send-2); //oblicz sumę kontrolną
    CRC_L = CRC; //CRC Check Low (send first)
    CRC_H = (CRC>>8);
    Funkcja wygląda następująco:
    Składnia: [ Pobierz ] [ Ukryj ]
    język c
    Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.

    Funkcja ta jest gotowcem ściągniętym z manuala od serva, który jest załączony poniżej 
    W funkcji wysyłającej ramkę do proface też nie ma żadnych czarów:
    Składnia: [ Pobierz ] [ Ukryj ]
    język c
    Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.

    Jeszcze króciutko wyjaśnię co się dzieje w obsłudze poszczególnych funkcji:

    0x03
    W Proface mam zadeklarowane 3 zmienne 16-bitowe, więc zapytanie jest o 3 słowa, przekazuje je do bajtów, które są obszarem danych.
    U mnie są to pulse – impulsy z enkodera, QB0 i QB1 są to bajty statusowe, zapalam i gaszę poszczególne bity (dzieje się to w funkcji 0x0F) i takty - to będzie coś związanego z pomiarem czasu.

    0x01
    Z funkcji tej aktualnie nie korzystam w moim programie, ale może ona służyć do obsługi takiej funkcji jak invert bit w panelu operatorskim, po naciśnięciu klawisza na panelu zdeklarowanego jako invert bit, proface zanim dokona operacji zmiany stanu bitu pyta się slave jaki jest aktulanie stan bitu 0 czy 1 i po otrzymaniu odpowiedzi wykonuje operację invert.

    0x0F
    //ustawianie bitów statusowych
    if(Modbus_RxBuf[7]==1) QB0 |= (1<<Modbus_RxBuf[3]); //ustawienie bitów na bajcie QB0
    if(Modbus_RxBuf[7]==0) QB0 &= ~(1<<Modbus_RxBuf[3]); //zerowanie bitów na bajcie QB0
    Po zdeklarowaniu w panelu przycisku set bit, reset bit lub momentary bit, w momencie gdy bit zostanie ustawiony to na bajcie 7 pojawia się 1, jeżeli wyzerowany to 0, bajt 3 oznacza, który bit jest ustawiany, jeżeli mamy 8 przycisków to przypisujemy im numery od 0 do 7.

    Mam nadzieję, że udało mi się w miarę jasno opisać obsługę modbusa. Zdaję sobie sprawę, że program jest daleki od doskonałości. Najlepiej dokonywać przełączanie na odbiór za pomocą przerwania USART_TXC_vect. Nie wykorzystałem tego, ponieważ nie chciało to dobrze działać, i z powodu braku czasu nie dopracowałem tego i jest tak jak teraz. Efekt był taki, że transmisja ruszała z kopyta, a po jakimś czasem coś się gubiło. Nie do końca też rozumiem zależności czasowym występujących w tym protokole, np. w manualu jest zdanie, że po każdej wymianie danych cisza na linii powinna trwać 10 ms. Jak sprawdzam na oscyloskopie u mnie jest to 5 ms. Jednakże u mnie to wszystko dobrze działa.
    Mam niewielki staż w programowaniu uC (ledwie rok) więc proszę o wyrozumiałość. Wszystkie uwagi są dla mnie cenne, mam nadzieję, że wspólnie dopracujemy temat tak by każdy mógł bez problemu implementować ten protokół w razie potrzeby ;)


    Załączniki:

    Aby zobaczyć załączniki musisz się zalogować. Tylko zalogowani użytkownicy mogą oglądać i pobierać załączniki.



    Góra
     Zobacz profil  
    cytowanie selektywne  Cytuj  
    Wyświetl posty nie starsze niż:  Sortuj wg  
    Utwórz nowy wątek Odpowiedz w wątku  [ Posty: 1 ] 

    Strefa czasowa: UTC + 1


    Kto przegląda forum

    Użytkownicy przeglądający ten dział: Brak zidentyfikowanych użytkowników i 1 gość


    Nie możesz rozpoczynać nowych wątków
    Nie możesz odpowiadać w wątkach
    Nie możesz edytować swoich postów
    Nie możesz usuwać swoich postów
    Nie możesz dodawać załączników

    Szukaj:
    Skocz do:  
    cron
    Sitemap
    Technologię dostarcza phpBB® Forum Software © phpBB Group phpBB3.PL
    phpBB SEO