PIClock Orologio Digitale
Orologio digitale basato sul PIC16F628A (Progetto scolastico)
Scarica il documento PDF
Scarica gli schemi elettrici in PDF
Di seguito si illustra il progetto di un orologio digitale.
Il progetto è molto utile per capire il funzionamento delle dinamiche interne di un circuito gestito da un microcontrollore, il che diventa utilissimo per moltissime applicazioni dove la logica cablata non è sufficiente a soddisfare le specifiche di progetto.
Pur essendo un progetto artigianale e scolastico, l’orologio non è molto ingombrante (7 cm di larghezza x 9cm di lunghezza)
Indice
- Introduzione
- Schema a Blocchi
- Schema elettrico
- Firmware
- Dimensionamento componenti
- Lista componenti
- Conclusioni
- Disegni tecnici
- Codice
Introduzione
Lo scopo del progetto è di realizzare un circuito in grado di mostrare l’ora in ore e minuti, di impostarla tramite dei pulsanti e di gestire il risparmio energetico a seconda che venga alimentato da rete o da batteria. Questo progetto non è quindi solo un puro e semplice esercizio, ma si tratta di un progetto con un’ applicazione pratica e funzionale.
L’orologio ha le seguenti specifiche:
- Display a 4 cifre con due punti lampeggianti che separano le ore dai minuti.
- Due tipi di alimentazione:
- Da rete elettrica: serve un trasformatore/raddrizzatore esterno con tensione raddrizzata e livellata 11 – 15 V.
- Con batteria da 7 – 9 V DC.
- Tre pulsanti di controllo: reset, select e set.
- Componenti del magazzino della scuola C. Zuccante laboratorio OEN2.
Schema a blocchi
Lo schema a blocchi, illustrato nella figura seguente, suddivide in due parti principali il progetto: l’alimentazione e l’orologio.
Schema elettrico
Clicca sull’immagine per ingrandirla
Alimentazione
Nel primo blocco, illustrato nella figura seguente, la tensione in ingresso al connettore a due poli X1-1 e X1-2 è continua di ampiezza compresa tra +7V e +9V, invece la tensione in ingresso al connettore jack J1 è anch’essa continua compresa tra +11V e +15V (si è utilizzato un trasformatore che trasforma i 230 V AC in 12 V DC).
Sono stati utilizzati due diodi 1N4007 in modo tale che in ingresso allo stabilizzatore LM7805 sia applicata la tensione del trasformatore se presente, altrimenti viene applicata quella della batteria, questo per far risparmiare energia alla batteria.
Al pin RA4 del microcontrollore viene inviata la tensione d’alimentazione del trasformatore tramite un apposito diodo Zener (BZX79C4V7) per segnalare al PIC se questa alimentazione è o meno presente per gestire la modalità a risparmio energetico se è presente solo l’alimentazione a batteria.
L’integrato stabilizzatore LM7805 fornisce in uscita 5V utilizzati per alimentare l’intero circuito.
Orologio
Il primo pulsante è connesso al pin RA5, il master clear, questo ha funzione di RESET, ovvero se premuto riavvia il sistema e tutti i led del display si accendono e si spengono in modo intermittente per segnalare che si deve impostare l’ora.
Il secondo pulsante è connesso al pin RB0 ed ha funzione di SELECT, se premuto per 4 volte consecutive, seleziona quale cifra del display deve essere regolata, quando viene premuto per la quinta volta fa partire l’orologio, funziona anche nella modalità basso consumo.
Il terzo pulsante è connesso al pin RB1 ed ha funzione di SET, premuto successivamente , permette di impostare la cifra desiderata.
L’oscillatore al quarzo di frequenza 32.768Hz è connesso ai pin RB6 e RB7, fornisce il clock al Timer1, periferica interna del PIC16F628A.
Sono stati utilizzati 4 diplay 7 segmenti a catodo comune , uno di essi, le decine dei minuti, è girato così da posizionare il suo decimal point in alto per separare le ore dai minuti. Non è stato utilizzato un decoder BCD/7 segmenti, quindi la decodifica viene gestita via software, dunque è stato utilizzato un integrato contenente 8 resistenze, questo per risparmiare spazio e collegamenti nel pcb. Inoltre per risparmiare energia i display sono multiplexati, ovvero è acceso un solo display alla volta, questo tramite 4 transistor BJT BC337 col collettore connesso al catodo comune di ciascun display, l’emettitore connesso a massa e la base comandata dai pin RA0, RA1, RA2, RA3 del microcontrollore PIC16F628A.
Per il funzionamento del PIC16F628A si è optato per l’oscillatore interno a 4 MHz per risparmiare componenti, il ciclo di una istruzione dura 1 microsecondo. Tra i pin di VDD e VSS è stato aggiunto un condensatore elettrolitico di disaccoppiamento da 1uF.
Riassumendo nella seguente tabella la definizione dei pin:
<br
Firmware
Flow chart alimentazione
Il diagramma di flusso relativo all’alimentazione del progetto si suddivide principalmente in due parti: alimentazione da rete e da batteria.
Per prima cosa viene controllato il pulsante SELECT: se non è premuto lascia il display nella modalità blinking. Se invece è premuto permette di impostare l’ora tramite il pulsante SET, l’ordine di impostazione dell’ora è decine delle ore, unità delle ore, decine dei minuti e unità dei minuti.
A questo punto parte il conteggio dei secondi tramite il Timer1. Adesso viene controllato il tipo di alimentazione:
- ALIMENTAZIONE DA RETE: Se il circuito è alimentato tramite la rete elettrica non c’è bisogno di risparmiare energia quindi si può selezionare tramite il pulsante SELECT se spegnere i display o lasciarli sempre attivi.
- ALIMENTAZIONE TRAMITE BATTERIA: Se il pulsante SELECT non è premuto il microcontrollore si trova in modalità basso consumo (SLEEP). Se invece è premuto, vengono eseguiti due blink e vengono spenti i display. Si passa così in modalità basso consumo. Successivamente si controlla se c’è una richiesta di interrupt, si esce dalla modalità basso consumo e si esegue il programma principale descritto dal flow chart.
Clicca sull’immagine per ingrandirla
Flow chart microcontrollore
Il diagramma di flusso seguente descrive il funzionamento dell’ISR (interrupt service routine).
Per prima cosa viene testato il Timer1, se il Timer non si è azzerato maschera l’interruzione RB0INT, “manda” GIE a 1 abilitando così le interruzioni, accende i display per otto secondi, azzera la richiesta di interruzione RB0INT e termina l’ISR. Se Timer1 si è azzerato e TMR1IF = 1, per prima cosa salva il contesto del microcontrollore, poi azzera la richiesta di interruzione di Timer1 e comincia il ciclo per il conteggio dei secondi, dei minuti e delle ore.
Clicca sull’immagine per ingrandirla
Dimensionamento dei componenti
Alimentazione
Resistenze del diodo Zener:
Valore più piccolo di Iz:
Per garantire che lo zener sia in conduzione occorre:
Valore più grande di Iz:
Per garantire che lo zener non si danneggi occorre:
Scelte di progetto:
Diodo zener: BZX9C4V7
Potenza dissipata:
Potenza massima dissipata:
Si sceglie:
Per avere con valore di I2 qualche mA si sceglie:
Condensatori
C1=330 nF e C2=100 nF sono stati scelti consultando il datasheets del regolatore di tensione.
100 microF per stabilizzare la tensione di alimentazione durante la commutazione dell’alimentazione da rete a batteria e viceversa.
Orologio
Resistenze dei transistor
8 Resistenze integrate dual in-line package – DIP 330Ω valore nominale
Per garantire la saturazione del transistor:
Calcolo il valore della resistenza di base del transistor:
Resistenze dei pulsanti
R3=R6=R7=10kΩ scelte consultando la documentazione Microchip AN590.
R4=R5=220Ω scelte consultando la documentazione Microchip AN590: 820 ohm, valore ridotto per adattarlo alle esigenze del progetto Piclock.
Quarzo
Lista componenti
Lista dei componenti utilizzati per realizzare il progetto:
Conclusioni
Il progetto è stato concluso con successo, anche se non si era partiti nel modo migliore, visto la scarsità di componenti reperibili nel magazzino del laboratorio OEN2 dell’istituto.
Sicuramente per questo tipo di progetto e per la larga memoria di programma si poteva optare per la programmazione in C del PIC16F628A.
Per diminuire le dimensioni del PCB si potevano utilizzare componenti SMD, non presenti però nel magazzino della scuola.
Disegni Tecnici
Clicca sull’immagini per ingrandirle
Codice
Codice in assebly fornito dal professore:
; IT Zuccante - Mestre - A.S. 2013/2014 ; Indirizzo Elettronica ed Elettrotecnica - Articolazione Elettronica - classe 4 EA ; TECNOLOGIE E PROGETTAZIONE DI SISTEMI ELETTRICI ED ELETTRONICI - E. Minosso, R. Bardelle ; ; 26 maggio 2014 ; ; Progetto " PICLOCK " ; ;********************************************************************************************************************************* ;================================================================================================================================= processor 16F628A #include P16F628A.INC ; imposta la CPU - Configuration Word ; __ ; CW<13> = CP = 1 Code Protection OFF _CP_OFF = 11 1111 1111 1111 ; ___ ; CW<8> = CPD = 1 Data Code Protection OFF _CPD_OFF = 11 1111 1111 1111 ; CW<7> = LVP = 0 Low-Voltage Programming disabled _LVP_OFF = 11 1111 0111 1111 ; CW<6> = BOREN = 0 Brown-Out Reset disabled _BOREN_OFF = 11 1111 1011 1111 ; ____ ____ ; CW<5> = MCLRE = 1 pin RA5/MCLR/Vpp function is MCLR _MCLRE_ON = 11 1111 1111 1111 ; _____ ; CW<3> = PWRTE = 0 Power-up Timer enabled _PWRTE_ON = 11 1111 1111 0111 ; CW<2> = WDTE = 0 Watchdog Timer disabled _WDTE_OFF = 11 1111 1111 1011 ; CW<4,1-0> = FOSC<2:0> = 100 INTOSC oscillator: ; I/O function on RA6/OSC2/CLKOUT RA7/OSC1/CLKIN pins _INTOSC_OSC_NOCLKOUT = 11 1111 1111 1100 ; ; Configuration Word Register = 1x xxx1 0011 0000 ; Configuration Word Register = 2130h __config _CP_OFF & _CPD_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_ON & _PWRTE_ON & _WDTE_OFF & _INTOSC_OSC_NOCLKOUT radix DEC ; COSTANTI ======================================================================================================================= Blank equ 10 ; utilizzata nella decodifica BCD/7seg per tenere spenti tutti i led del display ; VARIABILI ====================================================================================================================== cblock 0x20 ; definisce gli indirizzi delle variabili seguenti partendo dall'indirizzo 20h U_min ; unità dei minuti --> display n° 0 D_min ; decine dei minuti --> display n° 1 U_ore ; unità delle ore --> display n° 2 D_ore ; decine dedelle ore --> display n° 3 Due_punti ; memorizza l'accensione e lo spegnimento dei due punti --> display n° 1 e n° 2 Conta_sec ; contatore che va da 0 a 29 --> si incrementa ogni 2 secondi di tempo, per cui quando arriva a 30 significa ; che sono passati 30 x 2 secondi = 60 secondi Contatore1 ; variabile contatore Contatore2 ; variabile contatore Acceso ; variabile booleana: Acceso = 1 <--> display acceso; Acceso = 0 <--> display spento RB0_INT ; variabile booleana: RB0_INT = 1 <--> eseguita l'ISR dell'interruzione RB0/INT ; RB0_INT = 0 <--> non è stata eseguita l'ISR dell'interruzione RB0/INT ; INTERRUPT SERVICE ROUTINE STATUS_temp ; salva temporaneamente il contesto del uC (registro STATUS) endc ; fine della direttiva cblock W_temp equ 0x70 ; salva temporaneamente il contesto del uC (W deve essere salvato negli ; ultimi 16 GPRs del banco 00, che sono replicati anche negli altri tre ; banchi di memoria, così per accedere a W_temp non occorre modificare ; RP1 ed RP0 del registro STATUS) ; INIZIO PROGRAMMA =============================================================================================================== org 0 GOTO boot ; avvia il programma principale ; gestore dell'interruzione ====================================================================================================== org 4 BTFSS PIR1, TMR1IF ; SE TMR1IF = 1 esegui l'ISR "rtc" che aggiorna i secondi, i minuti e le ore GOTO wake_up ; ALTRIMENTI esegui l'ISR "wake-up" che accende nuovamente il display ; ISR rtc (real-time clock) ====================================================================================================== ; salva il contesto del uC rtc MOVWF W_temp ; salva il contenuto di W per poter usare subito W nell'ISR SWAPF STATUS, W ; copia temporaneamente in W i nibble scambiati di STATUS BCF STATUS, RP1 ; seleziona BCF STATUS, RP0 ; il banco 00 MOVWF STATUS_temp ; salva i nibble scambiati di STATUS BCF PIR1, TMR1IF ; azzera il flag di interruzione di Timer1 ; aggiorna il contatore dei secondi INCF Conta_sec, F ; Conta_sec = Conta_sec + 1 MOVLW 30 ; controlla XORWF Conta_sec, W ; se Conta_sec = 30 BTFSS STATUS, Z ; SE Conta_sec = 30 ALLORA azzera Conta_sec e incrementa U_min GOTO fine_ISR ; ALTRIMENTI finisci l'ISR ; aggiorna le unità dei minuti CLRF Conta_sec ; Conta_sec = 0 INCF U_min, F ; U_min = U_min + 1 MOVLW 10 ; controlla XORWF U_min, W ; se U_min = 10 BTFSS STATUS, Z ; SE U_min = 10 ALLORA azzera U_min e incrementa D_min GOTO fine_ISR ; ALTRIMENTI finisci l'ISR ; aggiorna le decine dei minuti CLRF U_min ; U_min = 0 INCF D_min, F ; D_min = D_min + 1 MOVLW 6 ; controlla XORWF D_min, W ; se D_min = 6 BTFSS STATUS, Z ; SE D_min = 6 ALLORA azzera D_min e incrementa U_ore GOTO fine_ISR ; ALTRIMENTI finisci l'ISR ; aggiorna le unità delle ore e le decine delle ore CLRF D_min ; D_min = 0 INCF U_ore, F ; U_ore = U_ore + 1 MOVLW 2 ; controlla XORWF D_ore, W ; se D_ore <> 2 BTFSC STATUS, Z ; SE D_ore <> 2 ALLORA controlla se U_ore = 10 GOTO azzera ; ALTRIMENTI controlla se U_ore = 4 MOVLW 10 ; controlla XORWF U_ore, W ; se U_ore = 10 BTFSS STATUS, Z ; SE U_ore = 10 ALLORA azzera U_ore e incrementa D_ore GOTO fine_ISR ; ALTRIMENTI finisci l'ISR CLRF U_ore ; U_ore = 0 MOVLW Blank ; controlla XORWF D_ore, W ; se D_ore <> blank BTFSC STATUS, Z ; SE D_ore <> blank ALLORA incrementa D_ore CLRF D_ore ; ALTRIMENTI D_ore = 0 INCF D_ore, F ; D_ore = D_ore + 1 GOTO fine_ISR ; finisci l'ISR azzera MOVLW 4 ; controlla XORWF U_ore, W ; se U_ore = 4 BTFSS STATUS, Z ; SE U_ore = 4 ALLORA azzera e finisci l'ISR GOTO fine_ISR ; ALTRIMENTI finisci l'ISR CLRF U_ore ; U_ore = 0 MOVLW Blank ; spegni MOVWF D_ore ; il display n° 3 ; ripristina il contesto del uC fine_ISR SWAPF STATUS_temp, W ; scambia e copia in W i nibble salvati in STATUS_temp MOVWF STATUS ; ripristina il contenuto di STATUS SWAPF W_temp, F ; scambia i nibble salvati in W_temp SWAPF W_temp, W ; scambia e ripristina il contenuto di W RETFIE ; riprendi il processo interrotto ; FINE ISR rtc =================================================================================================================== ; ISR wake_up ==================================================================================================================== wake_up BCF INTCON, INTE ; maschera l'interruzione RB0/INT BSF INTCON, GIE ; abilita le interruzioni BSF RB0_INT, 0 ; RB0_INT = 1 per segnalare che è stata fatta l'ISR wake-up MOVLW 2 ; inizializza MOVWF Contatore2 ; il Contatore2 iniz_cont1 MOVLW 200 ; inizializza MOVWF Contatore1 ; il Contatore1 due_punti_on3 BTFSS TMR1H, 6 ; SE TMR1H<6> = 1 ALLORA accendi i due punti GOTO due_punti_off3 ; ALTRIMENTI spegni i due punti BSF Due_punti, 7 ; accendi i due punti GOTO fai_ref ; fai un refresh due_punti_off3 BCF Due_punti, 7 ; spegni i due punti fai_ref CALL refresh ; fai il refresh del display DECFSZ Contatore1, F ; Contatore1 = Contatore1 - 1, SE Contatore1 = 0 ALLORA decrementa Contatore 2 GOTO due_punti_on3 ; ALTRIMENTI fai il refresh del display DECFSZ Contatore2, F ; Contatore2 = Contatore2 - 1, SE Contatore2 = 0 ALLORA finisci ISR e vai in power-down mode GOTO iniz_cont1 ; ATRIMENTI inizializza Contatore 1 BCF INTCON, INTF ; azzera il flag dell'interruzione RB0/INT RETFIE ; riprendi il processo interrotto ; FINE ISR wake_up =============================================================================================================== ; SUBROUTINE refresh ============================================================================================================= ; Legge i valori delle ore, dei minuti e dei 2 punti e accende in successione i 4 diplay a 7 segmenti, nell'ordine che ; va dal display n° 3 al display n° 0. ; Ogni display rimane acceso circa 5 ms, per cui tutta la subroutine viene eseguita in circa 20 ms. ; ; VARIABILI DI INGRESSO: ; U_min unità dei minuti --> display n° 0 ; D_min decine dei minuti --> display n° 1 ; U_ore unità delle ore --> display n° 2 ; D_ore decine dei minuti --> display n° 3 ; Due_punti memorizza l'accensione e lo spegnimento dei due punti --> display n° 1 e n° 2 ; VARIABILI DI USCITA: ; RA7 RA6 RA5 RA4 RA3 RA2 RA1 RA0 <-- PORTA ; DP a RESET BAT disp3 disp2 disp1 disp0 ; RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0 <-- PORTB ; T1OSI T1OSO b c d e f g ; ====================================================================================================================== refresh BSF STATUS, RP0 ; seleziona il banco 01 BCF TRISB, TRISB0 ; imposta RB0 come pin di ouput BCF TRISB, TRISB1 ; imposta RB1 come pin di output BCF STATUS, RP0 ; ritorna al banco 00 CLRF PORTA ; spegni i quattro display MOVF D_ore, W ; decodifica i segmenti da accendere CALL decod_BCD_7seg ; per il display n° 3 delle DECINE DELLE ORE MOVWF PORTB ; accendi/spegni i segmenti b, c, d, e, f, g del diaplay n° 3 ANDLW B'01000000' ; cattura il valore acceso/spento del segmento a IORWF PORTA, F ; accendi/spegni il segmento a BSF PORTA, RA3 ; accendi il display n° 3 CALL aspetta_5ms ; lascia acceso il display n° 3 per 5 ms CLRF PORTA ; spegni i quattro display MOVF U_ore, W ; decodifica i segmenti da accendere CALL decod_BCD_7seg ; per il display n° 2 delle UNITA' DELLE ORE MOVWF PORTB ; accendi/spegni i segmenti b, c, d, e, f, g del diaplay n° 2 ANDLW B'01000000' ; cattura il valore acceso/spento del segmento a IORWF PORTA, F ; accendi/spegni il segmento a MOVF Due_punti, W ; cattura il valore acceso/spento dei due punti IORWF PORTA, F ; accendi/spegni i due punti BSF PORTA, RA2 ; accendi il display n° 2 CALL aspetta_5ms ; lascia acceso il display n° 2 per 5 ms CLRF PORTA ; spegni i quattro display MOVF D_min, W ; decodifica i segmenti da accendere CALL decod_BCD_7seg ; per il display n° 1 delle DECINE DEI MINUTI MOVWF PORTB ; accendi/spegni i segmenti b, c, d, e, f, g del diaplay n° 1 ANDLW B'01000000' ; cattura il valore acceso/spento del segmento a IORWF PORTA, F ; accendi/spegni il segmento a MOVF Due_punti, W ; cattura il valore acceso/spento dei due punti IORWF PORTA, F ; accendi/spegni i due punti BSF PORTA, RA1 ; accendi il display n° 1 CALL aspetta_5ms ; lascia acceso il display n° 1 per 5 ms CLRF PORTA ; spegni i quattro display MOVF U_min, W ; decodifica i segmenti da accendere CALL decod_BCD_7seg ; per il display n° 0 delle UNITA' DEI MINUTI MOVWF PORTB ; accendi/spegni i segmenti b, c, d, e, f, g del diaplay n° 0 ANDLW B'01000000' ; cattura il valore acceso/spento del segmento a IORWF PORTA, F ; accendi/spegni il segmento a BSF PORTA, RA0 ; accendi il display n° 0 CALL aspetta_5ms ; lascia acceso il display n° 0 per 5 ms CLRF PORTA ; spegni i quattro display BSF STATUS, RP0 ; seleziona il banco 01 BSF TRISB, TRISB0 ; imposta RB0 come pin di input BSF TRISB, TRISB1 ; imposta RB1 come pin di input BCF STATUS, RP0 ; ritorna al banco 00 RETURN ; FINE SUBROUTINE refresh ======================================================================================================== ; SUBROUTINE decod_BCD_7seg ====================================================================================================== ; Esegue la decodifica BCD/7 segmenti con la seguente tavola di verità: ; ; N | D C B A || a b c d e f g a ; ------------------------------------------ ------- ; 0 | 0 0 0 0 || 1 1 1 1 1 1 0 | | ; 1 | 0 0 0 1 || 0 1 1 0 0 0 0 f | | b ; 2 | 0 0 1 0 || 1 1 0 1 1 0 1 | g | ; 3 | 0 0 1 1 || 1 1 1 1 0 0 1 ------- ; 4 | 0 1 0 0 || 0 1 1 0 0 1 1 | | ; 5 | 0 1 0 1 || 1 0 1 1 0 1 1 e | | c ; 6 | 0 1 1 0 || 1 0 1 1 1 1 1 | | ; 7 | 0 1 1 1 || 1 1 1 0 0 0 0 ------- ; 8 | 1 0 0 0 || 1 1 1 1 1 1 1 d ; 9 | 1 0 0 1 || 1 1 1 1 0 1 1 ; 10 | 1 0 1 0 || 0 0 0 0 0 0 0 <-- blank ; ; VARIABILE DI INGRESS0: ; W: codice BCD (numero da 0 a 10) ; VARIABILE DI USCITA: ; W: segmenti da accendere 1 <--> acceso 0 <--> spento ; ================================================================================================================================ decod_BCD_7seg ADDWF PCL, F ; seleziona il codice corrispondente al numero contenuto in W RETLW B'01111110' ; segmenti del numero 0 RETLW B'00110000' ; segmenti del numero 1 RETLW B'01101101' ; segmenti del numero 2 RETLW B'01111001' ; segmenti del numero 3 RETLW B'00110011' ; segmenti del numero 4 RETLW B'01011011' ; segmenti del numero 5 RETLW B'01011111' ; segmenti del numero 6 RETLW B'01110000' ; segmenti del numero 7 RETLW B'01111111' ; segmenti del numero 8 RETLW B'01111011' ; segmenti del numero 9 RETLW B'00000000' ; blank ; FINE SUBROUTINE decod_BCD_7seg ================================================================================================= ; SUBROUTINE aspetta_5ms ========================================================================================================= ; Utilizza Timer0 per tenere impegnata la CPU per un intervallo di tempo che dura circa 5 ms: ; ; Fosc = 4 MHz oscillatore interno ; Fosc/4 = 1 MHz frequenza base del segnale di clock ; Tck = 1 / 1 MHz = 1 microsec periodo base del segnale di clock ; D = 128 divisore del prescaler ; N = 39 numero di impulsi contati da Timer0 ; T = N x D x Tck = 39 x 128 x 1 microsec = 4,992 ms tempo impiegato da Timer0 per generare l'overflow ; N_iniz = 256 - 39 = 217 numero di partenza di Timer0 ; ================================================================================================================================ aspetta_5ms BCF INTCON, T0IF ; azzera il flag di interruzione delll'overflow di Timer0 MOVLW 217 ; inizializza e MOVWF TMR0 ; fai partire Timer0 test_T0IF BTFSS INTCON, T0IF ; SE TOIF = 1 ALLORA Timer0 si è azzerato, quindi ritorna GOTO test_T0IF ; ALTRIMENTI controlla Timer0 RETURN ; FINE SUBROUTINE aspetta_5ms ==================================================================================================== ; SUBROUTINE antirimbalzo ======================================================================================================== ; Tiene impegnata la CPU per circa 40 ms. ; ================================================================================================================================ antirimbalzo CALL aspetta_5ms CALL aspetta_5ms CALL aspetta_5ms CALL aspetta_5ms CALL aspetta_5ms CALL aspetta_5ms CALL aspetta_5ms CALL aspetta_5ms RETURN ; FINE SUBROUTINE antirimbalzo ================================================================================================== ; INIZIO PROGRAMMA PRINCIPALE ==================================================================================================== ; imposta PORTA e PORTB ---------------------------------------------------------------------------------------------------------- ; ; RA7 RA6 RA5 RA4 RA3 RA2 RA1 RA0 ; DP a disp3 disp2 disp1 disp0 <-- OUTPUT ; RESET BAT <-- INPUT ; ; RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0 ; b c d e f g <-- OUTPUT ; T1OSI T1OSO SET SELECT <-- INPUT ; boot CLRF PORTA ; azzera i latch di uscita di PORTA CLRF PORTB ; azzera i latch di uscita di PORTB MOVLW B'00000111' ; spegni i comparatori e abilita la funzione MOVWF CMCON ; di I/O digitale dei pin di PORTA BSF STATUS, RP0 ; seleziona il banco 01 MOVLW B'00110000' ; RA7 RA6 RA3 RA2 RA1 RA0 pin di output digitale MOVWF TRISA ; RA5 RA4 pin di input digitale MOVLW B'11000011' ; RB5 RB4 RB3 RB2 pin di output digitale MOVWF TRISB ; RB7 RB6 RB1 RB0 pin di input (Timer1 e pulsanti) ; imposta Timer0 ----------------------------------------------------------------------------------------------------------------- ; OPTION_REG<7> = RPBU = 1 resistenze interne di pull-up disattivate ; OPTION_REG<6> = INTEDG = 0 interrupt sul fronte di discesa del pin RB0/INT ; OPTION_REG<5> = T0CS = 0 segnale di clock interno ; OPTION_REG<4> = T0SE = 1 incrementa Timer0 sul fronte di salita ; OPTION_REG<3> = PSA = 0 assegna il prescaler a Timer0 ; OPTION_REG<210> = PS<210> = 110 divisore del prescaler D = 128 MOVLW B'10010110' MOVWF OPTION_REG BCF STATUS, RP0 ; seleziona il banco 00 ; imposta Timer1 ----------------------------------------------------------------------------------------------------------------- ; ; T1CON<76> = unimplemeted = 00 read as '0' ; T1CON<54> = T1CKPS<1:0> = 00 nessun divisore del prescaler ; T1CON<3> = T1OSCEN = 1 abilita l'oscillatore di Timer1 ; ______ ; T1CON<2> = T1SYNC = 1 non sincronizzare il clock esterno con la CPU ; T1CON<1> = TMR1CS = 1 sorgente esterna del segnale di clock (in questo caso l'oscillatore di Timer1) ; T1CON<0> = TMR1ON = 1 abilita Timer1 MOVLW B'00001111' MOVWF T1CON ; imposta l'interruzione dioverflow di Timer1 ------------------------------------------------------------------------------------ ; PIE Peripheral Interrupt Enable register BSF STATUS, RP0 ; seleziona il banco 01 BSF PIE1, TMR1IE ; abilita l'interruzione di overflow di Timer1 BCF STATUS, RP0 ; seleziona il banco 00 ; INTCON INterrupt CONtrol Register BSF INTCON, PEIE ; abilita le interruzioni non mascherate delle periferiche ; blinking iniziale ------------------------------------------------------------------------------------------------------------- ; fino a quando non viene premuto e rilasciato il pulsante SELECT, fa lampeggiare i 4 display ; contemporaneamente, accendendo tutti i led; i display stanno accesi e spenti per circa ; 300 ms = 15 refresh x 20 ms/refresh. ; prepara i led da accendere MOVLW B'10000000' ; accendi MOVWF Due_punti ; i due punti MOVLW 8 ; accendi tutti MOVWF D_ore ; i led del display n° 3 MOVLW 8 ; accendi tutti MOVWF U_ore ; i led del display n° 2 MOVLW 8 ; accendi tutti MOVWF D_min ; i led del display n° 1 MOVLW 8 ; accendi tutti MOVWF U_min ; i led del display n° 0 ; blinking CLRF PORTA ; disabilita i 4 display test_RB0_0 BCF PORTB, RB0 ; azzera il latch di ingresso di RB0 BTFSC PORTB, RB0 ; SE RB0 = 0 (SELECT rilaciato) ALLORA fai il blinking GOTO fine_blink ; ALTRIMENTI esci dal blinking (SELECT premuto) MOVLW 15 ; inizializza MOVWF Contatore1 ; il Contatore1 display_on CALL refresh ; fai il refresh del display DECFSZ Contatore1, F ; Contatore1 = Contatore1 - 1, SE Contatore1 = 0 ALLORA lascia spento il display GOTO display_on ; ALTRIMENTI fai il refresh del display MOVLW 15 ; inizializza MOVWF Contatore1 ; il Contatore1 display_off CALL aspetta_5ms ; fai CALL aspetta_5ms ; passare CALL aspetta_5ms ; circa CALL aspetta_5ms ; 20 ms DECFSZ Contatore1, F ; Contatore1 = Contatore1 - 1, SE Contatore1 = 0 ALLORA accendi il display GOTO display_off ; ALTRIMENTI tieni spento il display GOTO test_RB0_0 ; ripeti blink ; imposta le DECINE DELLE ORE --------------------------------------------------------------------------------------------------- fine_blink CALL antirimbalzo ; pausa antirimbalzo test_RB0_1 BTFSC PORTB, RB0 ; SE RB0 = 0 (SELECT rilasciato) ALLORA imposta le DECINE DELLE ORE GOTO test_RB0_1 ; ALTRIMENTI aspetta perché SELECT è ancora premuto CALL antirimbalzo ; pausa antirimbalzo ; prepara i led da accendere BCF Due_punti, 7 ; spegni i due punti MOVLW 0 MOVWF D_ore ; D_ore = 0 MOVLW Blank MOVWF U_ore ; U_ore = blank MOVLW Blank MOVWF D_min ; D_min = blank MOVLW Blank MOVWF U_min ; U_min = blank ; controlla RB1 (tasto SET) per l'avanzamento delle DECINE DELLE ORE: 0, 1, 2, 0, 1, 2, .... test_RB1_0 BCF PORTB, RB1 ; azzera il latch di ingresso di RB1 BTFSS PORTB, RB1 ; SE RB1 = 1 (SET premuto) ALLORA aspetta che SET venga rilasciato GOTO test_RB0_2 ; ALTRIMENTI controlla RB0 CALL antirimbalzo ; pausa antirimbalzo test_RB1_1 BTFSC PORTB, RB1 ; SE RB1 = 0 (SET rilasciato) ALLORA incrementa le DECINE DELLE ORE GOTO test_RB1_1 ; ALTRIMENTI aspetta perché SET è ancora premuto CALL antirimbalzo ; pausa antirimbalzo INCF D_ore, F ; D_ore = D_ore + 1 MOVLW 3 ; controlla XORWF D_ore, W ; se D_ore = 3 BTFSC STATUS, Z ; SE D_ore <> 3 ALLORA fai il refresh CLRF D_ore ; ALTRIMENTI D_ore = 0 GOTO ref_D_ore ; fai il refresh test_RB0_2 BTFSC PORTB, RB0 ; SE RB0 = 0 (SELECT rilasciato) ALLORA fai il refresh GOTO fine_D_ore ; ALTRIMENTI imposta le UNITA' DELLE ORE (SELECT premuto) ref_D_ore CALL refresh ; fai un refresh GOTO test_RB1_0 ; controlla RB1 ; imposta le UNITA' DELLE ORE ---------------------------------------------------------------------------------------------------- fine_D_ore CALL antirimbalzo ; pausa antirimbalzo test_RB0_3 BTFSC PORTB, RB0 ; SE RB0 = 0 (SELECT rilasciato) ALLORA imposta le UNITA' DELLE ORE GOTO test_RB0_3 ; ALTRIMENTI aspetta perché SELECT è ancora premuto CALL antirimbalzo ; pausa antirimbalzo ; prepara i led da accendere MOVLW 0 MOVWF U_ore ; U_ore = 0 MOVLW Blank MOVWF D_min ; D_min = blank MOVLW Blank MOVWF U_min ; U_min = blank ; controlla RB1 (tasto SET) per l'avanzamento delle UNITA' DELLE ORE: 0, 1, 2, 3, ..., 8, 9, 0, 1, 2, ... test_RB1_2 BCF PORTB, RB1 ; azzera il latch di ingresso di RB1 BTFSS PORTB, RB1 ; SE RB1 = 1 (SET premuto) ALLORA aspetta che SET venga rilasciato GOTO test_RB0_4 ; ALTRIMENTI controlla RB0 CALL antirimbalzo ; pausa antirimbalzo test_RB1_3 BTFSC PORTB, RB1 ; SE RB1 = 0 (SET rilasciato) ALLORA incrementa le UNITA' DELLE ORE GOTO test_RB1_3 ; ALTRIMENTI aspetta perché SET è ancora premuto CALL