Skocz do zawartości

[C] STM32, USART - Odbiór i wysyłanie ramek na przerwaniach


Treker

Pomocna odpowiedź

Witajcie, moją przygodę z STM32 zacząłem stosunkowo niedawno, praktycznie od samego początku staram się stworzyć "protokół" do bezawaryjnej komunikacji po USARcie, który wykorzystany będzie do transmisji danych z małego robota typu LineFollower.

Nie mam większej wiedzy, ani doświadczenia z tej dziedziny. Stworzyłem trochę kodu na podstawie własnych "przeczuć" liczyłbym na ocenę moich działań przez doświadczonych kolegów. Całość po zaimplementowaniu działa bardzo dobrze, dlatego opiszę jedynie algorytm.

Głównie zajmijmy się odbieraniem.

Robot wysyła ramkę w postaci:

{S typ_ramki dlugosc_danych A Dane1...DaneN K CRC8 O 0x0D}

Część pogrubiona to moje znaczniki początku i końca ramki, które zawsze są identyczne. Wszystko wysyłane jest liczbowo (binarnie), a nie w formie "stringów". Jako drugi bajt przesyłany jest typ ramki - w celu późniejszej interpretacji wyników oraz długość danych, ponieważ ramka nie jest stałego rozmiaru. Na końcu przesyłana jest jeszcze suma kontrolna CRC8 wyliczona ze wszystkich danych.

Odbieranie zrealizowane mam w przerwaniu, zrezygnowałem z DMA z racji różnej długości ramek. Algorytm prezentuje się następująco.

1. Jeśli zgłoszono przerwanie odbioru

2. Odczytujemy najnowszy bajt

3. Jeśli w buforze mamy >= 3 bajty, teraz odebrano A, a 3 bajty wcześniej S

[*]3.1 Ustawiamy flagę "wykrytoPoczątekRamki"

[*]3.2 Przenosimy wykryty "początek ramki" na początek bufora Rx, a to co było przed/po zerujemy - dzięki temu nie powinniśmy zgubić ramki, która z powodu błędu zaczęłaby być odbierana pod koniec bufora Rx

[*]3.3 Kolejne odebrane bajty doklejamy do bufora

4. Jeśli ustawiona flaga "wykrytoPoczątekRamki" 4.1. Sprawdzamy czy w buforze minimum 7 znaków (długość ramki bez danych)

4.2. Sprawdzamy czy ostatnio odebrany bajt to 0x0D, jeśli tak to możliwe, że mamy koniec ramki i szukamy przed nim znaków O oraz K.

    4.2.2. Jeśli wykryto koniec ramki, to zerujemy resztę buforu, zerujemy flagę "wykrytoPoczątekRamki". Ustawiamy flagę "odebranoRamkę" 4.3. Jeśli nie wykryto końca ramki, to zwiększamy indeks do tablicy, która jest buforem, aby następny znak wstawić w kolejne pole.

    Opis może trochę zawiły, ale wszystko działa 🙂 Kodu nie chce jeszcze pokazywać, więc jak coś będzie niejasne, postaram się to opisać dokładniej.

    Cały program w pętli głównej sobie "biega", aż globalna flaga debranoRamkę zostanie ustawiona, wtedy pobieramy z bufora ramkę i liczymy z niej CRC8, jeśli się zgadza to mamy ramkę, jeśli nie - to ją odrzucamy.

    Prosiłbym o ocenę tego rozwiązania, później opisze moje inne pomysły.

    Głównie w całym projekcie (robionym hobbystycznie) zależy mi bardziej na prędkości (ideałem byłaby ramką z 15 bitami danych co 2ms) niż na tym, aby odebrać wszystkie ramki bezbłędnie. Chociaż jak wiadomo byłoby to wskazane.

    Z góry dziękuję za pomoc.

Link do komentarza
Share on other sites

Przerwaniem zawalasz procesor w (być może) istotnym dla niego momencie.

Moja metoda jest prosta i opiera się tylko i wyłącznie o DMA.

1. Ustawiasz DMA w trybie cyklicznym z jakimś odpowiednio dużym buforem (np. kilkakrotność największej ramki)

2. W momencie jak procesor nie ma nic do roboty skaczesz do obsługi uarta i tam:

a) sprawdzasz czy są nowe bajty w ilości min. jednej ramki

