Hardware Interfacciamento

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.

 

matrice led 8x8Figura 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.

 

tabella-collegmento-arduino-max7219Figura 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:

 

max7219 formato datiFigura 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.

max7219 caricamento buffer datiFigura 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:

 

max7219-full-registers-table
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.

disposizione-LEDs
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.

 

QuadratoSuMatriceLED

 Figura 6: Risultato dello sketch di questo progetto

 


Commenti

14.09.2018 Gaito Santolo

Ottimo progetto, ottima descrizione, non mi resta che provarlo praticamente. Saluti e ringraziamenti.

17.09.2018 Gaito Santolo

Provato, funziona perfettamente.

06.01.2019 luca schiaoncin

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?

08.01.2019 Silvio Marzotto

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.

17.04.2019 giuseppe

ciao quello che mi domando è : perché continuate a scrivere presunte lezioni per quelli che gia sanno e ignorate quelli per cui dovreste veramente scrivendo istruire affinche sappiano dopo le varie lezioni che tra l'altro sono meravigliose mio caso per esempio leggo ,comprato 3 volumi di arduino studio ma!!! sono solo come tanti nello studio e quindi etc etc, ora pongo una domanda ,sotto le figure 3 e4 c'è un piccolo sketch che lei ha scritto potrebbe essere almeno lei un pò meno avaro e spiegarlo ad una cervice dura come me? la prego ...ovunque scrivo per apprendere o non mi rispondono o dicono la solita zolfa che si deve studiare . la prego di non offendersi perché tra l'altro le sto facendo un complimento data la sua bravura …..ma voi prof perché non ci aiutate? grazie se vorrà accontentarmi

17.04.2019 Silvio Marzotto

Ciao Giuseppe, in effetti quando si scrive di un determinato argomento è necessario "dare per scontate" alcune cose. Man mano che gli argomenti si fanno più complessi, cresce la quantità di conoscenze che si considerano già acquisite da chi legge, altrimenti si dovrebbe partire ogni volta da zero e questo renderebbe tutto troppo lungo e noioso! Nel caso del codice che hai citato, il suo scopo è realizzare nel modo più semplice possibile una interfaccia SPI (https://it.wikipedia.org/wiki/Serial_Peripheral_Interface) per far comunicare Arduino con il controller del modulo LED. In poche parole l'interfaccia SPI consente di trasferire i dati un bit per volta impostando il bit da trasmettere sulla linea dati e facendo passare da LOW a HIGH stato della linea CLOCK. Dato che vogliamo trasferire un byte e i bit in un byte sono 8, c'è un ciclo che li trasferisce uno ad uno. Si usa una maschera di bit 0x80 sul valore in modo da avere come risultato 0 se il bit più significativo è 0 oppure 0x80 se è 1. Qui c'è una "scorciatoia" dato che la funzione digitalWrite al suo interno ha questo codice: if (val == LOW) { *out &= ~bit; } else { *out |= bit; } e in wiring.h si trova la definizione di HIGH e LOW: #define HIGH 0x1 #define LOW 0x0 Si può quindi direttamente usare il risultato ottenuto (0x00 o 0x80) come LOW e HIGH e impostare così la linea dati al giusto valore. Poi si alza il clock per far elaborare il dato al controller della matrice. Terminato il primo ciclo si fa uno shift dei bit del valore da trasmettere (in questo modo la maschera sarà applicata al 2° bit che ha preso il posto del primo) e si ricomincia fino a quando tutti i bit sono stati trasmessi. Spero che questo ti aiuti a chiarire!

21.06.2019 Guglielmo

Buongiorno Silvio La seguo con interesse e sono alle prime armi. Sono molto incuriosito dalle matrici a 8 led,ho fatto tutto come nella sua descrizione e sono riuscito ad ottenere un quadrato sulla mia matrice. Quello che non capisco è il valore dato ai registri come viene calcolato ....il valore (0xff) e (0x81) 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 dando il valore 1-2-4-8-16-32-64-128 ai led delle colonne... ringrazio anticipatamente per il chiarimento.. Guglielmo

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

In questo articolo abbiamo parlato di

Ne abbiamo parlato anche in...