MAX7219 : libreria per usare display led a 7 segmenti con i microcontrollori PIC

Il giorno dopo in cui viene dichiarata la Pandemia a causa del Corona Virus, ho deciso di fare un regalo a tutti i lettori del mio blog, soprattutto a quelli che sono costretti a rimanere a casa e che quindi possono dedicarsi a quel meraviglioso hobby che è l’elettronica digitale.

In questo articolo illustro una libreria che ho scritto personalmente per utilizzare display a led 7-segmenti mediante un MAX7219 da microcontrollori Microchip® PIC®. In un articolo precedente abbiamo già visto come funziona questo circuito integrato prodotto dalla Maxim, adesso vediamo come poterlo utilizzare al meglio per scrivere su display 7 segmenti dai nostri amati PIC.

Supportate il mio lavoro acquistando un display ad 8 digits su Futura Elettronica o su Amazon

La libreria si trova sul mio repository Github (link alla fine dell’articolo) ed è distribuita con licenza AGPLv3: il che significa che, detto brevemente, la potete utilizzare per fare ciò che volete, ma se entra a far parte di un progetto, anche commerciale, siete obbligati a rilasciare e rendere pubblico il sorgente di tutti i moduli che la utilizzano, con la stessa  licenza e siete obbligati a documentare le modifiche fatte alla libreria.

La libreria include delle funzioni di scrittura per numeri interi, decimali a virgola fissa, stringhe di testo e consente di eseguire lo scrolling di una stringa di testo e di scrivere con effetto “cursore”. In questo video viene mostrato il funzionamento della demo inclusa nel codice:

Informazioni preliminari

La libreria consta di solo 2 files: MAX7219sz.h e MAX7219sz.c ed è pensata per essere utilizzata con l’MPLAB Code Configurator dal momento che utilizza alcune funzioni da esso create.

Avviate il Code Configurator (nella speranza che non vi siete persi questo articolo) ed impostate il clock

La libreria dovrebbe funzionare senza problemi sui microcontrollori PIC ad 8 bit con un clock fino ad 80MHz (che attualmente non mi risulta esistano, siamo arrivati a 64MHz con alcuni modelli di PIC18). Se la utilizzate con microcontrollori PIC che hanno un tempo di ciclo istruzione inferiore a 50nS bisogna inserire dei ritardi come spiegato nelle istruzioni su Github.

Scegliete 3 pin impostandoli come uscite digitali: ricordatevi quindi di disattivare la funzione analogica. Le resistenze di pull-up non sono necessarie a meno che non utilizziate qualche vecchio PIC che ha dei GPIO open-drain. Rinominate quindi i pin come:

  • MAX_DAT : pin del PIC da collegare al pin DIN del MAX7219 (pin n°1)
  • MAX_CLK : pin del PIC da collegare al pin CLK del MAX7219 (pin n°13)
  • MAX_LAT : pin del PIC da collegare al pin LOAD/CS del MAX7219 (pin n°12)

Nella demo tutte queste operazioni sono già pronte, la demo in particolare è realizzata per la scheda PIC16F15376 Curiosity Nano.

La libreria include i files device_config.h e pin_manager.h. Il primo header viene incluso perchè in esso il Code Configurator definisce la macro _XTAL_FREQ che è richiesta dalle librerie di delay del compilatore XC8, le quali vengono utilizzate da alcune funzioni non vitali (scrittura con effetto cursore, scrolling e lampeggio). Nel caso in cui vogliate utilizzare delle diverse librerie di delay o usate un PIC di altra fascia il cui compilatore non include le stesse librerie, potete rinunciare alle funzioni che ne fanno uso oppure modificare le 3 macro DELAYCURSOR, DELAYSCROLL e DELAYBLINK nel file header della libreria. L’header pin_manager.h invece è incluso per le funzioni di settaggio alto e basso dei pin: [PIN]_SetHigh() e [PIN]_SetLow().