b) jak są to czytasz jeden bajt i sprawdzasz czy jest poczatkiem ramki (moje ramki zazwyczaj maja 3 lub 4 bajty poczatkowe o okreslonych wartosciach). Jak nie pasuje do poczatku ramki to przeskakujesz o jeden bajt dalej.

c) jak odczytales 4 pierwsze bajty i jest to poczatek ramki to mozesz odczytac cala ramke (zakladajac, ze jest tyle bajtow w pamieci)

d) po odczytaniu calej ramki przesuwasz miejsce ostatnio zczytanego bajtu o wielkosc odczytanej ramki, potem sprawdzasz crc ramki itd.

Zalety tej metody:

a) praktycznie zerowe obciazenie procesora (analiza odbywa sie gdy nic waznego sie nie dzieje)

b) brak wywolywania przerwan co mogloby zaklocic prace innych czesci kodu (z zaleznosciami czasowymi)

c) o ile ktos nie wypelni nam bufora DMA to nie tracimy zadnej ramki, nawet jak ktoś wyśle kilka ramek jedna za drugą, a bufor DMA zawsze można w razie czego zwiększyć...

Metoda może nie jest prosta, ale z pewnością nie trudniejsza od zaprezentowanej przez Ciebie, a zyski znaczne.

Link do komentarza
Share on other sites

DMA odrzuciłem praktycznie od razu, bo w każdym poradniku, gdzie pojawiała się informacja, że ramki są różnej długości odradzano DMA.

Zastanawiam się teraz czy potrzebny jest zaznaczony koniec ramki. Skoro odbieramy początek i długość to automatycznie możemy sobie odliczyć odpowiednią ilość bajtów i sprawdzić tylko CRC.

Link do komentarza
Share on other sites

Koniec ramki to kompletnie zbędny bajt.

Nie wiem dlaczego odradzali DMA. DMA ustawione na konkretną długość ramki ma średni sens, bo po przyjęciu ramki musisz natychmiast wywołać przerwanie i odpalić na nowo DMA, albo następną szybko przychodzącą ramkę przegapisz... a skoro musisz wywołać przerwanie to nie możesz prowadzić transmisji w tle jak coś ważnego (zależności czasowe) dzieje się na procesorze...

Ja wykorzystuje opisany wyżej algorytm i działa bez problemu.

PS. Chyba Twoje problemy zmobilizują mnie do napisania artykułu, bo to nie może być na tyle skomplikowane, żeby nikt na to nie wpadł...

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Czyli co do samej ramki, usunę bajty końca, ale dodam dla pewności jeszcze jeden bajt na początek. To powinno zwiększyć bezpieczeństwo.

W takim razie czekam na Twój artykuł 🙂

Link do komentarza
Share on other sites

PS. Chyba Twoje problemy zmobilizują mnie do napisania artykułu, bo to nie może być na tyle skomplikowane, żeby nikt na to nie wpadł...

Walczyłem z DMA i UART'em ale się poddałem. Z niecierpliwością czekam na artykuł 😅

Link do komentarza
Share on other sites

Zazwyczaj typ ramki można powiązać z określoną długością ramki w bajtach, więc nie ma sensu tego dublować. Jednak to jest tylko jeden bajt mniej, więc nie ma to większego znaczenia o ile nie wysyłasz tysięcy ramek w krótkim czasie...

Ogólnie ramka jak najbardziej rozsądna.

Link do komentarza
Share on other sites

Przewiduję około 6 typów ramek, kilka z nich będzie tej samej długości, więc typ jest dla mnie przydatny.

Co do samego początku, lepiej dobrać jakieś losowe dane czy coś w stylu 0x00, 0xFF?

Link do komentarza
Share on other sites

1. Miałem na myśli, że typ ramki jest ważny (bo zazwyczaj przy danym typie ramki masz ściśle określoną wielkość ramki) a nie, że wielkość w bajtach jest ważna... czyli znając typ ramek nie potrzebujesz dodatkowego bajtu na wielkość ramki.

2. Jako identyfikator ramki używam losowe dane, które mają małą szansę na pojawienie się w ramce i najlepiej takie, które mają dosyć sporo bitów różnych... dlatego 0x00 to bardzo słaby pomysł, bo zazwyczaj często występuje wśród danych i praktycznie od razu prawdopodobieństwo spotkania przypadkiem takiej ramki w środku błędnie przesyłanych bajtów jest dużo większe...

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.