Jak działa komunikacja szeregowa w Arduino?

16

W odniesieniu do Arduino Uno, Mega2560, Leonardo i podobnych płyt:

  • Jak działa komunikacja szeregowa?
  • Jak szybki jest serial?
  • Jak połączyć się między nadawcą a odbiorcą?

Uwaga: jest to pytanie referencyjne.

Nick Gammon
źródło
To może być interesujące dla buforów po obu stronach Nano podłączonego do systemu Raspian z rejestratorem danych Python, używając zwykłego kabla USB do programowania między nimi: arduino.stackexchange.com/questions/11710/…
SDsolar

Odpowiedzi:

16

Asynchroniczna komunikacja szeregowa (zwykle nazywana szeregową) służy do wysyłania bajtów z jednego urządzenia do drugiego. Urządzeniem może być jedno lub więcej z poniższych:

  • Arduino
  • PC
  • GPS
  • Czytnik kart RFID
  • wyświetlacz LCD
  • Modem
  • Inny

Częstotliwość zegara i próbkowanie danych

W przeciwieństwie do komunikacji szeregowej SPI / USB / I2C nie ma sygnału zegarowego. Zegar próbkowania to uzgodniona częstotliwość próbkowania (znana jako prędkość transmisji). Zarówno nadawca, jak i odbiorca muszą być skonfigurowani do korzystania z tej samej prędkości, inaczej odbiornik otrzyma bez znaczenia dane (z powodu bitów, które nie są próbkowane z tą samą szybkością, z jaką zostały wysłane).

Transmisja jest asynchroniczna, co w zasadzie oznacza, że ​​bajty mogą być wysyłane w dowolnym momencie, ze zmiennymi przerwami między nimi. Ta grafika ilustruje wysłanie jednego bajtu:

Komunikacja szeregowa - wysyłanie jednego bajtu

Powyższa grafika pokazuje przesyłaną literę „F”. W ASCII jest to 0x46 (szesnastkowo) lub 0b01000110 (binarnie). Najmniej znaczący (low-order) bit jest przesyłany pierwszy, a więc w powyższy graficzny widać bity przybywających w kolejności: 01100010.

Czas „bezczynności” między bajtami jest przesyłany jako ciągłe bity „1” (w efekcie linia transmisyjna jest utrzymywana na wysokim poziomie w sposób ciągły).

Aby wskazać początek bajtu, bit startowy jest zawsze wskazywany przez pociągnięcie linii w dół, jak pokazano na grafice. Gdy odbiornik zobaczy bit startowy, czeka 1,5 razy czas próbkowania, a następnie próbkuje bity danych. Czeka 1,5 razy, aby:

  • Pomija bit startowy
  • Próbki w połowie następnego kawałka

Jeśli na przykład szybkość transmisji wynosi 9600 bodów, wówczas częstotliwość próbkowania będzie wynosić 1/9600 = 0.00010416sekundy (104,16 µs).

Zatem przy 9600 bodów po odebraniu bitu początkowego odbiornik czeka na 156,25 µs, a następnie próbki co 104,16 µs.

Rozpocznij synchronizację bitów

Celem Stop Bit jest upewnienie się, że zdecydowanie jest jeden bit między każdym bajtem. Bez bitu stop, jeśli bajt zakończyłby się na zero, sprzęt nie byłby w stanie stwierdzić różnicy między tym a bitem początkowym następnego bajtu.

Aby wygenerować powyższe dane wyjściowe na urządzeniu Uno, możesz napisać ten kod:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

Liczba bitów danych

Aby zaoszczędzić czas transmisji (w dawnych czasach, heh) pozwolono ci określić inną liczbę bitów danych. Sprzęt AtMega obsługuje numerację bitów danych od 5 do 9. Oczywiście im mniej bitów danych, tym mniej informacji można wysłać, ale tym szybciej to będzie.


Bity parzystości