Nel file header (MAX7219sz.h) è necessario modificare, eventualmente, questi parametri:

  • DIGITS : definisce il numero di digits collegati al MAX7219, 8 di default. Attualmente ho fatto test solo con 8 Digits: non assicuro il funzionamento con meno digits anche se ho cercato di essere quanto più accurato possibile parametrizzando tutto. Sicuramente per ora non funziona con più MAX7219 collegati in cascata, quindi con più di 8 digits, perchè non ho altri moduli a disposizione con cui provare. Ad ogni modo, come detto nell’articolo precedente, se usate meno di 8 digits ci sono delle considerazioni da fare sulla resistenza collegata al pin Iset del MAX7219 e col valore massimo di luminosità impostabile nel registro Intensity, altrimenti si rischia di bruciare display e circuito integrato.
  • MAXINTENSITY : imposta il valore massimo di luminosità, 9 di default (massimo valore ammissibile dal MAX7219). La funzione che setta il valore di intensità e quella che esegue il glowing utilizzeranno tale valore come limite massimo.

Detto questo non ci sono altri settaggi da fare.

Utilizzo della libreria

Inizializzazione

Prima di utilizzare un display a 7 segmenti pilotato da MAX7219 è necessario innanzitutto inizializzare il sistema come di consueto (funzione SYSTEM_Initialize() creata dal Code Configurator) e quindi richiamare la funzione MAX7210_init(). Altre volte è preferibile far partire la nostra applicazione con un test in maniera tale che possiamo essere sicuri che il nostro display non presenti segmenti bruciati: in questo caso possiamo richiamare prima la funzione MAX7210_test() che mette il MAX7219 in modalità test, dare un ritardo per essere sicuri di aver controllato bene il display e quindi solo successivamente richiamare la funzione MAX7210_init. Ricordo, difatti, che la modalità test, anche se viene disattivata, sovrascrive qualsiasi altra impostazione data in precedenza, per tale motivo dopo la funzione di test è necessario richiamare la funzione di Init che, oltre a disattivare la modalità test, ripristina  daccapo tutte le altre impostazioni di funzionamento.

Illustrerò di seguito solo le funzioni principali della libreria: per tutte le altre e per considerazioni più dettagliate vi invito a fare riferimento al repository Github che aggiorno e correggo man mano. Tenete conto che nelle funzioni i Digits sono numerati da 1 (quello più a destra) a 8 (quello più a sinistra) per seguire lo stesso ordine che ha il MAX7219. Le funzioni fanno tutte uso della modalità NODECODE.

Scrittura di numeri

La funzione da utilizzare per scrivere numeri è:

uint8_t MAX7219_putn(int32_t num, uint8_t decimals, uint8_t rightspace)

Tale funzione accetta 3 parametri e viene utilizzata per scrivere numeri interi con segno, senza segno o decimali a virgola fissa. Il numero normalmente viene stampato allineandolo alla destra del display, come fanno le calcolatrici. Il numero da stampare è definito da num, anche se tale parametro è definito come integer a 32bit, su un display da 8 digits non è comunque possibile visualizzare un valore superiore a 99’999’999 o inferiore a -9’999’999 dal momento che con questi numeri vengono occupati tutti e 8 i caratteri a disposizione: per tale motivo la libreria non tiene in considerazione un numero di cifre del numero da stampare superiore a 8.

Se intendete stampare numeri decimali, il parametro decimals vi viene in aiuto: questo parametro specifica quanti decimali devono essere stampati dopo il punto, stabilendo anche quale posizione deve avere il separatore decimale. Se diciamo che il numero num da stampare è, ad esempio, 245, e impostiamo il parametro decimals a 1, sul display verrà stampato 24.5.

In pratica, volendo stampare decimali, dovremo moltiplicare il nostro numero per una potenza di 10 pari al numero di decimali da stampare: devo scrivere 3.45? Moltiplico il numero per 100 (102) quindi ottengo 345 e imposto a 2 il numero di decimali. Il parametro rightspace stabilisce quanti spazi vuoti rimanere a destra del numero stampato: questo serve a spostare la scrittura del numero verso sinistra ma anche a lasciare spazio destinato alla scrittura delle unità di misura da apporre subito a destra del numero.

La funzione restituisce un intero che rappresenta il numero del digit più a sinistra occupato dalla scrittura del numero (segno negativo compreso): questo parametro viene utilizzato dalla funzione stessa nel caso in cui un numero stampato occupi meno digits del numero stampato in precedenza: in questo modo è possibile sapere da quale posizione cominciare a cancellare per fare in modo che scritture successive non si sovrappongano lasciando sul display numeri non significativi provenienti dalle stampe precedenti.

