UART.

Scopo e principio di funzionamento.

Tutte le interfacce di trasferimento dati, come puoi facilmente intuire dal nome, hanno un obiettivo: il trasferimento dei dati. Sensori, attuatori, indicatori, display, computer, smartphone e altri controller possono scambiare dati con il controller. E per scambio di dati non intendo solo comunicazione bidirezionale, ma anche comunicazione unidirezionale, quando, ad esempio, un sensore di temperatura trasmette informazioni al controller e non si aspetta nulla da esso in risposta.
Se parliamo di Arduino, quindi in forma hardware, tutte le schede hanno tre interfacce: UART, SPI e I2C. E alcuni molto avanzati hanno anche un adattatore CAN bus integrato. Ciascuna delle interfacce elencate ha i suoi vantaggi e svantaggi, che verranno menzionati separatamente, nonché i propri "client", ovvero dispositivi che utilizzano solo l'una o l'altra interfaccia per comunicare con il mondo esterno. Pertanto, è necessario conoscere tutte le opzioni ed essere in grado di utilizzarle tutte.
In questo articolo, daremo un'occhiata più da vicino alla prima interfaccia del nostro elenco: UART. È anche il primo sotto altri aspetti: il più semplice, il più comprensibile e diffuso. È qui che il principiante inizia a familiarizzare con l'argomento della comunicazione. Questo perché Arduino è collegato al computer per eseguire il flashing del programma attraverso di esso. Grazie a uno speciale adattatore integrato, si trasforma in uscita dalla scheda in una porta USB più comprensibile per un PC. Attraverso di esso vengono inviate informazioni e messaggi diagnostici al computer, nonché vengono ricevuti comandi e altre informazioni utili. Pertanto, questa interfaccia ci perseguiterà ovunque e sempre, il che, tuttavia, non è così male.
Quindi, UART - Universal Asynchronous Receiver-Transmitter, che in traduzione suona come un ricevitore-trasmettitore asincrono universale. Quanto a "universale", è comprensibile, è diffuso nel mondo elettronico, è utilizzato in varie forme da computer grandi e piccoli, controller, sensori, comunicazioni e altri dispositivi elettronici. La parola "asincrono" significa che la ricezione e la trasmissione dei singoli bit non sono allineati con i cosiddetti impulsi di clock, che sono allo stesso tempo punti positivi e negativi. L'unità differisce da zero solo nel tempo tra le cadute di livello del segnale, che è predeterminato dalla velocità di trasmissione.
Per completare il quadro, va detto che esiste una versione dell'UART nella sincronizzazione del segnale - USART, dove la lettera S sta per Synchronous. 
Perché il trasferimento dati asincrono è buono? La semplicità del protocollo, il minimo di fili e hardware coinvolti nel processo, la possibilità di full duplex (trasmissione dati simultanea in entrambe le direzioni). Qual è il suo svantaggio? Minore immunità al rumore e, di conseguenza, velocità e distanza massime nelle stesse altre condizioni. Tuttavia, per il 99,5% delle nostre attività, la velocità e la stabilità dell'opzione asincrona saranno sufficienti con un ampio margine.
l'UART utilizza due pin del controller: RX e TX, dove le prime lettere stanno rispettivamente per ricevitore e trasmettitore. Cosa significano le seconde X, nessuno lo sa per certo, ma tutti scrivono così e ci consigliano. È logico che per collegare due dispositivi occorrano due fili, che vanno collegati trasversalmente RX del primo a TX del secondo e viceversa. Dove uno trasmette, là un altro riceve.
Si scopre una X . E poi dovrebbe sorgere un'ipotesi improvvisa, da dove viene X nel nome dei porti! È possibile che sia così, la versione è davvero bella e la topologia è più facile da ricordare in questo modo.

