Come già accennato in precedenti post in questi mesi sto svolgendo una serie di attività di prototipazione rapida di sistemi di rilevazione ambientale presso la facoltà di Agraria di Padova e l’esigenza di espandere la quantità di ingressi analogici di Arduino è una necessità reale che può essere superata agevolmente con pochissimi euro (per la precisione 1€ 🙂 ) utilizzando l’integrato 4051 Multiplexer/Demultiplexer analogico ad 8 canali in grado appunto di ampliare il numero di I/O di Arduino.
Esigenze di questo tipo si presentano spessissimo, ad esempio nel caso voi vogliate collegare più sensori analogici ad un ESP che dispone di un solo ingresso analogico, o più semplicemente andare oltre i 6 pin analogici di Arduino UNO R3, situazione che si presenta ad esempio nel caso in cui si stanno occupando i pin analogici A4 e A5 per la comunicazione I2C per altri dispositivi.
L’integrato 4051 viene realizzato da diverse aziende vi allego i datasheet corrispondenti alla versione prodotta da Philips e Texas Instruments, identici nelle funzionalità:
All’interno trovate tutte le indicazioni necessarie per poterlo usare che vi riassumo nelle righe che seguono.
Piedinatura dell’integrato
Dove:
- Z: pin input/output (connesso agli Input/Output Arduino )
- E: pin di enable (attivo basso, cioè attivo su LOW da connettere a GND)
- VEE: tensione di alimentazione negativa (da connettere a gnd)
- VSS: terra (0 V)
- A0-A1-A2: pin di selezione input (connessi a tre pin digitali di Arduino) – A0 bit meno significativo (LSB), A2 bit più significativo (MSB)
- Da Y0 a Y7: pin di inputs/outputs
- VDD: tensione di alimentazione positiva (da 3V a 5v)
Tabella di verità
Dalla tabella si evince che per poter far funzionare l’IC è necessario connettere E (Enable) a GND.
Il 4051 è un integrato in tecnologia CMOS dotato di 8 ingressi (nominati Y) che può accettare segnali analogici compresi tra 0V e 5 V, tali segnali possono essere selezionati mediante tre pin di selezione nominati: A0, A1, A2 ed inviati direttamente ad un pin analogico di Arduino per la successiva elaborazione.
La selezione del canale Y scelto (in altre parole del segnale analogico che si desidera leggere o scrivere) può essere fatta sfruttando lo stesso Arduino mediante 3 pin digitali, sui 3 pin si comporrà il numero binario corrispondente all’ingresso analogico Y scelto.
A titolo di esempio, ricordando che con tre bit possiamo rappresentare tutti i numeri tra 0 e 7 (2^0 = 1; 2^1 = 2; 2^2 = 4):
- Se A0 = 1, A1 = 1 e A2 = 0 allora l’uscita selezionata sarà la Y3
(2^1 + 2^1 + 2^0 = 2 + 1 + 0 = 3) - Se A0 = 1, A1 = 0 e A2 = 1 allora l’uscita selezionata sarà la Y5
(2^1 + 2^0 + 2^2 = 2 + 0 + 4 = 5)
Per chiarirne il funzionamento analizziamo le due modalità operative:
- Input: lettura di segnali analogici presenti sugli ingressi Y del 4051
- Output: invio di un segnale analogico su una delle 8 uscite Y del 4051
4051 usato come multiplexer con Arduino
Lettura di segnali analogici presenti sugli ingressi Y del 4051
In questa modalità utilizzeremo Arduino per selezionare uno degli 8 ingressi Y a cui collegheremo 8 sensori, nell’esempio 8 LDR e per la lettura dei valori letti dai sensori utilizzeremo un solo ingresso analogico di Arduino.
Circuito
Lista componenti:
- Arduino UNO R3
- n. 8 LDR
- n. 8 Resitori da 1KOhm
- IC: 4051
Di seguito due Sketch di esempio che producono la medesima lettura però sono stati realizzati con funzioni differenti. Nel primo viene utilizzata la funzione bitRead() mentre nella seconda viene utilizzato l’operatore di shift bit a sinistra “<<“. Entrambe le modalità sono utili da conoscere in quanto l’esecuzione dei calcoli matematici realizzati con gli operatori al bit risultano più veloci.
Il funzionamento di ogni parte del codice con i relativi collegamenti tra i vari dispositivi viene dettagliato come commento all’interno dello sketch in modo che risulti più agevole la modifica e l’integrazione con le vostre note.
Sketch v01
/* * Prof. Maffucci Michele * 03.05.17 * * Espandere gli I/O analogici di Arduino * 4051 Multiplexer/Demultiplexer analogico ad 8 * * Uso in modalità Multiplexer - v01 * * Collegamento * * 4051 ------------- Arduino * A0 ------------- 2 * A1 ------------- 3 * A2 ------------- 4 * Z ------------- A0 * VCC ------------- 5V * GND ------------- GND * * VEE connesso GND * Da Y0 a Y7 connessi ad ingressi analogici * */ int canaliTotali = 8; // pin Arduino utilizzati per l'indirizzamento int pinA0 = 2; int pinA1 = 3; int pinA2 = 4; int valA0 = 0; // valore bit A0 per indirizzamento canale int valA1 = 0; // valore bit A1 per indirizzamento canale int valA2 = 0; // valore bit A2 per indirizzamento canale void setup() { Serial.begin(9600); // Impostiamo i pin di indirizzamento ad output pinMode(pinA0, OUTPUT); pinMode(pinA1, OUTPUT); pinMode(pinA2, OUTPUT); // Stampa l'intestazione della tabella dei dati provenienti dai sensori: Serial.println("Y0\tY1\tY2\tY3\tY4\tY5\tY6\tY7"); Serial.println("---\t---\t---\t---\t---\t---\t---\t---"); } void loop() { // Seleziona ciascun pin e legge il valore /* * bitRead(x,n) * x: numero che deve essere letto * n: numero del bit (0 oppure 1) del numero n (binario) che deve essere letto * Risultato: 0 oppure 1 * * https://www.arduino.cc/en/Reference/bitRead */ for(int i=0; i<canaliTotali; i++) { valA0 = bitRead(i,0); // Legge il valore del primo bit della selezione canale valA1 = bitRead(i,1); // Legge il valore del secondo bit della selezione canale valA2 = bitRead(i,2); // Legge il valore del terzo bit della selezione canale /* * La sequenza di lettura: 000 - 001 - 010 - 011 - 100 - 101 - 110 - 111 * in questo modo nei rispettivi valAn verrà memorizzato lo stato in cui deve * essere posto il pinAn in tal modo si crea l'indirizzo del canale da leggere */ // Invia l'indirizzo al multiplexer digitalWrite(pinA0, valA0); digitalWrite(pinA1, valA1); digitalWrite(pinA2, valA2); // Stampa sulla serial monitor il valore analogico int inputValue = analogRead(A0); // legge Z Serial.print(String(inputValue) + "\t"); } Serial.println(); // Al termine della lettura di 8 valori va a capo di una linea delay(1000); }
Sketch v02
/* * Prof. Maffucci Michele * 03.05.17 * * Espandere gli I/O analogici di Arduino * 4051 Multiplexer/Demultiplexer analogico ad 8 * * Uso in modalità Multiplexer - v02 * * Collegamento * * 4051 ------------- Arduino * A0 ------------- 2 * A1 ------------- 3 * A2 ------------- 4 * Z ------------- A0 * VCC ------------- 5V * GND ------------- GND * * VEE connesso GND * Da Y0 a Y7 connessi ad ingressi analogici * */ // Definizione dei pin const int selettorePin[3] = {2, 3, 4}; // A0-2, A1-3, A2-4 void setup() { Serial.begin(9600); // Inizializzazione della porta seriale // impostaione dei pin ad OUTPUT for (int i=0; i<3; i++) { pinMode(selettorePin[i], OUTPUT); } // Stampa l'intestazione della tabella dei dati provenienti dai sensori: Serial.println(" Y0\t Y1\t Y2\t Y3\t Y4\tY5\t Y6\t Y7"); Serial.println("----\t----\t----\t----\t----\t----\t----\t----"); } /* * Con il ciclo for stabilisce (in modo ciclico) quale canale Y deve essere letto, * cioè quale sensore analogico leggere. * La funzione selettoreMuxPin(pin) imposta i pin di selezione canale A0, A1, A2 * per aprire il canale Y richiesto durante il ciclo for nella funzione loop. * Sui pin di selezione canale si avrà in binario il numero del canale Y da leggere. * */ void loop() { // Cicla tra gli 8 pin for (byte pin=0; pin<=7; pin++) { selettoreMuxPin(pin); // Viene selezionato un pin alla volta int inputValue = analogRead(A0); // legge Z Serial.print(String(inputValue) + "\t"); } Serial.println(); delay(1000); } /* * La funzione selettoreMuxPin() imposta i pin A0, A1, e A2 * per selezionare uno dei pin da Y0 a Y7 * * Per l'impostazione dei pin selezione canale viene utilizzata l'operatore * << di shift bit a sinistra, che sposta a sinistra i bit di un numero di passi * pari a quello indicato dall'operando: * * Sintassi: * * variabile << numero_di_bit * * variabile può essere di tipo: byte, int, long * numero_di_bit <= 32 * * Per maggiori informazioni sull'uso degli operatori al bit consultare il reference: * https://www.arduino.cc/en/Reference/Bitshift * */ /* * Nel ciclo for della funzione selettoreMuxPin() vine preso in analisi * la posizione di ogni bit usando il prodotto al bit * (che operano sui singoli bit del byte che gli è stato passato, in questo caso pin). * Il ciclo for legge bit per bit il byte che gli viene passato. * * Ad ogni ciclo viene spostato di una posizione a sinistra un 1 * successivamente viene effettuata una moltiplicazione al bit con * il byte (pin), questa operazione restuirà un 1 binario se nella posizione * i-esima è presente un 1 mentre resituirà 0 se nella posizione i-esima è presente uno 0 * * Detta in altro modo possiamo considerare selettoreMuxPin() un'analizzatore di byte in grado di * capire se il bit i-esimo è alto o basso e portare ad HIGH o LOW il pin conservato nell'array * selettorePin[] nella posizione i-esima dell'array. * * */ void selettoreMuxPin(byte pin) { for (int i=0; i<3; i++) { if (pin & (1<<i)) digitalWrite(selettorePin[i], HIGH); else digitalWrite(selettorePin[i], LOW); } } /* * Alcune precisazioni * * Uno shift a sinistra equivale ad una moltiplicazione per 2; * mentre, uno shift a destra equivale ad una divisione per 2. * * Potreste trovati calcoli matematici in cui le operazioni di moltiplicazione e divisione * vengono realizzati rispettivamente utilizzando shift a sinistra e a destra in quanto * dal punto di vista del calcolo per Arduino l'operazione di spostamento del bit risulta * più efficiente. * * ATTENZIONE, non confondere & con && (& e' "bitwise and" vuol dire && e' "logical and"); * stessa cosa per l'operatore | "bitwise or" e || "logical or" * */
Valori letti
4051 usato come demultiplexer con Arduino
Invio di un segnale analogico su una delle 8 uscite Y del 4051
Circuito
Così come svolto per la modalità precedente vi propongo due versioni che funzionano in maniera analoga usando funzioni diverse.
Nei commenti la spiegazione del funzionamento del codice.
Sketch v01
/* * Prof. Maffucci Michele * 03.05.17 * * Espandere gli I/O analogici di Arduino * 4051 Multiplexer/Demultiplexer analogico ad 8 * * Uso in modalità Demultiplexer - v02 * * Collegamento * * 4051 ------------- Arduino * A0 ------------- 2 * A1 ------------- 3 * A2 ------------- 4 * Z ------------- A0 * VCC ------------- 5V * GND ------------- GND * * VEE connesso GND * Da Y0 a Y7 connessi ad ingressi analogici * */ // come ingresso analogico indirizzato su una dei canali Y viene scelto un segnale PWM // di Arduino ed utilizzato per variare per ogni LED connesso all'uscita Y l'intensità // in maniera crescente e decrescente per ogni singolo LED int canaliTotali = 8; // pin Arduino utilizzati per l'indirizzamento int pinA0 = 2; int pinA1 = 3; int pinA2 = 4; // pin 9 di Arduino di tipo PWM usato come pin input Z del 4051 int Z_UscitaAnalogica = 9; int valA1 = 0; // valore bit A0 per indirizzamento canale int valA2 = 0; // valore bit A1 per indirizzamento canale int valA3 = 0; // valore bit A2 per indirizzamento canale void setup() { Serial.begin(9600); // Impostiamo i pin di indirizzamento ad output pinMode(pinA0, OUTPUT); pinMode(pinA1, OUTPUT); pinMode(pinA2, OUTPUT); pinMode(Z_UscitaAnalogica, OUTPUT); // impostiamo A0 come INPUT (non è necessario) pinMode(A0, INPUT); } void loop() { // Seleziona ciascun pin e legge il valore for(int i=0; i<canaliTotali; i++){ valA1 = bitRead(i,0); // Legge il valore del primo bit della selezione canale valA2 = bitRead(i,1); // Legge il valore del secondo bit della selezione canale valA3 = bitRead(i,2); // Legge il valore del terzo bit della selezione canale // Invia l'indirizzo al multiplexer digitalWrite(pinA0, valA1); digitalWrite(pinA1, valA2); digitalWrite(pinA2, valA3); fading(Z_UscitaAnalogica); } } void fading(int ledPin) { // fade con luminosità crescente ad incrementi di 5 unità // nell'intervallo da 0 a 255 for (int valoreFade = 0 ; valoreFade <= 255; valoreFade += 5) { analogWrite(ledPin, valoreFade); // aspetta 30 millisecondi per apprezzare l'effetto fade delay(10); } // fade con luminosità decrescente ad incrementi di 5 unità // nell'intervallo da 255 a 0 for (int valoreFade = 255 ; valoreFade >= 0; valoreFade -= 5) { analogWrite(ledPin, valoreFade); // aspetta 30 millisecondi per apprezzare l'effetto fade delay(10); } }
Sketch v02
/* * Prof. Maffucci Michele * 03.05.17 * * Espandere gli I/O analogici di Arduino * 4051 Multiplexer/Demultiplexer analogico ad 8 * * Uso in modalità Demultiplexer - v02 * * Collegamento * * 4051 ------------- Arduino * A0 ------------- 2 * A1 ------------- 3 * A2 ------------- 4 * Z ------------- A0 * VCC ------------- 5V * GND ------------- GND * * VEE connesso GND * Da Y0 a Y7 connessi ad ingressi analogici * */ /* * Come ingresso analogico indirizzato su una dei canali Y viene scelto un segnale PWM * di Arduino ed utilizzato per variare per ogni LED connesso all'uscita Y l'intensità * in maniera crescente e decrescente per ogni singolo LED. * La sequenza di accensione sarà da sinistra verso destra e da destra a sinista. */ // Definizione dei pin const int selettorePin[3] = {2, 3, 4}; // A0-2, A1-3, A2-4 const int Z_UscitaAnalogica = 9; // pin 9 di Arduino di tipo PWM usato come pin input Z del 4051 const int TEMPO_DI_DELAY = 10; void setup() { // impostaione dei pin ad OUTPUT for (int i=0; i<3; i++) { pinMode(selettorePin[i], OUTPUT); digitalWrite(selettorePin[i], LOW); } pinMode(Z_UscitaAnalogica, OUTPUT); // Imposta Z come OUTPUT } void loop() { // Varia la luminosità in modo crescente partendo dal pin Y0 a Y7 for (int pin=0; pin<=7; pin++) { // Imposta i pin A0, A1 e A2 per selezionare uno dei canali da Y0 a Y7 selettoreMuxPin(pin); // intensità del LED crescente for (int intensita=0; intensita<=255; intensita++) { analogWrite(Z_UscitaAnalogica, intensita); delay(TEMPO_DI_DELAY); } // intensità del LED decrescente for (int intensita=255; intensita>=0; intensita--) { analogWrite(Z_UscitaAnalogica, intensita); delay(TEMPO_DI_DELAY); } } // Varia la luminosità in modo decrescente partendo dal pin Y7 a Y0 for (int pin=6; pin>=1; pin--) { // Imposta i pin A0, A1 e A2 per selezionare uno dei canali da Y0 a Y7 selettoreMuxPin(pin); // intensità del LED crescente for (int intensita=0; intensita<=255; intensita++) { analogWrite(Z_UscitaAnalogica, intensita); delay(TEMPO_DI_DELAY); } // intensità del LED decrescente for (int intensita=255; intensita>=0; intensita--) { analogWrite(Z_UscitaAnalogica, intensita); delay(TEMPO_DI_DELAY); } } } /* * Nel ciclo for della funzione selettoreMuxPin() vine preso in analisi * la posizione di ogni bit usando il prodotto al bit * (che operano sui singoli bit del byte che gli è stato passato, in questo caso pin). * Il ciclo for legge bit per bit il byte che gli viene passato. * * Ad ogni ciclo viene spostato di una posizione a sinistra un 1 * successivamente viene effettuata una moltiplicazione al bit con * il byte (pin), questa operazione restuirà un 1 binario se nella posizione * i-esima è presente un 1 mentre resituirà 0 se nella posizione i-esima è presente uno 0 * * Detta in altro modo possiamo considerare selettoreMuxPin() un'analizzatore di byte in grado di * capire se il bit i-esimo è alto o basso e portare ad HIGH o LOW il pin conservato nell'array * selettorePin[] nella posizione i-esima dell'array. * * */ void selettoreMuxPin(byte pin) { if (pin > 7) return; // Exit if pin is out of scope for (int i=0; i<3; i++) { if (pin & (1<<i)) digitalWrite(selettorePin[i], HIGH); else digitalWrite(selettorePin[i], LOW); } }
Per semplificare ancor di più l’utilizzo del 4051 è possibile utilizzare la libreria Analog MuxDemux di cui trovate indicazioni sull’utilizzo nel link indicato.
Installata la libreria, all’interno potete trovare una cartella di esempi che ne illustra l’utilizzo.
Nei commenti all’interno degli sketch trovate la descrizione del funzionamento delle funzioni introdotte dalla libreria.
Uso della libreria Analog MuxDemux
4051 usato come multiplexer con Arduino
Lettura di segnali analogici presenti sugli ingressi Y del 4051
/* * Prof. Michele Maffucci * 08.05.17 * * Espandere gli I/O analogici di Arduino * 4051 Multiplexer/Demultiplexer analogico ad 8 * * Uso in modalità Multiplexer utilizzando la libreria * analog multiplexer di Andrew Fisher * https://github.com/ajfisher/arduino-analog-multiplexer * * Uso in modalità Multiplexer - v03 * * Collegamento * * 4051 ------------- Arduino * A0 ------------- 2 * A1 ------------- 3 * A2 ------------- 4 * Z ------------- A0 * VCC ------------- 5V * GND ------------- GND * * VEE connesso GND * Da Y0 a Y7 connessi ad ingressi analogici * */ #include "analogmuxdemux.h" // Input analogico di Arduino usato #define pinanAlogico 0 // Numero di ingressi (sensori/canali) utilizzati #define canaliTotali 8 // Impostazione della funzione di multiplexing, nell'ordine A0, A1, A2 AnalogMux amux(2,3,4, pinanAlogico); void setup() { Serial.begin(9600); // Stampa l'intestazione della tabella dei dati provenienti dai sensori: Serial.println("Y0\tY1\tY2\tY3\tY4\tY5\tY6\tY7"); Serial.println("---\t---\t---\t---\t---\t---\t---\t---"); delay(1000); } void loop() { // Stampa sulla serial monitor il valore analogico proveniente dagli 8 ingressi Y for (int numeroPin=0; numeroPin < canaliTotali; numeroPin++){ // selezione del canale Y che si vuole inviare ad Arduino amux.SelectPin(numeroPin); uint16_t lettura = amux.AnalogRead(); Serial.print(String(lettura) + "\t"); } Serial.println(); delay(1000); }
Uso della libreria Analog MuxDemux
4051 usato come demultiplexer con Arduino
Invio di un segnale analogico su una delle 8 uscite Y del 4051
/* * Prof. Michele Maffucci * 08.05.17 * * Espandere gli I/O analogici di Arduino * 4051 Multiplexer/Demultiplexer analogico ad 8 * * Uso in modalità Demultiplexer utilizzando la libreria * analog multiplexer di Andrew Fisher * https://github.com/ajfisher/arduino-analog-multiplexer * * Uso in modalità Demultiplexer - v03 * * Collegamento * * 4051 ------------- Arduino * A0 ------------- 2 * A1 ------------- 3 * A2 ------------- 4 * Z ------------- A0 * VCC ------------- 5V * GND ------------- GND * * VEE connesso GND * Da Y0 a Y7 connessi ad ingressi analogici * */ #include "analogmuxdemux.h" #define Z_UscitaAnalogica 9 // connect to a PWM pin in order to be able to output an analog signal const int TEMPO_DI_DELAY = 10; // set up the DeMux ready to be used. Watch the order of S0, S1 and S2. AnalogDeMux admux(2, 3, 4, Z_UscitaAnalogica); void setup() { pinMode(Z_UscitaAnalogica, OUTPUT); // Stampa l'intestazione della tabella dei dati provenienti dai sensori: Serial.println("Y0\tY1\tY2\tY3\tY4\tY5\tY6\tY7"); Serial.println("---\t---\t---\t---\t---\t---\t---\t---"); delay(1000); } void loop() { // Seleziona ciascun pin e legge il valore for (int pin = 0; pin <= 7; pin++) { admux.SelectPin(pin); // seleziona il pin a cui inviare il segnale PWM admux.AnalogWrite(fading(Z_UscitaAnalogica)); // Scrive il valore analogico usando la funzione fading } // Seleziona ciascun pin e legge il valore for (int pin = 6; pin >= 1; pin--) { admux.SelectPin(pin); // seleziona il pin a cui inviare il segnale PWM admux.AnalogWrite(fading(Z_UscitaAnalogica)); // Scrive il valore analogico usando la funzione fading } } int fading(int ledPin) { // fade con luminosità crescente ad incrementi di 5 unità // nell'intervallo da 0 a 255 for (int valoreFade = 0 ; valoreFade <= 255; valoreFade += 5) { analogWrite(ledPin, valoreFade); // aspetta TEMPO_DI_DELAY millisecondi per apprezzare l'effetto fade delay(TEMPO_DI_DELAY); } // fade con luminosità decrescente ad incrementi di 5 unità // nell'intervallo da 255 a 0 for (int valoreFade = 255 ; valoreFade >= 0; valoreFade -= 5) { analogWrite(ledPin, valoreFade); // aspetta TEMPO_DI_DELAY millisecondi per apprezzare l'effetto fade delay(TEMPO_DI_DELAY); } }
Bene!
A questo punto siamo liberi di espandere le possibilità di Arduino.
Buona sperimentazione a tutti.
Esempi ed esercizi sempre molto utili e soprattutto ben spiegati. L’integrato in questione già lo conoscevo e l’ho anche usato, ma non sapevo dell’esistenza di una libreria. C’è sempre da imparare!!!!!!!!!!! Poi con un insegnante così…Peccato che sono troppo distante altrimenti mi avrebbe fatto piacere incontrarLa e conoscerLa di persona.
🙂 Un sincero grazie
Spero in futuro si possano trovare occasioni per incontrarci.
Un caro saluto.