Esempio:

MAX7219_putn(2, 2, 1);

Numero da stampare=primo parametro=2. Numero di decimali=secondo parametro=2 => questo mi indica che il numero passato è stato moltiplicato per 100. Spazio da lasciare a destra del numero=terzo parametro=1. Questo stamperà il valore 0.02 lasciando un digit vuoto a destra.

Scrittura di stringhe di testo

La funzione da utilizzare per scrivere stringhe è:

MAX7219_puts(const char *s, bool cursor)

Tale funzione accetta 2 parametri. *s è il puntatore all’array di char: potete sia scrivere direttamente la stringa tra virgolette doppie, sia definire un’array prima della funzione e poi nella funzione puntare a questo. Il parametro cursor definisce se visualizzare (true) o no (false) il cursore. Scegliendo di visualizzare il cursore, la scritta apparirà con effetto telescrivente: man mano che il puntatore si sposta verso destra, il digit viene prima occupato da un segno di underscore (cursore) che poi viene sostituito dal carattere da stampare. La velocità di scrittura col cursore è definita dalla macro DELAYCURSOR nel file header. 

Chiaramente stiamo parlando di un display a 7 segmenti per cui non aspettatevi di visualizzare le lettere in maniera perfetta: la scrittura risulterà difatti un misto di maiuscole e minuscole mista a caratteri che per forza di cose somiglieranno a numeri. Insomma ci vuole anche un po’ di fantasia. Nel file header ho difatti definito un mio font cercando di arrangiarmi coi 7 segmenti a disposizione: sono riuscito ad ottenere un buon risultato lasciando fuori soltanto le lettere W e X, che, se utilizzate, saranno sostituite da uno spazio vuoto.

La funzione di scrittura si appoggia alla funzione MAX7219_putch che stampa un singolo char. Tale funzione rileva il char passato e fa delle considerazioni: le lettere vengono trattate in maniera uguale sia che siano maiuscole che minuscole (‘A’ e ‘a’ vengono considerate uguali), e vengono riconosciuti numeri scritti come char e alcuni simboli come segno meno, underscore, virgola e punto.

La scrittura è allineata a sinistra del display come è naturale scrivere nella nostra cultura (da sinistra verso destra).  Se è necessario stampare stringhe lasciando spazio sulla sinistra, ovvero spostare la scrittura più verso destra , è possibile utilizzare la funzione:

MAX7219_lputs(const char *s, bool cursor, uint8_t startpos)

Che accetta un terzo parametro, startpos, che permette di definire da quale digit cominciare a scrivere, tenendo conto che la posizione 1 è quella più a destra. La funzione MAX7219_puts in pratica richiama questa passando DIGITS come terzo parametro, che, ricordo, rappresenta il numero di digit a disposizione sul nostro display e quindi anche la posizione più a sinistra.

Le funzioni di scrittura stringa non restituiscono nulla ma salvano in memoria l’ultimo digit (quello più a destra) occupato dalla scrittura: questo dato viene utilizzato dalle funzioni di cancellazione con cursore.

Funzioni di cancellazione

Le funzioni di cancellazione resettano il contenuto dei digits. Ci sono 3 funzioni per cancellare il display:

MAX7219_clear()

Questa funzione cancella tutto il display: rileva la modalità attiva, che viene salvata in una variabile globale, e in base a questa setta tutti i digits a zero se la modalità attiva è la NODECODE o a 0x0F se la modalità attiva è la DECODE. In modalità DECODE, infatti, inviare uno 0 significa visualizzare proprio il numero zero, mentre digit completamente spento è rappresentato da 0x0F.

MAX7219_clearc()

Questa cancella il display utilizzando lo stesso effetto cursore della funzione di scrittura stringhe, ma parte da destra spostandosi verso sinistra, proprio come se stessimo utilizzando il pulsante DEL sulla tastiera, arrivando al digit posto più a sinistra. Il cursore partirà dall’ultimo digit occupato dalla funzione di scrittura stringhe. E’ la funzione di cancellazione complementare a MAX7219_puts.

MAX7219_lclearc(uint8_t startpos)