Diverse schede Arduino hanno un numero diverso di porte hardware UART, molto spesso una, e l'enorme Mega ne ha quattro. Tuttavia, la semplicità di questa interfaccia consente di emulare la porta utilizzando metodi software, assegnando RX e TX a quasi tutti i pin scelti. Cioè, puoi usare porte su pin rigorosamente definiti (consigliato) o - con alcune restrizioni - "software" e, se necessario, entrambi contemporaneamente.

In generale, la trasmissione si presenta come una catena di segnali, ovvero bit divisi in byte, più (opzionalmente) segnali di servizio.

Mentre le informazioni non vengono trasmesse, nella linea viene mantenuto un livello logico alto (nel nostro caso con Arduino, questo è + 5V, la cosiddetta logica TTL). Il decadimento del segnale è un comando al lato ricevente che sta per iniziare qualcosa di interessante! E dopo un certo lasso di tempo, dipendente da una velocità prefissata, inizia la trasmissione di un byte sotto forma di serie di zeri e uno nel rispetto degli stessi, predeterminati, intervalli di tempo. Dopo l'ottavo bit segue un segnale di stop sotto forma di livello alto e la situazione si ripete fino a quando non vengono trasmessi tutti i byte di cui abbiamo bisogno.

Quando si parla di segnali di servizio, si intendono misure aggiuntive previste dal protocollo per combattere gli errori causati da interferenze nei fili e nei contatti. Sì, il mondo è imperfetto e si verificano interferenze, e più sottili e lunghi sono i fili, più spesso e più forti. Se si aggiunge al protocollo un cosiddetto bit di parità, questo verrà trasmesso nella catena di dati, immediatamente dopo l'ultimo bit del byte di informazione e prima del bit di stop.

La presenza o l'assenza di tale bit è anche specificata nel protocollo in anticipo, insieme alla velocità e ad altri parametri. Qui devi capire che il protocollo ha varietà e può differire per i diversi dispositivi, a seconda delle condizioni e delle preferenze dei programmatori. I parametri principali sono: velocità, numero di bit, parità di bit, lunghezza del segnale di arresto. Ciò implica una condizione importante per il funzionamento della UART: entrambi i dispositivi devono essere configurati allo stesso modo, altrimenti non si capiranno. 

Per quanto riguarda Arduino, il più delle volte non viene utilizzato il bit di parità, i dati del byte sono costituiti da otto bit e la lunghezza del segnale di stop è uguale alla lunghezza di un bit.

La velocità di trasferimento viene misurata in "baud", ovvero in bit al secondo. È altamente auspicabile scegliere tra valori standard preformulati:

Maggiore è la velocità, più velocemente i dati scorrono sui cavi, ma maggiore è la probabilità di errori, quindi si raccomanda di non abusare e seguire il principio della ragionevole sufficienza. Per la maggior parte dei casi si consiglia 9600 baud, è abbastanza veloce e molto affidabile anche a distanze decenti, un byte viene trasferito in circa 1ms. Se ciò non bastasse, puoi sempre "accelerare". La massima velocità di trasferimento dati possibile su UART hardware per schede Arduino è 250000 baud, su quelle soft - 115200. Ma, come accennato in precedenza, è meglio non overcloccare a tali valori senza necessità.

I nostri dispositivi possono scambiare dati in entrambe le direzioni, ma nonostante la possibilità teorica del full duplex, in realtà avviene in modo leggermente alternato, sebbene sia abbastanza trasparente all'utente per via del port buffering.

Implementazione in Arduino.

