Arduino: libreria per controllare uscite digitali e display su 74HC595 – it

 

In un precedente articolo vi ho presentato una nuova libreria per controllare in maniera semplice i chip 74HC595, sia per quanto riguarda le classiche uscite digitali che per quanto riguarda l’uso di display LCD. Ho aperto questa pagina per tenere traccia degli aggiornamenti che man mano verranno fatti, così non sono costretto a modificare l’articolo originale. Vi ricordo che la particolarità principale della libreria è la possibilità di gestire in CONTEMPORANEA sulla stessa linea fino a 256 chip 74HC595 con la possibilità di MESCOLARE in modo del tutto flessibile display e uscite digitali.

Schema

Prima di cominciare a postare le varie revisioni, vi riporto gli schemi di collegamento in modo da averli sempre sott’occhio. Come vedete ci sono due shift register, il primo collegato ad un display ed il secondo ad un ULN2803A, utile se ad esempio volete collegare dei relè. Il display deve essere collegato esattamente come nello schema, si notino le uscite libere numero 0 e 2 del corrispondete 74HC595, fatto voluto per mantenere la compatibilità con la libreria LiquidCrystal originale.

LCD e Out 74hc595_schem

Funzionamento della libreria

Ho preparato una tabella riassuntiva con tutte le funzioni della libreria. Nella prima colonna la funzione con i suoi parametri, nella seconda la versione in cui è stata implementata, nella terza è specificato se la funzione si applica ai display o alle uscite digitali (o entrambi), poi c’è una breve descrizione ed una colonna finale con un semplice esempio. So noti che le funzioni che cominciano con Set modificano i dati del buffer interno ma NON inviano i dati allo shift register, mentre le funzioni che iniziano per Send inviano i dati agli shift register.

