Ago 052013
 

In un precedente articolo avevamo visto come collegare e controllare un display HD44780 compatibile con Arduino, sia utilizzando la libreria LiquidCrystal che andando ad operare manualmente sulle uscite di Arduino. Oggi vediamo come ridurre considerevolmente il numero di pin utilizzati andando a sfruttare un 74HC595 che abbiamo già studiato in un precedente articolo. Il concetto è semplice, infatti anzichè collegare tutti i pin in parallelo facciamo una trasmissione seriale e poi tramite il 74HC595 smistiamo i segnali per tutti i pin. Se avete letto i precedenti articoli, questo vi sembrerà quasi banale. Andiamo infatti ad utilizzare 3 pin digitali che corrisponderanno al clock register, storage register e serial data, in questo modo inviamo i dati al 74HC595 che a sua volta convertirà in un segnale parallelo che distribuisce ai pin del display.
Il collegamento è riportato qui sotto. Da una parte abbiamo Arduino che comunica con lo shif register e questo che con le sue uscite si interfaccia con il display.

Qui sotto anche lo schema elettrico:

La cosa più complessa è quella di gestire correttamente l’uso dei pin connessi al display. Se avete letto la parte in cui è spiegata in dettaglio l’inizializzazione del display, ricorderete che la procedura è non tanto complessa quanto un po’ lunga, rigida e sicuramente cervellotica. A questa complessità si aggiunge quella di inviare i dati in maniera seriale, ma il vantaggio di risparmiare 3 pin digitali non è cosa da poco quando le risorse hardware scarseggiano.

LiquidCrystal modificata

Come sempre qualcuno è già passato prima di noi attraverso questa problematica, per cui esiste una versione modificata della LiquidCrystal che permette di lavorare direttamente attraverso il 74HC595 senza ulteriori scervellamenti. Potete scaricarla da questa pagina (alternativamente la trovate qui), dopodiché andate a sostituire quella originale con questa modificata (nella cartella libraries del software Arduino). La stessa libreria modificata permette anche l’utilizzo di display con connessione SPI, tecnica di cui non abbiamo ancora mai parlato e che quindi per ora tralasceremo. Ci basti sapere che è un protocollo nato per far comunicare dispositivi diversi attraverso tre conduttori, e tra i molti possibili dispositivi con questa interfaccia esistono anche alcuni display. Il supporto al protocollo SPI porta però ad una limitazione, ossia siamo costretti ad utilizzare il pin 13 per il clock ed il pin11 per i dati (MOSI). Il latch pin invece può essere lasciato al valore base (10) o essere modificato con uno qualsiasi dei restanti pin. Una volta fatti i collegamenti e sostituita la libreria originale non dobbiamo far altro che programmare la sketch. Rispetto a quanto visto nel primo articolo sui display lcd cambia unicamente l’istanzializzazione dell’oggetto LiquidCrystal in quanto andiamo a specificare unicamente il pin usato come latch (nell’esempio il 10). Qui sotto vi incollo l’esempio HelloWord che trovate nella libreria a cui ho modificato unicamente il latch pin e l’impaginazione.

#include <LiquidCrystal.h>
// initialize the library with the number of the sspin 
//(or the latch pin of the 74HC595)
LiquidCrystal lcd(10);
void setup() 
{
 lcd.begin(16, 2); // set up the LCD's number of columns and rows: 
 lcd.print("hello, world!"); // Print a message to the LCD.
}
void loop()
{
 // set the cursor to column 0, line 1
 // (note: line 1 is the second row, since counting begins with 0):
 lcd.setCursor(0, 1);
 lcd.print(millis()/1000); // print the number of seconds since reset:
}

Fin qui nulla di particolarmente difficile. Se il vostro scopo è usare questa tecnica nel modo più semplice possibile avete già tutte le informazioni necessarie a creare il vostro piccolo progetto per cui potete fermarvi anche a questo punto. Se invece volete approfondire ulteriormente, ora vedrò di implementare in modo diretto i comandi al display, esattamente come avevamo fatto nel precedente articolo. Vi chiederete perché perdere tempo ad implementare qualcosa che troviamo già funzionante ma le risposte non sono difficili da trovare. Ad esempio oltre al display potreste voler gestire in serie anche delle uscite digitali aggiungendo altri 74HC595 ma non mi risulta esista alcuna libreria pronta in grado di farlo. Allo stesso modo potreste decidere di abbandonare Arduino per passare ad un PICC, ma se non conoscete bene la teoria che ci sta sotto non riuscirete mai a convertire il vostro progetto. Un altro motivo potrebbe essere che i pin 11 e 13 vi servono già per altri motivi e volete usarne di altri.

Implementazione passo a passo

Rispetto a quanto visto nel precedente articolo dobbiamo andare a modificare la funzione che settava i pin del display e quella che alzava il pin Enable con le corrette tempistiche al fine di far interpretare al display il comando inviato (PulseEnable). Nella scorsa puntata le due funzioni si presentavano in questo modo:

void SetCommandPin(bool D7,bool D6,bool D5,bool D4)
{
 digitalWrite(pin_D7, D7); 
 digitalWrite(pin_D6, D6); 
 digitalWrite(pin_D5, D5); 
 digitalWrite(pin_D4, D4); 
}
void PulseEnable(void)
{
 digitalWrite(pin_E, LOW);
 delayMicroseconds(1); // >450ns
 digitalWrite(pin_E, HIGH);
 delayMicroseconds(1); // >450ns
 digitalWrite(pin_E, LOW);
 delayMicroseconds(100); // > 37us
}

Invece il listato iniziale di prova per la dimostrazione del funzionamento dello shift register si presentava così:

const int latchPin = 12; //Pin connected to latch pin (ST_CP) of 74HC595
const int clockPin = 11; //Pin connected to clock pin (SH_CP) of 74HC595
const int dataPin = 13; //Pin connected to Data in (DS) of 74HC595
void setup() 
{
 pinMode(latchPin, OUTPUT);
 pinMode(dataPin, OUTPUT); 
 pinMode(clockPin, OUTPUT);
}
void loop()
{
 for (int i=0;i<8;i++) 
 {
 digitalWrite(latchPin, LOW);
 shiftOut(dataPin, clockPin, MSBFIRST, 1<<i);
 digitalWrite(latchPin, HIGH);
 delay(90); 
 }
}

Ora dobbiamo fondere le due tecniche per creare un progetto unico. Di ques’ultimo listato teniamo sicuramente la definizione dei tre pin e il loro settaggio come output nel setup. A questo punto sorge una piccola difficoltà. Usando lo shift register ogni volta impostiamo tutti i pin per cui anche nel caso in cui dobbiamo modificarne solamente uno, siamo costretti ad inviare tutti i valori. Ne consegue che indipendentemente dal pin da settare il nostro software deve tenere traccia anche dei valori dei restanti pin. Per fare ciò usiamo una variabile, più nello specifico possiamo usare un semplice char che con i suoi 8 bit è in grado di contenere il valore di tutti i singoli pin. Per settare e leggere i singoli bit possiamo sfruttare le comode bitWrite e bitRead incluse fra le funzioni base di Arduino. Se ora voglio modificare la funzione SetCommandPin andrò prima a modificare i singoli bit della variabile dopodiché invio il tutto attraverso lo shift register. Vi faccio notare che nello schema sopra proposto abbiamo Q0 e Q2 liberi, non collegati, su Q1 abbiamo RS, su Q3 il pin Enable e da Q4 a Q7 i pin D4-D7. Ho usato questo schema per mantenere la compatibilità con la LiquidCrystal modificata, ma se non avete intenzione di usarla siete liberi di fare tutte le modifiche che volete. In particolare i pin 0 e 2 liberi del nostro 74HC595 ci permettono di aggiungere funzioni extra, ad esempio potremmo controllare l’accensione e spegnimento della retroilluminazione tanto per fare un esempio. Da quanto detto la nuova SetCommandPin potrebbe diventare così:

void SetCommandPin(bool D7,bool D6,bool D5,bool D4)
{
 bitWrite(serialdata,7,D7);
 bitWrite(serialdata,6,D6);
 bitWrite(serialdata,5,D5);
 bitWrite(serialdata,4,D4);
 digitalWrite(latchPin, LOW);
 shiftOut(dataPin, clockPin, MSBFIRST, serialdata);
 digitalWrite(latchPin, HIGH);
}

Nulla di nuovo: nelle prime quattro istruzioni settiamo i bits dal 4 al 7 nella variabile serialdata, dopodichè spediamo questa allo shiftregister nello stesso identico modo visto nel precedente articolo. Visto che l’invio allo shift register sarà un’operazione molto sfruttata, possiamo spostare le relative istruzioni in una sottofunzione che possiamo richiamare in tutte le funzioni che hanno bisogno di comunicare con il 74hc595.

Non ci rimane che modificare la PulseEnable sostituendo le chiamate dirette digitalWrite con il settaggio del relativo bit sulla variabile serialdata ed il relativo invio allo shifregister per il quale ho creato la sottofunzione Send595 come prima detto. La nuova funzione e la Send595 saranno simili a quanto segue :

void Send595(void)
{
 digitalWrite(latchPin, LOW);
 shiftOut(dataPin, clockPin, MSBFIRST, serialdata);
 digitalWrite(latchPin, HIGH); 
}

void PulseEnable(void)
{
 bitWrite(serialdata,3,LOW); // setta il pin enable a low
 Send595();
 delayMicroseconds(1); // >450ns
 bitWrite(serialdata,3,HIGH); // setta il pin enable a high
 Send595();
 delayMicroseconds(1); // >450ns
 bitWrite(serialdata,3,LOW); // setta il pin enable a low
 Send595();
 delayMicroseconds(100); // > 37us
}

A questo punto dobbiamo  preparare la fase di inizializzazione che però è identica a quanto già visto nel pregresso articolo. L’unica cosa a cui dobbiamo stare attenti è che ogni volta che abbiamo l’esigenza di modificare il pin RS, ossia passare da comandi a dati e viceversa, dobbiamo prima settare la variabile serialdata e poi eseguire un Send595. La sketch finale sarà:

const int latchPin = 12; //Pin connected to latch pin (ST_CP) of 74HC595
const int clockPin = 11; //Pin connected to clock pin (SH_CP) of 74HC595
const int dataPin = 13; //Pin connected to Data in (DS) of 74HC595

char serialdata=0;

void setup() {
 pinMode(latchPin, OUTPUT);
 pinMode(dataPin, OUTPUT); 
 pinMode(clockPin, OUTPUT);

 //procedura di reset
 delayMicroseconds(50000); // attesa iniziale....40ms ( ho usato tempi abbondanti

  bitWrite(serialdata,1,LOW); // comando con RS=LOW
  bitWrite(serialdata,3,LOW); // mi accerto che il pin E sia allo stao LOW prima di cominciare
  Send595(); // in realtà le due sopra non servivano visto che serialdata era già impostato a 0

 SetCommandPin(0,0,1,1); // invio 0011
 PulseEnable();
 delayMicroseconds(4500); // 4.1ms

  SetCommandPin(0,0,1,1); // reinvio 0011
  PulseEnable(); 
  delayMicroseconds(150); // 100us

  SetCommandPin(0,0,1,1); // reinvio 0011
  PulseEnable(); 

  SetCommandPin(0,0,1,0); // ...settiamo i 4 bit...
  PulseEnable();  

  SetCommandPin(0,0,1,0); // ...finalmente possiamo inviare il comando completo
  PulseEnable(); 
  SetCommandPin(1,1,0,0); // ..... i restanti quattro per settare font e righe
  PulseEnable(); 

 // accendo il display
  SetCommandPin(0,0,0,0); // primi 4
  PulseEnable();
  SetCommandPin(1,1,1,1); //ultimi 4 diplay acceso, cursore presente e lampeggiante
  PulseEnable();

 //clear
  SetCommandPin(0,0,0,0); // primi 4
 PulseEnable();
  SetCommandPin(0,0,0,1); 
 PulseEnable();

}
void loop()
{
 // scrivo una lettera
  bitWrite(serialdata,1,HIGH); // porto RS ad HIGH per indicare l'invio di DATI
  Send595();
  SetCommandPin(0,1,0,0); // primi 4
  PulseEnable();
  SetCommandPin(1,0,0,0); // ultimi 4
  PulseEnable();
   delayMicroseconds(50); // 37us

   delay(1000);

}

void SetCommandPin(bool D7,bool D6,bool D5,bool D4)
{
 bitWrite(serialdata,7,D7);
 bitWrite(serialdata,6,D6);
 bitWrite(serialdata,5,D5);
 bitWrite(serialdata,4,D4);
 Send595();
}

void Send595(void)
{
 digitalWrite(latchPin, LOW);
 shiftOut(dataPin, clockPin, MSBFIRST, serialdata);
 digitalWrite(latchPin, HIGH);  
}

void PulseEnable(void)
{
  bitWrite(serialdata,3,LOW); // setta il pin enable a low
  Send595();
  delayMicroseconds(1);   // >450ns
  bitWrite(serialdata,3,HIGH); // setta il pin enable a high
  Send595();
  delayMicroseconds(1);    //  >450ns
  bitWrite(serialdata,3,LOW); // setta il pin enable a low
  Send595();
  delayMicroseconds(100);   //  > 37us
}

Come per l’articolo precedente viene eseguita la procedura di inizializzazione e poi “scritta” una “H” sul display. A questo punto il lavoro di base è completo. Vi passo però alcuni spunti interessanti. Ad esempio perchè non usare due 74HC595 in serie per comandare due display in contemporanea? Allo stesso modo potete aggiungere delle uscite digitali sempre impiegando gli stessi tre pin digitali. Probabilmente scriverò una libreria apposta per gestire uscite digitali e display attraverso multipli 74HC595, ma per ora non è pronto nulla per cui vi lascio con la curiosità di sperimentare questa tecnica anche con i vostri progetti.