Nell'ambiente Arduino, la comunicazione con l'UART avviene utilizzando la classe Serial. Consideriamo alcune funzioni di questa classe.
Serial.begin(long);
Avvia il funzionamento della porta alla velocità di trasmissione specificata nel parametro. Mega ha 4 porte, vengono avviate da comandi con un numero, ad esempio Serial1.begin(9600); e così via. Tutti gli altri comandi alle porte Mega vengono eseguiti anche con l'indicazione del numero di porta.
Serial.end();
Arresta la porta se era precedentemente in esecuzione. In pratica è usato raramente, ma ci sono casi in cui è necessario liberare i pin 0 e 1, almeno per un po'.
= Serial.available();
Restituisce il numero di byte ricevuti nel buffer della porta come int. Se restituisce 0, non è stata ricevuta alcuna informazione. Solitamente utilizzato come trigger per la ricezione di informazioni.
= Serial.read();
Restituisce un byte dal buffer di ricezione. La chiamata successiva restituisce il byte successivo e così via. Se il buffer è vuoto, restituisce 0xFFFF.
Serial.print(xxx);
Fornisce un'ampia varietà di opzioni di trasferimento delle porte, da un byte a una stringa di caratteri fino a un numero in virgola mobile. Una funzionalità molto utile per il debug dei programmi.
Serial.println(xxx);
Si differenzia dal suddetto invio automatico di due caratteri di interruzione di riga di servizio dopo l'informazione dall'informazione contenuta nel parametro. Il messaggio successivo inizierà su una nuova riga.
Serial.write(xxx);
Invia dati binari alla porta. Restituisce il numero di byte trasferiti.
= Serial.read();
Restituisce i dati binari ricevuti.

L'elenco delle funzioni della classe Serial è tutt'altro che completo, ma possono essere utilizzate per eseguire quasi tutte le operazioni di scambio di dati. Vediamo come si fa con alcuni esempi pratici.

Esempi di utilizzo.

Cominciamo con il più semplice. Trasferimento di informazioni dal controller al computer con la sua visualizzazione nel monitor. Questa è una caratteristica molto importante che ti permette di osservare cosa sta succedendo nel programma se inserisci le stringe appropriate in esso. È possibile visualizzare il contenuto delle variabili, i segni di passaggio di alcuni punti e così via.

void setup() 
     Serial.begin(9600);        // avviare la porta
void loop() 
     Serial.print("timer: ");  // scrivi la parola timer: 
     Serial.print(millis());     // stampa il numero di millisecondi dall'inizio del programma
     Serial.println("ms");      // li firmiamo con ms, trasferisce la riga in una nuova
     delay(1000);                      // ritardo 1 sec
}

Quindi avviamo il monitor della porta integrato nell'ambiente Arduino utilizzando il pulsante della lente d'ingrandimento nell'angolo in alto a destra della finestra:

Due stringe di Serial.print() visualizzano in sequenza il testo e valore del timer millis(), la terza stringa di Serial.println() visualizza l'ultima parte di testo e trasferisce la riga . Si scopre una catena infinita di messaggi che possiamo leggere dallo schermo.

Il secondo esempio. 

E un po' più complicato. Invieremo i dati nella direzione opposta, dal computer al controller. I dati saranno i comandi per accendere o spegnere il LED integrato sul 13° pin di Arduino. 

void setup() 
     pinMode(13, OUTPUT); 
     Serial.begin(9600); 
void loop() 
     if (Serial.available() > 0)             // se il comando è arrivato.
     
          char incom = Serial.read();  // leggere, riconoscere, reagire. 
          if (incom == '1') 
          { 
               digitalWrite(13, HIGH); 
          } 
          else if (incom == '0') 
          { 
               digitalWrite(13, LOW); 
          } 
     } 
}

Ora, se digiti uno nella riga del monitor e la invii premendo il pulsante Invio, il LED sulla scheda si accenderà e se invii zero si spegnerà. Pertanto, possiamo controllare il programma direttamente dal monitor della porta o inviargli qualsiasi dato. Ad esempio, l'ora dell'orologio, il numero di giri che il motore deve fare o la luminosità della striscia LED. E questo è già un modo diretto per trasferire i dati direttamente tra i controller, è solo necessario sostituire i dati immessi nel monitor con l'invio dal programma del secondo controller. 

Il terzo esempio.