Funzione Vers. Applicazione Descrizione Esempio
hc595(latch,clock,data,sr); 1.00 LCD / DIGOUT Prepara l’istanza all’oggetto hc595. I parametri sono i pin di latch,clock e data. L’ultimo indica il numero di shift register in serie. hc595 My595(12,11,13,4);
Set595Pin(D7,D6,D5,D4,D3,D2,D1,D0,sr) 1.00 DIGOUT Setta il buffer interno con i singoli bit (D7-D0) per lo shift register indicato nell’ultimo parametro (il primo è lo 0). Notare che viene modificato solamente il buffer interno senza inviare nulla allo shiftregister: per farlo usare la Send595(); My595.Set595Pin(1,0,1,0,1,0,1,0,3);
Set595Pin(byte,sr) 1.00 DIGOUT Setta il buffer interno usando un byte per settare in contemporanea tutti gli 8 bit. Il secondo parametro è il numero dello shift register, come sempre il primo è il numero 0. Notare che viene modificato solamente il buffer interno senza inviare nulla allo shiftregister: per farlo usare la Send595(); My595.Set595Pin(1,0,1,0,1,0,1,0,3);
Send595(); 1.00 LCD / DIGOUT Prende i dati dal buffer interno e lo invia alla catena di shift register per cui verranno aggiornate le uscite di TUTTI gli shift registers. My595.Send595();
Send595Pin(val,sr); 1.00 DIGOUT Funziona come la somma della Set595Pin e la Send595, nel senso che setta gli 8 bit e li invia direttamente agli shift register. Se avete solo uno shift register da modificare vi conviene usare questa funzione. Se dovete cambiarne più di uno in contemporanea vi conviene usare due o più Set595 e alla fine la Send595. My595.Send595Pin(170,3);
ResetDisplay(options,sr);la funzione è stata sostituita con la DisplayReset nella v1.01 1.00  LCD Effettua l’inizializzazione del display collegato allo shifregister sr. Le opzioni possibili sono:LCD595_BASIC_DISPLAY_INIT: Esegue il reset “standard” del display LCD595_USEFONT_5X10: utilizza il font 5×10 anzichè il 5×8 LCD595_MORELINES: usa display con più di una linea. My595.ResetDisplay(LCD595_BASIC_DISPLAY_INIT | LCD595_MORELINES ,3);
SetCursor(X,Y,Type,sr) 1.00 LCD Setta la posizione del cursore su X,Y (a partire da 0,0). sr come negli altri casi indica il numero dello shift register. Type di base deve essere impostato ad uno ed è predisposto per display le cui prime locazioni di memoria del primo carattere delle varie righe è 0×00,0×40,0×14,0×54. Nel caso siano invece 0×00,0×40,0×10,0×50 come in alcuni display 16×4, impostare il parametro a 2. Eventuali nuovi valori potranno essere creati per altre configurazioni.  My595.SetCursor(5,2,2,3);
SetDDRAM_Address(address,sr); 1.00 LCD Anzichè usare la SetCursor, è possibile usare questa funzione per settare in modo diretto l’indirizzo DDRAM a cui posizionare il cursore. In questo modo potete usare anche i display che hanno indirizzi atipici e non sono ancora supportati dalla SetCursor My595.SetDDRAM_Address(0x40,3);Posiziona il cursore su 0x40 ossia il primo carattere della seconda riga della stragrande maggioranza dei display. Nell’esempio viene settato sul quarto shift-register (3).
DisplayChar(char,sr); 1.00 LCD Scrive un carattere sul display nella posizione del cursore. char rappresenta il carattere e sr il numero dello shift register.  My595.DisplayChar(‘A’,3);
DisplayWrite(string,sr); 1.00 LCD Scrive la stringa “string” nella posizione del cursore sul display collegato allo shift register numero sr.  My595.DisplayWrite(“Hello!”,3);
SendLcdCommanf(opzione,sr) 1.00 LCD Permette di inviare comandi diretti al display. La voce options altro non è che un char ove possiamo settare gli 8 bits che lo compongono. Ci sono delle opzioni che ho preconfigurato:LCD595_DISPLAY_ON_CB: Accende il display accendendo il cursore e facendo lampeggiare il carattere corrispondente. LCD595_DISPLAY_ON_C: Accende il display accendendo il cursore ma senza fal lampeggiare il carattere corrispondente. LCD595_DISPLAY_ON_B: Accende il display settando il lampeggiamento della posizione cursore LCD595_DISPLAY_ON: Accende il display senza cursore LCD595_DISPLAY_OFF: Spegene il display. LCD595_DISPLAY_CLEAR: Pulisce il display facendo un clear. My595.SendLcdCommand(LCD595_DISPLAY_ON_C,3);
CreateChar(carattere,valori,sr) 1.00 LCD Permette di ridefinire i caratteri da parte dell’utente. carattere rappresenta il numero del carattere da modificare (0-7),valori contiene un array di 8 bytes con la relativa mappatura e sr è il numero di shift register byte MyChar[8] = {0,10,21,10,0,17,14,3}; My595.CreateChar(0,MyChar,3); My595.DisplayChar(0,3);
Lcd_SetFreePin(pin,valore,sr) 1.01 LCD Setta lo stato di uno dei pin liberi (2 e 0) del 74HC595 connesso al Display. pin rappresenta il pin (0 o 2), valore è lo stato (HIGH o LOW) ed sr è lo shift register. My595.Lcd_SetFreePin(2,HIGH,3);
SetBackLight(pin,valore,sr) 1.01 LCD Se connettete la retroilluminazione del display ad uno dei pin liberi del 74HC595 (tramite reistenza e transistor!), potete usare questa funzione per settare lo stato del pin corrispondente. In realtà non è altro che la Lcd_SetFreePin con un diverso nome. My595.SetBackLight(2,HIGH,3);
DisplayClean(sr) 1.01 LCD Cancella il contenuto del display collegato al register sr. My595.Lcd_Clean(3);
DisplayReset(sr) 1.01 LCD Sostituisce la precedente ResetDisplay My595.DisplayReset(3);

Revisioni

Versione 1.00 Libreria hc595 (809 download )

E’ la prima versione della serie, quella originale allegata all’articolo introduttivo.hc595Pelletta

Versione 1.01  Libreria hc595 v1.01 (902 download )

