2026-06-15 · Davide Carrese

Watchdog IWDG su STM32:
prevenire i reset silenziosi nei firmware di produzione

STM32 · IWDG · Watchdog · STM32F4 · STM32U5 · Embedded

Non c'è niente di peggio di un reso da campo che "qualche volta si resetta" senza crash log, senza traccia Serial Wire Viewer e senza output UART perché il reset è avvenuto prima che il bootloader stampasse qualsiasi cosa. Un watchdog mal configurato o alimentato in modo improprio è il primo sospettato. L'Independent Watchdog (IWDG) degli STM32 è un semplice timer hardware pilotato dal suo oscillatore RC dedicato — funziona indipendentemente dal clock principale e resetta la MCU se il firmware non lo rinfresca in tempo. Configurare correttamente l'IWDG in un firmware di produzione significa scegliere il timeout giusto, alimentarlo alla giusta granularità e — crucialmente — rilevare il motivo per cui il watchdog è scattato.

L'IWDG non è il WWDG. Il Window Watchdog (WWDG) usa il clock di sistema (APB), può essere configurato con un interrupt di pre-allarme prima del reset, e richiede che il refresh avvenga all'interno di una finestra specifica — troppo presto o troppo tardi causa un reset. L'IWDG usa un oscillatore RC indipendente (LSI, tipicamente 32 kHz ± diversi percento su temperatura), non ha interrupt di pre-allarme (solo reset), e ha una finestra opzionale. Per la maggior parte delle applicazioni di produzione, l'IWDG è la scelta giusta: più semplice, più robusto contro i guasti del clock principale e più difficile da configurare accidentalmente in modo errato.

Architettura dell'IWDG negli STM32

Il periferico IWDG consiste in un down-counter a 12 bit clockato dall'LSI dopo un prescaler programmabile. Il contatore funziona in continuazione dopo essere stato avviato e quando raggiunge lo zero l'IWDG attiva un reset di sistema. Il firmware deve ricaricare il contatore con il valore del Reload Register (RLR) prima che si azzeri — questa operazione è chiamata "alimentare" o "kikkare" il watchdog.

Registri chiave:

RegistroOffsetScopo
KR (Key Register)0x00Scrivi 0x5555 per abilitare scritture PR/RLR; 0xCCCC per avviare IWDG; 0xAAAA per ricaricare il contatore
PR (Prescaler Register)0x04Seleziona il rapporto di divisione: [0=4, 1=8, 2=16, 3=32, 4=64, 5=128, 6=256]
RLR (Reload Register)0x08Valore di ricarica a 12 bit (0–4095); il contatore viene ricaricato a questo valore a ogni feed
SR (Status Register)0x0CFlag busy PVU (Prescaler Update) e RVU (Reload Update) — vanno controllati prima di cambiare PR/RLR dopo l'avvio
WINR (Window Register)0x10Limite superiore opzionale: il contatore deve essere tra WINR e 0 per il feed; alimentare sopra WINR causa reset immediato

La frequenza LSI varia significativamente con la temperatura — ST specifica 30–50 kHz per la maggior parte delle famiglie STM32, con un valore tipico di circa 40 kHz. Questo significa che il timeout effettivo del watchdog ha una tolleranza del ±20%. Non affidare mai l'IWDG a precisione sub-millisecondo; è una rete di sicurezza contro il blocco completo del firmware, non un periferico di temporizzazione di precisione.

Calcolo del Timeout

t(IWDG) = (RLR + 1) × PrescalerDiv / f(LSI)

Esempio con LSI = 40 kHz tipica:
  Prescaler = 64 (PR=4)  →  PrescalerDiv = 64
  RLR = 1250
  t = (1250 + 1) × 64 / 40000
  t ≈ 2,00 s

Con LSI minima = 30 kHz:
  t ≈ 2,67 s

Con LSI massima = 50 kHz:
  t ≈ 1,60 s

L'intervallo di timeout va da circa 100 µs (PR=4, RLR=0) a 26,2 s (PR=256, RLR=4095 a 40 kHz). Per firmware di produzione, un timeout tra 1 e 8 secondi è tipico — abbastanza lungo da sopravvivere a un picco di latenza degli interrupt durante una cancellazione flash o una correzione ECC, abbastanza corto da non causare un glitch visibile su un attuatore o un bus di comunicazione.

