Come funzionano le comunicazioni seriali su Arduino?


16

Con riferimento ad Arduino Uno, Mega2560, Leonardo e schede simili:

  • Come funzionano le comunicazioni seriali?
  • Quanto è veloce la seriale?
  • Come si collega un mittente e un destinatario?

Nota: questa è una domanda di riferimento.


Potresti trovare questo interessante sui buffer su entrambi i lati di un Nano collegato a un sistema Raspian che esegue un registratore di dati Python, usando solo un normale cavo di programmazione USB tra i due: arduino.stackexchange.com/questions/11710/…
SDsolar

Risposte:


16

Le comunicazioni seriali asincrone (generalmente definite seriali) vengono utilizzate per inviare byte da un dispositivo a un altro. Un dispositivo potrebbe essere uno o più dei seguenti:

  • Arduino
  • PC
  • GPS
  • Lettore di schede RFID
  • display LCD
  • Modem
  • Altro

Frequenza di clock e campionamento dei dati

A differenza delle comunicazioni seriali SPI / USB / I2C non ha un segnale di clock. L'orologio di campionamento è una frequenza di campionamento concordata (nota come baud rate). Sia il mittente che il destinatario devono essere configurati per utilizzare la stessa velocità o il destinatario riceverà dati privi di significato (a causa dei bit che non vengono campionati alla stessa velocità con cui sono stati inviati).

La trasmissione è asincrona, il che significa sostanzialmente che i byte possono essere inviati in qualsiasi momento, con divari variabili tra loro. Questo grafico illustra un singolo byte inviato:

Comunicazioni seriali - invio di un byte

Il grafico sopra mostra la lettera 'F' che viene trasmessa. In ASCII questo è 0x46 (in esadecimale) o 0b01000110 (in binario). Il minimo significativa (ordine inferiore) bit è trasmesso per primo, quindi nel grafico di cui sopra si vede i bit arrivano nell'ordine: 01100010.

Il tempo "inattivo" tra i byte viene trasmesso come bit "1" continui (in effetti, la linea di trasmissione viene mantenuta alta in modo continuo).

Per indicare l'inizio di un byte, il Bit di avvio viene sempre indicato tirando la linea in basso come mostrato nel grafico. Una volta che il ricevitore vede il bit di avvio, attende 1,5 volte il tempo di campionamento, quindi campiona i bit di dati. Aspetta 1,5 volte in modo che:

  • Salta il bit iniziale
  • Campioni a metà del prossimo bit

Se la velocità di trasmissione è di 9600 baud, ad esempio, la frequenza di campionamento sarà di 1/9600 = 0.00010416secondi (104,16 µs).

Pertanto, a 9600 baud, dopo aver ricevuto un bit di avvio, il ricevitore attende 156,25 µs, quindi campiona ogni 104,16 µs.

Inizia il bit timing

Lo scopo del bit di stop è garantire che ci sia sicuramente un 1 bit tra ogni byte. Senza il bit di stop, se un byte termina in uno zero, sarebbe impossibile per l'hardware distinguere tra quello e il bit iniziale del byte successivo.

Per produrre l'output sopra riportato su Uno è possibile scrivere questo codice:

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

void loop ()
  {
  }

Numero di bit di dati

Per risparmiare tempo di trasmissione (ai vecchi tempi, eh) ti è stato permesso di specificare diversi numeri di bit di dati. L'hardware AtMega supporta la numerazione dei bit di dati da 5 a 9. Chiaramente meno bit di dati meno informazioni è possibile inviare, ma più veloce sarà.


Bit di parità

Opzionalmente puoi avere un bit di parità. Questo viene calcolato, se richiesto, contando il numero di 1 nel carattere e quindi assicurandosi che questo numero sia dispari o anche impostando il bit di parità su 0 o 1 come richiesto.

Ad esempio, per la lettera "F" (o 0x46 o 0b01000110) puoi vedere che ce ne sono 3 lì (in 01000110). Quindi abbiamo già dispari parità. Quindi, il bit di parità sarebbe il seguente:

  • Nessuna parità: omessa
  • Parità pari: un 1 (3 + 1 è pari)
  • Parità dispari: uno 0 (3 + 0 è dispari)

Il bit di parità, se presente, appare dopo l'ultimo bit di dati ma prima del bit di stop.

Se il destinatario non ottiene il bit di parità corretto, viene chiamato "errore di parità". Indica che c'è qualche problema. Probabilmente il mittente e il destinatario sono configurati per utilizzare velocità di trasmissione diverse (bit), o c'era del rumore sulla linea che trasformava uno zero in uno o viceversa.

Alcuni sistemi precedenti utilizzavano anche la parità "mark" (in cui il bit di parità era sempre 1 indipendentemente dai dati) o la parità "space" (in cui il bit di parità era sempre 0 indipendentemente dai dati).


