Nei microcontrollori il Watchdog, letteralmente “cane da guardia“, è un contatore (o timer) alimentato da un oscillatore indipendente. Quando il contatore raggiunge il valore massimo, il Watchdog può generare un segnale di interrupt o di reset, a seconda di come è programmato: per evitare che il contatore vada in overflow esso deve essere resettato tramite una particolare istruzione. Il termine “Watchdog” è usato per indicare la funzione principale del circuito, ossia quella di controllare che il programma non si blocchi per cause interne (vicoli ciechi) o esterni (dispositivi che tengono occupato il codice) ed è stato pensato per quelle applicazioni in cui il dispositivo da controllare si trova in un luogo remoto e/o non facilmente raggiungibile e dove sia richiesto un intervento rapido ed in condizione di totale indipendenza dall’operatore.
In questo articolo analizzeremo il Watchdog ed impareremo ad usarlo nelle nostre applicazioni.
Qui di seguito vede lo schema a blocchi del circuito del Watchdog di un Atmega328P, il microcontrollore usato sulle schede Arduino UNO:
Come detto, il Watchdog è un contatore, o timer, il cui registro interno viene incrementato mediante una fonte di clock indipendente da quella usata per fornire il segnale di clock al resto del microcontrollore. Ciò è necessario affinché il Watchdog possa continuare a funzionare anche nel caso in cui il microcontrollore abbia il clock di sistema arrestato (ad esempio in modalità risparmio energetica). Sui microcontrollori Atmel tale clock è fornito da un oscillatore a 128 kHz il cui segnale passa attraverso un prescaler prima di essere utilizzato per incrementare il registro: il prescaler del Watchdog può essere impostato su diversi valori. Nel caso dell’ATmega328P essi sono 10, per tempi di intervallo fra un overflow ed il successivo che vanno da 16 ms fino ad 8 secondi:
N° di cicli | Intervallo |
2 K (2048) | 16 ms |
4 K (4096) | 32 ms |
8 K (8192) | 64ms |
16 K (16384) | 0,125 s |
32 K (32768) | 0,25 s |
64 K (65536) | 0,5 s |
128 K (131072) | 1,0 s |
256 K (262144) | 2,0 s |
512 K (524288) | 4,0 s |
1024 K (1048576) | 8,0 s |
(differenti chip ammettono differenti valori di prescaler, controllare il datasheet del modello che si sta usando)
L’azzeramento del contatore viene effettuato mediante l’istruzione WDR, che resetta il Watchdog tramite la linea di reset indicata in figura. Il contatore del Watchdog non ha un registro accessibile come gli altri timer del microcontrollore per cui l’unica operazione possibile è come detto il suo azzeramento.
Il Watchdog può essere utilizzato in diverse modalità. Nel caso dell’ATmega328P esse sono 3:
- Interrupt Mode: quando il contatore va in overflow, esso genera un segnale di interrupt che può essere intercettato mediante una ISR (Interrupt Service Routine) apposita;
- System Reset Mode: all’overflow del contatore, il Watchdog resetta il microcontrollore;
- Interrupt then System Reset Mode: al primo overflow, il Watchdog invia un segnale di interrupt e si imposta in modalità System Reset. Al successivo overflow, il microcontrollore viene resettato.
La prima modalità è comoda per dare un segnale di interrupt ad operazioni che sono durate più tempo del previsto (una sorta di timeout) oppure per risvegliare il microcontrollore da uno stato di sleep profondo (risparmio energetico). La seconda modalità può essere utilizzata per resettare il microcontrollore nel caso in cui il codice sia stato scritto in modo tale da creare un vicolo cieco che blocca l’esecuzione del programma. La terza modalità può essere utilizzata come un sistema sicuro di riavvio, ad esempio per salvare dei parametri di configurazione nella EEPROM interna prima del reset del microcontrollore.
Per utilizzare il Watchdog abbiamo 2 strade: la prima, più difficile, è quella di manipolare direttamente i registri del microcontrollore; la seconda, più facile, è quella di usare una libreria apposita, wdt.h, integrata nelle librerie della toolchain Avr. Ovviamente l’uso della libreria è consigliato a chi non conosce approfonditamente la manipolazione dei registri del microcontrollore, anche se la suddetta libreria permette di utilizzare il Watchdog nella sola modalità System Reset, quella che analizzeremo nella prima parte di questa guida dedicata al Watchdog.
Per usare la libreria non bisogna fare altro che includerla nel nostro sketch:
#include <avr/wdt.h>
Nel caso di un chip in standalone senza bootloader, la prima istruzione che è bene mettere nel setup() è sempre la disattivazione del Watchdog stesso: il Watchdog, infatti, resta attivo dopo il reset e, se non disabilitato, esso può provare il reset perpetuo del microcontrollore:
void setup() { wdt_disable(); ......il resto del codice qui.... }
Nel caso in cui il nostro microcontrollore utilizzi invece il bootloader Optiboot essa può essere omessa, perché questo bootloader provvede a disattivare il Watchdog.
[notice]Nota per gli utilizzatori dell’Arduino MEGA/MEGA2560: il bootloader di queste schede non disattiva il Watchdog per cui se si intende utilizzare il Watchdog è necessario utilizzare un bootloader modificato che può essere prelevato da qui. Tale modifica è necessaria perché l’esecuzione del bootloader impedisce l’immediata disattivazione del watchdog portando ad un reset infinito della scheda.[/notice]
Successivamente si passa ad impostare il timeout desiderato con l’istruzione wdt_enable():
wdt_enable(timeout);
I valori ammessi per timeout dipendedono dai valori ammessi dal microcontrollore per il prescaler (vedi sopra). Essi sono forniti dalla libreria stessa mediante costanti predefinite:
WDTO_15MS |
WDTO_30MS |
WDTO_60MS |
WDTO_120MS |
WDTO_250MS |
WDTO_500MS |
WDTO_1S |
WDTO_2S |
WDTO_4S |
WDTO_8S |
Quindi per impostare il Watchdog con un timeout di 1 secondo si scrive semplicemente:
wdt_enable(WDTO_1S);
Una volta attivato il Watchdog, dobbiamo ricordarci di resettarne il contatore prima che sia passato l’intervallo di tempo prestabilito mediante la seguente istruzione:
wdt_reset();
Generalmente qusta istruzione si mette all’ultimo posto del blocco di codice che vogliamo monitorare, ad esempio il loop(). E’ importante calcolare bene i tempi perché se il blocco di codice dura più del timeout selezionato, il microcontrollore verrà sempre resettato.
Qui di seguito un esempio di sketch che usa il Watchdog per resettare il microcontrollore nel caso in cui il codice si blocchi in un vicolo cieco:
#include<
avr/wdt.h>
void setup() { wdt_disable(); pinMode(3, INPUT); wdt_enable(WDTO_1S); } void loop() { boolean flag = true; do { if (digitalRead(3) == HIGH) { flag = false; } } while (flag); wdt_reset(); }
Qui si utilizza il Watchdog per inserire un timeout per resettare il chip nel caso lo stato del pin non cambi entro l’intervallo di 1 secondo.
Un altro possibile utilizzo è quello del reset a richiesta. In questo caso basta creare una funzione da richiamare quando si voglia resettare il microcontrollore (un reset svuota la memoria SRAM, reinizializza i registri del microcontrollore, ripristina lo stato iniziale dei pin):
void reset() { wdt_enable(WDTO_250MS); while(1); }
Basta richiamare la funzione reset() dal codice principale per attivare il Watchdog e far entrare il programma in un ciclo infinito, while(1), in attesa del reset del microcontrollore.
[important]IMPORTANTE: utilizzare sempre un intervallo non eccessivamente corto, per dar modo all’Optiboot oppure al vostro programma di disabilitare il Watchdog non appena viene avviata l’esecuzione del codice. Per esperienza, consiglio di scartare intervalli inferiori ai 120 ms.[/important]