Questa funzione è uguale alla precedente con la differenza che la cancellazione si arresta al digit indicato da startpos. E’ la funzione di cancellazione complementare a MAX7219_lputs.

Scrolling

Ho implementato una funzione di scrolling: dal momento che il font c’era e ho incluso la possibilità di scrivere stringhe.. divertiamoci un po’! La funzione di scrolling è la seguente:

MAX7219_scroll(const char *s, bool appear, bool disappear)

Il primo parametro è il puntatore all’array di char da visualizzare. Il parametro appear imposta come deve essere avviato lo scroll: indica se i primi DIGITS (8) caratteri della stringa devono comparire subito (parametro impostato a false) oppure se bisogna iniziare lo scrolling con il display vuoto (o meglio: eventualmente occupato da altri caratteri dato che questa funzione non esegue prima la cancellazione del display) e quindi la stringa deve cominciare a comparire un po’ alla volta (true). In pratica se appear==true, vengono aggiunti DIGITS (8) spazi bianchi alla sinistra della stringa.

Il parametro disappear invece imposta come deve terminare lo scroll. Se true, la stringa continua a scorrere fino a scomparire dal display. Se false, la stringa si ferma lasciando visualizzati gli ultimi DIGITS (8) caratteri della stringa. In pratica se disappear==true, vengono aggiunti DIGITS (8) spazi bianchi alla destra della stringa.

La velocità dello scrolling è regolata dal valore della macro DELAYSCROLL.

Effetti visivi

E’ possibile far lampeggiare tutto il display (alternanza tra on e off) mediante la funzione:

MAX7219_blink(uint8_t times)

Il parametro times indica quanti cicli di OFF/ON eseguire. La velocità di lampeggio è definita dal valore della macro DELAYBLINK.

Se invece del semplice OFF/ON vogliamo un effetto glow, ovvero un lampeggio eseguito con valori di luminosità graduali, c’è la funzione:

MAX7219_glow(uint8_t times)

Anche qui il parametro times indica il numero di cicli. La velocità dell’effetto glow non è parametrizzata dato che usa delay di diverso valore.

Scrittura simboli

Come “simboli” intendo caratteri particolari che non fanno parte del font che ho definito e quindi che non possono essere riconosciuti automaticamente dalla funzione MAX7219_putch, utilizzata dalle funzioni di scrittura stringa. Ad esempio nel video della demo potete vedere che ad un certo punto, per mostrare la funzionalità dello spazio lasciato alla destra di un numero, ho incluso affianco al numero il simbolo di grado e la lettera C maiuscola, quando nel font il simbolo di grado non c’è e la C è definita, invece, per essere visualizzata come minuscola. Nel file header difatti ho definito a parte questi due simboli:

#define GRADE       SEGA|SEGF|SEGB|SEGG
#define CELSIUS     SEGA|SEGF|SEGE|SEGD

Vedete che ho semplicemente sommato (con OR, rappresentato dal simbolo pipe |) i vari segmenti del display, definiti più in alto, dopodichè utilizzo la funzione MAX7219_send per visualizzarli su un preciso digit:

// those 3 instructions will write 24.1°C
    MAX7219_putun(241,1,2);
    MAX7219_send(2,GRADE);
    MAX7219_send(1,CELSIUS);

La funzione MAX7219_send viene utilizzata sia in modalità DECODE che NODECODE in maniera uguale, solo che, ovviamente, il senso del secondo parametro varia a seconda della modalità: in modalità DECODE il MAX7219 si aspetta che il secondo parametro, ovvero il valore da assegnare al digit, sia un valore da 0 a 9 per rappresentare il rispettivo numero (codifica BCD) o un valore da 0x0A a 0x0E per rappresentare alcune lettere e segno – e 0x0F per spegnere il digit. In modalità NODECODE, invece, che è la modalità già attiva di default (ma se non lo è viene attivata automaticamente quando si usa una delle funzioni di scrittura numero o stringa), il secondo parametro rappresenta quali segmenti del digit devono essere accesi.

Dal momento che è attiva la modalità NODECODE (e se non lo era, sarebbe stata attivata indirettamente dalla funzione MAX7219_putun), la funzione MAX7219_send dirà al MAX7219 di accendere il simbolo del grado (ottenuto visualizzando i segmenti A,F,B e G) sul digit n°2 (il secondo a partire da destra) e così via.

