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

Komunikacja radiowa pomiędzy PC(C#) - 2xATmegi (język C).

golas17 13 Sty 2011 00:20 2101 13
  • #1 13 Sty 2011 00:20
    golas17
    Poziom 16  

    Witam,

    Realizuję komunikację master-slave pomiędzy PC(master) i dwoma mikrokontrolerami (ATmega8 i ATmega16). Program na PC piszę w C# i korzystam z odbioru danych poprzez 'SerialDataReceivedEventHandler'. W obsłudze zdarzenia korzystam z funkcji Read(bufor, offset, ilosc_danych) do odczytania odebranych danych. Zależy mi na tym, żeby przesyłać ramki o stałej wielkości np. 8 bajtów. Niestety bardzo wiele ramek dociera do komputera w "poszatkowanej" postaci np. 2bajty w jednej ramce, 6 bajtów w drugiej. Tylko około 75% dociera w postaci 8 bajtów.
    Program na atmegach wydaje się w porządku - wyświetlałem na LCD wysyłane do PC ramki i były w porządku.
    Czy może to być spowodowane niewłaściwym kwarcem (korzystam z kwarcu 8MHz dla którego błąd transmisji po rs-ie wynosi 0.2%)?
    Może jakieś inne powody?

    Proszę o sugestie i porady.

    0 13
  • #2 13 Sty 2011 08:07
    chudybyk
    Poziom 27  

    Witam!
    Zdaje się, że zdarzenie, z którego korzystasz, jest wywoływane przez pierwszy przychodzący bajt i zanim zabierzesz się do odczytywania danych do bufora "załapie" się jeszcze parę bajtów.
    Widzę dwa sposoby na rozwiązanie problemu:
    1. Zbudować swój bufor, który będzie kolekcjonował i składał ósemki bajtów, a następnie je udostępniał dla twojego programu.
    2. W procedurze obsługi zdarzenia sprawdzić, czy przychodzących danych jest co najmniej 8. (właściwość - SerialPort.BytesToRead), Jeśli jest za mało, to wyjść i poczekać na następne dane. Kłopot jest, jeśli część ramki się zgubi, a Ty będziesz czekał w nieskończoność na jej koniec. Przydałby się jakiś time-out dla ramki.
    Pozdrawiam!

    0
  • #3 13 Sty 2011 11:07
    golas17
    Poziom 16  

    Poszedłem za twoją radą i zrobiłem coś takiego:

    Inicjalizacja timera dla timeout-u dla ramki:

    Code:
            timeoutDlaRamki.AutoReset = false ;
    
            timeoutDlaRamki.Interval = 10; //jeżeli cała ramka nie dojdzie w ciągu 5ms to ignoruj.
            timeoutDlaRamki.Elapsed += new ElapsedEventHandler(TimeoutDlaRamki);


    Odbiór danych:

    Code:
                while (port_szeregowy.BytesToRead > 0)
    
                {
                    byte wart = (byte)port_szeregowy.ReadByte();

                    if (idx > 0)
                    {
                        data[idx] = wart;
                        idx++;
                    }

                    if ((idx == 0) && (wart == 0xff)) //0xff - sygnalizacja początku nowej ramki
                    {
                        timeoutDlaRamki.Start();
                        data[idx] = wart;
                        idx++;
                    }

                    if (idx == data.Length)
                    {
                        idx = 0;
                        odebrano = true;
                        timeoutDlaRamki.Stop();
                        break;
                    }
                }


    Obsługa timera:
    Code:

        private static void TimeoutDlaRamki(object source, ElapsedEventArgs e)
        {
            idx = 0;
            timeoutDlaRamki.Stop();
        }


    Niestety niewiele to pomogło. Nadal bardzo dożo paczek dociera błędnych.
    Dodam tylko, że póki co odbieram dane tylko z jednego mikrokontrolera, drugi odłączyłem. Pytanie wysyłam co 100ms (ramka pytania i odpowiedzi mają takie same rozmiary - 8 bajtów).

    0
  • #4 13 Sty 2011 12:12
    chudybyk
    Poziom 27  

    Jaka jest szybkość transmisji szeregowej?
    Zrób zworkę na pinie nr 2 i 3 z portu szeregowego - dostaniesz natychmiast to co wysyłasz. Czy wtedy ramki też będą poszatkowane?

    0
  • #5 13 Sty 2011 20:46
    golas17
    Poziom 16  

    Najpierw kilka wyjaśnień. Komunikację realizuję z wykorzystaniem modułów radiowych, z tym że jeden z modułów jest to moduł USB widziany od strony systemu operacyjnego jako wirtualny port COM.
    Moduły radiowe komunikują się ze sobą z prędkością 40kbps. Prędkość komunikacji z PC z modułem USB i uC z modułem radiowym jest ustawiona na sztywno przez producenta modułów i wynosi 57600bps.
    Poprawiłem obsługę odbioru danych. Na MSDN wyczytałem, że nie każdy odebrany bajt może powodować przejście do obsługi zdarzenia odbioru danych. Wstawiłem na początku obsługi małe opóźnienie - 3ms.
    Dało to bardzo dobre efekty. Ramki są odbierane w całości, natomiast pojawił się inny problem. Dla niecałych 10% ramek nadanych nie dostaję informacji zwrotnej

    Testowałem zwarcie pinów tx i rx na module od strony mikrokontrolera. Okazuje się, że na 10tys. nadanych co 10ms paczek tylko niecałe 300 nadanych informacji nie dostało odpowiedzi. Taka statystyka by mnie satysfakcjonowała.

    0
  • Pomocny post
    #6 14 Sty 2011 07:48
    chudybyk
    Poziom 27  

    Zastanawiam się, czy problemem nie są zbyt małe odstępy między ramkami. dla 40kbps jeden bajt transmitowany jest ok. 1/4 ms, więc teoretycznie cała ramka leci w 2 ms.
    Pewnie odliczasz time-outy od momentu zapisu do bufora, więc zanim ramka zostanie wypchnięta, więc między ramkami pozostaje ci najwyżej 8 ms.
    Teraz dochodzą opóźnienia na obsłudze USB/COM i COM/radio, potem drugie radio/COM i odpowiedź mikrokontrolera. Bity na pewno nie są wysyłane od razu, każde z urządzeń musi zbuforować cały bajt, żeby go przetransmitować.
    Czy musisz tak mocno poganiać transmisję? Może lepiej byłoby wysyłać większe ramki, np. po 32 bajty, ale z większymi interwałami pomiędzy nimi - np. 50 ms.

    0
  • #7 14 Sty 2011 13:00
    golas17
    Poziom 16  

    Do podobnych wniosków doszedłem. Udało mi się w miarę bezbłędnie przeprowadzić komunikację dla parametrów 8bajtów ramka, co 50ms wysyłanie kolejnej ramki.
    Dla informacji dodam, że korzystałem z modułów radiowych MOBOT-RCR (firma WObit). Stały rozmiar ramki ma w ich przypadku duże znaczenie, bowiem moduły działają tak, że mają na stałe zapisany rozmiar ramki w pamięci i wysłanie wiadomości odbywa się w dwóch sytuacjach:
    - kiedy w buforze nadawczym znajdzie się ilość bajtów odpowiadająca ustawionemu rozmiarowi
    - kiedy minie 50ms od pojawienia się pierwszego bajtu w buforze nadawczym.

    Osiągnięte parametry wystarczą mi w aktualnie realizowanym projekcie (zaliczenie na studiach), natomiast w przyszłości będę pewnie korzystał z większego rozmiaru bufora, bo jest to zwyczajnie korzystniejsze.

    Dziękuję za pomoc chudybyk :)

    0
  • #8 24 Sty 2011 20:38
    pietia86
    Poziom 25  

    Witam

    Postanowiłem podpiąć się do tematu z podobnym pytaniem.

    Potrzebuję zrealizować komunikację bezprzewodową pomiędzy komputerem PC(master) a ok. 12 slave'ami na odległość max 150m.

    Komunikacja miałaby wyglądać następująco:
    - Master szuka slave'ów i nadaje im numery - tylko raz przy każdym uruchomieniu systemu
    - Każdy slave w odstępie ok 1s wysyła do mastera kilka bajtów danych do momentu całkowitego wyłączenia systemu

    Zastanawiam się jakich modułów do tego użyć. Widziałem gdzieś w sieci moduły Wi-Fi z SPI firmy Tibbo ale nie wiedzieć czemu w ich opisach jest zawsze napisane że współpracują tylko z produktami tej firmy. Poza tym są one dość drogie.



    Czy moglibyście podsunąć jakiś pomysł?

    0
  • #10 25 Sty 2011 18:36
    pietia86
    Poziom 25  

    Zaproponowałem Wi-Fi bo nie wiedziałem jak rozwiązać komunikację z tak wieloma slave'ami jednocześnie. Ale...

    ...przecież można masterem odpytywać po kolei każdy z tych modułów które właśnie przedstawiłeś i też zadziała ( czy może się mylę... ? ) - zwłaszcza że nie jest potrzebna bardzo szybka wymiana danych, jak już wspomniałem co 1s kilka bajtów wystarczy. Kolejnym plusem tego rozwiązania jest cena.

    Myślałem też nad modułami ZigBee ale wydaje mi się że to też może być lekka przesada.

    Co o tym myślisz?

    0
  • #11 26 Sty 2011 08:51
    chudybyk
    Poziom 27  

    Przy konfiguracji jeden master-wiele slave'ów sama komunikacja nie jest problemem, przecież będziesz po kolei odpytywał slave'y i i przy sprawnym połączeniu nie powinno dojść do kolizji. Musisz tylko zadbać o protokół komunikacji, który nie spowoduje przypadkowego "odezwania się" nieaktywnego slave'a.

    Kłopot będzie z identyfikacją slave'ów. Najprościej jest nadać im unikalne adresy na stałe - przez zworki lub programując eeprom. Rozpoczęcie pracy polega wtedy tylko na odpytaniu całej przestrzeni adresowej i rozpoznaniu aktywnych adresów.
    Gorzej jeśli slave'y mają być identyczne na etapie produkcji, wtedy musisz opracować sposób nadawania adresów, który będzie uwzględniał możliwość kolizji przy tej operacji. Jednym ze sposobów jest wygenerowanie ramki, na którą muszą odpowiedzieć wszystkie slave'y bez przydzielonego adresu - z losowo wygenerowanym opóźnieniem. Adres dostanie pierwszy, który się zgłosi z odpowiedzią, reszta nasłuchuje, czy inny nie zdążył przed nimi - rezygnuje w takim przypadku, aż do następnej okazji. Master musi zdecydować czy ramka odpowiedzi jest prawidłowa (czy nie nałożyły się dwie na siebie) i odpowiedzieć nadaniem adresu. Procedurę się powtarza aż nie przyjdzie żadne zgłoszenie od wolnych slave'ów, co oznacza, że wszystkie mają unikalne adresy. Wadą tego rozwiązania jest fakt, że przy każdym włączeniu slave może mieć inny adres.

    Co do ZigBee - nie pracowałem z takimi urządzeniami, z opisu wynika, że są projektowane do sieci typu mesh - rozproszonych. Nie znam ich zbytnio, więc nie będę się wypowiadał.

    0
  • #12 26 Sty 2011 19:02
    pietia86
    Poziom 25  

    chudybyk napisał:
    ...

    Kłopot będzie z identyfikacją slave'ów

    ...


    Ten problem mam zamiar rozwiązać w ten sposób że każdy ze slave'ów będzie oprócz danych w pakiecie wysyłał jakiś przypisany przeze mnie numer od 0 do 12

    Np.
    #04;xxxxxx# - gdzie x to dane
    #05;xxxxxx#

    Czy takie rozwiązanie miałoby sens?

    Pytam bo nie miałem do tej pory nigdy do czynienia z komunikacją bezprzewodową w systemach mikrokontrolerowych.

    0
  • #13 27 Sty 2011 07:59
    chudybyk
    Poziom 27  

    Slave'y będą miały różne unikalne numery od 0 do 12? Jeśli tak, to kłopot jest niewielki. :-) Przecież to master inicjuje każdy przesył danych. To master powinien "wywołać" slave'a, z którym chce gadać.

    Na przykład:
    Przyjmijmy, że dane są zawsze kodowane w kodzie ASCII, wtedy bajty o kodach od 0 do 31 można wykorzystać na kody sterujące transmisją, w tym adresy od 0-12.


    Master nadaje ramkę danych:
    16 - Niech 16 będzie początkiem ramki,
    00 - adres slave'a
    'A' - kod ASCII, przyjmijmy - wykonaj jakąś funkcję 1
    17 - koniec ramki

    wywołany slave odpowiada:
    18 - początek ramki slave'a (wiadomomo, że do mastera, więc nie trzeba adresu
    'O' - znak duże 'o'
    'K' - znak duże 'k'
    19 - koniec ramki slave'a

    Nieaktywne slave'y muszą odbierać wszystkie bajty i szukać dwóch bajtów - 16,adres danego slave'a, który jest unikalny dla całej transmisji i nie może wystąpić w żadnym innym kontekście, nawet w danych.

    Jeśli chcesz wysyłać dane w kodzie binarnym a nie w ASCII, wtedy możesz zastosować inne metody unikania kodów sterujących w paczkach danych. Na przykład przyjąć jeden bajt jako znak ESC, czyli zmieniający znaczenie następnego znaku. Wtedy każda para ESC,następny znak będzie interpretowana jako jeden znak.
    Na przykład:
    Przyjmijmy ESC jako #07.
    Przyjmijmy, że para ESC,X (gdzie X to bajt od 100-112) będzie oznaczał dane 0-12
    przyjmijmy, że para ESC,116 będzie oznaczać bajt 16
    wtedy ramka mastera może wyglądać tak:

    16 - kod początku ramki
    01 - wywołany slave nr 1
    40 - jakaś dana
    ESC - znak zmiany znaku
    100 - zamieniamy 100 na 0 (według umowy)
    ESC - kolejna zmiana znaku
    116 - zmianiamy 116 na daną 16
    17 - koniec ramki

    w takiej ramce master wysłał trzy bajty danych - 40, 0, 16, mimo, że znaków było więcej o dwa ESC. Za to żaden nieaktywny slave nie zinterpretuje danych jako kody.

    Dodano po 2 [minuty]:

    Przy stosowaniu kodu ESC należy jeszcze przyjąć zamianę ESC,jakiś kod na sam znak ESC - żeby można było przesłać daną o bajcie #07.

    1
  • #14 27 Sty 2011 17:37
    pietia86
    Poziom 25  

    Czyli jest tak jak przypuszczałem - można każdemu slave'owi dać jakiś unikalny adres na jego wywołanie oraz ewentualnie na identyfikację podczas transmisji i po kłopocie.

    Wielkie dzięki kolego chudybyk.

    Pozdrawiam :)

    0
  Szukaj w 5mln produktów