Wszystkie moje urządzenia mikrokontrolera komunikujące się z komputerem PC za pomocą UART używają ciągów ASCII do wysyłania poleceń i odbierania danych (jak zaimplementowano w Arduino). Tego się nauczyłem, kiedy zacząłem kopać w elektronice i zawsze uważałem, że wysyłanie nagich strun jest wystarczające. Zauważyłem jednak, że większość urządzeń, na które natknąłem się, używa wyrafinowanych protokołów binarnych, które obejmują kody funkcji, adresy i sprawdzanie błędów CRC.
Kiedy podstawowa komunikacja ASCII jest dopuszczalna i kiedy powinienem rozważyć coś bardziej zaawansowanego, na przykład Modbus? Czy urządzenia komercyjne używają takiego ASCII? Przemysłowy?
microcontroller
serial
communication
uart
protocol
Nie mam pojęcia co robię
źródło
źródło
Odpowiedzi:
ASCII i CRC nie wykluczają się wzajemnie. ASCII jest kodowaniem, a CRC służy do sprawdzania błędów.
WSZYSTKO można wysłać jako ASCII. My, starsi, z pewnością pamiętamy UUEncoding, który zamienia wszystko w ciąg ASCII.
A) Dla mnie zazwyczaj chodzi o szybkość i wydajność. Wysłanie dużej liczby 32-bitowej przez ASCII może zająć dużo czasu, ale przesłanie jej w postaci binarnej za pomocą protokołu szeregowego zajmuje tylko 4 bajty.
B) Wysłanie NUMERÓW przez ASCII oznacza, że musisz przekonwertować numer na ASCII, co jest wyraźnym dodatkowym krokiem (jest to część tego, co robi „printf”).
Jeśli w jakiś sposób stracisz swoje miejsce, spieprzysz, stracisz format, dostaniesz niewłaściwy endian itp., Binarny protokół komunikacyjny z pewnością może spieprzyć. Jeśli wysyłasz ASCII, łatwiej jest odzyskać dane po pomyłkach, po prostu wchodząc i WYSZUKAJ w strumieniu danych.
źródło
Oto kilka przemyśleń na ten temat:
źródło
Na najprostszym poziomie można powiedzieć, że prosty protokół komunikacyjny ma trzy warstwy: fizyczny, transportowy i aplikacyjny. (Istnieją modele z większą liczbą, takie jak OSI z 7 lub TCP / IP z 4. Liczba warstw nie jest szczególnie ważna w kontekście tego pytania.)
Warstwa aplikacji jest warstwą, z którą masz do czynienia bezpośrednio w kodzie, i koncentruje się na pytaniu. Jeśli chodzi o warstwę transportową, bajt przekazany do niej w send_data jest tylko wzorem binarnym, ale możesz zinterpretować ją w kodzie aplikacji jako literę „A”. Obliczenia CRC lub sumy kontrolnej będą takie same, niezależnie od tego, czy uważasz bajt za „A”, 0x41 lub 0b01000001.
Warstwa transportowa to poziom pakietu, w którym znajdują się nagłówki wiadomości i sprawdzanie błędów, niezależnie od tego, czy jest to CRC, czy podstawowa suma kontrolna. W kontekście oprogramowania układowego możesz mieć funkcję taką jak send_data, w której przekazujesz bajt do wysłania. Wewnątrz tej funkcji jest umieszczany w pakiecie, który mówi: „Hej, to jest normalny komunikat, wymaga potwierdzenia, a suma kontrolna to 0x47, bieżący czas to X”. Ten pakiet jest wysyłany przez warstwę fizyczną do węzła odbiorczego.
Warstwa fizyczna to miejsce, w którym definiuje się elektronikę i interfejs: złącza, poziomy napięcia, taktowanie itp. Warstwa ta może wahać się od kilku śladów sygnałów TTL dla podstawowego UART na płytce drukowanej do całkowicie izolowanej pary różnicowej, jak w niektórych Implementacje CAN .
W węźle odbiorczym pakiet wchodzi w warstwę fizyczną, jest rozpakowywany w warstwie transportowej, a następnie twój wzór binarny jest dostępny dla warstwy aplikacji. Od warstwy aplikacji węzła odbiorczego zależy, czy ten wzorzec powinien być interpretowany jako „A”, 0x41 lub 0b01000001 i co z nim zrobić.
Podsumowując, wysyłanie znaków ASCII jest prawie zawsze dopuszczalne, jeśli wymaga tego aplikacja. Ważne jest zrozumienie schematu komunikacji i włączenie mechanizmu sprawdzania błędów.
źródło
Jeszcze nie wspomniano, że niezależnie od tego, czy używa się ASCII, czy protokołu binarnego, wysłanie znaku wymazywania przed każdym pakietem zapewni, że nawet jeśli szum linii lub błędy ramkowania pojawią się przed rozpoczęciem pakietu, wszystkie znaki po pocieszeniu wyjście zostanie poprawnie wykadrowane przy braku dodatkowego hałasu. W przeciwnym razie, jeśli ktoś wysyła pakiety w sposób ciągły i nie zawiera żadnych znaków, które gwarantują resynchronizację, możliwe jest, że jedna usterka może uszkodzić wszystko, co nastąpi, do następnej przerwy w transmisji. Znak 0xFF jest fajny, ponieważ gwarantuje, że każdy odbiorca będzie mógł ponownie zsynchronizować następujący znak.
(*) 0xFF - nazywany wymazywaniem, ponieważ ktoś, kto wpisuje błędny znak podczas wpisywania danych na taśmie papierowej, może nacisnąć przycisk „przesuń taśmę do tyłu” i nacisnąć przycisk wymazywania, aby zastąpić błędnie wybity znak 0xFF, co spowoduje być ignorowanym przez większość odbiorców).
źródło
Jedną z zalet wysyłania ciągów ASCII jest to, że kody sterujące mogą być następnie użyte do zasygnalizowania początku / końca komunikatu. np. STX (char 2) i ETX (char 3) mogą sygnalizować rozpoczęcie i zakończenie transmisji. Możesz też dodać prosty wiersz do oznaczenia końca transmisji.
Podczas przesyłania danych binarnych staje się to bardziej skomplikowane, ponieważ nie można zarezerwować żadnego konkretnego wzorca bitów dla kodu sterującego (bez dodatkowego obciążenia lub złożoności), ponieważ prawidłowy bajt danych może mieć ten sam wzorzec.
źródło
ASCII jest w porządku, używam go we wszystkich projektach. To sprawia, że debugowanie jest znacznie łatwiejsze do monitorowania portu, i stałoby się problemem, gdyby było dużo danych do wysłania.
Kolejna korzyść: używam szeregowych urządzeń radiowych, aby odbierać wiadomości między arduinami, i mogę używać szeregowego monitora podłączonego do mojego laptopa i wysyłać wiadomości, aby pewne rzeczy się zdarzyły. Idealne do testowania.
Ponadto wysyłanie rzeczy jako pliku binarnego nie jest niemożliwe do debugowania, a w zależności od narzędzi można pobrać plik binarny i przekonwertować go na coś czytelnego dla człowieka. Lub jeśli wiesz, czego szukasz, możesz wizualnie sprawdzić strumień danych i rozpoznać wartości tam, gdzie powinny one być, i znaleźć sposób winy, choć nie tak łatwo. tzn. rozpoznasz wzorce bajtów i rozpoznasz oczekiwane wartości
źródło
Zamiast Modbus rozważ HDLC . Dostajesz wykrywanie błędów (co jest ważne w przypadku głośnych linii szeregowych). Synchronizacja jest solidna, ucieczka jest solidna.
Użyłem HDLC w sieciach RS-485 bez żadnych problemów, a PPP również z niego korzysta.
źródło
ASCII przez UART jest najbardziej popularny częściowo dlatego, że:
Podczas debugowania jest czytelny dla ludzi (jeszcze nie widziałem analizatora logiki, który nie dekoduje ASCII).
Jest bardzo łatwy do wdrożenia, masz tabelę ASCII za pośrednictwem szybkiego google, który jest dobrze znormalizowany.
Ma wbudowaną synchronizację z bitami start / stop.
Prawie cały świat hobbystów skonfigurował ASCII nad szeregowo, więc wszelkie nowe metody będą musiały sobie z tym poradzić, a to wcale nie jest łatwe.
Następnie pojawia się sytuacja, gdy zaczynasz wysyłać określone kodowanie, takie jak wysyłanie reprezentacji liczby zmiennoprzecinkowej w pamięci w porównaniu z konwersją liczby zmiennoprzecinkowej na ASCII, wysyłanie tej wartości przez port szeregowy, który może być znacznie dłuższy niż 4 bajty, a następnie konwersja do reprezentacji w pamięci na hoście. Zamiast tego po prostu wysyłasz reprezentację 4-bajtową za każdym razem. Oczywiście możesz samodzielnie rozpocząć kodowanie, ale musisz skonfigurować tagi początkowe / końcowe, kolejność itp.
Zamiast tego można użyć rzeczy takich jak Protobuf . Zostało to wykorzystane w projekcie, nad którym pracowałem, i było niezwykle korzystne, robi komunikaty o zmiennej długości, obsługuje dla ciebie endian i kilka innych fajnych funkcji. Nie ma też tak dużego rozmiaru kodu i można określić wszystko, co ma być statycznie przydzielane podczas uruchamiania. Jeśli będziesz tego potrzebował, będziesz musiał samodzielnie wprowadzić sumę kontrolną.
źródło