Pilotare display led 7 segmenti mediante 74HC595 – esempio con PIC e Arduino
Ho già illustrato progetti ed esempi utilizzanti display a led a 7 segmenti (vedi paragrafo Link in fondo a questo articolo). Ho trovato qualche giorno fa in un cassetto un vecchio display a 3 cifre, in cui ogni cifra è pilotata da uno shift register 74HC595. Senza dilungarmi e ripetere cose già dette, vi rimando al vecchio articolo sugli shift register e faccio qui solo un breve riassunto di come funzionano.
Indice dei contenuti
Funzionamento del 74HC595
Questi circuiti integrati sono detti anche SIPO (Serial In Parallel Out) e come il nome suggerisce accettano in ingresso un dato trasmesso in seriale e lo propagano in parallelo su un determinato numero di uscite. In particolare il 74HC595 ha le seguenti caratteristiche:
- Ha 8 uscite che riescono a fornire 6mA ciascuna, per cui sono adatte a pilotare led, in particolare rossi, a cui i 6mA bastano e avanzano per poter fornire un’ ottima luminosità.
- Nella configurazione minima richiede soltanto 3 pin per poter essere pilotato (Segnale seriale, Clock e Latch)
- Possono essere collegati in cascata.
Ogni singolo bit che viene inviato sulla linea seriale, viene memorizzato mediante un colpo di clock sul pin SCLK (che io chiamo semplicemente Clock). Ogni colpo di clock fa scorrere i bit memorizzati di una posizione (dal MSB all’LSB) e il bit appena arrivato viene memorizzato nella posizione più alta. Un colpo di clock sul pin RCLK (Latch) “conferma” il dato abilitando il latch di uscita.
Per colpo di clock intendo questo: immaginate che la linea sia tenuta a livello basso costantemente. Ad un certo punto la si manda a livello alto per un istante, dopodichè la si riporta a livello basso. Normalmente le linee vengono tenute a livello alto mediante resistenze di pull-up ed è il microcontrollore che forza la linea a livello basso, per poi portarsi a livello alto o in alta impedenza per dare il colpo di clock
Il 74HC595 ha un pin per l’output enable: portando questo a livello alto, le uscite vanno in alta impedenza (uscite tristato), portandolo a livello basso, il latch di uscita si collega all’uscita fisica e il dato memorizzato nello shift register viene riportato sui pin (tutti i bit contemporaneamente). Nel modulino da me utilizzato, il pin output enable è collegato insieme al pin di latch: quest’ultimo difatti è tenuto normalmente a livello basso dal microcontrollore (come dicevo prima): il colpo di clock che serve ad abilitare le uscite, dura un istante e in quell’istante le porte di uscita vengono anche portate in alta impedenza (le cifre scompaiono per un istante impercettibile), dopodichè il latch torna a livello basso e così anche l’output enable, di conseguenza compare la cifra sul display.
Il vantaggio di questo sistema è evidente: non stiamo lavorando in multiplexing, per cui lato codice le operazioni sono davvero molto poche e in aggiunta le cifre appaiono più luminose perchè non si spengono e accendono una alla volta, ma sono tutte accese contemporaneamente. In più stiamo usando solo 3 pin per pilotare il display, quindi oltre alla semplicità software, abbiamo anche risparmiato numerosi I/O. Per contro, sicuramente l’assorbimento è maggiore proprio perchè i digits sono accesi tutti insieme, e in più a livello di sbroglio circuitale la situazione è più complessa perchè c’è un circuito integrato per ogni cifra, e bisogna aggiungere anche che ogni singola cifra deve avere le sue 8 resistenze per limitare la corrente nei led. Se si prende un modulino giù fatto, in cui vengono utilizzati componenti subminiaturizzati, la cosa è però fattibile con una spesa irrisoria. In un modulo in multiplexing, infatti, le resistenze sono solo 8 e vengono usate per tutte le cifre dato che i segmenti sono tutti in parallelo.
Il 74HC595 ha anche un pin di reset, indicato come SCLR (pin 10) che azzera il contenuto dello shift register. Tale pin viene tenuto normalmente a livello alto con una resistenza di pull-up per evitare reset indesiderati. Nel mio esempio questo pin non viene utilizzato.
Più 74HC595 possono essere collegati in cascata: si collega l’uscita seriale di uno (pin 9, indicato come QH’) all’ingresso seriale del successivo: quando si trasmette il dato al primo 74HC595, arrivati al nono bit, il primo bit inviato viene trasferito attraverso QH’ allo shift register successivo e così via.
Lo schema
Qui riporto lo schema del modulino da me utilizzato, che ho disegnato a mano:
Come vedete i segmenti dei digits non sono collegati alle uscite degli shift register aventi la stessa lettera (cioè il segmento A non è collegato all’uscita QA del latch e così via), questo probabilmente per questioni di sbroglio: magari era più facile collegarli in quel modo: in qualunque modo fossero stati collegati, avremmo comunque dovuto dichiararli, pertanto non ci cambia nulla. Il modulo, oltre ai due pin di alimentazione (+3.3V e GND) si collega al microcontrollore con 3 linee che nei software ho indicato come SR_DAT, SR_CLK_ e SR_LAT. Ricapitolando: la linea SR_DAT si occupa di trasferire i bytes dal MSB all’LSB, ogni bit viene trasferito mediante un colpo di clock sulla linea SR_CLK, alla fine del trasferimento tutti i dati vengono trasferiti sulle uscite con un colpo di clock su SR_LAT (purchè l’output enable sia a livello basso).
Il software
Come dicevo nel titolo, ho fatto un esempio sia per Arduino che per i microcontrollori PIC e i nomi delle funzioni utilizzate sono gli stessi, per cui quando descriverò le funzioni, a meno che non sia specificato, il funzionamento è identico per i due sistemi. In particolare su Arduino, per la serializzazione del dato, ho utilizzato la funzione shiftOut. Su Arduino tale funzione accetta 4 parametri in questo ordine: pin utilizzato per i dati (linea SR_DAT), pin utilizzato per il clock (linea SR_CLK), ordine dei bit (si scrive MSBFIRST se i bit devono essere trasferiti dal più significativo al meno significativo, LSBFIRST se viceversa) e byte da trasferire.
Sul PIC ovviamente non esistono queste semplificazioni introdotte da Arduino e ho scritto una funzione ShiftOut che accetta come parametro soltanto il byte da trasferire dato che tutti gli altri parametri si trovano in un file header (per comodità ho fatto due files -h e c- che contengono le funzioni per pilotare questo display). Ovviamente se avete un display diverso che usa lo stesso sistema è banale fare delle modifiche, che illusterò dopo.
Oltre alla funzione shiftout che serve solo a serializzare il singolo byte e trasferirlo sull’uscita, è presente una funzione DisplayUpdate che trasferisce 3 bytes (uno per ogni digit del display) e da il colpo di clock sulla linea del latch. Per comodità i 3 bytes sono caricati in un array dichiarato come globale anzichè passato come argomento alle funzioni.
Sono quindi presenti 3 funzioni:
-
display_write(uint8_t)
mostra un numero da 0 a 255 sul display (eventualmente potete cambiare il tipo dell’argomento passandolo a uint16_t per scrivere fino a 999, nel caso del PIC dovete cambiarlo sia nel prototipo di funzione, contenuto nel file header, sia nella definizione della funzione contenuta nel file C).
-
display_reset()
cancella il display (tutti i segmenti vengono spenti).
-
display_set(a,b,c)
dove a,b,c rappresentano il simbolo che deve apparire sul primo, secondo e terzo digit (per primo intendo quello più a sinistra). Per cui scrivendo display_set(1,2,3), sul display comparirà 123. Dato che ho definito anche delle lettere (quelle usate per il sistema esadecimale) potete anche scrivere, ad esempio, display_set(DIG_A,DIG_B,DIG_C) per visualizzare AbC sul display. Le definizioni dei simboli/lettere si trovano nel file header per il PIC e nell’unico file .ino per Arduino.
La demo fa semplicemente questo:
Adattamenti per tipi di display diversi
Se utilizzate un diverso tipo di display (purchè gestito da 74HC595 per singolo digit), tenete conto di queste modifiche principali:
- Definizione dei segmenti – Come dicevo quando ho illustrato lo schema, ogni segmento del display è collegato ad un bit dello shift register, nei sorgenti (.ino per Arduino e 7seg74HC595.h per il PIC) ho usato questa notazione:
#define SEG_A 0b00010000 #define SEG_B 0b00000010 #define SEG_C 0b00001000 #define SEG_D 0b01000000 #define SEG_E 0b10000000 #define SEG_F 0b00000001 #define SEG_G 0b00100000 #define SEG_P 0b00000100 // decimal point
Vedete che il segmento A (SEG_A), ha tutti 0 e un 1 in corrispondenza del bit dello shift register a cui è collegato, per cui l’operazione da fare per la modifica è banale: spostate l’1.
- Display ad anodo o a catodo comune – Nel mio caso il display è ad anodo comune, il che vuol dire che ogni segmento si accende inviandogli uno ‘0’ dato che l’anodo (positivo) è collegato fisso per ogni segmento. Per tale motivo nell’array che contiene la definizione dei simboli ho messo il segno di negazione per invertire i bit:
uint8_t digits[]= { // index value=digit showed on display ~(SEG_A|SEG_B|SEG_C|SEG_D|SEG_E|SEG_F), // 0=0 ~(SEG_B|SEG_C), // 1=1 ~(SEG_A|SEG_B|SEG_D|SEG_E|SEG_G), // 2=2 [...]
Se utilizzate un display a catodo comune, semplicemente rimuovete i segni di tilde da ogni riga. Nel caso del PIC, ogni simbolo ha anche il type-casting (uint8_t) davanti ogni riga per evitare un warning durante la compilazione (con il quale, comunque funziona tutto lo stesso, ma io non sopporto di vederli).
Se poi avete più di 3 display in cascata, dovrete modificare prima di tutto la funzione display_update per includere i digits aggiuntivi, dopodichè dovrete modificare anche le altre, in particolare display_set per farle accogliere anche le migliaia. In aggiunta, se l’ordine dei digits sui vostri display è diverso (nel mio caso il primo digit, cioè quello più a sinistra, è in realtà l’ultimo della catena, per cui il byte che gli deve essere inviato deve trovarsi in ultima posizione per poter apparire per primo), dovete anche invertire i cicli per la scrittura in display_update e in display_set.
Display compatibili in commercio
Attenzione: molti display in commercio, hanno, si, uno o due 74HC595, ma prevedono però il pilotaggio in multiplexing, quelli che invece possono essere utilizzati con il sistema appena illustrato possiedono un singolo 74HC595 per digit e spesso nelle inserzioni presentano la dicitura static driving ad indicare proprio che non sfruttano il multiplexing, ovvero ogni digit viene pilotato in maniera esclusiva tramite uno shift register dedicato. Esempi di inserzioni sono queste:
In particolare il primo tipo è simpatico, perchè vengono venduti i digits singoli, ognuno con il suo 74HC595 e resistenze montate al di sotto, e se ne possono collegare in cascata quanti se ne vogliono (immagino che un limite, comunque, ci sarà): gli elementi si possono collegare uno all’altro con dei jumpers per fare le prove (inserzione centrale). L’immagine della terza inserzione è relativa ad un display con 4 digits già collegati in cascata.
Downloads
L’esempio per Arduino l’ho provato con Arduino Leonardo, perchè questo funziona a 3.3V e il mio display richiede quella tensione. L’esempio per PIC l’ho provato su un PIC16F18446 montato su una scheda MPLAB Xpress/Curiosity Nano, ma trasportalo su un altro PIC è un’operazione indolore perchè c’è da modificare solo la definizione delle porte se decidete di collegare le linee ad altri I/O: avendo inoltre il progetto pronto per MPLAB Code Configurator, il cambio è molto veloce. In particolare il PIC è clockato a 32MHz e non è stato necessario mettere dei ritardi nelle funzioni di strobe (i clock).
Links
- Utilizzo degli Shift Register
- Pilotare 16 led con 3 IO e un 74HC595
- 16 led + 16 pulsanti con un PIC12F
- Pilotare una bargraph a led con i 74HC595
Questi altri esempi invece utilizzano i display a 7 segmenti, ma con la tecnica del multiplexing: