ATNEL tech-forum
https://forum.atnel.pl/

ATMega128 + MODBUS RTU
https://forum.atnel.pl/topic24058.html
Strona 1 z 1

Autor:  slawus1998 [ 9 gru 2021, o 00:12 ]
Tytuł:  ATMega128 + MODBUS RTU

Witam wszystkich forumowiczów,

Temat postu mniej więcej sugeruje z czym się zmagam. Otóż implementuję obsługę MODBUSa RTU na mikrokontrolerze ATMega 128 (przy czym testuję program również na ATMega 324P). Niestety mimo wielu godzin, zmagam się z jednym problemem. A mianowicie: mój mikrokontroler połączony jest za pośrednictwem RS485 z falownikiem. Zadanie jest proste - wysłać dane, odebrać odpowiedź od falownika. Bazą do komunikacji jest biblioteka MK_USART, obsługująca pojedynczy port UART (w moim przypadku UART0). O ile nie ma żadnych problemów z nadawaniem danych, problem pojawia się z ich odbiorem. Co do idei, zgodnie ze standardem MODBUS, koniec ramki definiowany jest ciszą na magistrali trwającą około 3.5 znaku, co przy prędkości 9600 wynosi około 4ms. Koncepcja jest zatem prosta - utworzyć timer, który będzie się resetował a następnie uruchamiał wraz z każdym odebranych bajtem z portu szeregowego. Przysłanie danych ciągiem będzie resetowało ten timer. Przysłanie ostatniego bajtu uruchomi go, a z powodu braku resetu - wywoła przerwanie. Tak więc przysłanie danych ciągiem powinno wywołać przerwanie tylko raz, na samym końcu. Przynajmniej tyle teorii...

Praktyka niestety daje inne rezultaty. Falownik wysyła do uC ramkę MODBUSa, składającą się z 8 bajtów. Są one wysyłane ciągiem. Niestety mój mikrokontroler, z jakiegoś powodu dwa razy rzuca przerwanie od timera, dzieląc odebrane dane na dwa kawałki 1 i 7 bajtów. Najważniejsze fragmenty kodu wyglądają następująco:

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


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


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


Próbowałem chyba już wszystkiego - zmian timera, zmian trybu z CTC na tryb licznika, redukcji kodu do absolutnego minimum, pełnej diagnostyki z wysyłaniem dosłownie wszystkiego przez UART1 do komputera. Skutek zawsze zostawał taki sam - zamiast przerwania i 8 bajtów, dwa przerwania i ramka z podziałem 1-7 bajtów. Dla jasności, po "sklejeniu" tych dwóch kawałków ze sobą, odebrane dane z falownika są w pełni poprawne.

Pojawia się moje pytanie, czy ktoś ma pomysł co może być nie tak w tym przypadku? Podejrzewam, że to może być jakiś idiotyczny błąd, którego nie zauważam przez ilość godzin spędzonych nad problemem. Będę wdzięczny za jakiekolwiek sugestie.

Pozdrawiam!

Autor:  JarekK [ 9 gru 2021, o 08:45 ]
Tytuł:  Re: ATMega128 + MODBUS RTU

Na bazie biblioteki UART Mirka coś tam jeden kolega na forum skomunikował w modbus:
post231110.html#p231110
Nie patrzyłem na kod, ale może warto sprawdzić: "Powyższe wszystkie obliczenia dotyczą ramki UART 8-N-1 (tzn. długość 10 bitów)"

a na atmega 128 też takie coś znalazłem w necie:
http://sebastianpawlak.com/pl/Informaty ... index.html

Autor:  fofex [ 9 gru 2021, o 11:18 ]
Tytuł:  Re: ATMega128 + MODBUS RTU

Z tych fragmentów kodu niewiele można wywnioskować.
Ja bym szedł w trochę innym kierunku. Zbocze opadające na lini RX startuje timer i się wyłącza (albo i nie, sprawa do przemyślenia). Przerwanie RX resetuje timer tj. ustawia licznik na wartość początkową. Przerwanie OVF albo COMP "zamyka ramkę" i się wyłącza oraz włącza przerwanie INT włączające timer (albo i nie, jw.). "Zamknięcie" ramki zrobił bym przez wstawienie do bufora Rx jakiegoś kodu (zakładając, że są jakieś kody nie pojawiające się w strumieniu Rx). Trzeba przemyśleć czy w warunkach brzegowych to zagra (np. przy jednoczesnym pojawieniu się przerwania INT i OVF). To tak na szybkiego.
Albo może na odwrót - przerwanie UART Rx startuje timer a zbocze opadające go resetuje?

Autor:  slawus1998 [ 9 gru 2021, o 12:01 ]
Tytuł:  Re: ATMega128 + MODBUS RTU

JarekK napisał(a):
a na atmega 128 też takie coś znalazłem w necie:
http://sebastianpawlak.com/pl/Informaty ... index.html


Obejrzałem dokładnie to co znajduje się pod tym linkiem. Zastanowiło mnie to, że autor tego kodu chcąc zresetować wartość timera czyli ustawić TCNT = 0, robi coś takiego:
Składnia: [ Pobierz ] [ Ukryj ]
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.


Czyli mniej więcej: ubija timer przez odcięcie go od clocka, resetuje wartość TCNT, resetuje flagę przerwania TOV3, ponownie ustawia OCR i finalnie ponownie ustawia preskaler, tym samym uruchamiając timer. W całym kodzie nie ma praktycznie wykorzystanego bitu OCIE3A, za wyjątkiem jednokrotnego ustawienia go na wartość '1' w celu uruchomienia timera. Muszę sprawdzić takie rozwiązanie, dam znać czy coś to pomogło.