Opcjonalnie możesz mieć bit parzystości. Jest to obliczane, jeśli jest to wymagane, przez zliczenie liczby 1 w znaku, a następnie upewnienie się, że liczba ta jest nieparzysta lub nawet przez ustawienie bitu parzystości na 0 lub 1 zgodnie z wymaganiami.

Na przykład dla litery „F” (lub 0x46 lub 0b01000110) widać, że są tam 3 (w 01000110). Zatem mamy już nieparzystą parzystość. Zatem bit parzystości byłby następujący:

  • Brak parzystości: pominięty
  • Parzystość parzysta: 1 (3 + 1 jest parzyste)
  • Dziwna parzystość: a 0 (3 + 0 jest nieparzyste)

Bit parzystości, jeśli jest obecny, pojawia się po ostatnim bicie danych, ale przed bitem stopu.

Jeśli odbiornik nie otrzyma poprawnego bitu parzystości, jest to nazywane „błędem parzystości”. Wskazuje, że jest jakiś problem. Być może nadawca i odbiornik są skonfigurowani do korzystania z różnych prędkości transmisji (bitów) lub na linii wystąpił szum, który zmienił zero na jeden lub odwrotnie.

Niektóre wczesne systemy stosowały również parzystość „znacznik” (gdzie bit parzystości zawsze wynosił 1 niezależnie od danych) lub parzystość „przestrzeń” (gdzie bit parzystości zawsze wynosił 0, niezależnie od danych).


9-bitowa transmisja

Niektóre urządzenia komunikacyjne wykorzystują dane 9-bitowe, więc w takich przypadkach bit parzystości jest zamieniany na 9-bit. Istnieją specjalne techniki wysyłania tego 9-go bitu (rejestry to 8-bitowe rejestry, więc 9-ty bit należy umieścić gdzie indziej).


Liczba bitów stopu

Wczesne wyposażenie działało elektronicznie nieco wolniej, więc aby dać odbiorcy czas na przetworzenie przychodzącego bajtu, czasami określano, że nadawca wyśle ​​dwa bity stopu. Zasadniczo wydłuża to czas, w którym linia danych jest utrzymywana wysoko (jeszcze jeden bit), zanim pojawi się następny bit startowy. Ten dodatkowy czas bitowy daje odbiornikowi czas na przetworzenie ostatniego przychodzącego bajtu.

Jeśli odbiornik nie otrzyma logicznego 1, gdy ma być bit stopu, jest to nazywane „błędem ramkowania”. Wskazuje, że jest jakiś problem. Całkiem możliwe, że nadawca i odbiorca są skonfigurowani do używania różnych prędkości transmisji (bitów).


Notacja

Zazwyczaj komunikacja szeregowa jest wskazywana przez podanie prędkości, liczby bitów danych, rodzaju parzystości i liczby bitów stopu, jak poniżej:

9600/8-N-1

To mówi nam:

  • 9600 bitów na sekundę
  • 8 bitów danych
  • Brak parzystości (zamiast tego możesz zobaczyć: E = parzysty, O = nieparzysty)
  • 1 bit stopu

Ważne jest, aby nadawca i odbiorca zgodzili się na powyższe postanowienia, w przeciwnym razie komunikacja prawdopodobnie się nie powiedzie.


Pin-out

Arduino Uno ma cyfrowe styki 0 i 1 dostępne dla szeregowego sprzętu:

Piny szeregowe Arduino Uno

Aby połączyć dwa Arduinos, zamień Tx i Rx w następujący sposób:

Łączenie dwóch Arduino razem


Prędkość

Obsługiwany jest szeroki zakres prędkości (patrz rysunek poniżej). „Standardowe” prędkości są zwykle wielokrotnością 300 bodów (np. 300/600/1200/2400 itp.).

Inne „niestandardowe” prędkości mogą być obsługiwane przez ustawienie odpowiednich rejestrów. Klasa HardwareSerial robi to za Ciebie. na przykład.

Serial.begin (115200);  // set speed to 115200 baud

Jako ogólną zasadę, zakładając, że używasz danych 8-bitowych, możesz oszacować liczbę bajtów, którą możesz przesłać na sekundę, dzieląc szybkość transmisji przez 10 (z powodu bitu początkowego i bitu zatrzymania).

