Esercitazioni ASM - PIC18

 


ESERCITAZIONE # 12a


Esecuzione

Per la verifica dell' hardware vale quanto detto nell' esercitazione precedente.

Per il software, il progetto MPLAB è scaricabile qui.

Una volta assemblato il sorgente e caricata la memoria del PIC, basta lanciare il programma con  Run  e, se tutto è a posto, il display visualizzerà il messaggio di prompt.
Se questo non succede, dopo aver verificato con cura i collegamenti, provate a regolare il contrasto: i caratteri dovranno apparire chiaramente.

E' possibile anche il funzionamento step by step, ma, data la complessa ripetitività delle routines, è meglio utilizzare breakpoint da posizionare nei punti in cui si vuole interrompere l' esecuzione.


Il programma esegue la stessa sequenza dell' esercitazione precedente, ovvero:

  1. inizializza I/O per il display
  2. inizializza il display
  3. scrive un messaggio, cadenzando la presentazione delle lettere di 0.5 secondi una dall' altra
  4. attesa di 2 secondi
  5. pulisce il display e riporta i cursore all' inizio
  6. attesa di 1 secondo
  7. ripresa dal punto 3

Osserviamo che il programma è uguale al precedente dell' Es 12 !
La sostituzione del driver, con gli stessi entry point, permette di mantenere identico il flusso principale.


Una NOTA

C'è qualcuno che commenta:" Ma come, per comandare un display occorrono così tante righe di sorgente? Altri autori se la cavano con molto meno...".
Certamente vero, basta semplicemente abolire ogni commento "superfluo" e il testo sorgente si riduce a ben poco; ecco la versione lite: 

;**********************************************************
;* lcd8bf_l018.asm
;* Driver per display LCD a caratteri
;**********************************************************
; bus dati su PORTC
; RS su portB,0
; RW su portB,1
; E  su portB,2
;**********************************************************
;----------------------------
LCDIoIni: ; set I/O per LCD
    bcf  PORTB,2       
    bcf  PORTB,0      
    bcf  PORTB,1 
    clrf PORTC 
    clrf TRISC 
    bcf  TRISB,2
    bcf  TRISB,1
    bcf  TRISB,0
    return

;----------------------------
LCDSwini        ; Inizializzazione per istruzioni.
    rcall Delay15ms
    movlw 0x30       ; comando 30h
    movwf PORTC
    rcall LCDclk     ; primo clock
    rcall Delay4ms   ; primo ritardo
    rcall LCDclk     ; secondo clock
    rcall Delay100us ; secondo ritardo
    rcall LCDclk     ; terzo clock
    rcall LCDrdbfa   ;check per Busy
    movlw 0x38       ;8 bit/2 linee/ font 5x7
    rcall LCDWrCmd
    movlw 0x10       ;display off
    rcall LCDWrCmd
    movlw 0x01       ;Clear & Home
    rcall LCDWrCmd
    movlw 0x06       ;cursor move l->r
    rcall LCDWrCmd
    movlw 0x0E       ;display on/cursor off/blink off
    rcall LCDWrCmd
    return

;----------------------------
LCDwrcmd:  ; Trasmette un comando. Comando in WREG
     bcf   PORTB,0 ; RS = 0 per comando
     bra   lcwr

LCDwrdat:  ; Trasmette un dato. Dato in WREG
     bsf   PORTB,0 ; RS = 1 per dati
lcwr movwf LCD_portw
     bcf   PORTB,1 ; RW = 0 per scrittura
     rcall LCDclk 
; reset LCD bus e controlli
     clrf  PORTC 
     bcf   PORTB,1 
     bcf   PORTB,0 
     return

;----------------------------
LCDclk bsf PORTB,2 ; E = 1
     nop
     nop
     bcf PORTB,2   ; E = 0
     nop
     return

;----------------------------
LCDrdram:  ; Leggi RAM dall' LCD
     bcf   PORTB,0     ;RS=0 per la RAM
     bra   lcdr0 

LCDrdbfa:  ; Leggi BF + AC dall' LCD
     bsf   PORTB,0     ;RS=1 per BF+AC

lcdr0 setf TRISC       ;data port = input
     bsf   PORTB,1     ;RW=1 per leggere
     nop ;stabilizzazione

