ESERCITAZIONE # 12b
La routine che esegue la scrittura verso il modulo LCD è comune alle
operazioni per dati o comandi.
In effetti, la procedura è identica: la destinazione come dato o come comando
è impostata dallo stato del pin RS.
Una serie di condizionamenti #if-#else- #endif
consente di compilare la versione adatta per l' uso della linea RW oppure
no.
Nel primo caso si verifica lo stato del flag BF prima di accedere in scrittura.
Questo è sensato in quanto il comando precedente è stato probabilmente già
eseguito e quindi la routine di check del flag BF uscirà molto rapidamente.
Nel secondo caso, si effettua l'operazione, poi si aggiunge un tempo standard di
attesa del completamento del comando. Questo è stato dedico in quanto, tra una
scrittura random di un dato o di un comando e la successiva è possibile che
trascorrano più dei 37-100 us di esecuzione, ma solitamente sul display si
scrivono sequenze di comandi e dati successivi; inoltre i comandi con tempo di
esecuzione lungo impiegano millisecondi. Quindi, non potendo verificare lo stato
del flag di busy, occorre avere la certezza che l' operazione precedente sia
conclusa prima di lanciarne un'altra, altrimenti, al minimo, si rischia che il
dato o il comando vadano persi.
In uscita, la routine riporta a livello basso tutti i pin di interfaccia;
questa non è una operazione inutile e non appesantisce particolarmente il
firmware, dato che impiega solamente 2 o 3 cicli addizionali (a 4 MHz di clock
sono 2-3 us in più, mentre la sola attesa di conclusione di un comando vale
37-100 us). E', invece, indispensabile per conoscere con certezza lo stato delle
linee di I/O utilizzate per il display nel caso in cui queste siano utilizzate
anche per altre funzioni, come ad esempio una tastiera.
Viene utilizzata una locazione di RAM , LCDbuf,
come buffer.
;********************************************************************
; LCDWrCmd - LCDWrDat - Subroutine
; Transmit data or command to LCD :
; - if LCDRW = 0 don't use RW pin and add delay
; - if LCDRW = 1 use RW pin and check BF
; Data or command come on W
; Resources : ram LCDbuf
; call LCDchkbusy or BusyTime, LCDXMIT, LCDClk
;********************************************************************
; If RW pin is enabled, first chech busy status and performs
; the write action only when the display don't be busy (BF=0).
; If RW pin is disabled, makes writing then inserts a delay depending
; on the type of data or command
; send command to LCD
LCDWrCmd:
movwf LCDbuf
; save command
#if LCDRW == 1
; if check-BF-mode
rcall LCDChkBusy
; check if busy
#endif
; else no operation
; RS=0 for command - line just cleared by LCDChkBusy
bra lcwr
; send data to LCD
LCDWrDat:
movwf LCDbuf
; save command
#if LCDRW == 1
; if check-BF-mode
rcall LCDChkBusy
; check if busy
#endif
; else no operation
bsf LCD_RSw
; RS=1 for data
; send
lcwr movf LCDbuf,w
; restore byte to send
LCDXMIT
; out data to LCD bus
rcall LCDClk
; clock E
; reset LCD port and controls
#if LCDRW == 0
; if no-check-BF-mode
rcall BusyTime
; add busy time
#else
; else (BF mode)
bcf LCD_RWw
; clear RW
#endif
clrf LCDportw
; clear PORT
bcf LCD_RSw
; clear RS
return |
Se la linea RW non viene utilizzata, occorre impiegare in alternativa dei
tempi standard di attesa di fine comando.
I tempi sono in funzione del comando inviato. Comandi con codice minore di 3
necessitano di un tempo di esecuzione maggiore di quello degli altri.
Se si tratta di dati, il tempo necessario è quello minimo.
;----------------------------------------------------
#if LCDRW == 0
; if no-check-BF-mode selected
; compile BusyTime subroutine
;********************************************************************
; BusyTime - Subroutine
; Automatically insert a long and short time delay for
; command and data sended to the display.
; Resources : ram LCDbuf
;********************************************************************
; routine apply time for busy
BusyTime
; check if data or command
btfsc
LCD_RSr ; if command - jump
bra
shrtd ; if not, short delay
; which command ?
movlw
d'3' ; higher than 3 ?
cpfsgt LCDbuf
; if >3 short delay
bra
longd
shrtd DelayUS LCDSHRTWAIT ; otherwise long delay 1.6 ms
return
longd DelayUS LCDLONGWAIT ; short delay
return |
L' operazione di lettura del flag BF ha una struttura identica a quella di
lettura generica dal modulo LCD, quindi le routine sono intersecanti.
- LCDRdRam ritorna con il contenuto della cella
di RAM letta in WREG.
- LCDRdBfa ritorna con l' indirizzo corrente e
lo stato di BF in WREG. Questa routine non è bloccante, ovvero viene
rilasciata non appena effettuata la lettura.
- LCDChkBusy ritorna con l' indirizzo corrente
e lo stato di BF in WREG. Questa routine è bloccante, ovvero non viene
rilasciata fino a che il flag BF non è valido.
Va notato che qui è stato aggiunto un punto che solitamente è trascurato,
se non del tutto ignorato. Tra l' abilitazione del controller e la possibilità
di leggere dati stabili sulle linee di uscita è necessario un certo tempo, TDDR.
Si tratta di un tempo dell' ordine dei 260-400ns, che nel caso di PIC con clock
bassi sono ininfluenti, ma che con clock elevati (e cicli di istruzione da 100
ns) possono diventare sensibili. In particolare, i tempi di risposta del
controller aumentano con il diminuire della tensione di alimentazione. e di
questo va tenuto conto.
Certamente con un 16F84 a 4MHz i cicli WHILE non sono necessari, ma va tenuto
presente che questi sono presento SOLO nel sorgente: durante la compilazione l'
Assembler li prenderà in considerazione, inserendo i nop sottintesi SOLO
quando il clock del processore sarà tale da richiederlo. Negli altri casi non
verrà inserito nulla. Quindi, questa struttura a cicli WHILE non è di alcun
peso per il risultato della compilazione, anzi, costituisce un automatismo che
lo adatta alle esigenze reali dell' hardware.
Per contro sono stati inseriti vari nop addizionali allo scopo di
stabilizzare lo stato delle lineen di comunicazione tra PIC e modulo LCD; questo
si rende necessario nel caso di cablaggi lunghi. Potranno essere rimossi nel
caso di distanza minima, ma conviene non eliminarli, in quanto, rispetto al
ciclo di accesso al modulo, influiscono in maniera trascurabile.
#else
; check BF mode, RW active
; Read Ram or BF + address is possible only if RW is active
; compile read subroutines and check busy
;********************************************************************
; LCDRdRam - LCDRdBfa - Subroutine
; Routine reads data or address + BF
; Save result in WREG
; Resources : WREG
;********************************************************************
LCDRdRam:
; read data from ram
bsf LCD_RSw
; RS=1 for RAM
bra chby0
;
LCDRdBfa
; read BF + AC
bcf LCD_RSw
; RS=0 for BF+AC
chby0
setf LCDtris
; data port = input
bsf LCD_RWw
; apply read direction
nop
; additional safety
chby1
bsf LCD_Ew
; set enable
; From E=1 to data valid need a minimum time
; WHILE cycles inserts a delay depending from the clock
nop
; extra safety wait
VARIABLE vCNT
vCNT = LCDTDDR ; assign pre-defined constant
WHILE vCNT > 0x0
nop
vCNT -= 1
ENDW
bra notbusy
; return w/ I/O cleared
;********************************************************************
; LCDChkBusy - Subroutine
; Routine to poll busy flag from LCD.
; The routine returns only if BF = 0.
; Save results in WREG.
; Resources : WREG
;********************************************************************
LCDChkBusy
; check BF=0
bcf LCD_RSw
; RS=0 for BF + AC
setf LCDtris
; data port = input
bsf LCD_RWw
; apply read direction
nop
; additional safety
chby
bsf LCD_Ew
; set enable
; add TDDR for data stable
nop
; extra safety wait loop
VARIABLE vCNT
vCNT = LCDTDDR ; assign pre-defined constant
WHILE vCNT > 0x0
nop
vCNT -= 1 ; decrement
ENDW
btfss LCDportr, 7
; if bit set display is busy
bra notbusy
; if bit clear, busy end
bcf LCD_Ew
; disable E
; insert nops to comply with manufacturer specification
; for min. delay before next E pulse.
nop
VARIABLE vCNT
vCNT = LCDTCYE ; assign pre-defined constant
WHILE vCNT > 0x0
nop
vCNT -= 1
ENDW
bra chby
; another check loop
; busy cycle end
notbusy
movf LCDportr, w
; save W content
nop
bcf LCD_Ew
; remove enable
nop
; stabilization
clrf LCDportw
; clear PORT
bcf LCD_RWw
; clear RW
bcf LCD_RSw
; clear RS
clrf LCDtris
; set port = output
return
#endif |
Le routines di scrittura fanno uso di una macro
;local macro
LCDXMIT macro
movwf LCDportw
; put to LCD data lines
endm
|
Potrebbe parere del tutto inutile dichiarare una macro pari ad una linea di
istruzione, ma questo serve per unificare la struttura delle varie versioni del
driver, come vedremo in seguito.
|