Esercitazioni ASM - PIC18

 


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.


 

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