lcdr1 bsf  PORTB,2     ;E = 1 - abilita LCD
     btfss PORTB,0     ;RS = 1?
     bra   notbusy     ;n - legge RAM
;s - test busy dall' LCD
     btfss PORTC, 7    ;se 1 = busy
     bra   notbusy     ;se = 0 busy end
     bcf   PORTB,2 
     nop
     nop 
     bra   lcdr1
notbusy movf PORTC, w ;salva il dato letto
     nop
     bcf   PORTB,2
     nop
     nop
; reset LCD bus e controlli
     clrf  PORTC
     bcf   PORTB,1 
     bcf   PORTB,0 
     clrf  TRISC 
     return

;----------------------------
LCDWrCmd:  ; Trasmette un comando.
    rcall  LCDwrcmd
    bra    LCDrdbfa   ; check busy

LCDWrDat:  ; Trasmette un dato. 
    rcall  LCDWrDat   ; RS = 1 per dati
    bra    LCDrdbfa

;----------------------------
LCDLine1   ;porta il cursore alla linea
    movlw  0x80      ; linea 1
    bra    LCDWrCmd
LCDLine2  
    movlw  0xA0      ; linea 2
    bra    LCDWrCmd

; end of the module

E, per alleggerirlo ulteriormente, possiamo eliminare la strutturazione, tutti i commenti, la forma in subroutine e scrivere le istruzioni direttamente nel main...
E poi ?  Il testo risulta certo più corto, ma è comprensibile ? E' sensato un procedere del genere ?

Dato che:

  1. La versione "completa" di commenti e costanti non impiega più tempo ad essere compilata di quella ridotta.
  2. Inoltre, trattandosi di un driver, nel sorgente del programma in cui verrà usato apparirà solamente come una linea di #include. Basta un comando NOLIST all' inizio del driver per impedirne la stampa nel listato ottenuto dall' assembler, che risulterà leggero al massimo.
     
  3. E una versione completa di commenti e descrizioni esaustive delle funzioni e delle procedure fornisce LO STESSO codice della versione priva di commenti, non un bit in più !!!

Il driver, una volta scritto e collaudato, non richiede più di essere nè scritto, nè letto, nè collaudato ulteriormente: va semplicemente utilizzato richiamando i suoi entry point, ovvero le funzioni, macro o subroutine, che svolgono un dato compito.

Le versioni lite non sono per niente conformi all' idea che si sta proponendo, ovvero quella di una programmazione quanto possibile strutturata e modulare, con macro componenti facili da utilizzare e sorgenti semplici da gestire.
E questa semplificazione di uso richiede una maggiore complicazione nei vari moduli.
Anche solo se non trasformiamo tutti gli assoluti in label, ci troveremo in difficoltà serie una volta che si renda necessaria una modifica. 
Ad esempio, nel listato qui sopra, si è rinunciato ad associare i port con label. Il risultato è che se si devono usare altri pin, occorre modificare l' intero listato. Una serie di definizioni label= assoluti permette invece di cambiare le assegnazioni agendo solo ed esclusivamente nelle definizioni.
E le molte linee delle assegnazioni e delle label producono lo steso codice di una versione priva di label e assegnazioni, ma, rispetto a questa, sono infinitamente più maneggevoli, modulari, modificabili, comprensibili !!!

Un esempio per tutti: per capire cosa faccia un movlw 0xA0 , occorre consultare il foglio dati; l' assegnazione di una tabella di comandi=label esplicativa rende tutto più semplice.

Ovviamente ognuno potrà scegliere la forma e lo stile che più ritiene adeguata durante la scrittura dei sorgenti. 
Il problema non è la forma, ovvero usare *** al posto di ---- o label maiuscole al posto di minuscole, nè quanti commenti inserire, ma è quello di

  • usare una forma e uno stile che siano chiari
  • il numero di commenti necessari a chiarire cosa facciano le istruzioni e 
  • realizzare una programmazione che non sia fine a se stessa, funzionale solamente all' applicazione contingente, ma che renda il proprio lavoro, tempo e sforzi impegnati, tale da poter essere fruttuoso anche in futuro.

 

 

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