La funzione di scrittura dei numeri, infatti, utilizza la funzione MAX7219_numch, che viene usata per scrivere un numero da 0 a 9 immesso come integer, in formato carattere ovvero utilizzando la modalità NODECODE e passando i segmenti da accendere. Per fare questo, la funzione rileva la modalità attiva e la cambia se non è quella giusta. La stessa cosa fa la funzione MAX7219_putch usata per scrivere un carattere ASCII.

Altre funzioni

Fino ad ora ho illustrato le funzioni principali, quelle che poi utilizzeremo normalmente nei nostri dispositivi, ma ce ne sono anche altre, che sono ben illustrate e documentate sul repository Github:

extern void MAX7219_init(void); // Initializes MAX7219
extern void MAX7219_send(uint8_t reg, uint8_t dat); // Sends byte data to register
extern void MAX7219_clear(void); // Clears MAX7219 shift register in both decode modes
extern void MAX7219_clearc(void); // Clears MAX7219 shift register using a cursor effect
extern void MAX7219_lclearc(uint8_t startpos); // Clears MAX7219 shift register using a cursor effect arriving at position defined by startpos
extern void MAX7219_putch(uint8_t digit, char ch, bool point); // Writes a char on the digit eventually turning on the comma/decimal point
extern void MAX7219_numch(uint8_t digit, uint8_t n, bool point); // Writes a number from 0 to 9 as char eventually turning on the comma/decimal point
extern void MAX7219_setDecode(void); // Sets the decode mode
extern void MAX7219_setNoDecode(void); // Sets the no-decode mode
extern void MAX7219_setIntensity(uint8_t val); // Sets display brightness from 0(lowest) to MAXINTENSITY (highest)
extern void MAX7219_test(void); // Turns on the test mode
extern void MAX7219_shutdown(bool yesno); // Turns on/off the display visualization
extern void MAX7219_puts(const char *s, bool cursor); // Writes a string using (true) or no (false) the cursor effect
extern void MAX7219_lputs(const char *s, bool cursor, uint8_t startpos); // Writes a string using (true) or no (false) the cursor effect, starting from startpos
extern uint8_t MAX7219_putun(uint32_t num, uint8_t decimals, uint8_t rightspace); // Writes an unsigned integer, returns most-left digit used
extern uint8_t MAX7219_putn(int32_t num, uint8_t decimals, uint8_t rightspace); // Writes an integer, returns most-left digit used
extern void MAX7219_scroll(const char *s, bool appear, bool disappear); // Writes a string using the scrolling effect
extern void MAX7219_glow(uint8_t times); // Glows the display
extern void MAX7219_blink(uint8_t times); // Blinks the display

Raccomandazioni 

Ho già detto ad inizio articolo con quale licenza ho distribuito la libreria, quindi inutile ripetere che se la usate, dovete farlo allo stesso modo in cui ho fatto io adesso, per una questione legale. Sarebbe cosa buona e giusta, nel caso che utilizzaste questa libreria per un vostro lavoro (illustrato anche eventualmente sui vostri blog), indicare l’autore (Giovanni Bernardo) includendo un link a questo articolo, dato che ci ho lavorato tanto e, come vedete, gratuitamente per il bene della comunità in cui credo e che mi ha permesso di imparare cose che desideravo e che mi sono state negate a tempo debito.

Se poi volete anche ringraziarmi in altro modo, non è necessario inviare per forza denaro, dato che ci sono tantissimi altri modi che ho elencato qui.

Links

Il primo link punta alla libreria su Github: è quello da consultare per avere la libreria sempre aggiornata e per leggere le istruzioni di uso. Il secondo link è quello alla demo per la scheda PIC16F15376 Curiosity Nano: se in futuro aggiornerò la libreria, non è detto che aggiorni anche questa demo. L’ultimo link è l’articolo precedente in cui illustro come funziona il MAX7219.

Se questo articolo ti è piaciuto, condividilo su un social:
Se l'articolo ti è piaciuto o ti è stato utile, potresti dedicare un minuto a leggere questa pagina, dove ho elencato alcune cose che potrebbero farmi contento? Grazie :)