Esempio Pratico: Configurazione IWDG su STM32F4

Ecco una sequenza di configurazione completa per STM32F401/F411 usando accesso a livello di registro, che funziona identicamente su STM32F4, STM32L4, STM32G4 e la maggior parte dei membri della famiglia STM32U5:

/* iwdg_f4.c — configurazione IWDG production-grade per STM32F4 */

#include "stm32f4xx.h"

/* Timeout desiderato ≈ 3,0 s @ 40 kHz LSI tipica */
#define IWDG_PRESCALER   4    /* PR=4 → /64 */
#define IWDG_RLR         1875 /* (1875+1)*64/40000 ≈ 3,0 s */

/* Flag busy del registro di stato */
#define IWDG_SR_PVU      (1U << 0)
#define IWDG_SR_RVU      (1U << 1)

static void iwdg_write_enable(void)
{
    IWDG->KR = 0x5555;   /* Abilita scrittura a PR e RLR */
}

static void iwdg_write_disable(void)
{
    IWDG->KR = 0x0000;   /* Disabilita scrittura */
}

static void iwdg_wait_for_update(void)
{
    /* Attendi fino a che PVU e RVU non sono azzerati */
    while (IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) { }
}

void IWDG_Init(void)
{
    /* Passo 1: Abilita scrittura a PR e RLR */
    iwdg_write_enable();

    /* Passo 2: Imposta prescaler a /64 */
    IWDG->PR = IWDG_PRESCALER;
    iwdg_wait_for_update();

    /* Passo 3: Imposta valore di ricarica */
    IWDG->RLR = IWDG_RLR;
    iwdg_wait_for_update();

    /* Passo 4: Avvia il watchdog scrivendo la chiave */
    IWDG->KR = 0xCCCC;

    /* Passo 5: Disabilita scrittura */
    iwdg_write_disable();
}

void IWDG_Feed(void)
{
    /* Scrive la chiave di ricarica */
    IWDG->KR = 0xAAAA;
}

/*
 * NOTA: Una volta abilitato, l'IWDG può essere fermato SOLO da un reset.
 * Non esiste un registro per disabilitare l'IWDG — è intenzionale.
 * Per mettere in pausa il watchdog durante il debug, configura DBGMCU freeze.
 */

L'Anti-Pattern "Feed nel Main Loop"

L'errore più comune che vedo nei firmware di produzione è alimentare il watchdog dentro il ciclo while(1) principale all'inizio di ogni iterazione. Questo rende l'IWDG quasi inutile: finché lo scheduler continua a girare, ogni subroutine bloccante è invisibile al watchdog. Una transazione SPI che resta in attesa di un flag TXE che non si imposterà mai? Il main loop continua ad alimentare. Un task FreeRTOS che si blocca su una coda per sempre? L'idle hook o il main loop continuano ad alimentare.

⚠ Anti-Pattern

Non alimentare mai il watchdog nel main loop o in un interrupt timer ad alta frequenza. L'IWDG deve validare che tutti i sottosistemi critici abbiano fatto progressi entro la finestra di timeout.

Il pattern corretto è alimentare a checkpoint strategici — dopo un blocco di lavoro che il firmware deve completare per garantire la sicurezza del sistema. Per esempio:

void main(void)
{
    SystemInit();
    IWDG_Init();              /* Avvia IWDG, ~3 s timeout */

    while (1)
    {
        sensor_read();        /* Lettura I2C bloccante */
        IWDG_Feed();          /* Checkpoint 1 */

        control_loop();       /* Aggiornamento PID */
        IWDG_Feed();          /* Checkpoint 2 */

        can_transmit();       /* CAN TX */
        IWDG_Feed();          /* Checkpoint 3 */

        if (sdcard_write_pending()) {
            sdcard_write_block();
            IWDG_Feed();      /* Checkpoint 4 */
        }

        __WFI();              /* Entra in sleep */
    }
}

Se sensor_read() si blocca per sempre, il watchdog scatta dopo ~3 secondi. Questo è il comportamento che vuoi: un singolo guasto in qualsiasi sottosistema causa un recupero controllato, non uno stallo silenzioso.