Per l'implementazione, abbiamo bisogno di due schede Arduino qualsiasi, tra le quali guideremo i dati. Ad ognuno di essi collegheremo allo stesso modo due pulsanti e due led, e li collegheremo trasversalmente RX-TX, come descritto sopra.
Nota: Se le schede sono collegate a diverse fonti di alimentazione, è indispensabile collegare i loro pin GND, altrimenti non ci sarà supporto per i segnali sulla scheda adiacente.
I pulsanti collegati a una scheda controlleranno i LED collegati a un'altra. E viceversa. Per fare ciò, ogni scheda deve trasmettere informazioni su ciò che sta accadendo sui suoi pulsanti a un'altra scheda, ricevendo contemporaneamente da essa gli stessi dati e controllando i suoi LED in base ad essi. Gli assiemi sono simmetrici, le funzioni sono le stesse, il che significa che i programmi su entrambe le schede saranno gli stessi.
Compiliamo entrambi:

#define LED_1 4  // LED 1 
#define LED_2 5  // LED 2 
#define BUT_1 2  // pulsante1 
#define BUT_2 3  // pulsante 2
byte but[2];           // variabili per il monitoraggio dei pulsanti.
void setup() 
{ 
     // inizializzare i pin, avviare la porta seriale
     pinMode(LED_1, OUTPUT); 
     pinMode(LED_2, OUTPUT); 
     pinMode(BUT_1, INPUT); 
     pinMode(BUT_2, INPUT); 
     Serial.begin(9600); 

void loop() 
     if (get_but())                         // succede qualcosa con i pulsanti.
     {  
          Serial.write(but[1]);     // invia un nuovo stato del pulsante alla porta. 
     }
     if (Serial.available() > 0)  // se i dati provenivano da pulsanti vicini.
     {  
          byte incom = Serial.read();                // leggi questi dati 
          digitalWrite(LED_1, !(incom / 2));    // accendere o spegnere il LED 1 
          digitalWrite(LED_2, !(incom % 2));  // accendere o spegnere il LED 2 
     } 
}
byte get_but() 
     static unsigned long timer; 
     if (timer + 50 > millis()) return 0;                                            // polling ogni 50 ms (rimbalzo).
     timer = millis(); 
     but[0] = but[1]; 
     but[1] = digitalRead(BUT_1) + digitalRead(BUT_2) * 10;  // dati da entrambi i pulsanti in una variabile. 
     if (but[0] != but[1]) return 1;                                                      // se c'è un cambiamento, segnaliamo con 1. 
     return 0;                                                                                            // in caso contrario, restituire 0.
}

Premiamo i pulsanti, vediamo che tutto funziona come previsto. I byte con i comandi vengono eseguiti avanti e indietro e in qualsiasi sequenza, incl. contemporaneamente.
Naturalmente, questo è il modo più primitivo per trasferire i dati, anche se funziona abbastanza. Non c'è controllo degli errori e controllo dell'esecuzione, ma questo può essere facilmente risolto aggiungendo un checksum e un feedback sotto forma di conferma al protocollo. Non c'è limite alla perfezione, tutto dipende solo dai requisiti e dal tempo dedicato al programma.

Assemblaggio del circuito.

Conclusione.

UART è senza dubbio e meritatamente l'interfaccia di trasferimento dati più famosa e ampiamente utilizzata. Con il suo aiuto, sensori, attuatori, indicatori e display, moduli GPS e GPRS sono collegati ad Arduino. Attraverso di esso, il programma viene caricato su Arduino e sottoposto a debug. Con esso, puoi organizzare facilmente e rapidamente lo scambio di comandi e informazioni con un altro controller. Tuttavia, presenta anche degli svantaggi, come la limitazione della velocità, l'immunità al rumore relativamente bassa, il requisito della precisione della frequenza di clock sulle schede trasmittenti e riceventi, che è particolarmente critico per i controller alimentati da un generatore RC interno. Tuttavia, rimane uno strumento molto importante con un grande potenziale. Qualsiasi maestro del fai da te, senza eccezioni, dovrebbe conoscere questa interfaccia ed essere in grado di usarla. 

Crea il tuo sito web gratis! Questo sito è stato creato con Webnode. Crea il tuo sito gratuito oggi stesso! Inizia