Zatem przy prędkości 9600 bodów można przesyłać 960 bajtów ( 9600 / 10 = 960) na sekundę.


Błędy prędkości transmisji

Szybkość transmisji w Atmega jest generowana przez podzielenie zegara systemowego, a następnie zliczanie do z góry ustalonej liczby. Ta tabela z arkusza danych pokazuje wartości rejestru i wartości procentowe błędów dla zegara 16 MHz (takiego jak ten na Arduino Uno).

Błędy prędkości transmisji

Bit U2Xn wpływa na dzielnik częstotliwości taktowania (0 = dzielenie przez 16, 1 = dzielenie przez 8). Rejestr UBRRn zawiera liczbę, do której zlicza procesor.

Tak więc z powyższej tabeli widzimy, że otrzymujemy 9600 bodów z zegara 16 MHz w następujący sposób:

16000000 / 16 / 104 = 9615

Dzielimy przez 104, a nie 103, ponieważ licznik jest względem zera. Zatem błąd 15 / 9600 = 0.0016jest bliski temu, co mówi powyższa tabela (0,02%).

Zauważysz, że niektóre prędkości transmisji mają wyższy błąd niż inne.

Zgodnie z arkuszem danych maksymalny procent błędu dla 8 bitów danych mieści się w zakresie od 1,5% do 2,0% (więcej informacji znajduje się w arkuszu danych).


Arduino Leonardo

Arduino Leonardo i Micro mają inne podejście do komunikacji szeregowej, ponieważ łączą się bezpośrednio przez USB z komputerem hosta, a nie przez port szeregowy.

Z tego powodu musisz poczekać, aż Serial stanie się „gotowy” (ponieważ oprogramowanie ustanawia połączenie USB), z dodatkowymi kilkoma liniami, jak poniżej:

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

Jeśli jednak chcesz komunikować się za pomocą pinów D0 i D1 (zamiast kabla USB), musisz użyć Serial1 zamiast Serial. Robisz to tak:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

Poziomy napięcia

Zauważ, że Arduino używa poziomów TTL do komunikacji szeregowej. Oznacza to, że oczekuje:

  • Bit „zero” wynosi 0 V.
  • „Jeden” bit to + 5 V.

Starsze urządzenia szeregowe zaprojektowane do podłączenia do portu szeregowego komputera prawdopodobnie wykorzystują poziomy napięcia RS232, a mianowicie:

  • Bit „zero” wynosi od +3 do +15 woltów
  • Bit „jeden” wynosi od –3 do –15 woltów

Jest to nie tylko „odwrócone” w odniesieniu do poziomów TTL („jeden” jest bardziej ujemny niż „zero”), ale Arduino nie jest w stanie wytrzymać ujemnych napięć na swoich pinach wejściowych (ani dodatnich powyżej 5 V).

Dlatego potrzebujesz obwodu interfejsu do komunikacji z takimi urządzeniami. Tylko dla wejścia (do Arduino) wystarczy prosty tranzystor, dioda i kilka oporników:

Bufor odwracający

W przypadku komunikacji dwukierunkowej musisz być w stanie generować ujemne napięcia, więc wymagany jest bardziej złożony obwód. Na przykład układ MAX232 zrobi to w połączeniu z czterema kondensatorami 1 µF, które będą działać jako obwody pompy ładującej.


Oprogramowanie szeregowe

Istnieje biblioteka o nazwie SoftwareSerial, która umożliwia szeregową komunikację (do pewnego stopnia) w oprogramowaniu, a nie w sprzęcie. Ma to tę zaletę, że można używać różnych konfiguracji pinów do komunikacji szeregowej. Wadą jest to, że wykonywanie szeregowe w oprogramowaniu wymaga więcej procesora i jest bardziej podatne na błędy. Aby uzyskać więcej informacji, patrz Serial oprogramowania .


Mega2560

