Elektroda.pl
Elektroda.pl
X
AM TechnologiesAM Technologies
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Organizacja komunikacji pomiędzy PC a trzema AVR'ami.

21 Maj 2017 17:44 1458 23
  • Poziom 13  
    Cześć.
    Pracuję nad takim małym systemem komunikacji pomiędzy kilkoma urządzeniami pomiarowymi a PC. Pomiędzy urządzeniami a PC jest coś w rodzaju hub'a, co sam zaprojektowałem i wykonałem - ogólny schemat jest taki: Organizacja komunikacji pomiędzy PC a trzema AVR'ami.
    Wspomniane przeze mnie urządzenia pomiarowe nie są pierwszej nowości, więc każde z nich wyposażyłem w uP (XMega128A3U), który odpytuje je z mierzonych parametrów parametrów (wykorzystałem obsługę UART z przerwaniami). Wszystko spiąłem razem magistralą RS485 celem podpięcia do komputera - przez jakąkolwiek przejściówkę do USB.
    Każde z tych urządzeń (obecnie są 3) jest trochę inne to i programy do nich są inne. Ta część działa jednak bez zarzutu - każde urządzenie jest odpytywane przez uP (zgodnie z algorytmem dla każdego z urządzeń) z odpowiednich danych i te dane są przysyłane do uP. Nic się nie tnie, transmisja jest ciągła nawet po wielu godzinach. Także z samą częścią czysto elektryczną na PCB jak i napisaniem softu do odpytywania urządzeń nie miałem kłopotu.
    Kłopot pojawił się jak zacząłem pisać oprogramowanie na PC do wizualizacji i interpretacji odebranych danych. Mój zamysł był prosty - wysłać komendę do uP z żądaniem wysłania danych i w ramach odpowiedzi - do PC leci paczka z danymi z urządzenia. W tym celu zastosowałem ramkę protokołu MODBUS.
    Na czym polega kłopot? Mianowicie: transmisja danych pomiędzy PC a uP z niewiadomych dla mnie przyczyn co jakiś czas się zacina. NIE WIERZĘ, że nie da się tego zrobić w sposób wysoko niezawodny (szczególnie, że problem nie jest skomplikowany) i jestem przekonany, że ja robię gdzieś błąd. Tym bardziej, że wymiana danych pomiędzy uP, a urządzeniami działa wysoko niezawodnie, co wydawało mi się trudniejsze. Oto jak zaplanowałem algorytm do komunikacji pomiędzy PC a uP:
    1. (PC) Wyślij ramkę z rozkazem z do odpowiedniego uP.
    2. (uP) Zeruj rejestr CNT timera za każdym odebranym bajtem.
    3. (uP) Po odebraniu ostatniego bajtu - czyli po przerwaniu od przepełnienia timer'a, który ustawiłem na 50ms - analizuj odebraną ramkę.
    4. (uP) Jeśli ramka jest poprawna to postaw flagę zezwalającą na wysyłkę danych do PC.
    5. (PC) Zinterpretuj dane z uP.
    Kody są obszerne więc przedstawiam fragmenty odpowiedzialne za odbiór danych z PC i interpretację odebranej ramki.

    Fragment 1. - odbiór bajtu z PC:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Fragment 2. - obsługa przerwania od przepełnienia:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Fragment 3. - analiza odebranej ramki z PC, wykonywane w pętli głównej programu:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    No i dalej, jeśli zostanie postawiona flaga sendResponse = true; to w następnej instrukcji warunkowej mam kapsułkowanie odebranych danych i wysyłkę do PC.
    Pytam Was Kolegów, bo jesteście mądrzejsi i bardziej doświadczeni - czy widzicie gdzieś błąd w algorytmie? A może w ogóle inaczej byście zorganizowali komunikację?
    Proszę uprzejmie o wskazówki i pozdrawiam :)

    Edit1 - dodam jeszcze taką obserwację - kiedy odpytywałem z PC tylko jeden uP, a nie 3 po kolei, to transmisja zacinała się o wiele rzadziej

    Edit2 - dodam jeszcze ogólny szkielet programu:
    Kod: c
    Zaloguj się, aby zobaczyć kod
  • AM TechnologiesAM Technologies
  • Pomocny post
    Poziom 23  
    Problemów może być wiele, ja miałem następujący : gdy przełączałem MAX485 na nadawanie, był odłączany odbiornik w MAX485 (trzeci stan). Nogi /RE, DE były połączone razem. Niespodzianką dla mnie było, że dla pin'u RxD, po włączeniu UART'a ustawienia o pullUp'ie były ignorowane. Trzeci stan podłączony do wejścia RxD bez pullUp powodował, że co jakiś czas zbierane były śmieci, które uniemożliwiały zadziałanie timeOut'u i cała transmisja leżała. Problem rozwiązałem wyłączając odbiornik w UART'e na czas nadawania na RS485 i włączałem go dopiero po przełączeniu MAX485 na odbiór.
  • Poziom 13  
    O widzisz, o tym nie wiedziałem. A jakiego uP używałeś?
  • Pomocny post
    Poziom 23  
    ATtiny841 + MAX485
  • Poziom 13  
    Zrobiłem tj napisałeś, czyli na czas nadawania wyłączyłem odbiorniki UARTa. Wydawało mi się na początku, że jest poprawa, ale po odebraniu ok. 4500 pakietów nagle nie odebrałem pięciu kolejnych. Jakie mogą być jeszcze przyczyny?
  • Pomocny post
    Poziom 23  
    Aby rozwiązać takie sporadyczne rzeczy, trzeba podpiąć analizator. Proponuję podróbę saleae, jest stosunkowo najtańszy na rynku a do takich amatorskich spraw nadaje się idealnie. Monitorować : RxD TxD, linię od kierunku transmisji i dwie linie od 485. Jeszcze jedno wszystkie układy są zasilane z tego samego źródła ? Ewentualnie czy mają tą samą masę lub izolację galwaniczną.
  • Poziom 13  
    Wszystkie procesory i max'y (max3485 konkretnie) są zasilane z jednego źródła, czyli stabilizatora liniowego LDO 3.3V. Stabilizator jest z kolei zasilany z przetwornicy TRACO TSR 1-2450. Przetwornica jest nieizolowana, ale płytkę zasilam z zasilacza 12V, który już jest izolowany. Masa tych układów jest wspólna, na płytce wylany jest też polygon masy. Na oscyloskopie zasilanie wygląda przyzwoicie, żadnych szpilek czy widocznych gołym okiem szumów nie zauważyłem.
  • Poziom 23  
    Przy problemie raz na 4500 pakietów bez analizatora i wyłuskania tej jednej transmisji, trudno cokolwiek powiedzieć.
  • AM TechnologiesAM Technologies
  • Poziom 13  
    Jasne, rozumiem. Póki nie mam analizatora to jeszcze poprzeglądam kod. Ale rzeczywiście, bez tego chyba się nie obejdzie.
  • Pomocny post
    Użytkownik usunął konto  
  • Poziom 13  
    Piotrus_999 napisał:

    Czy nie prościej interpretować odbieraną treść, a timer pozostawiając tylko do ew timeout-ów?

    A problemy Twoje to podejrzewam że cześć pakietu poszla jednym buforem a część drugim z długa przerwą pomiędzy nimi. No i pakiety sie rozjadą - bo nie analizujesz treści komunikatu tylko uważasz że czas warunkuje odebranie ramki.


    Mógłbyś nieco rozwinąć temat? Wyszła taka sieczka bo... zbieram doświadczenie. Zrobię to porządnie to głupich błędów nie powtórzę. No i tak, założyłem, że czas warunkuje odebranie ramki - ileś ms od ostatniego bajtu. Jak mam rozumieć, że pakiet poszedł dwoma buforami z długą przerwą pomiędzy nimi?
    Jeżeli jest inne, lepsze kryterium/podejście do tego zagadnienia - to @Piotrus_999 - dla mnie każda uwaga jest dosłownie bezcenna :)

    Co masz na myśli mówiąc, żebym analizował treść komunikatu? W jakiś sposób muszę ten proces zacząć i tu kryterium czasu wydało mi się najlepsze. Poza tym protokół MODBUS mówi o czasie większym od 3.5T pomiędzy ramkami. Ja wiem, że dałem o wiele za dużo i dlatego dziwię się, że czasami mam kichę, a nie transmisję.
  • Pomocny post
    Użytkownik usunął konto  
  • Poziom 13  
    Odnośnie ramki - zastosowałem format MODBUS'a RTU. Oczywiście nie napisałem obsługi całości, tylko dwa czy trzy kody funkcji. Ramka jest taka: adres uP | kod funkcji | dane | CRC|. I tego się trzymam. Moje pytanie tyczy się bardziej tego KIEDY zacząć analizować ramkę, bo chyba tu jest problem skoro stałe czasowe to lipa. O ile wiem to nie powinienem tego robić w przerwaniu, bo ono powinno być wykonywane jak najszybciej. Naturalne wydaje mi się to zrobić w pętli głównej programu. Co jednak powinienem wpisać do przerwania oprócz "USART_RXComplete(&USART_RS485_data);" ? Czy może analizować pierwszy i drugi bajt w przerwaniu?
    Ja zdaję sobie sprawę, że tutaj możliwości mam od groma i raczej błąd leży w sofcie. Nie mam jednak innego pomysłu na rozpoczęcie analizy ramki niż ta stała czasowa, która jak ustaliliśmy - jest pomysłem, w tym przypadku, niefortunnym.
    W przypadku gdy długość ramki jest sztywna - dla pewnych funkcji modbusa tak się składa, że liczba bajtów jest stała to wtedy po prostu zwiększam licznik odebranych bajtów w przerwaniu, zgadza się?
    Gdy jednak liczba bajtów jest wpisana do ramki to oczywistym jest, że muszę to odczytać. Jak wskazałem - nie wiem kiedy jest dobra pora. Bo w przerwaniu nie bardzo zdaje się i stała czasowa to też zły pomysł. Więc kiedy zacząć w tym przypadku?
  • Pomocny post
    Użytkownik usunął konto  
  • Poziom 13  
    Tak tak, problem jest na linii pc<->hub. Komunikacja hub<->urządzenia pomiarowe działa bez zastrzeżeń.

    Chodziło mi, że w moim przypadku stała czasowa to lipa, bo to nie system real time.

    I tak, dobrze rozumiesz - pc łączy się z hubem przez VCOMa. Z tym, że odpytywanie urządzeń przez uP trwa cały czas, żeby skrócić czas oczekiwania na zestaw danych z urządzenia. Także po odebraniu rozkazu co najwyżej czekam na zakończenie wysyłki z urządzenia. Ta część programu działa w porządku.
  • Użytkownik usunął konto  
  • Poziom 13  
    Czyli w tej procedurze mam zawrzeć liczenie crc, analizę danych itd? Nie będzie to za długo trwało jak na przerwanie?

    Dodano po 1 [godziny] 4 [minuty]:

    Tak wygląda obecnie obsługa przerwania:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Jeśli mam analizować nadchodzące dane to czy jest sens zapisywać je do software'owego bufora odbiorczego? Chodzi o to, żebym analizował każdy nadchodzący bajt w tej procedurze?
  • Pomocny post
    Poziom 12  
    Piotrus_999 napisał:
    Ale jednej rzeczy nie należy robić łącząc komputer bardzo nie real-time z czymś innym - to zakładać jakieś stałe czasowe.

    Popieram. Poleganie na zależnościach czasowych gdy komunikujesz się z PC nie jest najlepszym pomysłem. Do przesyłania danych o zmiennej długości ja zwykle stosuję "consistent overhead byte stuffing". Na Wikipedii jest gotowy kod w C...
  • Poziom 13  
    Znam tę technikę. Piękna sprawa, okazała się strzałem w dziesiątkę gdy robiłem przy komunikacji za pomocą nrf24l01+. Ale nie pomyślałem o tym teraz akurat. Co sądzisz o tym, żeby połączyć tę technikę z ramką MODBUS? Wyglądałoby to zdaje się tak:
    1) zbudowanie ramki MODBUS.
    2) potraktowanie ramki MODBUS algorytmem COBS.
    3) wysyłka ramki potraktowanej COBSem.
    4) Gdy uP dostanie bajt zerowy to znaczy, że odebrał całą ramkę.
    5) Dekodowanie ramki COBS.
    6) Analiza ramki MODBUS.
  • Pomocny post
    Poziom 12  
    Nie ma znaczenia jakiego protokołu użyjesz powyżej. Traktuj to jako kolejną warstwę stosu komunikacyjnego, która służy do dzielenia strumienia danych na poszczególne ramki.
  • Poziom 13  
    Dodałem sobie funkcję do obsługi COBS i chcę sprawdzić na najprostszym układzie działanie transmisji między PC a trzema Xmega128A3U.
    Chcę to sprawdzić tak:
    1. Z PC przychodzi zakodowana COBSem ramka
    2. uC odczytuje i analizuje, czy ramka jest zaadresowana do niego.
    3. Jeśli tak to wysyła odkodowaną ramkę na terminal do PC.
    Kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Identyczny program jest na trzech uC z różnicą tylko w instrukcji if po odkodowaniu ramki (tam gdzie zakomentowane). Wysyłane są trzy różne ramki:
    1 0 0 1, 2 0 0 1, 3 0 0 1. Oczywiście najpierw pakuje je COBSem.

    Odbiór całej ramki za pomocą do...while bo czekamy na marker końca w postaci zera.

    Jednak to się nie zachowuje jak powinno. Oto jak wygląda problem:
    1. Pierwsza transmisja z PC, wysyłam np. 2 1 1 2 1 0 - wszystko ok, na terminal wraca 1 0 0 1.
    2. Druga transmisja z PC, wysyłam inną ramkę, np. 2 3 1 2 1 0 - brak odpowiedzi z uC.
    3. Trzecia transmisja z PC, wysyłam ponownie 2 3 1 2 1 0 - odpowiedź prawidłowa.
    I tak za każdym razem. Po pierwszej prawidłowej transmisji muszę wysłać ramkę dwa razy żeby dostać odpowiedź. Gdzieś zgrzeszyłem w kodzie, ale za diabła nie mam pojęcia gdzie. Macie może Koledzy jakieś pomysły co w tak prostym programie poszło nie tak?

    Dodano po 3 [godziny] 34 [minuty]:

    Już wiem, niedawno mnie coś olśniło - przecież ja jestem na linii RS485 i wszystkie procesory nasłuchują również swoich własnych odpowiedzi. Skoro wysyłałem w odpowiedzi zwrotnej coś bez zera na końcu to programy czekały w pętlach while na 0 i dlatego musiałem wysłać komunikat raz jeszcze.
    Wyszło znowu moje robienie szybciej od myślenia...
    Wniosek: COBS należy umieścić po obu stronach transmisji, czyli to co wysyłam z uC też zakodować COBSem, żeby pozostałe procesory mogły skończyć odczyt wiadomości - celem ich zignorowania oczywiście, ale wyjdą z pętli while jak należy.
  • Pomocny post
    Poziom 12  
    Jakiś timeout też by się przydał. Tak na wypadek błędów podczas transmisji.
  • Poziom 13  
    Oczywiście :) Testowałem najprostszą konfigurację, żeby wyeliminować wpływ innych błędów, które mogłem popełnić.
    Tutaj timeout zrobić w taki sposób, że włączam timer przed pętlą nadawczą/odbiorczą i wyłączam za pętlą?
  • Użytkownik usunął konto