Dopo aver scritto l’articolo e resa disponibile la libreria, ho “pubblicizzato” il mio lavoro sul forum italiano di Arduino per cercare suggerimenti e persone volenterose a testare la libreria. E’ così che ho “conosciuto” il miticissimo “Pelletta” che s’è prestato a testare la libreria con due LCD ed un display a 7 segmenti. Lui mi ha fornito preziosi suggerimenti che ho inglobato in questo primo semplice aggiornamento. Vi allego una sua immagine durante il test:

  • Aggiunta compatibilità per la vecchia IDE
  • Aggiunta la funzione Lcd_SetFreePin che permette di gestire direttamento lo stato dei due pin liberi (0 e 2)  del 74HC595.
  • Aggiunta la funzione SetBackLight per attivare o disattivare la retroilluminazione attraverso l’uso di uno dei due pin liberi (0 o 2): ovviamente al pin devono essere stati collegati la resistenza ed il transistor a loro volta connessi alla retroilluminazione del display. Questa funzione è del tutto identica alla Lcd_SetFreePin a parte il nome per cui sono del tutto intercambiabili.
  • Aggiunta la funzione DisplayClean: ripulisce il contenuto del display.
  • Aggiunta nella cartella della libreria di alcuni esempi già pronti all’uso.
  • Per omogeneità di nomenclatura con le altre funzioni ho cambiato il nome della ResetDisplay con DisplayReset.
  • Ora la DisplayReset non interferisce più con i pin 0 e 2 liberi.

Versione 1.02    Libreria hc595 v1.02 (912 download )

  • Alcune ottimizzazioni nel codice
  • Ottimizzati i timing di comunicazione con gli lcd
  • Riportato in minuscolo il nome della cartella e dei files in quanto su sistemi linux il percorso dei files è case sensitive.
  • Aggiunta e supporto della libreria “FastArduino”.

In questa nuova versione ho fatto un grosso lavoro di ottimizzazione del codice finalizzato all’incremento della velocità di esecuzione. Chiunque abbia una conoscenza almeno discreta di Arduino, conosce i limiti di velocità della funzione digitalWrite e la possibilità di ovviare al problema con la programmazione diretta delle porte. Purtoppo la programmazione diretta non è utilizzabile quando il pin non è stabilito a priori mentre la libreria che vi fornisco vi permette di usare qualunque pin. Per rendervi conto delle grandezze di cui parliamo, dai test che ho effettuato la programmazione diretta impiega circa 0.3 microsecondi per essere eseguita mentre la digitalWrite fornita con le API Arduino impiega quasi 6 microsecondi, ossia 15-20 volte tanto. In questa versione ho lavorato a lungo, scrivendo diverse versioni della libreria, alcune in C puro ottimizzato all’estremo, altre in Cpp, sia con codice built-in che con chiamate a funzioni esterne. Decidere poi quale adottare non è stato semplice, da una parte avevo il codice più compatto e veloce del C, dall’altra la versione più “user-frendly” del cpp. Dopo una settimana di prove intense ho preso una decisione. La libreria resta più o meno l’originale cpp con alcune ottimizzazioni, ma ho aggiunto una libreria esterna chiamata FastArduino che contiene versioni riviste della shiftOut e della digitalWrite. In questo modo oltra ad aumentare la velocità della libreria, avete due nuove funzioni che potete usare nelle vostre sketch o altre librerie per velocizzarne il funzionamento. Gran parte dell’ottimizzazione si basa sull’ignorare gli interupt ed il pwm che per forza di cose non sono usati nelle porte dello shift register. La FastdigitalWrite da me implementata impiega circa 3.5microsecondi, molto meglio della versione originale anche se quasi 10 volte più lenta della programmazione diretta delle porte. La sketch d’esempio che scrive dei caratteri a “zig-zag” su un display lcd, passa dagli originali 7268 microsecondi  ai 4872 della versione otttimizzata. Si noti che la libreria può funzionare anche senza la “FastArduino”, in questo caso vengono utilizzate le funzioni originali, in modo da poter evitare eventuali problemi di compatibilità.

Versione 1.03  Libreria hc595 v1.03 (1775 download )

  • Marcato incremento di velocità della gestione dei display

