Lezione 22
🎓 PIC16 — From Zero to Hero · Lezione 22 · Modulo 4
Watchdog Timer e Sleep Mode 🐕💤
Il WDT come cane da guardia contro i blocchi software. La modalità SLEEP
per scendere a ~1µA. Risveglio con interrupt o WDT per applicazioni a batteria.
🎯 Cosa impari in questa lezione
- Come funziona il Watchdog Timer: counter autonomo che resetta il PIC se non viene azzerato
- Istruzione
CLRWDT: dove e quanto spesso inserirla nel codice - Configurare il postscaler WDT con PSA=1 per timeout da 18ms a 2.3s
- Rilevare la causa del reset leggendo i bit TO e PD di STATUS all’avvio
- Istruzione
SLEEP: ferma il clock, scende a ~1µA, mantiene la RAM - Risveglio da SLEEP con interrupt esterno (RB0/INT) o con scadenza WDT
- Calcolo dell’autonomia a batteria con duty cycle attivo/sleep
📋 Scheda lezione
Lezione: 22 / 42
Modulo: 4 — Interrupt avanzati
Device: PIC16F84A
Prerequisiti: Lez. 18–19
Difficoltà: ★★★★☆
Istruzioni nuove: SLEEP, CLRWDT
Bit nuovi: TO, PD in STATUS
1 · Watchdog Timer — il cane da guardia
Il WDT è un counter completamente indipendente dal clock principale: continua a contare anche se il programma si blocca in un loop infinito, si perde in un salto errato o viene corrotto da un glitch elettrico. Se il software non esegue CLRWDT entro il timeout configurato, il WDT forza un reset hardware del PIC, facendo ripartire l’esecuzione da 0x0000. È il meccanismo di affidabilità fondamentale nei sistemi embedded industriali.
Dove mettere CLRWDT: all’inizio del main loop principale, in modo che venga eseguito ad ogni iterazione. Se il loop ha rami che possono essere molto lenti (delay lunghi, attese bloccanti), aggiungere CLRWDT anche lì. Regola pratica: il timeout WDT deve essere almeno 2× il tempo massimo di iterazione del loop principale in condizioni normali.
Codice — WDT con rilevamento reset
; ============================================================ ; Lezione 22 — Watchdog Timer (WDT) ; Il WDT resetta il PIC se il software si blocca. ; CLRWDT deve essere eseguito entro il timeout WDT. ; PIC16F84A @ 4 MHz WDT abilitato in __CONFIG ; ============================================================ LIST P=16F84A #INCLUDE <p16f84a.inc> __CONFIG _XT_OSC & _WDT_ON & _PWRTE_ON & _CP_OFF ; WDT timeout base = ~18ms (senza postscaler) ; Postscaler WDT (PSA=1, PS bits): 000=×1, 111=×128 → max ~2.3s ; Con PSA=1 e PS=110 → ×64 → WDT timeout ≈ 18ms × 64 = 1.152s CBLOCK 0x0C LOOP_COUNT ; contatore main loop WDT_RESET_CNT ; quante volte il WDT ha fatto reset (debug) ENDC ORG 0x0000 GOTO INIT ORG 0x0005 INIT ; Controlla se stiamo venendo da un reset WDT ; TO=0 e PD=1 dopo WDT reset (vedi STATUS bits 4:3) BTFSS STATUS,4 ; TO bit: 0=WDT timeout, 1=normale INCF WDT_RESET_CNT,F ; conta i reset da WDT BSF STATUS,RP0 ; PSA=1 (postscaler al WDT), PS=110 → ×64 → ~1.15s timeout MOVLW b'00001110' ; PSA=1, PS=110 MOVWF OPTION_REG CLRF TRISA BCF STATUS,RP0 CLRF PORTA CLRF LOOP_COUNT MAIN CLRWDT ; <<< deve essere eseguito entro 1.15s ; Lavoro del main loop INCF LOOP_COUNT,F MOVF LOOP_COUNT,W ANDLW 0x0F MOVWF PORTA ; mostra contatore su PORTA ; Simula un delay breve (sicuro) CALL SHORT_DELAY ; --- Test: per simulare un blocco, metti un GOTO infinito qui --- ; GOTO HANG ; HANG GOTO HANG ← questo provocherebbe reset WDT dopo ~1.15s GOTO MAIN SHORT_DELAY MOVLW D'50' MOVWF 0x70 SDL DECFSZ 0x70,F GOTO SDL RETURN END
2 · SLEEP mode — risparmio energetico estremo
L’istruzione SLEEP mette il PIC in modalità a bassissimo consumo: il clock principale si ferma, il Timer0 (se su clock interno) si ferma, la CPU si ferma. La RAM viene mantenuta grazie a un circuito di hold separato. Il consumo scende da ~2mA a ~1µA — un fattore 2000×. Il PIC si sveglia con un interrupt abilitato (se GIE=1) o con la scadenza del WDT. Dopo il risveglio, esegue l’istruzione immediatamente successiva a SLEEP, poi — se GIE=1 — salta alla ISR.
Codice — SLEEP con risveglio su RB0/INT
; ============================================================ ; Lezione 22 — SLEEP mode ; Il PIC entra in sleep, si sveglia con RB0/INT ; Consumo in sleep: ~1µA (vs ~2mA attivo @ 4MHz) ; PIC16F84A @ 4 MHz ; ============================================================ LIST P=16F84A #INCLUDE <p16f84a.inc> __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF CBLOCK 0x0C W_SAVE STATUS_SAVE WAKE_COUNT ; contatore risvegli ENDC ORG 0x0000 GOTO INIT ; ============================================================ ; ISR — eseguita AL RISVEGLIO da RB0/INT ; Nota: dopo SLEEP, l'istruzione successiva a SLEEP viene ; eseguita PRIMA di saltare alla ISR se GIE=1 ; ============================================================ ORG 0x0004 ISR MOVWF W_SAVE SWAPF STATUS,W MOVWF STATUS_SAVE BTFSS INTCON,INTF GOTO ISR_EXIT BCF INTCON,INTF INCF WAKE_COUNT,F ISR_EXIT SWAPF STATUS_SAVE,W MOVWF STATUS SWAPF W_SAVE,F SWAPF W_SAVE,W RETFIE ORG 0x0020 INIT BSF STATUS,RP0 MOVLW b'11000000' ; RBPU=1, INTEDG=1 (fronte salita) MOVWF OPTION_REG CLRF TRISA MOVLW 0x01 ; RB0 input MOVWF TRISB BCF STATUS,RP0 CLRF PORTA CLRF WAKE_COUNT ; Abilita INT (non GIE ancora — lo attiviamo prima di SLEEP) MOVLW b'10010000' ; GIE=1, INTE=1 MOVWF INTCON ; ============================================================ ; MAIN LOOP — lavora, poi va a dormire ; ============================================================ MAIN ; Fase attiva: esegui operazioni (qui esempio) BSF PORTA,0 ; LED ON: "sono sveglio" CALL WORK_PHASE ; simula elaborazione BCF PORTA,0 ; LED OFF: vado a dormire ; Preparazione sleep CLRWDT ; azzera WDT prima di sleep (se WDT abilitato) ; *** SLEEP *** ; Il clock si ferma. Consumo scende a ~1µA. ; Tutte le periferiche si fermano (TMR0 si ferma se T0CS=0). ; Lo stato della RAM è mantenuto. SLEEP ; <<< Esecuzione riprende qui dopo risveglio >>> ; (l'istruzione dopo SLEEP viene eseguita prima della ISR) NOP ; buona pratica: NOP dopo SLEEP GOTO MAIN WORK_PHASE ; Simula 200 cicli di elaborazione MOVLW D'200' MOVWF 0x70 WP DECFSZ 0x70,F GOTO WP RETURN END
3 · SLEEP + WDT come timer a basso consumo
Combinando SLEEP e WDT si ottiene un timer a bassissimo consumo: il PIC dorme, il WDT lo sveglia ogni ~2.3 secondi, campiona un sensore, torna a dormire. È il pattern ideale per datalogger, stazioni meteo e dispositivi IoT alimentati a batteria. A differenza del risveglio da interrupt, il risveglio da WDT genera un reset — l’esecuzione riparte da 0x0000 e bisogna leggere TO per capire la causa.
Codice — datalogger periodico con WDT
; ============================================================ ; Lezione 22 — SLEEP + WDT come timer a basso consumo ; Il WDT sveglia il PIC periodicamente (no interrupt esterno) ; Utile per: datalogger, sensori periodici, batterie ; PIC16F84A WDT ON, postscaler ×128 → ~2.3s ; ============================================================ LIST P=16F84A #INCLUDE <p16f84a.inc> __CONFIG _XT_OSC & _WDT_ON & _PWRTE_ON & _CP_OFF CBLOCK 0x0C SAMPLE_COUNT ; conta i campionamenti SENSOR_VAL ; valore letto dal "sensore" (simulato) ENDC ORG 0x0000 GOTO INIT ORG 0x0005 INIT BSF STATUS,RP0 ; PSA=1, PS=111 → WDT postscaler ×128 → ~18ms × 128 = ~2.3s MOVLW b'00001111' ; PSA=1, PS=111 MOVWF OPTION_REG CLRF TRISA CLRF TRISB BCF STATUS,RP0 CLRF PORTA CLRF PORTB CLRF SAMPLE_COUNT CLRF SENSOR_VAL ; ============================================================ ; MAIN: campiona → mostra → dormi ~2.3s → ripeti ; ============================================================ MAIN ; 1. Campiona sensore (simulato: leggi PORTA come ADC fittizio) MOVF PORTA,W MOVWF SENSOR_VAL INCF SAMPLE_COUNT,F ; 2. Mostra valore su PORTB MOVF SENSOR_VAL,W MOVWF PORTB ; 3. Lampeggia LED di conferma campionamento BSF PORTA,0 CALL SHORT_BLINK BCF PORTA,0 ; 4. Entra in SLEEP — il WDT lo sveglierà dopo ~2.3s ; GIE=0: nessun interrupt, solo WDT può svegliare ; Al risveglio da WDT: TO=0 in STATUS CLRWDT SLEEP NOP ; eseguita dopo il risveglio ; 5. Verifica sorgente risveglio (TO=0 → WDT, altrimenti INT) BTFSS STATUS,4 ; TO=1 → risveglio normale (non WDT) GOTO MAIN ; TO=0 → era WDT → ricampiona ; Se TO=1 arriverei qui solo se GIE=1 e c'è un INT — non configurato GOTO MAIN SHORT_BLINK MOVLW D'100' MOVWF 0x70 SBL DECFSZ 0x70,F GOTO SBL RETURN END
4 · WDT vs SLEEP — quando usare quale
WDT senza SLEEP
Sempre attivo in background. Non richiede modifiche al flusso. Protegge da blocchi software in qualsiasi applicazione. Overhead: zero — basta aggiungere CLRWDT nel loop principale. Abilitare sempre nei prodotti finali.
SLEEP + risveglio WDT
Per applicazioni a batteria che campionano raramente. Il programma richiede struttura speciale (check TO all’avvio). RAM mantenuta ma non i registri periferici (da reinizializzare). Autonomia fino a 5+ anni su batteria AA.
🧪 Prova tu
- Carica il programma WDT. Rimuovi la riga CLRWDT (commentala). Esegui e osserva: dopo il timeout simulato il PC torna a 0x0000, STATUS bit4 (TO) è 0, e WDT_RESET_CNT si incrementa.
- Carica il programma SLEEP. Porta RB0 a 1 nel pannello I/O mentre il PIC è in SLEEP (PC puntato sull’istruzione SLEEP). Osserva: l’esecuzione riprende sul NOP, poi entra nella ISR, poi torna al MAIN.
- Analisi TO/PD: dopo il risveglio da RB0/INT, leggi STATUS nel pannello registri. Quali sono i valori di TO (bit4) e PD (bit3)? Consulta la tabella SVG e identifica la causa.
- Calcolo autonomia: nel datalogger con WDT ×128 (≈2.3s), se la fase attiva dura 1000 cicli @ 4MHz (250µs), qual è il duty cycle? Con batteria da 2000mAh, quante ore dura il sistema?
❓ Quiz rapido
1. Perché dopo SLEEP si mette sempre un NOP?
Mostra risposta
Al risveglio da SLEEP il clock del PIC riprende ma impiega 1024 cicli del clock RC interno (o 1024 cicli dell’oscillatore esterno con PWRTE) per stabilizzarsi. Il datasheet Microchip raccomanda di inserire almeno un NOP dopo SLEEP per garantire che la prima istruzione vera venga eseguita con il clock stabile. In pratica il NOP è una buona norma difensiva: non costa nulla e previene comportamenti erratici su alcuni device o con certi circuiti di clock.
2. Il programma usa __CONFIG _WDT_ON. Si può abilitare o disabilitare il WDT a runtime con un’istruzione assembly?
Mostra risposta
No. Sul PIC16F84A il WDT è controllato esclusivamente dal bit di configurazione WDT in __CONFIG (word di configurazione a 0x2007), programmato al momento del flashing. Non esiste un registro accessibile a runtime per abilitare/disabilitare il WDT. Si può però controllare il suo effetto: CLRWDT lo azzera, e PSA/PS in OPTION_REG ne regolano il timeout tramite il postscaler. Nei PIC più recenti (es. PIC16F1xxx) esistono invece registri di controllo WDT accessibili a runtime.
3. Nel datalogger con SLEEP+WDT, perché la variabile SAMPLE_COUNT non si azzera ad ogni risveglio nonostante il WDT faccia un reset?
Mostra risposta
Un reset da WDT (o da /MCLR) non azzera la RAM del PIC16. I registri file (GPR) mantengono il loro valore attraverso qualsiasi tipo di reset — solo il power-on reset da zero volt azzera la RAM in modo affidabile (e nemmeno sempre, dipende dalla velocità di discesa della tensione). SAMPLE_COUNT sopravvive quindi al reset WDT. Questo è un comportamento documentato e utilizzabile: si può mantenere stato tra cicli di sleep/wake tramite variabili in RAM, verificando all’avvio che il valore sia sensato (es. range check) per escludere corruzione da power-on.
Prossima lezione →
Lezione 23 — EEPROM: lettura e scrittura dati non volatili
I 64 byte di EEPROM del PIC16F84A. La sequenza di scrittura con disabilitazione GIE. Salvataggio di configurazioni e contatori attraverso i cicli di alimentazione.
© Prof. D. Bertolino · PIC16 From Zero to Hero · WebPicSimulator · Lezione 22