Lezione 16
🎓 PIC16 — From Zero to Hero · Lezione 16 · Modulo 3 — Prima lezione
Il Timer0 — Tempo senza contare cicli ⏱
Il PIC16F84A ha un timer hardware che conta da solo.
Come configurarlo con OPTION_REG, calcolare la precarica e rilevare l’overflow con T0IF.
⏱ Inizio Modulo 3 — Timer. Il modulo introduce il controllo del tempo tramite hardware dedicato, eliminando i loop di delay bloccanti.
🎯 Cosa impari in questa lezione
- Struttura interna del Timer0: TMR0, prescaler, sorgente di clock
- Leggere e configurare OPTION_REG (Bank 1)
- Calcolare il valore di precarica per un dato intervallo di tempo
- Rilevare l’overflow con polling su T0IF nel registro INTCON
- Differenza tra polling e interrupt (anticipazione Modulo 4)
- Programma blink preciso con Timer0 al posto dei loop di delay
- Come sostituire DELAY_5MS del cronometro con una versione Timer0
📋 Scheda lezione
Lezione: 16 / 42
Modulo: 3 — Timer
Device: PIC16F84A
Prerequisiti: Lez. 07, 15
Difficoltà: ★★★☆☆
Registri nuovi: TMR0, OPTION_REG, INTCON
Tecnica chiave: polling T0IF
1 · Struttura interna del Timer0
TMR0 (indirizzo 0x01, Bank 0) è un registro da 8 bit che si auto-incrementa. Quando passa da 0xFF a 0x00 (overflow), viene settato il flag T0IF in INTCON. Il prescaler è un divisore programmabile: invece di incrementare TMR0 ad ogni ciclo macchina, lo incrementa ogni N cicli (N = 2, 4, 8 … 256, selezionabile tramite OPTION_REG).
Scrittura su TMR0: quando si scrive su TMR0 (precarica), il prescaler viene azzerato automaticamente. Dopo la scrittura il prossimo tick arriverà dopo altri N cicli completi — comportamento voluto per la precisione.
2 · OPTION_REG e INTCON
OPTION_REG (0x81, Bank 1, reset 0xFF) configura il Timer0. I bit chiave: T0CS (bit 5) = sorgente clock (0=interno Fosc/4); PSA (bit 3) = prescaler a TMR0 (0) o WDT (1); PS2:PS0 (bit 2:0) = divisore (000=÷2 … 111=÷256); RBPU (bit 7) = pull-up PORTB (0=abilitati).
INTCON (0x0B, entrambi i banchi) contiene il flag T0IF al bit 2: settato a 1 quando TMR0 va in overflow. Va azzerato manualmente con BCF INTCON,T0IF — il PIC non lo azzera da solo.
3 · Formula di precarica
Configurazione per 5ms con prescaler ÷32
; Configurazione Timer0 per 5ms ; PS=÷32 → PS2:PS0 = 100 ; T0CS=0 (interno), PSA=0 (TMR0), RBPU=1 (pull-up off) ; OPTION_REG = b'10000100' = 0x84 ; In INIT (Bank 1): BSF STATUS,RP0 MOVLW b'10000100' ; RBPU=1, T0CS=0, PSA=0, PS=100(÷32) MOVWF OPTION_REG BCF STATUS,RP0 ; Avvia il timer (precarica per 5ms): ; preload = 256 - (5000/32) = 256 - 156 = 100 = 0x64 MOVLW D'100' MOVWF TMR0 ; precarica TMR0, azzera prescaler BCF INTCON,T0IF ; pulisci flag overflow (INTCON bit 2) ; Attendi overflow (polling): WAIT_T0 BTFSS INTCON,T0IF ; skip se T0IF=1 (overflow avvenuto) GOTO WAIT_T0 ; ancora in attesa ; Qui sono passati esattamente ~5ms
4 · Programma — Blink preciso con Timer0
LED su RA0: ~500ms ON, ~500ms OFF. Si usa prescaler ÷256 e si contano 8 overflow da ~65.5ms ciascuno ≈ 524ms. La subroutine WAIT_OVERFLOWS astrae il meccanismo: riceve in OVF_CNT il numero di overflow da aspettare.
; ============================================================ ; Lezione 16 — Blink preciso con Timer0 ; LED su RA0: 500ms ON, 500ms OFF ; PIC16F84A @ 4 MHz ; Timer0: prescaler ÷256, precarica 0 → overflow ogni 65.5ms ; 8 overflow × 65.5ms ≈ 524ms (o precarica 62 per 500ms esatti) ; ============================================================ LIST P=16F84A #INCLUDE <p16f84a.inc> __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF CBLOCK 0x0C OVF_CNT ; contatore overflow (quanti ne servono) ENDC ORG 0x0000 GOTO INIT ORG 0x0005 INIT BSF STATUS,RP0 MOVLW b'11000111' ; RBPU=1, T0CS=0, PSA=0, PS=111(÷256) MOVWF OPTION_REG MOVLW 0x00 MOVWF TRISA ; RA0 = output (LED) BCF STATUS,RP0 CLRF PORTA ; LED spento LOOP ; LED ON per ~500ms (8 overflow × 65.5ms) BSF PORTA,0 MOVLW D'8' MOVWF OVF_CNT CALL WAIT_OVERFLOWS ; LED OFF per ~500ms BCF PORTA,0 MOVLW D'8' MOVWF OVF_CNT CALL WAIT_OVERFLOWS GOTO LOOP ; ============================================================ ; WAIT_OVERFLOWS: attende OVF_CNT overflow di Timer0 ; Ogni overflow = 256 × 256µs = 65536µs ≈ 65.5ms @ 4MHz ; ============================================================ WAIT_OVERFLOWS ; Precarica TMR0 a 0 e pulisci flag CLRF TMR0 ; precarica 0 → overflow dopo 256×256=65536 cicli BCF INTCON,T0IF ; pulisci flag WAIT_LOOP BTFSS INTCON,T0IF ; overflow avvenuto? GOTO WAIT_LOOP ; Overflow: decrementa contatore BCF INTCON,T0IF ; pulisci per il prossimo CLRF TMR0 ; riavvia conteggio DECFSZ OVF_CNT,F GOTO WAIT_LOOP ; ancora overflow da aspettare RETURN END
5 · Sostituzione drop-in di DELAY_5MS
Sostituire DELAY_5MS della Lezione 15 con una versione Timer0: stessa interfaccia (CALL/RETURN), stessa durata (5ms), ma garantita dall’hardware invece che dal conteggio di istruzioni.
; Timer0 per 5ms esatti: ; PS=÷32 → PS2:PS0 = 100 ; preload = 256 - (5000/32) = 256 - 156 = 100 ; ; OPTION_REG = b'10000100': ; bit7 RBPU=1 (pull-up PORTB off, gestiti a parte) ; bit5 T0CS=0 (clock interno) ; bit3 PSA=0 (prescaler → TMR0) ; bit2:0 PS=100 (÷32) DELAY_5MS_TMR MOVLW D'100' ; precarica = 100 MOVWF TMR0 ; carica e azzera prescaler BCF INTCON,T0IF ; pulisci flag overflow D5T_WAIT BTFSS INTCON,T0IF ; attendi overflow GOTO D5T_WAIT BCF INTCON,T0IF ; pulisci per uso futuro RETURN ; passati esattamente 5ms ; Corrispondente configurazione in INIT (Bank 1): ; MOVLW b'10000100' ; MOVWF OPTION_REG
✓ Vantaggi Timer0
Precisione garantita dall’hardware. Non dipende da ottimizzazioni. Facile da modificare (cambia solo preload). Preparatorio per interrupt (Mod. 4).
⚠ Limitazioni del polling
Il PIC è ancora bloccato nel loop BTFSS/GOTO. Non può fare altro durante l’attesa. Per vero multitasking servono gli interrupt (Modulo 4).
⚠ Bug comuni: dimenticare BCF INTCON,T0IF prima dell’attesa (il secondo wait ritorna subito) · PSA=1 per errore (prescaler al WDT, TMR0 conta senza divisione) · T0CS=1 non intenzionale (TMR0 conta impulsi su RA4 invece del clock interno) · Bank 1 non ripristinato dopo OPTION_REG.
🧪 Prova tu
- Carica il programma blink, aggiungi TMR0 al pannello registri e usa Run. Vedi il valore salire e azzerarsi all’overflow?
- Calcola il preload per 10ms con PS=÷64: preload = 256 − (10000/64) = 100. Stesso risultato di PS=÷32 per 5ms — perché?
- Misura l’overhead: nel simulatore, conta i cicli tra MOVWF TMR0 e la lettura del T0IF. Dovrebbe essere ~4992 (preload=100, PS=÷32 → 156×32). Quanto scosta?
- Sfida — 1 secondo preciso: qual è il preload per avvicinarsi a 500ms in un solo overflow con PS=÷256? (256×256=65.536ms; 500ms/65.536ms≈7.6 overflow — non intero. Alternativa: precarica 62 → 194×256=49.664ms × 10 ≈ 496ms.)
❓ Quiz rapido
1. OPTION_REG = 0x07. Qual è il prescaler assegnato a TMR0 e qual è il divisore?
Mostra risposta
0x07 = 0b00000111. PSA=0 → prescaler a TMR0. PS2:PS0=111 → divisore ÷256. T0CS=0 → clock interno. TMR0 si incrementa ogni 256µs a 4MHz. Un overflow completo (0→255) = 256×256 = 65536 cicli = 65.536ms.
2. Perché si deve fare BCF INTCON,T0IF prima di attendere l’overflow, e non solo dopo?
Mostra risposta
T0IF non viene azzerato automaticamente dall’hardware. Se dal ciclo precedente T0IF=1, l’istruzione BTFSS lo trova già a 1 e salta subito — il delay risultante è 0ms invece dei 5ms attesi. La pulizia preventiva (BCF prima di MOVWF TMR0) garantisce che BTFSS parta da uno stato noto.
3. Cosa succede se si imposta PSA=1 per errore?
Mostra risposta
Con PSA=1 il prescaler va al WDT invece che a TMR0. Il Timer0 riceve il clock direttamente da Fosc/4 senza divisione: si incrementa ogni ciclo macchina (1µs a 4MHz) e va in overflow ogni 256µs invece dei 5ms attesi. Il delay risulta 156× più breve del previsto (se PS era ÷32). Con _WDT_OFF il WDT è disabilitato quindi non ci sono altri effetti, ma i tempi sono completamente sbagliati.
4. Qual è la differenza concettuale tra polling su T0IF e interrupt?
Mostra risposta
Nel polling il firmware controlla attivamente il flag con un loop BTFSS/GOTO — la CPU è occupata. Con gli interrupt (Modulo 4), quando T0IF=1 l’hardware interrompe automaticamente l’esecuzione, salta al vettore 0x0004, esegue la ISR e torna al punto interrotto. Nel frattempo il codice principale può fare altro. Il polling è più semplice; gli interrupt sono più efficienti per sistemi concorrenti.
Prossima lezione →
Lezione 17 — Timer0 con precarica dinamica e PWM software
Precarica calcolata a runtime, misura di frequenza su T0CKI e generazione di segnali PWM software con duty cycle variabile.
© Prof. D. Bertolino · PIC16 From Zero to Hero · WebPicSimulator · Lezione 16