fofex napisał(a):
Z tych fragmentów kodu niewiele można wywnioskować.
Ja bym szedł w trochę innym kierunku. Zbocze opadające na lini RX startuje timer i się wyłącza (albo i nie, sprawa do przemyślenia). Przerwanie RX resetuje timer tj. ustawia licznik na wartość początkową. Przerwanie OVF albo COMP "zamyka ramkę" i się wyłącza oraz włącza przerwanie INT włączające timer (albo i nie, jw.). "Zamknięcie" ramki zrobił bym przez wstawienie do bufora Rx jakiegoś kodu (zakładając, że są jakieś kody nie pojawiające się w strumieniu Rx). Trzeba przemyśleć czy w warunkach brzegowych to zagra (np. przy jednoczesnym pojawieniu się przerwania INT i OVF). To tak na szybkiego.
Albo może na odwrót - przerwanie UART Rx startuje timer a zbocze opadające go resetuje?


Pomysł ze zboczem opadającym jako trigger resetu timera też jest ciekawy. Teoretycznie wielkiej różnicy to nie zrobi, bo albo zareagujemy na zbocze opadające, albo kilka "bitów dalej" na przerwanie od RXa. Ale racjonalne pomysły przestały już działać, może to coś pomoże. Dam znać jak sprawdzę.

Z jedną i drugą odpowiedź już teraz dziękuję.

Autor:  fofex [ 9 gru 2021, o 13:21 ]
Tytuł:  Re: ATMega128 + MODBUS RTU

slawus1998 napisał(a):
resetuje flagę przerwania TOV3

Przypuszczam, że przypisanie do licznika wartości zero może generować przerwanie więc gaszenie flagi jest jak najbardziej uzasadnione, natomiast manewry z preskalerem wydają mi się "nadmiarowe". Ale może się mylę.
Twoje rozwiązanie może też by zgrało gdybyś gasił flagę przerwania przed wystartowaniem timera (albo po) . Może spróbuj tego na początek?

Autor:  JanuszT [ 9 gru 2021, o 13:38 ]
Tytuł:  Re: ATMega128 + MODBUS RTU

Witam

Może spróbuj skorzystać z tego, że w MODBUS-ie wiesz jaka długa będzie ramka i że na końcu jest suma kontrolna. Jeżeli twój uC to Master, to na magistrali jest cisza, potem ty "coś" wysyłasz i potem falownik odpowiada. Wiedząc dokładnie co wysłałeś, wiesz dokładnie ile bajtów oczekujesz w odpowiedzi. Po zakończeniu wysłania zapytania odczytujesz w przerwaniu bajty, założoną ich ilość łączysz w całość i sprawdzasz czy mają strukturę ramki (adres:funkcja:ilość:dane:CRC) i czy zgadza się suma kontrolna. Jeżeli jest dobrze to koniec tej transmisji, jeżeli nie, to odczekujesz maksymalny czas trwania transmisji i ponawiasz zapytanie. Nie jest to może optymalne czasowo, ale nie podejrzewam abyś wymieniał dużo/szybko dane z falownikiem, a tak może być prościej.

Pozdrawiam
Janusz

Autor:  slawus1998 [ 9 gru 2021, o 21:30 ]
Tytuł:  Re: ATMega128 + MODBUS RTU

JanuszT napisał(a):
Może spróbuj skorzystać z tego, że w MODBUS-ie wiesz jaka długa będzie ramka i że na końcu jest suma kontrolna. Jeżeli twój uC to Master, to na magistrali jest cisza, potem ty "coś" wysyłasz i potem falownik odpowiada. Wiedząc dokładnie co wysłałeś, wiesz dokładnie ile bajtów oczekujesz w odpowiedzi. Po zakończeniu wysłania zapytania odczytujesz w przerwaniu bajty, założoną ich ilość łączysz w całość i sprawdzasz czy mają strukturę ramki (adres:funkcja:ilość:dane:CRC) i czy zgadza się suma kontrolna. Jeżeli jest dobrze to koniec tej transmisji, jeżeli nie, to odczekujesz maksymalny czas trwania transmisji i ponawiasz zapytanie. Nie jest to może optymalne czasowo, ale nie podejrzewam abyś wymieniał dużo/szybko dane z falownikiem, a tak może być prościej.


Niestety nie mogę sobie na coś takiego pozwolić, bo na magistrali docelowo będą się znajdować trzy falowniki.

Tak czy tak, problem udało się rozwiązać. Rozwiązania nie do końca rozumiem, ale działa. Musiałem pozbyć się manewrowania bitem OCIE w rejestrze TIMSK, jako sposobem na włączenie/wyłączenie przerwania. Zastosowałem włączanie i odłączanie preskalera (TCCR0B = 0) w celu uruchamiania i zatrzymywania timera.

Otwieram teraz dyskusję, dlaczego to rozwiązanie działa. W tej chwili operuję na całym timerze, wcześniej jedynie na aktywacji/deaktywacji przerwania. Dodatkowo wcześniej przed każdym ustawienie OCIE na 1, ustawiałem TCNT na 0, tak żeby mieć pewność, że timer zawsze zaczyna liczenie od początku, a nie gdzieś w środku. Skąd w takim razie może wynikać różnica w działaniu tych dwóch rozwiązań?

JarekK, przede wszystkim Tobie dziękuję za podesłane linki z bibliotekami, których mi się nie udało w sieci znaleźć, a w których podpatrzyłem rozwiązanie finalnie sprawne. Dziękuję jednocześnie za rady i sugestie od wszystkich pozostałych.

Strona 1 z 1 Strefa czasowa: UTC + 1
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/