Trasmissione a 9 bit

Alcune apparecchiature di comunicazione utilizzano dati a 9 bit, quindi in questi casi il bit di parità viene trasformato nel 9 ° bit. Esistono tecniche speciali per l'invio di questo 9 ° bit (i registri sono registri a 8 bit, quindi il 9 ° bit deve essere inserito altrove).


Numero di bit di stop

Le prime apparecchiature tendevano a essere un po 'più lente elettronicamente, quindi per dare al ricevitore il tempo di elaborare il byte in entrata, a volte veniva specificato che il mittente avrebbe inviato due bit di stop. Questo in pratica aggiunge più tempo in cui la linea di dati viene mantenuta alta (un altro bit) prima che possa apparire il prossimo bit di inizio. Questo tempo di bit in più dà al ricevitore il tempo di elaborare l'ultimo byte in entrata.

Se il ricevitore non ottiene un 1 logico quando si suppone che sia il bit di stop, questo viene chiamato "errore di framing". Indica che c'è qualche problema. Molto probabilmente il mittente e il destinatario sono configurati per utilizzare diverse velocità di trasmissione (bit).


Notazione

Comunemente, la comunicazione seriale viene indicata indicando la velocità, il numero di bit di dati, il tipo di parità e il numero di bit di stop, in questo modo:

9600/8-N-1

Questo ci sta dicendo:

  • 9600 bit al secondo
  • 8 bit di dati
  • Nessuna parità (potresti vedere invece: E = pari, O = dispari)
  • 1 bit di stop

È importante che il mittente e il destinatario concordino quanto sopra, altrimenti è improbabile che la comunicazione abbia successo.


Pin-out

Arduino Uno ha pin digitali 0 e 1 disponibili per hardware seriale:

Pin seriale Arduino Uno

Per connettere due Arduinos devi scambiare Tx e Rx in questo modo:

Connettere due Arduinos insieme


Velocità

È supportata un'ampia gamma di velocità (vedere la figura seguente). Le velocità "standard" sono generalmente un multiplo di 300 baud (es. 300/600/1200/2400 ecc.).

Altre velocità "non standard" possono essere gestite impostando i registri appropriati. La classe HardwareSerial fa questo per te. per esempio.

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

Come regola empirica, supponendo che si stiano utilizzando dati a 8 bit, è possibile stimare il numero di byte che è possibile trasmettere al secondo dividendo il baud rate per 10 (a causa del bit di avvio e di arresto).

Pertanto, a 9600 baud è possibile trasmettere 960 byte ( 9600 / 10 = 960) al secondo.


Errori di baud rate

La velocità di trasmissione su Atmega viene generata dividendo l'orologio di sistema e quindi contando fino a un numero predefinito. Questa tabella dal foglio dati mostra i valori di registro e le percentuali di errore per un clock a 16 MHz (come quello sull'Arduino Uno).

Errori di baud rate

Il bit U2Xn influenza il divisore della frequenza di clock (0 = divide per 16, 1 = divide per 8). Il registro UBRRn contiene il numero fino al conteggio del processore.

Quindi dalla tabella sopra, vediamo che otteniamo 9600 baud da un clock a 16 MHz come segue:

16000000 / 16 / 104 = 9615

Dividiamo per 104 e non 103 perché il contatore è zero-relativo. Quindi l'errore qui è 15 / 9600 = 0.0016che è vicino a quello che dice la tabella sopra (0,02%).

Noterai che alcuni baud rate hanno un errore maggiore rispetto ad altri.

Secondo il foglio dati, la percentuale massima di errore per 8 bit di dati è compresa tra 1,5% e 2,0% (vedere il foglio dati per maggiori dettagli).


Arduino Leonardo

Arduino Leonardo e Micro hanno un approccio diverso alle comunicazioni seriali, poiché si collegano direttamente tramite USB al computer host, non tramite la porta seriale.

Per questo motivo, devi attendere che Serial diventi "pronto" (poiché il software stabilisce una connessione USB), con un paio di linee extra, come questo:

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

void loop ()
  {
  }

Tuttavia, se si desidera comunicare effettivamente tramite i pin D0 e D1 (anziché tramite il cavo USB), è necessario utilizzare Serial1 anziché Serial. Fai così:

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

void loop ()
  {
  }

Livelli di tensione

Si noti che Arduino utilizza i livelli TTL per le comunicazioni seriali. Ciò significa che si aspetta:

  • Un bit "zero" è 0V
  • Un bit "one" è + 5V

Le apparecchiature seriali precedenti progettate per essere collegate alla porta seriale di un PC utilizzano probabilmente livelli di tensione RS232, vale a dire:

  • Un bit "zero" va da +3 a +15 volt
  • Un bit "one" va da -3 a -15 volt

