UART (Universal Asynchronous Receiver-Transmitter) è una periferica hardware che consente una comunicazione seriale asincrona, con formato dati e velocità configurabili. La periferica UART normalmente funziona a livello logico: i segnali elettrici sono poi generati da un diverso circuito, secondo gli standard propri del bus di comunicazione scelto.
Ad esempio la classica “porta seriale” dei personal computer si basa sullo standard EIA RS-232, standard che definisce a livello fisico come i segnali sono generati sul mezzo di comunicazione. Esistono appositi chip (il più famoso è sicuramente il MAX232 di Maxim Integrated) per convertire i livelli logici di una periferica UART in segnali secondo lo standard EIA RS232:
Il chip esp32 offre 3 controller UART. Questi controller sono collegati alla matrice GPIO; in tal modo è possibile assegnare loro uno i diversi pin digitali del chip:
Il framework esp-idf contiene un driver (uart.c) per semplificare l’utilizzo dei controller; per utilizzarlo includiamo il relativo file header nel nostro programma:
#include "driver/uart.h" |
I nomi dei 3 controller per il driver sono:
In questo primo articolo vediamo l’utilizzo base di un controller; eventi ed interrupts saranno argomento di un prossimo articolo.
Iniziamo con il configurare il controller tramite la struct uart_config_t:
uart_config_t uart_config = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE }; |
- baud_rate indica la velocità di trasmissione
- data_bits, e stop_bits indicano il numero di bit per ogni “parola” e il numero di bit di stop
- parity indica se trasmettere o meno il bit di parità
- flow_ctrl indica la tipologia di controllo di flusso (hardware, software o disabilitata)
Vi sono due ulteriori parametri (rx_flow_ctrl_thresh e use_ref_tick) utilizzabili per indicare la soglia per il segnale RTS in caso di controllo di flusso hardware e per utilizzare il segnale REF_TICK come clock per il controller UART.
Le costanti da utilizzare per data_bits, stop_bits… sono definite nel file uart.h.
Spesso i parametri da utilizzare per comunicare con un dispositivo in modalità seriale sono espressi in forma “condensata”, ad esempio se trovate indicato 9600,8N1 significa
[checklist]
- velocità 9600 baud
- “parola” di 8 bit
- Nessuna parità
- 1 stop bit
[/checklist]
Configuriamo il controller con il metodo:
uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config); |
indicando il numero del controller scelto (uart_num) e la struct che contiene la configurazione preparata in precedenza (uart_config).
Terminiamo la configurazione indicando al controller quali pin utilizzare per i diversi segnali:
uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num); |
Possiamo utilizzare la costante UART_PIN_NO_CHANGE se quel particolare segnale non è utilizzato o se vogliamo mantenere il pin di default.
Ad esempio per mappare il controller 0 sui pin 4 e 5 senza utilizzare i segnali RTS e CTS:
uart_set_pin(UART_NUM_0, 4, 5, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); |
Possiamo ora installare il driver con:
uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t* uart_queue, int intr_alloc_flags); |
Oltre al numero del controller, dobbiamo indicare la dimensione dei due buffer di ricezione e trasmissione. I parametri relativi alla queue li vedremo nel prossimo articolo.
Vediamo ora come inviare dati. Per meglio comprendere le differenze tra i comandi disponibili, è necessario comprendere come esistano due buffer: uno hardware, integrato nel controller UART e uno software, implementato nel driver: Il primo comando di invio – da usare quando viene disabilitato il buffer di trasmissione software – è uart_tx_chars(): Tale comando invia len bytes prendendoli da buffer. Non utilizzando un buffer software, è possibile che il comando non riesca a inviare tutti i bytes specificati per riempimento del buffer hardware; il metodo uart_tx_chars restituisce quindi il numero di bytes effettivamente inviati. Per utilizzare il buffer software invece esiste il comando uart_write_bytes(): Questo comando copia size bytes dall’array src nel buffer del driver: sarà poi il driver in maniera autonoma a gestire la comunicazione con il controller e l’effettivo invio dei dati. Anche il comando uart_write_bytes() restituisce il numero di bytes effettivamente scritti nel tx buffer. In ricezione, possiamo utilizzare il comando uart_read_bytes(): Tale comando legge un massimo di length bytes dal buffer di ricezione e li memorizza nell’array buf. Il comando rimane in attesa di dati il numero di ticks specificati, quindi ritorna il numero di bytes effettivamente letti. E’ possibile sapere il numero di bytes attualmente presenti nel buffer di ricezione con: Il framework esp consente di utilizzare uno dei controller UART come periferica per le funzionalità di I/O standard. Gli stream standard Unix (stdin, stdout e stderr) sono infatti associati a operazioni di RX e TX da tale controller; è quindi possibile utilizzare i metodi standard C quali printf(), scanf()… per scrivere e leggere da tale controller. Tramite menuconfig è possibile indicare quale controller utilizzare, i parametri di tale controller o se disabilitare del tutto questa funzionalità: Nel seguente filmato (sottotitoli in italiano disponibili) mostro il funzionamento del controller UART del chip esp32 collegandolo ad un convertitore USB->seriale. Sia il convertitore che la scheda di sviluppo esp32 sono connessi al mio laptop: in questo modo posso inviare dei dati dal laptop alla porta UART1 del chip, leggerli e inviarli nuovamente al laptop tramite la porta UART0.int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len);
int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size);
int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length,
TickType_t ticks_to_wait);
uart_get_buffered_data_len(uart_port_t uart_num, size_t* size);
UART e stdio
Demo
Hello Luca,
I think the arrows in the representation of the data flow are not all correct.
There is no arrow that goes into the driver.
see: http://www.lucadentella.it/en/2017/11/06/esp32-26-uart/
Kind regards. Rolf Spilger
Hi Rolf, the program calls two functions (uart_write_bytes and uart_tx_chars) of the driver
Hi luca, thanks for example.
i would like to know if you have ever implementred UART interrupt handler. i am lloking to use UART interrupt directly, however i didnt come across any example or support.
regards
sushant
Hi, not at the moment but it’s a good suggestion for a new post… stay tuned 😉
Ciao Luca e grazie per il tuo tutorial.
Vorrei avere una traccia per capire una cosa. Se io volessi “trasmettere” dei dati ricevuti via seriale da un ESP32 utilizzando il wifi.. per spiegarmi meglio… un software manda delle info per muovere due motori passo passo ad un Arduino via usb. Posso interporre un ESP32 in modo che l’Arduino (dotato di scheda wifi) riceva i dati dall’ESP32 “via radio” e svolga il lavoro che il software gli manda?
Spero di essermi spiegato perché Arduino lo uso da molti anni, ma di wifi e di dati seriali sono completamente a digiuno…
Grazie per ogni dritta.
lanfranco
Ciao! Si, puoi farlo. Immagino che la tua idea sia di utilizzare ESP32 per ricevere via wifi dei dati dal computer e inviarli poi via seriale ad Arduino giusto? In tal caso ti basta anche un semplice ESP8266,