Per firmware basati su FreeRTOS, uso un task watchdog dedicato alla priorità più bassa che alimenta solo dopo aver ricevuto notifiche dagli altri task critici:

/* Pattern task watchdog per FreeRTOS */
void vWatchdogTask(void *pvParameters)
{
    TickType_t last_wake = xTaskGetTickCount();

    for (;;)
    {
        /* Attende che tutti i task critici si siano fatti vivi */
        ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2500));

        /* Se un task ha mancato la scadenza, non arriviamo qui */
        IWDG_Feed();
    }
}

/* Ogni task critico chiama questa alla fine del suo ciclo: */
void Watchdog_CheckIn(TaskHandle_t watchdog_task)
{
    xTaskNotifyGive(watchdog_task);
}

Il timeout IWDG deve essere più lungo del tempo di esecuzione peggiore combinato di tutti i task critici, inclusa la latenza degli interrupt impilati. Regola pratica: imposta il timeout a 3× il tempo di ciclo previsto del percorso critico più lento.

Modalità Debug: Congelare l'IWDG

Per impostazione predefinita, quando il debugger ferma la CPU (breakpoint, passo singolo), il contatore IWDG continua a funzionare. Se il timeout scade mentre stai eseguendo il passo singolo, il watchdog resetta la MCU — e perdi la sessione di debug. La soluzione è configurare il periferico DBGMCU per congelare il contatore IWDG durante l'arresto del debug:

/* In SystemInit() o all'inizio — prima di avviare IWDG */

/* STM32F4: registro DBGMCU_APB1_FZ */
DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_IWDG_STOP;

/* STM32U5/STM32G4: stesso registro, stesso bit */
DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_IWDG_STOP;

Questa è una delle prime cose che aggiungo a qualsiasi template di progetto.

Rilevare i Reset del Watchdog all'Avvio

Senza un meccanismo per rilevare che si è verificato un reset del watchdog, sei cieco ai guasti sul campo. Ogni STM32 ha un registro RCC (Reset and Clock Control) che memorizza l'ultima causa di reset:

/* Verifica se l'ultimo reset è stato causato dall'IWDG */

uint32_t rcc_csr = RCC->CSR;

if (rcc_csr & RCC_CSR_IWDGRSTF) {
    log_event("WATCHDOG: Reset IWDG a %lu ms dall'accensione", get_uptime_ms());

#if defined(STM32F4)
    RCC->CSR |= RCC_CSR_RMVF;
#elif defined(STM32U5)
    RCC->RSR |= RCC_RSR_RMVF;
#endif
}
⚠ Importante

Su STM32F4, il registro CSR è protetto in scrittura dal bit di abilitazione dell'oscillatore LSI. Devi abilitare LSI nell'RCC prima di scrivere CSR, anche se vuoi solo leggerlo. Per una lettura sola, RCC->CSR è accessibile, ma pulire RMVF richiede RCC->CSR |= RCC_CSR_RMVF che potrebbe richiedere LSION impostato prima. Su STM32U5 e famiglie più recenti, la gestione è diversa — leggi il manuale di riferimento.

Nei firmware di produzione, memorizzo la causa del reset in un registro di backup alimentato a batteria (RCC Backup SRAM o RTC Backup Register) in modo che sopravviva a un ciclo di alimentazione o a un successivo reset del watchdog che cancella i flag CSR. In questo modo, anche dopo reset multipli consecutivi, il bootloader può leggere il registro di backup e decidere se entrare in modalità sicura o continuare il boot normale.

/* Memorizza causa reset nel registro backup RTC */
void store_reset_cause(void)
{
    uint32_t rcc_csr = RCC->CSR;
    uint32_t cause = 0;

    if (rcc_csr & RCC_CSR_IWDGRSTF)    cause = 1;
    else if (rcc_csr & RCC_CSR_WWDGRSTF) cause = 2;
    else if (rcc_csr & RCC_CSR_SFTRSTF) cause = 3;
    else if (rcc_csr & RCC_CSR_PINRSTF) cause = 4;
    else if (rcc_csr & RCC_CSR_PORRSTF) cause = 5;

    if (cause) {
        PWR->CR |= PWR_CR_DBP;
        RTC->BKP0R = (RTC->BKP0R & 0xFF00) | cause;
        PWR->CR &= ~PWR_CR_DBP;
    }

#if defined(STM32F4)
    RCC->CSR |= RCC_CSR_RMVF;
#endif
}