Non solo questo è "invertito" rispetto ai livelli TTL (un "uno" è più negativo di uno "zero"), l'Arduino non può gestire tensioni negative sui suoi pin di ingresso (né quelli positivi superiori a 5 V).

Pertanto è necessario un circuito di interfaccia per comunicare con tali dispositivi. Solo per l'ingresso (nell'Arduino), un semplice transistor, un diodo e un paio di resistori lo faranno:

Inversione del buffer

Per la comunicazione bidirezionale è necessario essere in grado di generare tensioni negative, quindi è necessario un circuito più complesso. Ad esempio, il chip MAX232 lo farà, in combinazione con quattro condensatori da 1 µF, fungendo da circuiti della pompa di carica.


Seriale del software

Esiste una libreria chiamata SoftwareSerial che consente di effettuare comunicazioni seriali (fino a un certo punto) nel software anziché nell'hardware. Ciò ha il vantaggio di poter utilizzare diverse configurazioni dei pin per le comunicazioni seriali. Lo svantaggio è che fare seriale nel software richiede più processore e tende a errori. Vedere Seriale software per maggiori dettagli.


Mega2560

Arduino "Mega" ha 3 porte seriali hardware aggiuntive. Sono contrassegnati sulla scheda come Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3. Dovrebbero essere usati preferibilmente al SoftwareSerial, se possibile. Per aprire quelle altre porte usate i nomi Serial1, Serial2, Serial3, in questo modo:

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

interrupt

Sia l'invio che la ricezione, utilizzando la libreria HardwareSerial, utilizzano gli interrupt.

invio

Quando si esegue un Serial.print, i dati che si sta tentando di stampare vengono collocati in un buffer "di trasmissione" interno. Se si dispone di 1024 byte o più di RAM (come su Uno) si ottiene un buffer a 64 byte, altrimenti si ottiene un buffer a 16 byte. Se il buffer ha spazio, quindi Serial.printrestituisce immediatamente, quindi non ritardando il codice. Se non c'è spazio, si "blocca" in attesa che il buffer si svuoti abbastanza da consentire lo spazio.

Quindi, quando ogni byte viene trasmesso dall'hardware, viene chiamato un interrupt ("USART, Data Register Empty") e la routine di interruzione invia il byte successivo dal buffer dalla porta seriale.

ricevente

Quando vengono ricevuti i dati in entrata, viene chiamata una routine di interrupt (l'interruzione "USART Rx Complete") e il byte in entrata viene inserito in un buffer "di ricezione" (delle stesse dimensioni del buffer di trasmissione menzionato sopra).

Quando chiami Serial.available, scopri quanti byte sono disponibili in quel buffer di "ricezione". Quando si chiama Serial.readun byte viene rimosso dal buffer di ricezione e restituito al codice.

Su Arduinos con 1000 byte o più di RAM, non c'è fretta di rimuovere i dati dal buffer di ricezione, a condizione che non si riempia. Se si riempie, tutti gli altri dati in arrivo vengono eliminati.

Si noti che a causa delle dimensioni di questo buffer non ha senso attendere l'arrivo di un numero molto elevato di byte, ad esempio:

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

Questo non funzionerà mai perché il buffer non può contenere così tanto.


Suggerimenti

  • Prima di leggere, assicurarsi sempre che i dati siano disponibili. Ad esempio, questo è sbagliato:

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

    Il Serial.availabletest garantisce solo la disponibilità di un byte, tuttavia il codice tenta di leggerne due. Può funzionare, se ci sono due byte nel buffer, in caso contrario verrà restituito -1 che apparirà come 'ÿ' se stampato.

  • Essere consapevoli di quanto tempo ci vuole per inviare i dati. Come accennato in precedenza, a 9600 baud si trasmettono solo 960 byte al secondo, quindi provare a inviare 1000 letture da una porta analogica, a 9600 baud, non avrà molto successo.


Riferimenti


Nel 1 ° grafico: con le frecce sembra che il bit di stop sia trasmesso per primo. Se hai scambiato Rx / Tx e la direzione delle frecce, penso che sia meno confuso.
ott--

Doveva essere letto da sinistra a destra (come è questa frase) e quindi le cose a sinistra accadono per prime. Per dirla in questo modo: su un oscilloscopio, ecco come vedresti la traccia.
Nick Gammon

Ok, con l'esplosione dell'oscilloscopio, lo compro. :-)
ott--

Tuttavia, ho pensato che il tuo punto abbia molto senso. Cosa pensano gli altri? Sarebbe più chiaro se le frecce fossero invertite e io scambiassi Rx / Tx?
Nick Gammon

1
@ linhartr22 L'ho modificato per leggere "dati insignificanti" che è probabilmente più vicino.
Nick Gammon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.