Esercitazioni ASM - PIC18

 


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.


 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 13/01/13.