23
2015
Matrici LED 8×8 con Arduino e MAX7219


Le matrici led sono dispositivi che si prestano ad essere utilizzati in molti progetti ed esperimenti con Arduino grazie alla loro estrema versatilità e semplicità di utilizzo; in questo articolo cercherò di mettere in luce gli aspetti fondamentali legati al loro collegamento con Arduino e alla programmazione degli sketch nel caso delle matrici controllate tramite il circuito integrato MAX7219.
Figura 1: Una matrice LED 8×8 montata su modulo con controller MAX 7219
Collegamenti hardware
Iniziamo con l’analizzare quali siano i collegamenti da effettuare tra il modulo e Arduno. Il modulo presenta cinque pin VCC, GND, DIN, CS e CLK, il cui compito è, nell’ordine: positivo alimentazione, negativo alimentazione, ingresso dati seriali, selezione dispositivo e clock. Questi pin andranno collegati ad altrettanti pin di Arduino come indicato nella tabella di Figura 2.
Figura 2: collegamenti MAX 7219 – Arduino
Trasmissione dei dati
I dati trasmessi devono avere un formato ben definito affinché il controller della matrice li interpreti in modo corretto. Il formato dei dati si può ricavare dal datasheet del MAX 7219:
Figura 3: Formato dati MAX 7219
(Fonte: datasheet Maxim Integrated Products 19-4452)
I bit saranno caricati nel buffer di Figura 3 e 4 a partire da D0, andranno quindi inviati in “ordine inverso” cioè a partire dai quattro bit più a sinistra (quelli con la X in Figura 3, il cui valore verrà ignorato), seguiti dai quattro bit dell’indirizzo e completati dagli 8 bit dei dati.
Figura 4: Schema del buffer di ingresso
(Fonte: datasheet Maxim Integrated Products 19-4452)
Per trasmettere i sedici bit che costituiscono un comando si può utilizzare la libreria SPI oppure scrivere una semplice funzione per il trasferimento dei dati come quella riportata qui sotto:
void writeByte(byte data) { for(byte i=0;i<8;i++) { digitalWrite(currentClkPin,LOW); digitalWrite(currentDinPin,data&0x80); data = data<<1; digitalWrite(currentClkPin,HIGH); } }
void write(byte address, byte data) { digitalWrite(currentCSPin,LOW); writeByte(address); writeByte(data); digitalWrite(currentCSPin,HIGH); }
La prima funzione si occupa di scrivere un singolo byte, passato come primo parametro, bit per bit iniziando da quello più significativo (data&0x80), alzando ed abbassando opportunamente la linea di clock e facendo uno shift dei dati verso sinistra per ogni ciclo di clock.
La seconda funzione invia all’indirizzo specificato dal primo parametro il dato passato come secondo paramentro e si preoccupa di generare un fronte si salita sul pin CS.
I più attenti avranno sicuramente individuato tre variabili non definite nello snippet di codice riportato qui sopra:
- currentClkPin
- currentCSPin
- currentDinPin
Nel caso delle due funzioni prese autonomamente per comandare un singolo modulo andranno definite come variabili globali e rappresenteranno nell’ordine:
- Il pin di Arduino a cui è collegato il pin CLK (clock) del modulo
- Il pin di Arduino a cui è collegato il pin CS (chip select) del modulo
- Il pin di Arduino a cui è collegato il pin DIN (data in) del modulo
Con riferimento alla tabella di figura 2 (collegamenti MAX 7219 – Arduino) il codice di inizializzazione delle variabili globali da inserire all’inizio dello sketch sarà:
int currentClkPin=2; int currentCSPin=3; int currentDinPin=4;
Inizializzazione di Arduino
La prima cosa da fare, dal punto di vista software, prima di poter iniziare ad accendere i led della matrice è inizializzare i pin necessari per la comunicazione tra Arduino e il controller MAX7219. Per farlo è necessario impostare in OUTPUT i tre pin Clk, Cs e Din e dare loro i valori iniziali, rispettivamente LOW, LOW, LOW.
Il pin CS, anche se il nome può trarre in inganno, serve a trasferire i bit caricati nello shift register del controller ai registri veri e propri: quando sul pin CS si verifica una transizione LOW->HIGH, i bit di dati vengono effettivamente caricati nel registro indicato dai bit di indirizzo.
Quindi, indipendentemente dal fatto che nello shift register ci siano dei dati, questi verranno effettivamente recepiti dal controller solo a seguito di un passaggio LOW->HIGH del pin CS (questo diventerà molto importatnte quando si andrà a lavorare con più di una matrice).
La funzione setup() dello sketch inizierà quindi nel modo seguente:
void setup() { pinMode(currentClkPin,OUTPUT); pinMode(currentDinPin,OUTPUT); pinMode(currentCSPin,OUTPUT); digitalWrite(currentCSPin,LOW); digitalWrite(currentClkPin,LOW); digitalWrite(currentDinPin,LOW);
Inizializzazione della matrice
Ora che sono impostati correttamente i pin di Arduino possiamo passare a inviare i dati di inizializzazione del controller del modulo. I dati da inviare per una inizializzazione sicura sono i seguenti:
- Metodo di decodifica: nessuna decodifica
- Cifre del display: 7
- Spegnimento: No
- Test display: No
L’invio dei dati si effettua scrivendo il valore desiderato all’indirizzo dell’opzione corrispondente. In pratica si vanno a riempire i valori dei registri che il controller utilizza per svolgere il suo compito.
Oltre ai quattro registri indicati sopra dai datasheet si può individuare un quinto registro che permette di determinare la luminosità dei led della matrice.
- Luminosità: 3
Sempre dai datasheet si può ricavare l’indirizzo di ciascuno dei registri:
Tabella 1: Elenco dei registri e relativi indirizzi del MAX 7219
(Fonte: Datasheets Maxim Integrated Products)
Il codice di inizializzazione della matrice da aggiungere alla funzione setup() sarà quindi:
write(0x09, 0x00); //decode (always no decode) write(0x0a, 0x03); //Brightness (0..15) write(0x0b, 0x07); //Display digits (always 7, digit = column) write(0x0c, 0x01); //Shutdown (0x01 Normal | 0x00 Shutdown) write(0x0f, 0x00); //Display test (0x01 Test | 0x00 Normal)
Inviare le informazioni di accensione dei LED
Ora che la matrice, o meglio il suo controller è correttamente inizializzato, è possibile passare alla trasmissione dei dati di accensione dei singoli LED.
I LED sono organizzati sulla matrice in 8 colonne ciascuna composta da 8 LEDs. Per ciascuna colonna esiste un registro a 8 bit identificato da uno specifico indirizzo. I LED di ogni colonna sono accesi o spenti in funzione di come sono impostati gli 8 bit del suo registro.
Figura 5: Disposizione dei LEDs nella matrice
I registri che controllano lo stato dei LED si trovano agli indirizzi che vanno da 0x01 a 0x08; i valori da assegnare ai singoli registri possono essere calcolati come indicato più avanti. In generale il più semplice codice da aggiungere allo sketch per impostare i led accesi e spenti sarà simile al seguente:
write(0x01, 0xff); // digit 0 -> first column write(0x02, 0x81); // digit 1 write(0x03, 0x81); // digit 2 write(0x04, 0x81); // digit 3 write(0x05, 0x81); // digit 4 write(0x06, 0x81); // digit 5 write(0x07, 0x81); // digit 6 write(0x08, 0xff); // digit 7 -> last column }
Per completare questo sketch non dimenticate la funizione loop() che in questo caso sarà vuota.
void loop() { }
Calcolo del valore dei registri
Per calcolare il valore dei registri in modo semplice e veloce è sufficiente associare ad ogni LED di una colonna un valore progressivo a partire dal primo verso l’ottavo in questa successione: 1, 2, 4, 8, 16, 32, 64, 128.
Poi, partendo da zero si sommano i valori corrispondenti ai LED che si vogliono accendere. Ad esempio: se volessimo accendere il secondo e il quarto led di una colonna dovremmo impostare il relativo registro a 10. (2+8 cioè il valore associato al secondo led della colonna + il valore associato al quarto led della colonna).
Lo sketch completo
Riassumento i passaggi fondamentali sono:
- Cablaggio
- Inizializzazione variabili global (Pin Arduino per DIN/CS/CLK)
- Inizializzazione pin Arduino (in OUTPUT e con i valori inizali corretti)
- Inizializzazione controller (invio dati configurazione registri da 0x09 a 0x0f)
- Inizializzazione stato dei LED (invio dati configurazione registri da 0x01 a 0x08)
Lo sketch definitivo per questo progetto è dunque:
int currentClkPin=2; int currentCSPin=3; int currentDinPin=4; void writeByte(byte data) { for(byte i=0;i<8;i++) { digitalWrite(currentClkPin,LOW); digitalWrite(currentDinPin,data&0x80); data = data<<1; digitalWrite(currentClkPin,HIGH); } } void write(byte address, byte data) { digitalWrite(currentCSPin,LOW); writeByte(address); writeByte(data); digitalWrite(currentCSPin,HIGH); } void setup() { pinMode(currentClkPin,OUTPUT); pinMode(currentDinPin,OUTPUT); pinMode(currentCSPin,OUTPUT); digitalWrite(currentClkPin,LOW); digitalWrite(currentDinPin,LOW); digitalWrite(currentCSPin,LOW); write(0x09, 0x00); //decode (always no decode) write(0x0a, 0x03); //Brightness (0..15) write(0x0b, 0x07); //Display digits (always 7, digit = column) write(0x0c, 0x01); //Shutdown (0x01 Normal | 0x00 Shutdown) write(0x0f, 0x00); //Display test (0x01 Test | 0x00 Normal) write(0x01, 0xff); // digit 0 -> first column write(0x02, 0x81); // digit 1 write(0x03, 0x81); // digit 2 write(0x04, 0x81); // digit 3 write(0x05, 0x81); // digit 4 write(0x06, 0x81); // digit 5 write(0x07, 0x81); // digit 6 write(0x08, 0xff); // digit 7 -> last column } void loop() { }
Se sulla vostra matrice è comparso un quadrato, allora tutto è andato per il meglio! In caso contrario non vi scoraggiate e ripercorrete passo passo tutte le operazioni, vedrete che alla fine tutto funzionerà! In ogni caso ricordatevi di seguire Sisuino BLOG!!! Alla prossima.
Figura 6: Risultato dello sketch di questo progetto
Ottimo progetto, ottima descrizione, non mi resta che provarlo praticamente.
Saluti e ringraziamenti.
Provato, funziona perfettamente.
ciao Silvio ottimo progetto molto chiaro da capire e completo, ti chiedo giusto una cosa per essere autonomo, se ho più matrici led in cascata come posso fare per comandarli?
Ciao Luca,
Il modo più immediato é quello di collegare i moduli in cascata cioè collegare l’ingresso del secondo modulo all’uscita del primo, l’ingresso del terzo all’uscita del secondo e così via per quanto riguarda il segnale “data” (Din e Dout). Tutti gli altri segnali andranno comandati in parallelo. Poi nello sketch sarà sufficiente inviare una quantità di dati sufficiente a configurare tutte le matrici.