list P=16f84A , R=DEC #INCLUDE P16F84A.inc __FUSES _CP_OFF&_WDT_OFF&_XT_OSC CBLOCK 0ch Timer1 ;Subcontatore per DelayP Timer2 ;Contatore per DelayP Counter ;Contatore valore da mostrare a display CounterOld ;valore di Contatore precedente a UpdateDisplay DispTemp ;contenitore temporaneo per calcoli routine display DispCnt ;contatore generico per gestione display X2asctmp ;valore temporaneo per conversione hex2asc TabTemp ;memoria temporanea per implementazione tabelle costanti ENDC CounterPreset=1 ;valore presettato di contatore AntiDebounce=200 ;timer antirimbalzo per pulsanti ;inizio programma dopo un reset org 0 ;inizializzazione sistema e variabili bsf STATUS,RP0 movlw 01110001b ;0=uscita 1=ingresso movwf TRISB movlw 11100000b movwf TRISA bcf STATUS,RP0 movlw CounterPreset ;setta il contatore al valore di default movwf Counter movwf CounterOld incf CounterOld,f ;trucco per aggiornare la visualizzazione da subito ... ;) ;inizializzazione display call DispInit ;Frase iniziale sul display "Contatore: " clrf DispCnt ;azzera contatore carattere messaggio Msg1 movf DispCnt,w ;preleva puntatore carattere messaggio call TabellaMsg1 ;preleva carattere relativo call DispD ;scrivilo sul display incf DispCnt,f ;incrementa puntatore ai caratteri movlw 11 ;10 caratteri da visualizzare (1 in +... prima incrementiamo POI controlliamo) subwf DispCnt,w ;sottrai contatore caratteri messaggio... btfss STATUS,Z ;...per vedere se siamo alla fine goto Msg1 ;loop principale programma Loop call Button ;aggiorna il valore del contatore in base allo stato dei pulsanti call DispUpdate movlw AntiDebounce ;antirimbalzo pulsanti di 20 millisecondi call DelayP goto Loop ;ricomincia da capo ;********************************************** ;subroutine Button , legge lo stato dei pulsanti e modifica il valore del contatore ;********************************************** Button bsf PORTB,1 ;accende led di debug ;pulsante 1: incrementa contatore btfsc PORTB,4 ;leggi lo stato del pulsante 1 incf Counter,f ;se premuto incrementa il valore del settaggio tempo ;pulsante 2: decrementa contatore btfsc PORTB,5 ;controlla ora il pulsante di decremento decf Counter,f ;decrementa il contatore ;pulsante 3: azzera contatore movlw CounterPreset ;carica il valore di default contatore btfsc PORTB,6 ;controlla il terzo pulsante movwf Counter ;se premuto carica il valore di preset nel contatore ;attendiamo il rilascio dei pulsanti eventualmente premuti Butt1 movlw 01110000b ;maschera di AND per stato pulsanti andwf PORTB,w ;maschera i bit che non servono e deposita il risultato in W non PORTB !!! btfss STATUS,Z ;se risultato = zero, nessun pulsante premuto goto Butt1 bcf PORTB,1 ;spegne led 1 di debug return ;esce dalla subroutine ;********************************************** ;subroutine DispUpdate , aggiorna display solo se il valore di Contatore è variato ;********************************************** DispUpdate bsf PORTB,2 ;accende led 2 di debug movf Counter,w ;preleva contatore attuale subwf CounterOld,w ;sottraigli il valore del loop precedente btfsc STATUS,Z ;controlla se =0 cioè se valori ancora uguali goto DispUpdateEnd ;esci se i valori sono uguali movlw 8ch ;comando posizione cursore sul numero da aggiornare call DispC ;invia comando al display swapf Counter,w ;preleva il nibble alto in quello basso di W andlw 0fh ;maschera i bit che non servono call Hex2asc ;converti in ascii visualizzabile call DispD ;mostra a display movlw 0fh ;maschera i bit che non servono andwf Counter,w call Hex2asc ;converti in ascii visualizzabile call DispD ;mostra a display movf Counter,w ;aggiorna il valore di CounterOld al nuovo valore movwf CounterOld DispUpdateEnd bcf PORTB,2 ;spegne led 2 di debug return ;********************************************** ;* DispInit: Inizializza il display all'accensione ;********************************************** DispInit ;come da datasheet inviamo 3 comandi di settaggio interfaccia ad 8 bit ;prima per sicurezza attendiamo 10 msec che il chip si accenda per benino... movlw 100 ;10 msec di ritardo call DelayP movlw 00000011b ;comando display per interfaccia ad 8 bit, lavoriamo a 4 bit !!! movwf PORTA bsf PORTB,7 ;abilita display nop ;meglio mettere una pausa di 1 microsecondo bcf PORTB,7 ;disabilita display movlw 1 ;attendi almeno 40 microsecondi, 100 va meglio ! call DelayP bsf PORTB,7 ;idem come sopra nop bcf PORTB,7 movlw 1 call DelayP bsf PORTB,7 nop bcf PORTB,7 movlw 1 call DelayP ;Ora il display è ben inizializzato, commutiamo l'interfaccia a 4 bit movlw 00000010b ;comando display per interfaccia a 4 bit movwf PORTA bsf PORTB,7 ;abilita display nop ;meglio mettere una pausa di 1 microsecondo bcf PORTB,7 ;disabilita display movlw 1 ;attendi almeno 40 microsecondi, 100 va meglio ! call DelayP movlw 1 ;attende 100 microsecondi per dare il tempo al display di digerire il comando call DelayP ;da questo momento lavoriamo a 4 bit, finalmente ! ora possiamo usare DispC per inviare qualunque comando movlw 5 ;5 comandi da inviare al display movwf DispCnt DispIniLoop movf DispCnt,w ;preleva numero comando da inviare call Tabella1 ;preleva il relativo comando dalla tabella call DispC ;invia comando al display movlw 20 ;attendi circa due millisecondi pari alla durata del comando + lungo call DelayP decfsz DispCnt,f ;decrementa contatore numero comandi goto DispIniLoop ;reitera se ci sono ancora comandi da inviare return ;********************************************** ;DispC: invia un comando al display LCD ;********************************************** DispC movwf DispTemp ;ripone al sicuro il comando swapf DispTemp,w ;inverte i nibble e ne recupera il contenuto in W movwf PORTA ;invia il comando al display bcf PORTA,4 ;azzera bit 4 per selezione RS=0 -> istruzione bsf PORTB,7 ;abilita il display nop bcf PORTB,7 ;disabilita il display movf DispTemp,w ;preleva comando movwf PORTA ;invia il comando al display bcf PORTA,4 ;azzera bit 4 per selezione RS=0 -> istruzione bsf PORTB,7 ;abilita il display nop bcf PORTB,7 ;disabilita il display movlw 1 ;attende 100 microsecondi per dare il tempo al display di digerire il comando call DelayP return ;********************************************** ;DispD: invia un dato al display LCD ;********************************************** DispD movwf DispTemp ;ripone al sicuro il comando swapf DispTemp,w ;inverte i nibble e ne recupera il contenuto in W movwf PORTA ;invia il comando al display bsf PORTA,4 ;setta bit 4 per selezione RS=1 -> dato bsf PORTB,7 ;abilita il display nop bcf PORTB,7 ;disabilita il display movf DispTemp,w ;preleva comando movwf PORTA ;invia il comando al display bsf PORTA,4 ;setta bit 4 per selezione RS=1 -> dato bsf PORTB,7 ;abilita il display nop bcf PORTB,7 ;disabilita il display movlw 1 ;attende 100 microsecondi per dare il tempo al display di digerire il dato call DelayP return ;********************************************** ;subroutine DelayP: Ritarda l'esecuzione di 100 microsecondi per ogni unità di W ;+ 4 microsecondi aggiuntivi sempre presenti ;********************************************** DelayP movwf Timer2 ;deponi il valore passato in W nel contatore che useremo movlw 19 movwf Timer1 ;presetta subtimer1 19 ciclix 5 istruzioni = 95 microsecondi ;cui vanno aggiunti i cicli esterni al loop D1 D1 nop nop decfsz Timer1,f goto D1 movlw 19 ;occorre ogni ciclo ricaricare Timer 1 con il preset movwf Timer1 nop decfsz Timer2,f goto D1 return ;********************************************** ;hex2asc: codifica un nibble in w in un byte ascii sempre in w ;********************************************** Hex2asc movwf X2asctmp sublw 9 ;guarda se > 9 btfss STATUS,C goto Hex2asc1 ;oltre se lo e' movf X2asctmp,w ;recupera il dato addlw 30h ;somma 30 per trasformarlo in ascii return Hex2asc1 movf X2asctmp,w ;recupera il dato addlw 37h ;somma 37h per avere da A ascii in poi (41h...) return ;********************************************** ;Tabelle varie ;********************************************** org 300h ;ultimo blocco da 256 byte prima di 3FFh che è il fine ROM ;tabella valori per inizializzazione display Tabella1 movwf TabTemp ;riponi W al sicuro movlw high(Tabella1) ;parte alta dell'indirizzo programma corrente movwf PCLATH ;deponila in PCLATH movf TabTemp,w ;recupera puntatore alla tabella salvato precedentemente addwf PCL,f ;W deve contenere il puntatore alla tabella nop ;il numero zero NON lo usiamo retlw 00000001b ;pulisce il display e riporta il cursore a posizione zero retlw 00000110b ;incrementiamo posizione caratteri dopo ogni trasferimento, non shiftiamo il display retlw 00001100b ;display acceso, cursore spento e non lampeggiante retlw 00010100b ;shifta il cursore verso destra retlw 00101000b ;display a due righe, nota che dobbiamo riconfermare il modo 4 bit ! TabellaMsg1 movwf TabTemp movlw high(TabellaMsg1) movwf PCLATH movf TabTemp,w addwf PCL,f retlw 'C' retlw 'o' retlw 'n' retlw 't' retlw 'a' retlw 't' retlw 'o' retlw 'r' retlw 'e' retlw ':' retlw ' ' end