Arduino „Mega” ma 3 dodatkowe sprzętowe porty szeregowe. Są one oznaczone na planszy jako Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3. Jeśli to możliwe, należy ich używać zamiast SoftwareSerial. Aby otworzyć te inne porty, użyj nazw Serial1, Serial2, Serial3, takich jak:

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

Przerwania

Zarówno wysyłanie, jak i odbieranie, przy użyciu biblioteki HardwareSerial, wykorzystuje przerwania.

Wysyłanie

Gdy to zrobisz Serial.print, dane, które próbujesz wydrukować, są umieszczane w wewnętrznym buforze „przesyłania”. Jeśli masz 1024 bajty lub więcej pamięci RAM (na przykład Uno), otrzymujesz bufor 64-bajtowy, w przeciwnym razie otrzymujesz bufor 16-bajtowy. Jeśli w buforze jest miejsce, to Serial.printnatychmiast wraca, nie opóźniając twojego kodu. Jeśli nie ma miejsca, „blokuje” ono oczekiwanie na wystarczające opróżnienie bufora, aby było miejsce.

Następnie, ponieważ każdy bajt jest przesyłany przez sprzęt, wywoływane jest przerwanie (przerwanie „USART, Data Register Empty”), a procedura przerwania wysyła następny bajt z bufora z portu szeregowego.

Odbieranie

Gdy odbierane są dane przychodzące, wywoływana jest procedura przerwania (przerwanie „USART Rx Complete”), a przychodzący bajt jest umieszczany w buforze „odbierającym” (o takim samym rozmiarze jak wspomniany powyżej bufor nadawczy).

Kiedy zadzwonisz Serial.available, dowiesz się, ile bajtów jest dostępnych w tym buforze „odbierającym”. Po wywołaniu Serial.readbajt jest usuwany z bufora odbiorczego i zwracany do kodu.

W Arduinos z 1000 bajtami lub więcej pamięci RAM nie ma pośpiechu, aby usunąć dane z bufora odbiorczego, pod warunkiem, że nie pozwolisz się zapełnić. Jeśli się zapełni, kolejne dane przychodzące są odrzucane.

Zauważ, że ze względu na rozmiar tego bufora nie ma sensu czekać na bardzo dużą liczbę bajtów, na przykład:

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

To nigdy nie zadziała, ponieważ bufor nie może pomieścić tak dużo.


Porady

  • Przed czytaniem zawsze upewnij się, że dane są dostępne. Na przykład jest to źle:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    Serial.availableTestu gwarantuje tylko masz jeden bajt dostępny, jednak kod próbuje odczytać dwa. Może działać, jeśli w buforze są dwa bajty, jeśli nie, otrzymasz -1, które po wydrukowaniu będą wyglądały jak „ÿ”.

  • Pamiętaj, ile czasu zajmuje przesłanie danych. Jak wspomniano powyżej, przy prędkości 9600 bodów przesyłasz tylko 960 bajtów na sekundę, więc próba wysłania 1000 odczytów z portu analogowego przy prędkości 9600 bodów nie zakończy się sukcesem.


Bibliografia

Nick Gammon
źródło
Na 1. grafice: ze strzałkami wygląda to tak, jakby bit stopu był przesyłany jako pierwszy. Jeśli wymieniłeś Rx / Tx i kierunek strzałek, uważam, że jest to mniej mylące.
ott--
Miał być czytany od lewej do prawej (jak to zdanie), a zatem rzeczy po lewej zdarzają się jako pierwsze. Ujmij to tak: na oscyloskopie tak zobaczyłbyś ślad.
Nick Gammon
Ok z eksplozją oscyloskopu to kupuję. :-)
ott--
Jednak myślałem, że twój punkt ma wiele sensu. Co myślą inni? Czy byłoby lepiej, gdyby strzałki zostały odwrócone, a ja wymieniłem Rx / Tx?
Nick Gammon
1
@ linhartr22 Zmieniłem go tak, by czytał „dane bez znaczenia”, które prawdopodobnie są bliżej.
Nick Gammon