Skocz do zawartości

Jak powinna wyglądać transmisja szeregowa (ramki danych)?


Mogway

Pomocna odpowiedź

Witam forumowiczów !
Jako iż po kilku dniach poszukiwania odpowiedzi na dręczące mnie pytania postanowiłem poszukać pomocy u osób bardziej obeznanych w temacie.

Po obejrzeniu kilkudziesięciu kursów , projektów ARDUINO itp. nadal mam problem z logiczny zrozumieniem problemu (może to juz wiek?)

Chcę przesłać transmisją szeregową kilka zmiennych  pomiędzy dwoma arduino  i wyświetlić te zmienne gdzieś tam(to mniej istotne).

Problemem jest dla mnie jak powinienem podejść do tematu przesyłania zmiennych - powiedzmy zmiennoprzecinkowych. Próbuje na różne sposoby ale nic mi nie wychodzi . Prawdopodobnie gdzieś robię jakiś błąd logiczny bo jedyne co udaje mi się odbierać przez Serial.read() to jakieś przypadkowe cyfry podając tylko jedna zmienną Serial.print(zmienna) .

Link do komentarza
Share on other sites

Pokaż kody nadajnika i odbiornika. Bez tego można tylko wróżyć, a szklana kula akurat w konserwacji...

Tak przy okazji - jeśli zwalasz coś na wiek to napisz ile masz lat, bo inaczej będę traktować dziarskiego emeryta, a inaczej zmęczonego życiem gimnazjalistę 😉

Link do komentarza
Share on other sites

(edytowany)

Próbowałem wielu kodów, sęk w tym że chciałem sie nauczyć robić to dobrze . Aktualnie próbuje opcje która najbardziej mi się spodobała czyli struktura danych. Znalazłem w sieci taki kod ale nadal odbieram smieci.
 

// Nadajnik

struct strukturaDanych {
  int zmiennaA;
  int zmiennaB;
} mojeDane;

void setup() {

  Serial.begin(9600);
 
  mojeDane.zmiennaA = 20;
  mojeDane.zmiennaB  = 80;

}

void loop() {


  Serial.write((uint8_t *)&mojeDane, sizeof(mojeDane));

  delay(1000);
}
// Odbiornik

struct strukturaDanych {
  int zmiennaA;
  int zmiennaB;
 } *mojeDane;

uint8_t  usartBuffer[sizeof(strukturaDanych)]   = {0};


void setup() {

  Serial.begin(9600);
  Serial.print(sizeof(strukturaDanych));
 
}

void loop() {
 
  if (Serial.readBytes(usartBuffer, sizeof(strukturaDanych))){
    
    mojeDane = (strukturaDanych*)usartBuffer;
    Serial.print(mojeDane->zmiennaA);
 
    }

}

 

 

Oczywiście nie upieram się przy tej metodzie ale wydaje się ona "elegancka"

Edytowano przez Mogway
Link do komentarza
Share on other sites

Sposób jest bardzo dobry... ale w jaki sposób zagwaranrujesz, że odbiornik zsynchronizuje się z nadajnikiem a nie rozpocznie w połowie transmisji?

Spróbuj w odbiorniku zresetować odczyt, jeśli w ciągu np. Pół sekundy nie odbierzesz kolejnego bajtu.

I jeszcze techniczna uwaga: zadbaj o czytelność umieszczanego kodu, bo fatalnie się to czyta. Edytor na forum pozwala na umieszczanie kodu w spoób czytelny, jeśli nie będziesz.z tego korzystać nikt Ci nie pomoże.

  • Lubię! 1
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

Chyba się poddaje w temacie- wszędzie piszą że to proste wręcz banalne a jednak nie udało mi się spowodować prawidłowego przesłania i odzwierciedlenia 2 liczb zmiennoprzecinkowych. Czy się da ?- tak ale znalazłem bibliotekę do arduino która zrobi to za mnie.

Dzieki za chęć pomocy  - chętnie by się dowiedział gdzie zrobiłem błąd

Link do komentarza
Share on other sites

@Mogway, witam na forum 😉 Widzę, że to Twoje pierwsze kroki na Forbocie, oto najważniejsze informacje na start:

  • Chcesz przywitać się z innymi członkami naszej społeczności? Skorzystaj z tematu powitania użytkowników.
  • Opis najciekawszych funkcji, które ułatwiają korzystanie z forum znajdziesz w temacie instrukcja korzystania z forum - co warto wiedzieć?
  • Poszczególne posty możesz oceniać (pozytywnie i negatywnie) za pomocą reakcji - ikona serca w prawym dolnym rogu każdej wiadomości.

43 minuty temu, Mogway napisał:

Czy się da ?- tak ale znalazłem bibliotekę do arduino która zrobi to za mnie.

Daj znać z ciekawości co to za biblioteka 🙂

Jeśli chodzi o Twój program, to możliwości jest wiele. Najprościej stworzyć ramki, które będą miały znak startu i stopu. Dzięki temu będziesz miał pewność, że odbiornik odebrał cały ciąg znaków i może go poprawnie zinterpretować. Przykładowo zamiast wysyłać dwie liczby w takiej postaci: "123,456" możesz wysyłać je w ramce: "@123,456#". Wtedy w odbieranych danych szukasz danych pasujących do wzorca, czyli zaczynających się od znaku małpy, a kończących się znakiem #. Wtedy będziesz miał pewność, że odebrano wszystkie dane. Jeśli wysyłasz samo "123,456" to podczas odebrania "123,45" nie będziesz wiedział czy to błąd czy faktycznie druga wartość to "45". Inaczej mówiąc wysyłasz paczkę danych i zakładasz, że odbiornik odebrał identyczne dane. Czasami pojawiają się błędy, które mogą się kumulować i cała komunikacja będzie skutkowała właśnie odczytywaniem jakiś "losowych wartości". Oczywiście opisuję to w olbrzymi skrócie, bo możliwości rozwiązania tego problemu jest bardzo dużo. Pamiętaj jednak, że zapewnienie dobrej, bezbłędnej komunikacji wcale nie jest bardzo prostym zadaniem.

Link do komentarza
Share on other sites

@Treker biblioteka to EasyTransfer i w symie dziala tak jak w moim przykładzie, tylko lepiej.

 

Co do moich problemów to problem leży gdzieś pomiędzy zrozumieniem jak działa hardware a jak do tego dorobić software. Z teorii jestem dobry wiem że można zrobić znak końca i początku ale jak to fizycznie zapisać to juz problem. Bez przykładowego programu jak to działa nie dam rady w przyzwoicie krótkim czasie poznać możliwości standardowych bibliotek.

Jak napisał ethanak - rozumiem to ze powinienem zastosować jakąś kontrole początku ,końca moich danych ale jak to zrobić fizycznie w moim programie jeszcze nie wiem. Ponieważ próbuje odczytać strukturę danych to zapewne powinienem ja zacząć odczytywać jak jakaś komenda będzie sprawdza jakiś ustalony znak na porcie i wtedy uruchomi odczyt struktury.

Jakieś dwadzieścia kilka lat temu coś pisałem w C - troche w C++  i tak mi się zachciało ...

Link do komentarza
Share on other sites

Hardware z którego korzystasz przesyłając informacje przez port szeregowy (tzw. UART) umie wysłać i odebrać bajt i nie ma dla niego znaczenia co to jest. Tak samo potraktuje każdą dowolną kombinację 8 bitów więc jeśli próbujesz wprost wysłać obszar pamięci RAM zawierający binarną reprezentację liczby zmiennoprzecinkowej to musisz się liczyć z faktem, że wysyłasz bajty o każdej możliwej wartości. W tej sytuacji nie masz szans wymyślić takiego bajtu który wstawiony np. na początku będzie unikalnym znakiem/znacznikiem początku transmisji. I to samo na końcu. Korzystając z takiego hardware musisz ograniczyć repertuar przesyłanych bajtów. To właśnie proponuje Treker. Przykładowo liczba zmiennoprzecinkowa w pamięci RAM może wyglądać tak:

0x73, 0x28, 0xC1 0x58

Zmyśliłem te wartości i być może powyższy ciąg nie ma sensu (w kontekście poprawnej liczby float), ale to tylko taki eksperyment myślowy. Nie możesz takiego ciągu poprzedzić/zakończyć jakimś arbitralnie wybranym bajtem, bo taką samą wartość za chwilę może przyjąć któryś z bajtów wnętrza liczby i odbiornik włączony w przypadkowym momencie zgłupieje. Możesz oczywiście wprowadzić nadmiarowość, np. wysyłać jako początek transmisji 4 bajty 0x55, 0x55, 0x55 i 0x55 lub jakąkolwiek inną kombinację 32 bitów i na taką sekwencję czekać w odbiorniku. To znacznie zmniejsza możliwość złej synchronizacji po stronie odbiorczej i takie rozwiązania też się stosuje. Wszystko jest kwestią szacunku ryzyka i oceny kosztów ewentualnego błędu.

Niemniej jednak dość powszechną metodą jest - jak już wspomniałem używanie protokołów znakowych.Ludzie już dawno umówili się na pewien zestaw obowiązujących liczb/kodów które reprezentują znaki czyli litery, cyfry itp.Powszechnie używanym standardem jest ASCII - skrót rozszyfruj sam:

https://ascii.cl/

Widzisz tutaj litery duże (kody od 0x41 do 0x5A), małe (0x61-0x7A), cyfry (0x30-0x39) itd. Ciekawym pomysłem jest zarezerwowanie pewnych kodów poniżej spacji (<0x20) na pewne informacje specjalne. Poczytaj o tych kodach bo tego właśnie potrzebujesz:

https://ascii.cl/control-characters.htm

Transmisja znakowa wymaga jednak, by w polu danych pojawiały się tylko znaki tekstu a więc litery, cyfry, separatory (przecinki, spacje) itp. Dlatego zanim zaczniesz coś nadawać, musisz swoje liczby przekonwertować na tekst. Zatem zamiast powyższego 4-bajtowego ciągu binarnego musisz mieć w pamięci ciąg znaków, np: "125.37", który w RAMie będzie wyglądał tak:

0x31, 0x32, 0x35, 0x2E, 0x33, 0x37

Do tego dopisujesz drugą liczbę np. po przecinku (sprawdź jaką dopisałem):

0x31, 0x32, 0x35, 0x2E, 0x33, 0x37, 0x2C, 0x33, 0x2E, 0x31, 0x34

i dopiero coś takiego uzupełniasz znakami specjalnymi:

0x01, 0x31, 0x32, 0x35, 0x2E, 0x33, 0x37, 0x2C, 0x33, 0x2E, 0x31, 0x34, 0x04

Teraz wystarczy, że odbiornik będzie czekał na bajt 0x01 i wiesz, że jest to na pewno początek transmisji a gdy  odbierze 0x04 to może zająć się analizą tego co odebrał.

Wszelkie manipulacje przygotowujące dane do nadania możesz robić za pomocą buforów i funkcji konwersji, np. standardowej itoa() lub formatowanych (s)print'ów.Transmisje znakowe mają też tę zaletę, że ich poprawność możesz obserwować na zwykłym terminalu (monitorze portu szeregowego) w komputerze. Wadą jest konieczność dokonywania konwersji po obu stronach.

 

Edytowano przez marek1707
  • Lubię! 1
Link do komentarza
Share on other sites

Gość es2

Sposobów na przesłanie danych binarnych jest wiele. Do wcześniej opisanych mogę dodać ASCII HEX - opis znajdziesz w opisie protokołu IntelHex. Prosty tekstowy, o którym pisał Treker, możesz uzyskać stosując sprintf i scanf (uwaga na zakres danych). Protokoły tekstowe mają poważna wadę, trzeba przesłać dużo nadmiarowych danych, co najmniej dwa razy więcej niż trzeba.

Jeśli chcesz zmniejszyć liczbę przesyłanych danych musisz użyć transmisji binarnej. Pierwszym prostym rozwiązaniem jest skorzystanie z timeoutu o czym już było napisane, inny sposób to znak synchronizacji. Nie to będzie np 0xFF. Co gdy taka wartość pojawi się w danych? Nadajesz go dwa razy. Na podobnej zasadzie mógł działać Z80SIO tyle, że na poziomie bitów a nie bajtów. Nie pamiętam nazwy tego protokołu ale nadal jest używany w transmisjach radiowych.

Poza wykrywaniem początku ramki, warto byłoby określić gdzie się kończy. Najczęściej  używa się pola długości. Czasem, gdy przesyłane sa komendy, jej rodzaj może określać długość ramki. Poza długością warto dodać CRC. Cała ramka wygląda wtedy tak:

  • SYNC - Znak/znaki synchronizacji
  • LEN - bajt(słowo) długość danych
  • DATA - dane o ilości określonej w polu LEN
  • CRC
  • Wybór CRC jest dużo, od prostych ADD, XOR, przez CRC8,16, 32.

Podsumowując:

  • dane przesyłane tekstem (para sprintf scanf), są czytelne dla człowieka ale mogą być problemy z konwersja dużych liczb, trzeba uzupełnić dane o znak początku/końca.Warto dodać CRC, może jak w GPS?
  • IntelHex i podobne (S-rekord) są doskonałe do przesyłania danych binarnych, kodowanie/dekodowanie jest bardzo szybkie (proste dodawanie/odejmowanie i warunek if), niestety dane nie są zbyt czytelne, ale wszystko ładnie widać w terminalu i są gotowe programy (np HexPlorer) do konwersji (podglądania) danych.
  • Dane przesyłane binarnie są krótkie, niestety mało czytelne nie da się ich podejrzeć w terminalu. Do ich podglądania najczęściej trzeba napisać własną aplikację.
Edytowano przez es2
Poprawiłem formatowanie list.
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.