IWDG vs WWDG: Quando Usare l'Uno o l'Altro

CaratteristicaIWDGWWDG
Sorgente clockLSI (indipendente, 32 kHz RC)HCLK / APB (dipende da clock sistema)
Precisione timeout±20–30% (deriva RC)Alta (clockato da PLL/HSE)
Interrupt pre-allarme❌ No✅ Sì (flag EWI prima del reset)
FinestraOpzionale (WINR)Obbligatoria
Sopravvive a guasto clock✅ Sì (oscillatore indipendente)❌ No
Ideale perRete sicurezza, brown-out, guasto cristalloMonitoraggio scadenze precise

La mia regola: usa l'IWDG come watchdog primario a livello di sistema (sempre abilitato nelle build di produzione) e usa il WWDG solo quando un task specifico ha una scadenza hard real-time che deve essere rispettata entro microsecondi. Per la maggior parte delle applicazioni embedded, l'IWDG da solo è sufficiente.

Checklist Pratica

ScenarioAzione
Configurare IWDG per la prima voltaConfigura PR, RLR, avvia con 0xCCCC; controlla SR prima di cambiare PR/RLR
Alimentare il watchdogScrivi 0xAAAA a KR a checkpoint strategici, NON nel main loop
Debug con IWDG abilitatoCongela IWDG via DBGMCU_APB1_FZ prima di avviare
Rilevare reset watchdogLeggi RCC->CSR (o RSR) all'avvio; logga IWDGRSTF/WWDGRSTF
Preservare causa reset multipliMemorizza in registro backup RTC
Selezione timeout IWDG3× percorso critico peggiore; considera tolleranza LSI
Build produzione vs debugAbilita IWDG solo in release; disabilita o congela in debug
FreeRTOS + IWDGTask watchdog dedicato con pattern task notification
WWDG per scadenze hardUsa WWDG solo quando precisione IWDG è insufficiente

Come lo Affronterei su un Progetto Cliente

Ecco il mio template standard di integrazione IWDG per qualsiasi firmware STM32 di produzione:

  1. Aggiungi configurazione IWDG durante l'init della scheda, subito dopo la configurazione del clock. Usa un timeout target di 3–5 secondi con un prescaler che mantenga RLR nella parte centrale del range a 12 bit (512–2048) per flessibilità.
  2. Congela l'IWDG in modalità debug immediatamente — evita di perdere sessioni di debug e fa risparmiare ore di frustrazione.
  3. Implementa una funzione store_reset_cause() come prima cosa in main(), prima di qualsiasi init periferiche. Salva il risultato in un registro di backup e in un'area di log persistente in flash.
  4. Definisci punti di feed strategici basati sul flusso di controllo dell'applicazione — non sul loop dello scheduler. Ogni punto di feed dovrebbe essere preceduto da un'operazione completa che non deve rimanere bloccata.
  5. A ogni reset del watchdog, incrementa un contatore nel registro di backup e implementa una politica di "escalation dei reset": se il watchdog scatta più di N volte entro T minuti, entra in una modalità sicura che disattiva gli attuatori e logga diagnostica prima di tentare il recupero completo.
  6. Disabilita l'IWDG nelle build di debug (via conditional preprocessor su NDEBUG o un define PRODUCTION_BUILD personalizzato). Testare con il watchdog abilitato è importante durante l'integrazione di sistema, ma durante il debugging a livello di task costa tempo.

Questo approccio ha salvato diverse implementazioni sul campo. Su un recente nodo sensore IoT basato su STM32U5, il watchdog ha catturato una race condition nella cache write-back della scheda SD che si manifestava solo dopo tre settimane di funzionamento continuo — il tipo di guasto che nessun test di laboratorio riesce mai a riprodurre. Il registro di backup mostrava esattamente "IWDG reset at 1872000 ms", che puntava dritto alla routine della scheda SD.

Fonti e Riferimenti

💬 Commenti via email

Rispondi a questo articolo via email — leggo e rispondo a ogni messaggio.