Non ho fatto in tempo a presentarvi la precedente versione che ho scoperto un banale baco che una volta corretto ha drammaticamente aumentato la velocità di gestione dei Display. La scoperta è nata da un commento di un utente che mi ha spinto a vedere qual’era la velocità della LiquidCrystal. La solita sketch d’esempio viene eseguita dalla LiquidCrystal in circa 3676 microsecondi, il che mi ha fatto approfondire la cosa. Trovato il baco, risolto ed i tempi della mia hc595 sono passati dai precedenti 4872 agli attuali 1120.

Versione 1.04 – Non ancora disponibile

  • Ulteriore significativo miglioramento della velocità nella gestione dei display

Con la nuova versione la solita sketch d’esempio passa dai precedenti 1120ms agli attuali 844

La libreria è stata incorporata nel McMajan library pack che trovate a questa pagina.

  12 Responses to “Arduino: libreria per controllare uscite digitali e display su 74HC595 – it”

  1.  

    Un utente mi ha chiesto come fare per visualizzare variabili di tipo float sul display. Scrivo qui la risposta per venire incontro a tutti coloro che hanno poca dimestichezza con il C. La mia libreria contiene solo due funzioni per “stampare” scritte sul display, una per i caratteri, una per le stringhe. Per scrivere un valore numerico dobbiamo prima convertirlo in una stringa. Come fare? Prima di tutto dobbiamo definire un array di caratteri sufficientemente lungo a contenere il valore. Ad esempio se dovessimo scrivere 180.45 ci vorrà una stringa di almeno 6 caratteri.
    Non sapendo a priori quante cifre andiamo ad impiegare, dobbiamo usare una stringa in grado di contenere qualsiasi cifra rappresentabile da Arduino, quindi 15 caratteri andranno bene.
    A questo punto abbiamo numerose possibilità ma, visto che l’utente che mi ha posto il quesito non conosce molto berne il C/C++, mi concentrerò sulla via più semplice che è l’uso di una funzione già bella che pronta anche se utilizza circa 1.6K di memoria flash. La funzione in questione è la dtostrf() che accetta 4 parametri che in sequenza sono: valore da convertire,larghezza minima della stringa, numero di cifre dopo la virgola e il puntatore all’array di caratteri

    Vediamo un esempio:

    float fvalue=1000;
    char fstring[15];

    void setup() {
    Serial.begin(9600);
    while (!Serial)
    {
    ; // wait for serial port to connect. Needed for Leonardo only
    }

    Serial.println(“Conversione valori float”);
    }

    void loop()
    {
    dtostrf(fvalue,6,4,fstring) ;
    Serial.println(fstring);
    delay(1000);
    fvalue+=0.13485;
    }

    Questo piccolo programmino imposta un float a 1000 e ad ogni ciclo aggiunge 0.13485 stampandone il valore sulla porta seriale, ogni secondo. L’output sarà:

    Conversione valori float
    1000.0000
    1000.1348
    1000.2697
    1000.4045
    1000.5393
    1000.6741
    1000.8090
    …..

    Se vogliamo trasformare questo esempio perchè scriva i valori sul display LCD, dobbiamo usare la funzione DisplayWrite (e non la DisplayChar). Ora non ho un display pronto per fare la prova, ma il seguente listato dovrebbe funzionare:

    #include
    #include

    float fvalue;
    char fstring[15];

    hc595 My595(12,11,13,2); // latch,clock,data,number of 74hc595

    void setup()
    {
    fvalue=1000;
    My595.DisplayReset(LCD595_BASIC_DISPLAY_INIT | LCD595_MORELINES ,0); // multiline display
    My595.SetCursor(0,0,1,0); // cursor at 0,0, display type 1 on first 595
    My595.DisplayWrite(“Setup is OK…”,0); // Write string
    delay(1500); // waiting for…
    My595.DisplayClean(0); // clear display

    }

    void loop()
    {
    dtostrf(fvalue,6,4,fstring) ; // qui viene convertito il valore float in stringa
    My595.SetCursor(0,0,1,0); //setto la posizione del cursore
    My595.DisplayWrite(fstring,0); // scrivo il vsalore sul display

    fvalue+=0.13485; // incremento il valore float
    delay(500); // aspetto mezzo secondo e riparto con il ciclo
    }

    Spero di essere stato chiaro ed esauriente.

  2.  

    Ciao ti ringrazio per il lavoro svolto perchè sto cercando di implementarlo al posto della classica ” liquid crystal.
    Per ora tutto bene con LCD, a parte che non capisco se posso cancellare solo una riga ……
    Vorrei chiederti come posso fare per sostituire l’accensione e lo spegnimento di ogni singola uscita del 595 come si fa con la arduino,
    per spiegarmi meglio :digitalWrite(ledGiallo, HIGH)……digitalWrite(ledGiallo, LOW);
    ho capito che se uso My595.Send595Pin(0,1), ma li spengo tutti, vorrei capire come spegnerli individualme.
    grazie

    •  

      Non mi prendere in gito ma non la uso da un po’ (sto sviluppando un groooosso progetto) e non me lo ricordo nemmeno io:-) Ma leggendo la documentazione che ho scritto per gli smemorati come me vedo che la Send595Pin(val,sr); setta ed invia TUTTI i bit dello specifico shift register, quindi il primo parametro è un numero da 0 a 255 che rappresenta la SOMMA di tutti gli 8 bit, ognuno dei quali può essere 0 o 1. Quindi se vuoi modificare uno solo dei bit devi usare gli operatori booleani (spero tu sappia di cosa parlo), in alternativa puoi usare la seconda forma Set595Pin(D7,D6,D5,D4,D3,D2,D1,D0,sr) dove ogni Dx rappresenta ogni singola uscita e può essere quindi impostato a 0 o 1.Questa non invia direttamente i dati allo shift register per cui deve essere accompagnata dalla Send595();

      Spero di essere stato sufficientemente chiaro, in caso riscrivimi pure….

  3.  

    non vedo perchè dovrei prenderti in giro…….
    comunque non so cosa siano gli operatori booleani ne come si gestiscono.
    non sarebbe male se tu mi facessi qualche esempio, perchè a sto punto uso questo comando : My595.Set595Pin(0,0,0,0,0,0,0,0,1); // Tutti Spenti
    My595.Send595();, solo che tutte le volte dovrei mettermi a contare sul monitor la posizione del bit da accendere o spegnere.
    guardavo appunto se ci fosse la possibilità di avere qualcosa di più pratico e specifico tipo digitalWrite(ledGiallo, HIGH)……digitalWrite(ledGiallo, LOW);

    grazie

  4.  

    visto che ci sei, vorrei mandarti uno sketch, che magari ti potrebbe tornare utile da pubblicare,da controllare, perchè secondo me c’è qualcosa che non va
    se mi dai una mail te lo mando

    grazie ancora

  5.  

    potreste darmi dei consigli su un algoritmo per invertire l’ordine dei bit in un byte?

  6.  

    Buona sera sto cercando di fare un orologio con delle VDF/14 16 segmenti che periodicamente mi dia ora, poi data , poi temperatura e umidità usando dei 74hc595 e un rtc DS3231
    il problema è che sono riuscito per provare a visualizzare un contatore con le VDF,
    ma poi quando devo visualizzare l’orologio mi sono bloccato e non riesco a farlo.
    se vuoi puoi vedere questi video che ho pubblicato:
    https://youtu.be/aRmNmZihD8o
    https://youtu.be/KZMISO55mGQ
    mi puoi dare qualche suggerimento?
    ho visto che spieghi molto dettagliatamente i lavori che fai.
    grazie.
    Marco

    •  

      scusa i tempi….sei un po’ troppo generico nella domanda. Cos’è che non riesci a fare? il contatore funziona bene per cui tutta la parte di visualizzazione direi che la padroneggi senza problemi. Non devi far altro che “scrivere” le singole cifre dell’ora. La cosa più semplice è gestire l’ora come una stringa hhmm e poi prelevare i singoli caratteri (es c[0], c[1], etc) e “scriverli” come fai con il contatore.