ESERCITAZIONE # 12b
Il file delle subroutines LCDuni8s_18F.asm
è il core del driver.
Subroutine |
Funzione |
LCDInit |
Inizializza il modulo secondo le specifiche del
costruttore (inizializzazione software completa).
es.: call
LCDInit inizializza
l' I/O dedicato ed effettua una
completa inizializzazione software del modulo LCD |
LCDWrDat |
Invia la modulo il carattere in W.
Es.: call LCDWrDat
invia al display il dato contenuto in WREG |
LCDWrCmd |
Invia la modulo il comando in W.
Es.: call LCDWrCmd
invia al display il dato contenuto in WREG come comando |
*LCDRdRam |
Legge la RAM all' indirizzo corrente (solo per
LCDRW=1)
Es.: call
LCDRdRam ritorna
con il contenuto della corrente locazione di RAM in W |
*LCDRdBfa |
Legge BF + AC (solo per LCDRW=1).
Es.: call LCDRdBfa
ritorna con l' indirizzo di RAM corrente e il flag BF in
W |
*LCDCheckBusy |
Come sopra, legge BF + AC, ma rilascia solo se BF=0 (solo per
LCDRW=1).
Es.: call LCDCheckBusy
ritorna con l' indirizzo di RAM corrente e il flag BF in
W solamente quando il flag BF è a zero |
**LCDLdCGRAM |
Carica una locazione della CGRAM con il contenuto
di una tabella da 8 bytes.
La locazione di CGRAM viene richiamata da LCDCGADR.
La tabella viene definita dalla macro
SETTABLE.
Es.:
carica nella locazione 4 della CGRAM il contenuto
della tabella LCDSCchar1
LCDCGADR 4
; imposta l' indirizzo di destinazione
SETTABLE
LCDSCchar1 ; imposta i pointer per la tabella
call LCDLdCGram
; scrive 8 bytes in CGRAM
|
**LCDLd8CGRAM |
Carica la CGRAM con il contenuto di una tabella da
64 bytes.
La tabella viene definita dalla macro SETTABLE che viene richiamata
subito prima.
Es.:
SETTABLE
LCDSCtable
call
LCDLd8CGram
Occorre aver definito la tabella LCDSCtable,
che contiene gli 8 caratteri della CGRAM, ognuno formato da 8
bytes, per un totale di 64 bytes.
Una tabella LCDSCtable
viene definita per default in coda al file LCDuni8s_18F.asm.
Questa tabella potrà essere modificata secondo le necessità. |
**LCDTableOut |
Invia
una stringa di caratteri al display.
Richiede che l' utente carichi preventivamente il
puntatore della tabella, la sua lunghezza e un eventuale
timing (che stabilisce una cadenza tra ogni carattere
per effetti speciali). Se
0 non c'è ritardo |
**Hex1_Lcd2 |
Converte un hex in W in due caratteri ASCII sul display.
Es. W = 0x41
visualizza sul display i
caratteri 4 e 1 |
**Bin1_LCD8 |
Converte
un hex in W in 8 caratteri ASCII sul display.
Es. W= 0x41 manda al
display la stringa di caratteri 01000001 |
**Delay5ms |
Ritardo
di 5ms per il numero di volte contenuto in W |
* indica subroutines che saranno compilate solo per LCDRW
= 1
** indica subroutines che saranno compilate solo per LCDSHORTDRIVE = 0
Vediamo maggiori dettagli.
Una delle routines interne ha lo scopo di generare l' impulso di enable portando a livello
1 il pin E.
Solitamente la cosa viene liquidata sbrigativamente con tre righe del genere:
LCDClk bsf LCD_Ew
; E=1 LCD enable
nop
; extra safety wait
bcf LCD_Ew
; E=0 LCD disabled
return
La cosa funziona quasi sempre, ma non perchè la routine sia corretta, bensì
perchè i controller dei moduli LCD hanno in buona parte ottime prestazioni e
perchè i lavori presentati in rete generalmente si basano su antichi PIC con
clock a 4MHz.
Altrimenti, basta una occhiata al foglio dati di un qualsiasi
controller, per verificare che il tempo in cui il segnale E deve restare a
livello alto (TWEH) deve avere una minima lunghezza.
Le istruzioni di cui sopra, per un PIC con clock a 4 MHz determinano un livello
alto di E per 2 cicli, ovvero 2 us, il che è sufficiente anche per il
controller più lento. Ma se il clock fosse 40 MHz, il tempo diventerebbe 200ns,
che è al di sotto della durata minima per moltissimi controller.
Occorre, allora, che la durata del livello alto di E sia modulata in funzione
del clock del processore. Questo lo otteniamo facilmente inserendo un
automatismo in compilazione, che si basa su un parametro LCDTWEH definito nel
setup iniziale del driver.
Nei punti critici, dove è necessario rispettare certe temporizzazioni, sono
stati introdotti dei cicli WHILE che
aggiungono nop in quantità
sufficiente. Un nop equivale ad un tempo di ciclo istruzione; se il
microcontroller lavora a frequenze relativamente basse (4-8 MHz), un ciclo
istruzione equivale a 1000-500ns; questo, assieme alla necessità di impiegare
più istruzioni per una operazione, copre ampiamente le necessità di tempo
richieste dal controller dell' LCD. La gran parte dei driver per LCD che si
trovano sparsi sul WEB funziona, anche se non tiene minimamente di questi
fattori, semplicemente perchè usata con microcontroller con clock limitati (ma
fallisce quando il clock aumenta oltre i 20MHz). Infatti, se il microcontroller
lavora frequenze superiori, il tempo di ciclo scende: a 40MHz il ciclo è di
soli 100ns, a 48MHz è di 52ns e a 64MHz è di soli 39ns. In questo caso è
necessario addizionare cicli di wait.
Cicli di wait extra, prodotti con nop,
possono essere richiesti anche nel caso in cui le connessioni tra
microcontroller e modulo LCD siano piuttosto lunghe e quindi sia necessaria una
stabilizzazione dei segnali prima dell' impulso di enable.
I cicli WHILE, il cui oggetto è un nop, si aggiungono in modo tale da mantenere
il tempo TWEH sempre maggiore di quello minimo indicato nei fogli dati.
In effetti si ottiene un tempo almeno di 1 ciclo maggiore, che è voluto per
annullare l' eventuale effetto di un cablaggio un po' lungo tra microcontroller
e modulo LCD.
;********************************************************************
; LCDClk - Subroutine
; Clock E pin on LCD
; Automatically insert delay for Tw
; Resources : none
;********************************************************************
LCDClk bsf LCD_Ew
; E=1 LCD enable
nop
; extra safety wait
; WHILE cycles insert LCDTWEH x nops to comply with
; manufacturer timing
VARIABLE vCNT
vCNT = LCDTWEH
; assign pre-defined constant
WHILE vCNT > 0
nop
vCNT -= 1
ENDW
bcf LCD_Ew
; E =0 LCD disabled
return
|
Per chiarezza, osserviamo che, se non implementiamo l' automatismo di
inserzione dei nop, la routine si riduce a:
;********************************************************************
; LCDClk - Subroutine
; Clock E pin on LCD
; Automatically insert delay for Tw
; Resources : none
;********************************************************************
LCDClk bsf LCD_Ew
; E=1 LCD enable
nop
; extra safety wait
nop
bcf LCD_Ew
; E =0 LCD disabled
return
|
dove i due nop costituiscono il tempo di permanenza a livello alto del
segnale E, il che dipende dalla lunghezza del ciclo di istruzione del
microcontroller, ovvero dalla sua frequenza di clock.
|