Lezione 17 di 43

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.

Fosc/4 1 MHz @ 4MHz T0CKI (RA4) clock esterno MUX T0CS T0CS=0 → Fosc/4 PSA switch PSA=0→TMR0 Prescaler ÷2 … ÷256 (PS0-PS2) TMR0 (0x01) 8 bit, 0x00–0xFF T0IF overflow 0xFF→0x00

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).

OPTION_REG (0x81, Bank 1) — reset value 0xFF RBPU bit 7 0=pull-up ON INTEDG bit 6 1=rising edge T0CS bit 5 0=Fosc/4 T0SE bit 4 edge T0CKI PSA bit 3 0=prescaler TMR0 PS2 bit 2 prescaler PS1 bit 1 ratio PS0 bit 0 bits PS2:PS0 = 000→÷2 001→÷4 010→÷8 011→÷16 100→÷32 101→÷64 110→÷128 111→÷256

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.

Formula di precarica preload = 256 − (T_us / prescaler) T_us = intervallo desiderato in µs a 4 MHz: 1 ciclo macchina = 1 µs prescaler = valore divisore (2,4,8…256) Esempi pratici @ 4 MHz 5ms, PS=÷32: 256−(5000/32) = 256−156 = 100 10ms, PS=÷64: 256−(10000/64)= 256−156 = 100 20ms, PS=÷128: 256−(20000/128)= 256−156 = 100 1ms, PS=÷4: 256−(1000/4) = 256−250 = 6

3 · Formula di precarica

INTCON (0x0B, entrambi i banchi) — reset value 0x00 GIE bit 7 Global Int En EEIE bit 6 EEPROM Int En T0IE bit 5 Timer0 Int En INTE bit 4 RB0/INT En RBIE bit 3 PORTB Ch. En T0IF bit 2 ← noi Timer0 overflow INTF bit 1 RB0/INT flag RBIF bit 0 PORTB Ch. flag

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

  1. Carica il programma blink, aggiungi TMR0 al pannello registri e usa Run. Vedi il valore salire e azzerarsi all’overflow?
  2. Calcola il preload per 10ms con PS=÷64: preload = 256 − (10000/64) = 100. Stesso risultato di PS=÷32 per 5ms — perché?
  3. 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?
  4. 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