Dopo aver visto tutta (o quasi) la teoria che c’è alla base del pilotaggio dei display LCD basati sul controller Hitachi HD44780 (o compatibile) eccoci qui pronti a scrivere qualche programmino di esempio da caricare sul nostro picmicro.
In questa lezione sfrutteremo una libreria per il pilotaggio degli LCD scritta dal sig. Andrea Bonzini e disponibile anche sul sito di Sergio Fiocco.
Tale libreria prevede il controllo dell’LCD in modalità 4 bit e in sola lettura (il pin RW, difatti, andrà collegato a massa e non sarà pilotabile via software). Ho apportato delle piccole modifiche a tale libreria in maniera da avere disponibili anche altre funzioni. L’elenco delle modifiche da me apportate è descritto nel file changelog.txt allegato alla libreria, ho inoltre incluso la libreria originale.
Se spulciate nella cartella “samples” dell’installazione Hitec-C, troverete una routine simile in “LCDemo”
Passiamo a vedere come montare il circuito.
Schema e montaggio del circuito
Anche se in apparenza può sembrare complicato, lo schema in realtà è abbastanza semplice, non vi impressionate:
A parte le porzioni di circuito che abbiamo già visto nelle precedenti lezioni, ci sono le seguenti novità:
- I 4 led: ogni led avrà la sua resistenza da 330Ohm in serie, il terminale lungo del led andrà verso il pic (con la resistenza in serie), il terminale corto verso massa. Il led contrassegnato come LED1 andrà collegato ad RB7 fino ad arrivare a LED4 collegato a RB4.
- I 3 pulsanti: sono contrassegnati sullo schema come BTNOK (collegato a RB2), BTNDOWN (collegato a RB1) e BTNUP (collegato a RB0). I 3 pin del picmicro ai quali sono collegati i pulsanti sono costantantemente alimentati ognuno da una propria resistenza di pullup da 1KOhm. Premendo il pulsante, il pin relativo viene a trovarsi a massa, quindi intercetteremo via software la pressione del pulsante verificando che il pin relativo sia a livello logico basso.
Se si abilitano le resistenze di pullup sulle porte B, non è necessario includere le resistenze da 1KOhm.
- Cicalino: collegato su RE1. Per questa prova è assolutamente necessario utilizzare un cicalino non autooscillante dal momento che genereremo un diverso tono per ogni funzione. La resistenza in serie al cicalino può anche essere omessa.
- Il display LCD. Controllate bene la piedinatura del display che avete in vostro possesso. Il terminale GND va a massa, il terminale Vcc va ai 5 volt positivi, il terminale V0 (contrasto) va collegato al cursore (pin centrale) di un trimmer da 10KOhm, le altre due estremità del trimmer vanno una ai 5 volt e l’altra a massa.
Il terminale dell’LCD contrassegnato come RS (Register Select) lo collegheremo sul pin 21 del 16F877 (RD2).
Il terminare dell’LCD E o EN (Enable) va al pin 22 (RD3)
I terminali da D4 a D7 vanno rispettivamente collegati ai pin da 27 a 30 (da RD4 a RD7)
Se vogliamo, possiamo collegare all’alimentazione i due pin del display destinati alla retroilluminazione.
I rimanenti pin del display vanno posti a massa.
Se utilizzate la scheda Freedom di Mauro Laurenti, è disponibile il connettore LCD, i quali pin portano già alle porte del picmicro qui definite.
La libreria per il pilotaggio dell’LCD
La libreria (lcd.c) è scaricabile in fondo all’articolo insieme ai programmi presentati in queste pagine, è caldamente raccomandata la lettura del codice sorgente da effettuarsi confrontandola con la prima parte di questa lezione: solo così potete capire perfettamente tutta la tecnica che c’è alla base.
Tale libreria comprende numerose utili funzioni, basterà semplicemente includerla nel nostro programma nel modo consueto:
#include "lcd.c"Tale libreria ha bisogno delle routine Delay per poter funzionare, tali routine sono comunque incluse nel download e sono le stesse presenti nelle lezioni precedenti.
Riassumo qui le funzioni disponibili:
LCD_INIT();
Serve ad effettuare l’inizializzazione del display come detto nella parte precedente, va richiamata una solta volta e prima di qualsiasi altra funzione, altrimenti il display non sarà in grado di operare.
LCD_CLEAR();
Effettua la pulizia del display, ovvero cancella il contenuto della DDRAM e porta il cursore nella prima posizione (riga 1, colonna 1).
LCD_CMD(char);
Invia un comando al display, tale funzione viene utilizzata, ad esempio da LCD_CLEAR e da altre, è anche possibile inviare comandi personalizzati, come ad esempio i codici numerici associati ai vari entry mode, sarà comunque una funzione molto poco sfruttata direttamente.
LCD_GOTO(riga,colonna);
Posiziona il cursore per la scrittura alla riga e alla colonna specificata. La numerazione è in base 1, per cui scrivendo:
LCD_GOTO(1,1);
Il cursore si posizionerà alla prima riga, prima colonna.
LCD_PUTCH(char);
Scrive un unico carattere sul display, char ovviamente può essere immesso in qualsiasi formato numerico (decimale, binario -anteponendo come sempre 0b-, esadecimale -anteponendo 0x- o formato carattere -scrivendo il carattere tra singoli apici ”-), è utile per scrivere quei caratteri che non hanno corrispondenza del codice ascii sulla tastiera, come specificato nella precedente parte della lezione: basta avere sottomano la tabella con l’elenco dei codici carattere del controller che abbiamo a disposizione sull’LCD. Esempio:
LCD_PUTCH(0b11110100); //scrive il simbolo omega
LCD_PUTS(“stringa”);
Scrive una stringa intera sul display. Se ci posizioniamo alla prima colonna della prima riga, e abbiamo a disposizione un LCD 16×2 e in tale stringa scriviamo una parola superiore ai 16 caratteri, ovviamente i caratteri dal diciassettesimo in poi non saranno visibili, per cui stiamo attenti quando utilizziamo questa funzione se non vogliamo avere effetti indesiderati sul display. Esempio:
LCD_PUTS("Settorezero.com");
LCD_PUTUN(numero);
Scrive un numero privo di segno sul display, come numero sono accettati il formato CHAR (8bit) o INT (16 bit), per cui sono ammessi numeri interi da 0 fino a 65535.
LCD_PUTSN(numero);
Scrive un numero compreso di segno. Per numeri positivi il segno non viene rappresentato, per numeri negativi, viene anteposto il segno meno davanti al numero. Supporta sia CHAR che INT in ingresso, ovviamente accettando un numero “signed” i valori ammissibili vanno da -32768 a 32767.
LCD_CUSTOMCHAR(pos,byte1…byte8);
Permette la personalizzazione del carattere in posizione “pos”. Vedremo questa funzione nei dettagli nella terza parte di questa lezione, che sarà dedicata soltanto alla personalizzazione dei caratteri.
Il file lcd.c va impostato prima di poter essere utilizzato. Innanzitutto apriamolo e andiamo alla riga 94:
94 95 96 97 98 99 | #define LCD_RS RD2 // Register select #define LCD_EN RD3 // Enable #define LCD_D4 RD4 // LCD data 4 #define LCD_D5 RD5 // LCD data 5 #define LCD_D6 RD6 // LCD data 6 #define LCD_D7 RD7 // LCD data 7 |
Qui impostiamo le linee dati. Se seguite lo schema elettrico proposto in cima alla lezione, vedete che i pin del display (indicati come LCD_XX) sono già associati ai rispettivi pin del picmicro.
Successivamente dalla riga 110 fino alla riga 115 incontriamo altri parametri da impostare:
110 | #define LCD_ROWS 2 // valid numbers are: 1,2 (set to 2 for 2 or more rows) |
Qui va impostato il numero di righe che ha il nostro display, per le nostre prove terremo conto di avere un display da 2 righe, se avete un display da 4 righe dovete comunque lasciare il valore 2, se avete un display da una riga dovrete cambiare il 2 con l’1, ma poi dovrete anche modificarvi i programmi mostrati in questa lezione.
111 | #define LCD_COLS 16 // valid numbers are: 8,16,20 |
numero di colonne dell’LCD, qui utilizzo un display da 16 colonne, se avete un display da 20 colonne, modificate il numero, se avete un display da 8 colonne, oltre al numero dovrete modificarvi anche i programmi.
112 | #define LCD_CURSOR_VISIBLE 0 // 1:cursor on 0:cursor off |
se mettiamo 0, il cursore non sarà visibile, se mettiamo 1, potremo vedere il cursore visualizzato sul display, il quale si presenterà come una lineetta in basso ai caratteri.
113 | #define LCD_CURSOR_BLINK 0 // 1:cursor blinks 0:cursor doesn't blinks |
se mettiamo 0, il cursore rimarrà fisso, impostando ad 1 il cursore lampeggerà
114 | #define LCD_TYPEWRITE 0 // 1:delay between characters |
se mettiamo 0, la scrittura sul display sarà immediata, impostando ad 1, ci sarà un breve ritardo tra la scrittura di un carattere e l’altro, dando un effetto “macchina da scrivere”.
115 | #define LCD_TW_DELAY 50 // milliseconds between characters |
questo rapprenta il ritardo espresso in millisecondi tra la scrittura di un carattere e il successivo, se abilitiamo il settaggio precedente.
Gli ultimi 4 settaggi, relativi per lo più ad effetti grafici, sono già impostati ai valori più adatti per gli esempi, difatti avere la scrittura rallentata, col cursore, in un menù può essere molto fastidioso.
Impostati questi parametri, non abbiamo bisogno più di niente per far funzionare il display (a parte richiamare la funzione LCD_INIT prima di usarlo).
In questa lezione farò 2 esempi di programma: il primo, molto semplice, è il classico “HELLO WORLD!” che non poteva mancare, serve soltanto a farvi capire in maniera molto elementare come scrivere sul display, ed è un tutorial che si trova un po dappertutto, pertanto in questo esempio non faremo uso ne dei led, ne dei pulsanti ne del cicalino. Il secondo esempio, un po’ più elaborato è un semplice menù mostrato sul display, nel quale potremo navigare con i pulsanti e attivare/disattivare le 4 uscite alle quali sono collegati i led, visualizzandone lo stato sul display.
Hello World!
Il file settings è molto semplice, abbiamo soltanto impostato tutte le porte del pic come uscita.
Nel file main.c, dopo la config word, abbiamo le 3 inclusioni:
#include "settings.h" #include "delay.c" #include "lcd.c"
Come già detto in precedenza, è necessario includere prima delay.c e poi lcd.c, in quanto lcd.c fa uso delle funzioni delay.
Abbiamo quindi il main:
void main(void) { settings(); // imposto le porte LCD_INIT(); // inizializzo l'LCD DelayMs(100); // piccolo ritardo iniziale LCD_CLEAR(); // ripulisco il display LCD_GOTO(1,1); // posiziono il cursore sull'LCD: riga1, colonna1 LCD_PUTS("HELLO"); LCD_GOTO(2,1); // riga2, colonna1 LCD_PUTS("WORLD !"); while(1) { } }
Come vedete, dopo aver eseguito le impostazioni, inizializzamo il display come detto prima con l’apposita funzione, personalmente dopo l’inizializzazione do sempre un piccolo ritardo per stabilizzare le linee di comunicazione, in realtà non ce ne sarebbe bisogno.
Effettuo quindi la pulizia del display con LCD_CLEAR(), mi posiziono alla prima riga, prima colonna e scrivo “HELLO”, mi posiziono quindi alla seconda riga, prima colonna e scrivo “WORLD !”, effettuo quindi il ciclo continuo, vuoto, per non far bloccare il pic. Il risultato è il seguente:

Avendo impostato il cursore lampeggiante e l’effetto macchina da scrivere, vedrete il cursore muoversi mentre compone la scritta.
Come vedete, utilizzando una apposita routine, scrivere su un LCD è un gioco da ragazzi: bastano poche righe di codice per avere una gran bella soddisfazione!
Spero non vi siate accontentati di questo semplice esempio, preparatevi perchè vi attende qualcosa di molto più complicato (ma non troppo).
Esempio di semplice menù con LCD e PICmicro
In questo programma, controlleremo lo stato di 4 uscite del picmicro dal display: due tasti (BTNUP e BTNDOWN) ci permetteranno di muoverci su e giù nel menù, un terzo tasto (BTNOK) ci permetterà di cambiare lo stato dell’ uscita selezionata. Lo stato dell’uscita sarà segnalato sul display (e comunque evidenziato anche dall’accensione/spegnimento del led ad essa collegato: tenete conto che al posto dei led potete anche montare dei relè come spiegato nelle precedenti lezioni). Premendo i tasti di navigazione, sarà udibile un suono prodotto dal cicalino, la pressione del tasto OK, invece, produrrà un suono di tipo diverso.
Il menù avrà questo aspetto:

In pratica con i tasti su e giù spostiamo la freccia, posizionandola affiando alla voce desiderata (L1 sta per LED1), premendo OK, cambiamo lo stato dell’uscita selezionata, la scritta affianco quindi si aggiornerà riflettendo lo stato dell’uscita (ON se livello logico alto e quindi relativo led acceso).
Analizziamo prima il file di configurazione, settings.h:
#define BTNUP RB0 #define BTNDOWN RB1 #define BTNOK RB2 #define LED1 RB7 #define LED2 RB6 #define LED3 RB5 #define LED4 RB4 #define PRESSED 0 // per verifica pulsante premuto #define ARROW 0x7E // codice del carattere freccia #define ON 1 #define OFF 0 #define CICALINO RE1
Qui ci sono i vari define, abbiamo soltanto dato dei nomi più semplici da ricordare alle varie uscite/ingressi.
Ho definito un simbolo PRESSED che utilizzerò per fare la verifica del pulsante premuto, avrei difatti potuto fare come sempre:
if (!BTNOK) // oppure if(BTNOK==0) { // istruzioni }
Ma qui espongo invece un metodo alternativo, che può aiutare a capire meglio:
if (BTNOK==PRESSED) { // istruzioni }
avendo definito PRESSED come zero, la sostanza non cambia, ma forse, scritto così, il programma è più semplice da leggere.
C’è quindi un simbolo “ARROW” che corrisponde al codice carattere della freccia rivolta verso destra sul controller HD44780 (vedete la tabella caratteri nella prima parte di questa lezione), ho espresso il codice in esadecimale (come vedete infatti il numero è preceduto da 0x).
Ho quindi definito due simboli ON e OFF, difatti per alcuni il codice è più leggibile se scriviamo:
LED1=ON; // anzichè LED1=1;
Segue quindi il define del cicalino. Proseguendo troviamo:
signed char menupos=0; // variabile che tiene conto della posizione della freccia nel menù
Questa variabile la utilizzo per tenere in memoria la posizione attuale della freccia che utilizzo per navigare nel menù: in alto a sinistra (posizione L1) questa variabile varrà zero, fino ad arrivare al valore 3 nell’ultima posizione (4 posizioni in totale). Le do l’attributo signed perchè può accadere che assuma anche valore negativo.
Segue quindi la funzione settings che non ha nulla di particolare: tutte le porte impostate come uscita, tranne quelle destinate ad alloggiare i pulsanti. I led e il cicalino vengono posti a livello logico basso all’accensione.
La routine lcd.c allegata al progetto (scaricabile insieme al resto in fondo all’articolo per gli utenti iscritti) è configurata in maniera tale da non visualizzare il cursore e da fornire una scrittura diretta (senza ritardi).
Analizziamo quindi il main.c, notiamo qualcosa che fino ad ora non avevamo utilizzato, ovvero i prototipi di funzione:
// prototipi di funzione void main(void); // programma principale void beep(char beeptype); // esegue il beep, usato quando premo i tasti void posizionafreccia(char posizione); // posiziona la freccia nel menù void cambiastato(char posizione); // inverte lo stato del led void scrivistato(char posizione,char stato); // scrive lo stato del led sul display
Come vedete, un prototipo di funzione è la funzione scritta normalmente, ma seguita dal punto e virgola anzichè dalle parentesi graffe contenenti le routine da eseguire (una funzione definita, ma vuota). Questo viene fatto per avvisare il compilatore delle funzioni (che verranno poi definite realmente), in maniera tale da non generare errori se incontra un simbolo che non è stato precedentemente dichiarato, è la stessa cosa che si fa con le variabili: così come dichiaramo una variabile scrivendo:
signed char menupos=0;
Così dichiaramo una funzione, dicendo anche se restituisce o meno un valore e se accetta o meno parametri in ingresso
Ricordo ancora una volta che il void indica che non ci sono valori: il void davanti indica che la funzione non ritorna valori, il void tra parentesi indica che la funzione non prevede argomenti da elaborare.
Nel main prepariamo quindi il display, in maniera da farlo apparire come l’immagine presentata più in alto.
Nel ciclo infinito controlliamo unicamente la pressione dei 3 pulsanti. Ogni pulsante è dotato di antirimbalzo software come spiegato nelle precedenti lezioni. La pressione del tasto BTNUP (SU) decrementa la variabile menupos (che come abbiamo detto serve per tener traccia di dove si trova la freccia: un indice, in pratica, che ci permette di ricordarci in tutto il programma, in quale punto del menù ci troviamo), se il valore di questa variabile diviene minore di zero, allora le assegno il valore 3, in maniera tale che se ci troviamo in posizione 0 e premiamo ancora SU, la freccia verrà a trovarsi in posizione 3 (ultima posizione):
// pressione tasto SU // decremento il contatore che mi posiziona la freccia if (BTNUP==PRESSED) { DelayMs(100); if (BTNUP==PRESSED) { beep(0); // suono menupos--; // decremento la posizione // se in posizione 0 ho premuto su // vado in posizione 3 if (menupos<0) { menupos=3; } // posiziono quindi la freccia posizionafreccia(menupos); } } // fine pressione tasto su
Per chi non lo ricordasse: la seconda verifica della pressione del pulsante, dopo un ritardo, costituisce l’antirimbalzo software come spiegato in una lezione precedente.
All’atto della pressione, richiamo la funzione beep per emettere il suono. La funzione beep accetta in ingresso un valore numerico (da 0 a 2) in maniera tale da emettere un suono diverso (una diversa frequenza) in base al valore che passiamo:
void beep(char beeptype) { char F; switch (beeptype) { case 0: F=70; break; case 1: F=90; break; case 2: F=110; break; } for (char a=40; a--; a!=0) { DelayUs(F); CICALINO=ON; DelayUs(F); CICALINO=OFF; } }
Questa funzione in pratica è uguale a quella vista nelle precedenti lezioni con due sostanziali differenze: il ritardo necessario a generare l’onda quadra è variabile e dipendente dall’argomento passato alla funzione, e inoltre viene eseguito un ciclo di 40 ripetizioni, ma anzichè andare da 0 a 40 come normalmente è spontaneo scrivere, il ciclo viene eseguito al contrario: da 40 verso zero. La sostanza non cambia, ma scritto in questo modo ci permette di rendere il ciclo più efficiente come spiegato in questo articolo su microchipc.
Le funzioni svolte dal tasto BTNDOWN sono pressochè identiche ma eseguite al contrario: il contatore verrà incrementato, se supera il valore di 3, gli verrà dato il valore zero in maniera tale da far ricomparire la freccia in prima posizione.
La pressione di questi due pulsanti (BTNUP e BTNDOWN), richiama la funzione “posizionafreccia” la quale accetta in ingresso il valore di posizione attuale. Tale routine esegue 3 funzioni principali. Prima di tutto cancella (scrive uno spazio) nelle 4 posizioni in cui deve apparire la freccia, in maniera tale da cancellare il carattere freccia precedente:
LCD_GOTO(1,1); LCD_PUTCH(32); // 32=codice dello spazio LCD_GOTO(2,1); LCD_PUTCH(32); LCD_GOTO(1,10); LCD_PUTCH(32); LCD_GOTO(2,10); LCD_PUTCH(32);
Facile capire che quei LCD_GOTO puntano alle posizioni dove le frecce devono comparire:

Successivamente si fa utilizzo di un costrutto switch (vedi manuale Tricky C per sapere come si usa) per analizzare la posizione attuale (passata alla funzione): in base al numero di posizione, viene piazzato il cursore nella “casella” giusta del display:
switch (posizione) { case 0: LCD_GOTO(1,1); break; case 1: LCD_GOTO(2,1); break; case 2: LCD_GOTO(1,10); break; case 3: LCD_GOTO(2,10); break; }
Infine, viene stampato il carattere della freccia:
LCD_PUTCH(ARROW);
Ricordo che abbiamo definito il valore di “ARROW” in settings.h. Come vedete utilizzo la funzione LCD_PUTCH che mi permette di scrivere un solo carattere.
Spero che fin qui sia tutto chiaro: premiamo i pulsanti su e giù e quindi posizioniamo la freccia sul display. Andiamo avanti.
La pressione del tasto BTNOK, produce un beep differente e richiama un’altra routine: cambiastato, alla quale viene sempre passato il valore di posizione attuale. Tale routine definisce innanzitutto una variabile:
char STATO_LED;
la quale serve per tener conto dello stato attuale dell’uscita dopo il cambio. Viene quindi effettuato uno switch in base alla posizione nel menù, in maniera tale da invertire lo stato dell’uscita relativa:
switch (posizione) { case 0: LED1=LED1^1; STATO_LED=LED1; break; case 1: LED2=LED2^1; STATO_LED=LED2; break; case 2: LED3=LED3^1; STATO_LED=LED3; break; case 3: LED4=LED4^1; STATO_LED=LED4; break; }
La variabile STATO_LED, come vedete, memorizza lo stato attuale dell’uscita (del led) che abbiamo selezionato. Infine viene richiamata l’ultima funzione:
scrivistato(posizione,STATO_LED);
la quale ha la funzione di scrivere ON oppure OFF nella posizione relativa sul display: per fare questo dovremo sempre passare la posizione in cui ci troviamo e quindi anche lo stato dell’uscita. Quest’ultima funzione si articola nella stessa maniera delle altre, tranne che per la posizione in cui andare a scrivere. Da notare che ON sarà scritto con uno spazio finale, in maniera tale da cancellare l’ultima lettera (F) di OFF, se tralasciamo lo spazio, apparirà sul display una cosa del genere: L1:ONF
Downloads
Il file zip contiene lo schema elettrico, la routine lcd.c e i codici sorgente e compilati dei due programmi di esempio qui esposti, per ognuno dei due programmi c’è la copia della routine lcd.c già impostata, ho inoltre incluso il foglio excel che ho utilizzato io per disegnare come appare il display, che può essere utile per fare i vostri progetti di menù e ricavare quindi la posizione del cursore.
File di supporto alla settima lezione (parte 2) del corso di programmazione picmicro in C (195)
Lasciate commenti.
Articoli che potrebbero interessarti:







#1 da emanuele il 29 novembre 2009
ciao ma e possibile convertire il codice in arduino???
#2 da Giovanni Bernardo il 29 novembre 2009
E’ possibile sicuramente dal momento che arduino ha degli I/O e può essere programmato in C
#3 da cimistrufoli il 1 dicembre 2009
Ciao, t ho già fatto i complimenti e credo ke t sarò riconoscente a vita x questo splendido tutorial sui pic!!! :)
continuando nelle mie prove con il pic 18f452 e utilizzando come board d sviluppo la PICDEM2 sto riscontrando un problema con il display LCD…
lo sto utilizzando in modalita 4 bit, e l’iniziallizazione e il posizionamento del cursone funzionano perfettamente…purtroppo ho un problema con la visualizzazione dei caratteri,perkè dopo aver settato la linea RS a 1 e posto sulle linee dati le prime 4 cifre del codice x la visualizzazione del carattere, quando faccio lo strobe dell’enable appena la linea E diventa atttiva la linea RS torna a 0 e nn riesco a capire il motivo…in fondo sono collegate su due linee distinte della porta A, rispettivamente alla RA1 per RS e RA3 per E!! :(
secondo te pkè nn riesco a mantenere ad 1 entrambe le linee contemporaneamente?
#4 da Giovanni Bernardo il 1 dicembre 2009
Posta il codice che stai usando
#5 da cimistrufoli il 2 dicembre 2009
questa è la libreria LCD ke uso…le porte D e A le setto nel main come uscite cioè a inizio main scrivo TRISA = 0×00; e TRISD = 0×00;
#ifndef FLAG_LCD
#define FLAG_LCD
//*****************************************************************************
// Costanti LCD
#define LCD_D4 PORTDbits.RD0
#define LCD_D5 PORTDbits.RD1
#define LCD_D6 PORTDbits.RD2
#define LCD_D7 PORTDbits.RD3
#define LCD_E PORTAbits.RA1
#define LCD_RW PORTAbits.RA2
#define LCD_RS PORTAbits.RA3
//*********************************************************************************************
//altre costanti
#define LEFT 0
#define RIGHT 1
//*********************************************************************************************
// Funzione ritardo per il display
void delayLCD (int attesa)
{
int i = 0;
for (i; i> 7;
D2 = (Element & 0b01000000) >> 6;
D1 = (Element & 0b00100000) >> 5;
D0 = (Element & 0b00010000) >> 4;
SendCommand (D3,D2,D1,D0);
D3 = (Element & 0b00001000) >> 3;
D2 = (Element & 0b00000100) >> 2;
D1 = (Element & 0b00000010) >> 1;
D0 = (Element & 0b00000001);
SendCommand (D3,D2,D1,D0);
LCD_RS = 0;
}
//*********************************************************************************************
// Funzione che permettere di scrivere sul display una stringa costante
// ESEMPIO WriteStringLCD (“Hello World”) scrivo la stringa costante
void WriteStringLCD(const rom char *buffer)
{
while(*buffer)
{
WriteCharLCD(*buffer);
buffer++;
}
return;
}
//*********************************************************************************************
// Funzione che permette di scrivere una stringa (Array di caratteri) sul display.
// La stringa di caratteri deve avere come ultimo elemento il valore speciale ”
void WriteVarLCD(char *buffer)
{
while(*buffer)
{
WriteCharLCD(*buffer);
buffer++;
}
return;
}
//*********************************************************************************************
// Funzione che pulisce il Display
void ClearLCD (void)
{
SendCommand (0,0,0,0);
SendCommand (0,0,0,1);
}
//*********************************************************************************************
// Questa funzione permette di settare visibile o invisibile il cursore del display e il suo lampeggio
// 0 = Invisibile 1 = Visibile
void CursorLCD(char ON_OFF,char Blink)
{
SendCommand (0,0,0,0);
SendCommand (1,1,ON_OFF,Blink);
}
/************************************************************************************************
* Inizializza il display LCD : queste funzione deve essere utilizzata una sola volta e sempre *
* prima di iniziare ad utilizzare le altre funzioni. Lo scopo è inizializzare il display, *
* pulire le righe, posizionare il cursore all’inizio e togliere il suo lampeggio. *
* N.B.: una mancata esecuzione di tale funzione lascia scura la prima riga del display *
*************************************************************************************************/
void OpenLCD(void)
{
LCD_RS = 0;
LCD_E = 0;
delayLCD (4000);
SendCommand (0,0,1,1);
delayLCD (4000); // delay is more than 4mS
SendCommand (0,0,1,1);
delayLCD (4000); // delay is more than 4mS
SendCommand (0,0,1,1);
delayLCD (4000); // delay is more than 4mS
SendCommand (0,0,1,0);
SendCommand (0,0,1,0); // invio al display il comando 0010 1000 che indica
SendCommand (1,0,0,0); // al display l’interfaccia a 4 bit, 2 righe e grandezza font 5×8
SendCommand (0,0,0,0);
SendCommand (1,1,1,0);
CursorLCD (1,0);
ClearLCD ();
}
/****************************************
Writes unsigned numbers to the LCD
FUNZIONE PRESA DALLA TUA LIBRERIA LCD :)
*****************************************/
void visualizza (unsigned int visio)
{
unsigned char t1,i,wrote;
unsigned int k;
wrote=0;
for (i=4;i>=1;i–)
{
switch(i)
{
case 4: k=10000;
break;
case 3: k=1000;
break;
case 2: k=100;
break;
case 1: k=10;
}
t1=visio/k;
if((wrote)||(t1!=0))
{
WriteCharLCD(t1+’0′);
wrote=1;
}
visio-=(t1*k);
}
WriteCharLCD(visio+’0′);
}
#endif
#6 da cimistrufoli il 2 dicembre 2009
scusa nn so cosa ho toccato ma mi si è cancellato un pezzo…
void delayLCD (int attesa)
{
int i = 0;
for (i; i> 7; // Splitting of the first nibble
D2 = (Element & 0b01000000) >> 6;
D1 = (Element & 0b00100000) >> 5;
D0 = (Element & 0b00010000) >> 4;
SendCommand (D3,D2,D1,D0);
D3 = (Element & 0b00001000) >> 3; // Splitting of the second nibble
D2 = (Element & 0b00000100) >> 2;
D1 = (Element & 0b00000010) >> 1;
D0 = (Element & 0b00000001);
SendCommand (D3,D2,D1,D0);
LCD_RS = 0;
}
#7 da cimistrufoli il 2 dicembre 2009
ok copiando e incollando il pezzo mancante da notepad++ viene fuori sempre la stessa cosa, cioè m elimina un pezzo di codice :(
provo a mandarti via mail il codice sxando ke nn si cancelli nuovamente il pezzo!!
#8 da Giovanni Bernardo il 2 dicembre 2009
Ricevuto. Dato che è parecchia roba devi darmi un po di tempo per controllarlo, nel frattempo prova ad utilizzare soltanto la mia libreria lcd e vedi se ti funziona, le modifiche che devi fare per usarla con un pic18 a quanto vedo sono ben poche, oppure forse funziona già così… Fai una prova.
#9 da cimistrufoli il 2 dicembre 2009
ho pensato di fare proprio quello, cioè usare la tua libreria modificando solamente la dichiarazione dei pin dell’LCD per impostarle come predefinite dalla board ma niente da fare…mi da lo stesso errore!!
ho fatto un altra prova: ho provato ad impostare i pin di comando dell’LCD sulla porta D (bit 7, bit6, bit5), e in questo caso nn ho avuto nessun problema ad impostare ad 1 contemporaneamente la linea E e la linea RS e il progetto funziona!!peccato ke nella PICDEM siano collegati i piedini sulla portA e nn posso cambiarli…tornerò a leggere il datasheet del pic 18f452 x vedere d scoprire come mai la portA si comporti differentemente dalla portD!!
#10 da Giovanni Bernardo il 2 dicembre 2009
Dando uno sguardo rapido, posso dire che quel “DelayLCD” non mi piace affatto, non tiene conto della frequenza del quarzo utilizzato, per cui a 40Mhz quel ritardo sarà brevissimo, sarà più lungo con un quarzo da 10MHz ecc ecc, quindi già con questo i ritardi non vengono rispettati, e i ritardi sono essenziali per gestire le corrette temporizzazioni. Usa il DelayMs come nel mio codice, ma arrivati a questo punto ti direi di modificare la mia libreria per usarla col C18, sono pochissime modifiche da fare (al posto di RB6, ad esempio, devi mettere PORTBbits.RB6), ma almeno ti rendi indipendente dal quarzo assicurando la giusta temporizzazione. Tieni conto, inoltre, che non tutti i controller, pure se dichiarati compatibili, hanno le stesse temporizzazioni e a volte pure un millisecondo fa la differenza. Il codice originale che usavo io, difatti, testato su un HD44780 a me dava problemi col KS0066: ogni tanto la seconda linea del display non appariva, ho aumentato di poco i ritardi e ora funziona sia con l’uno che con l’altro.
#11 da Giovanni Bernardo il 2 dicembre 2009
Non è che la porta A è a collettore aperto? In questo caso non è in grado di fornire il segnale logico alto ma solo quello basso, in questo caso devi mettere una resistenza di pullup sui piedini interessati. Ora controllo pure io il datasheet.
#12 da Giovanni Bernardo il 2 dicembre 2009
Ho dato uno sguardo rapido, l’unica porta a collettore aperto è la RA4, per cui se usi RA4 non puo darti il livello logico alto se non metti una resistenza di pullpup, secondariamente il banco A sul 18F452 è anche analogico, per cui abbi l’accortenza di settare le porte in digitale anzichè in analogico, questo magari lo fai prima di richiamare la libreria LCD.
#13 da cimistrufoli il 3 dicembre 2009
è proprio quello che ho fatto ieri sera dopo aver rivisto la parte sulle porte del datasheet!! :) m sono accorto infatti che non impostando il registro ADCON1 in modo da far funzionare la porta in modo digitale queste lavorano come analogiche!!! adesso funziona anche con i pin del display collegati alla portA!!
Grazie ancora x il tempo dedicatomi…utilizzerò la tua libreria così da non avere problemi di temporizzazione!! ;)
#14 da Giovanni Bernardo il 3 dicembre 2009
:) ok, ottimo.
#15 da S.D.R. il 21 febbraio 2010
Ciao Gianni ;
Ho notato che hai messo sul Setting.h l’istruzione “define BTN4 RB3″ e poi l’hai inibita come testo e poi hai prediposto la main per una terza tonalità di suono per il cicalino ,
Avevi intenzione di fare altre cose ?
#16 da Giovanni Bernardo il 21 febbraio 2010
Il BTN4 l’ho messo perchè avevo fatto una schedina con 4 pulsanti e 4 led da collegare interamente alla porta B, ma per quell’applicazione il 4° pulsante non lo utilizzavo quindi l’ho commentato. Il tipo di suono per il cicalino è una possibilità in più che ho rimasto così… giusto per far vedere in che modo si può fare per suonare diversamente…. Mi fa piacere che te ne sei accorto, sei molto attento.
A tal proposito, visto che sei così interessato all’argomento, ne approfitto per renderti partecipe di uno stupendo link che ho trovato girovagando: http://www.romanblack.com/picsound.htm leggi attentamente anche l’algoritmo, concettualmente è abbastanza semplice anche se ancora non ho capito bene come metterlo in pratica. Qui: http://www.talkbotbrain.com/TalkBot_SourceFiles.htm ci sono anche dei sorgenti di esempio, però scritti in MikroC. Il primo che riesce a combinarci qualcosa… Dà un colpo… ok? VALE PER TUTTI QUELLI CHE STANNO LEGGENDO!!!! ;) Penso sia una cosa davvero stupenda… Suonare un wav con un pic, senza costosi hardware esterni… Una favola non trovi? Pensa alla marea di applicazioni che si possono creare
#17 da subliminal il 26 febbraio 2010
ciao giovanni….ho provato a scaricare i tuoi file di questa lezione per poterli testare…quando ho importato i file in MPLAB e dopo compilato ho avuto una serie di errori…mi potresti aiutare?
#18 da Giovanni Bernardo il 26 febbraio 2010
Spero che hai letto le prime lezioni. Devi importare solo “main.c” e non tutto
#19 da Luca il 7 marzo 2010
Stavolta ti contatto proprio per disperazione, sono ancora fermo ancora all’hello world. Il display è questo http://cgi.ebay.it/ws/eBayISAPI.dll?ViewItem&item=390156241378&ssPageName=STRK:MEWNX:IT
il resto già lo sai.
Modificato l’ lcd.c impostando le porte giuste , cancellando il setting.h e includendo nel main TRISA=0 e TRISB=0 e INTCON=0 (perchè usarlo?), ho messo anche dei led per una sorta di debugging, noto che: il programma funziona a dovere poichè i led di controllo mi notificano che il programma funziona e non si blocca (ho messo che il primo si spegne prima dell’inizializzazione ed il secondo si spegne prima del ciclo while (anche questo, a cosa serve?). Ma sopratutto il problema è: sullo schermo per un’istante si intravede un cursore fuggire via, poi lo schermo bianco…(o nero, dipende da come regolo il trimmer). Sono due giorni che riprovo daccapo, rifacendo tutto nuovamente ogni volta, dal circuito al programma, ed ho provato anche tre display diversi…
Qualche hint? Mi stanno proprio cadendo le braccia…
#20 da Giovanni Bernardo il 7 marzo 2010
Ho visto il datasheet di quel particolare display, la piedinatura è corretta. Prova, nel file lcd.c, a mettere a zero l’impostazione per il cursore e l’impostazione per l’effetto “macchina da scrivere”. Poi: non mi hai detto che pic stai usando. Adesso non ne ho idea, ma se stai utilizzando un circuito diverso non tutto potrebbe essere uguale. La porta RA4, per esempio, su quasi tutti i pic è a collettore aperto, il che vuol dire che non può fornire il segnale logico alto ma solo il basso, è quindi necessaria una resistenza di pullup (un 10K verso il +5V). Se stai utilizzando la porta RA4 senza la resistenza di pullup per comandare l’lcd, questo potrebbe essere uno dei problemi. La dimenticanza del resistore di pullup su RA4 quando viene usato come uscita è un errore molto comune. (leggete sempre bene il datasheet! All’ inizio c’è una pagina in cui, per ogni pin, viene indicato come funziona). Poi sono importanti le temporizzazioni come spiegato all’inizio dell’articolo, se ancora non va, prova ad aumentare un poco i ritardi nei “Delay” messi nell’ultima funzione di lcd.c, che si occupa del settaggio dell’ LCD. Come spiegato, alcuni controller, pur essendo compatibili, hanno dei tempi leggermente differenti. Se ancora non va, dimmi che pic stai usando, e a quali linee hai collegato l’ LCD.
Poi:
INTCON=0; (perchè usarlo?) => difatti l’ho messo a zero e quindi non lo sto usando! Ho incluso l’istruzione perchè vorreste poterlo usare.
Il ciclo while(1), come già spiegato, serve per far eseguire all’infinito la parte di programma incluso in esso, trattandosi solo di visualizzare una scritta sul display, potrebbe anche essere omesso ma è buona regola usarlo sempre per non perdere l’abitudine e incorrere in errori. Alcuni dicono che ci vuole per forza per non far bloccare il pic, in realtà non è vero niente perchè se provi a toglierlo, in questo esempio, tutto dovrebbe funzionare lo stesso. Si mette quando una parte di programma deve essere eseguita di continuo, altrimenti, ovviamente, giunti all’ultima istruzione il programma termina.
#21 da Luca il 7 marzo 2010
Sto usando un pic16f628a, quarzo 20mhz esterno, data4-7 rb0-rb3 , en ra3, rs ra2. Su rb4 e rb5 ho i due led di controllo. I tempi da modificare sono quelli di lcd_init? I tempi del main li ho già aumentati leggermente.
RA4 non l’ho utlizzata…
:-S veramente non sò più dove mettere mano…
Piedinatura LCD/Funzione/relativo Pin pic16f628a
1 gnd gnd
2 vdd vdd
3 V0 trimmer
4 RS RA2
5 R\W gnd
6 E ra3
7-10 data 0-3 gnd
11-15 data4-7 RB0-RB3
15 leda vdd
16 ledb gnd
Main:
————————————————
#define XTAL_FREQ 20MHZ
#include
__CONFIG (UNPROTECT & LVPDIS & BORDIS & MCLREN & PWRTEN & WDTDIS & HS);
#include “delay.c”
#include “lcd.c”
#define LED RB5 // invece di scrivere RD0, scriverò LED, così mi è più facile ricordare
#define LED2 RB4
void main(void)
{
TRISA=0b00000000;
TRISB=0b00000000;
INTCON=0b00000000;
LED2=1;
LED=1;
DelayMs(500);
LED=0;
LCD_INIT(); // inizializzo l’LCD
DelayMs(100); // piccolo ritardo iniziale
LCD_CLEAR(); // ripulisco il display
LCD_GOTO(1,1); // posiziono il cursore sull’LCD: riga1, colonna1
LCD_PUTS(“Ciao”);
LCD_GOTO(2,1); // posiziono il cursore sull’LCD: riga2, colonna1
LCD_PUTS(“Funzionaa”);
LED2=0;
while(1){}
}
————————————————
Impostazioni in lcd.c:
#define LCD_RS RA2 // Register select
#define LCD_EN RA3 // Enable
#define LCD_D4 RB0 // LCD data 4
#define LCD_D5 RB1 // LCD data 5
#define LCD_D6 RB2 // LCD data 6
#define LCD_D7 RB3 // LCD data 7
…
#define LCD_ROWS 2 // valid numbers are: 1,2 (set to 2 for 2 or more rows)
#define LCD_COLS 16 // valid numbers are: 8,16,20
#define LCD_CURSOR_VISIBLE 1 // 1:cursor on 0:cursor off
#define LCD_CURSOR_BLINK 1 // 1:cursor blinks 0:cursor doesn’t blinks
#define LCD_TYPEWRITE 1 // 1:delay between characters
#define LCD_TW_DELAY 200 // milliseconds between characters
… — … !!!!
#22 da Giovanni Bernardo il 7 marzo 2010
Sotto il define del quarzo c’è un include vuoto, toglilo.
Non puoi mettere 500 come valore in DelayMs, quella funzione accetta un char come argomento, quindi puoi mettere massimo 255, così come sta ti causa il blocco del pic. E poi aumentare quel ritardo non serve a nulla. Dopo fatta questa modifica riprova, se ancora non va prova poi a mettere tutti le linee di controllo su un’unica porta, tutto solo su PortB o tutto solo su PortA.
#23 da Luca il 7 marzo 2010
Avevi ragione, ho spostato tutto sulle porte B ed ora funziona; ma com’è possibile? Sembra che il problema sia sul RA2 col Register Select, poichè se sposto il rs sulle porte B e rimango l’Enable sul RA3 funziona. Qualche idea?
Ps. Hai ragione sul DelayMs; ma ignorando il problema anche dandogli in pasto x>255 mi ha sempre funzionato..bha..strano sul serio!
Quando ho tempo finisco la lezione, ora scappo, e grazie mille per la disponibilità. Buona domenica
#24 da Giovanni Bernardo il 7 marzo 2010
Suppongo sia sempre legato a qualche problema di temporizzazione dovuto al collegamento delle linee di controllo del display su due banchi diversi e/o ottimizzazione del codice scritto in C da parte del compilatore