WatchDog

A watchdog timer is a hardware timer that resets the system if the software fails to operate correctly. It helps recover from software faults by restarting the microcontroller when the timer is not periodically cleared by the running program. This ensures the system continues to function even if it gets stuck or crashes.

A watchdog timer works by counting down from a preset value. If the timer reaches zero, it triggers a system reset. The running program (usually the main loop or a critical task) must periodically reset or "kick" the watchdog timer before it expires. This is done by writing a specific value to a watchdog control register. If the program hangs or gets stuck, it fails to reset the timer, causing the watchdog to reset the system and restore normal operation.

No, the watchdog timer is not enabled by default on most microcontrollers, including STM32. You must explicitly configure and enable it in your code if you want to use it.

Watchdogs: Introduction

IWDG

IWDG: Main Features

Timeout Values

image.png

IWDG: Timeout Values

IWDG: Hardware Watchdog

IWDG: Register Access and Protection

image.png

IWDG

image.png

#define IWDG_KEY_ENABLE             0x0000CCCU
#define IWDG_KEY_WR_ACCESS_ENABLE   0x00005555U
#define IWDG_PRESCALER_4            0x00000000U
#define IWDG_RELOAD_VAL             0xFFF
#define IWDG_KEY_RELOAD             0x0000AAAAU

static uint8_t isIwdg_ready(void);

void iwdg_init(void)
{
    /*Enable the IWDG by writing 0x0000CCCC in the IWDG_KR register*/
    IWDG->KR = IWDG_KEY_ENABLE;

    /*Enable register access by writing 0x0000 5555 in the IWDG_KR register*/
    IWDG->KR = IWDG_KEY_WR_ACCESS_ENABLE;

    /*Set the IWDG Prescaler*/
    IWDG->PR  = IWDG_PRESCALER_4;

    /*Set the reload register (IWDG_RLR) to the largest value 0xFFF*/
    IWDG->RLR = IWDG_RELOAD_VAL;

    /*Wait for the registers to be updated (IWDG_SR = 0x0000 0000)*/
    while (isIwdg_ready() != 1) {}

    /*Refresh the counter value with IWDG_KR (IWDG_KR = 0x0000 AAAA)*/
    IWDG->KR = IWDG_KEY_RELOAD;
}

static uint8_t isIwdg_ready(void)
{
    return ((READ_BIT(IWDG->SR, IWDG_SR_PVU | IWDG_SR_RVU) == 0U) ? 1UL : 0UL);
}

// ...main.c
uint8_t g_btn_press;

int main(void)
{
    uart2_tx_init();
    gpio_interrupt_init(); // init button interrupt
    led_init();
    check_reset_source();
    iwdg_init();
    while(1)
    {
        if( g_btn_press != 1 )
        {
            /*Refresh IWDG down-counter to default value*/
            IWDG->KR = IWDG_KEY_RELOAD;
            led_blink();
        }
    }
}

static void check_reset_source(void)
{
    if ((RCC->CSR & RCC_CSR_IWDGRSTF) == (RCC_CSR_IWDGRSTF))
    {
        /*Clear IWDG Reset flag*/
        RCC->CSR = RCC_CSR_RMVF;

        /*Turn LED On*/
        led_on();
        printf("RESET was caused by IWDG.....\n\r");

        while( g_btn_press != 1 )
        {
        }

        g_btn_press = 0;
    }
}

static void btn_callback(void)
{
    g_btn_press = 1;
}

void EXTI15_10_IRQHandler(void)
{
    if((EXTI->PR & EXTI_IMR_IM13) == (EXTI_IMR_IM13))
    {
        /*Clear EXTI flag*/
        EXTI->PR = EXTI_IMR_IM13;

        //Do something...
        btn_callback();
    }
}

WWDG


WWDG: Introduction

• Watchdogs are used to detect software faults

• The window watchdog has a programmable free-running downcounter that must be refreshed within a window period that guarantees proper software execution.

• If a system fault occurs and the programmed time period expires, the window watchdog generates a system reset.

• The window watchdog can be used to detect abnormally late or early behaviors of the firmware.

• The WWDG is best suited for applications which are required to react within an accurate timing window.

• An Early Wakeup Interrupt (EWI) can be generated before a reset happens, this can be used to manage certain actions before a system reset.


WWDG: Main Feature

• Programmable free-running downcounter

• Conditional Reset


WWDG: How it works

• PCLK1 is the clock source for the WWDG

• Bits T[6:0] of the watchdog control register (WWDG_CR) counts down until they roll over from 0x40 to 0x3F, when this happens a reset is generated.

• Bits W[6:0] of the watchdog configuration register (WWDG_CFR) contains the window value

• Bits T[6:0] and W[6:0] are compared in order to evaluate the time to refresh the downcounter in the configuration windows

• If the downcounter is reloaded too early or too late, the WWDG will reset the microcontroller

image.png

image.png


WWDG: Clock Source

• PCLK1 = APB1 Clock


WWDG: Settings

The WWDG time base is pre-scaled from PCLK1 clock

There is a 4096 internal divider and 4 pre-dividers: 1, 2, 4 or 8 selectable by the WWDG configuration register (WWDG_CFR)

image.png


WWDG: Source of Reset

• Once the WWDG generates a reset, a status flag called WWDGRSTF is set in the RCC_CSR register identifying the source of the reset.


WWDG: Early Wakeup Interrupt (EWI)

• The EWI interrupt occurs when the downcounter value reaches 0x40

• The EWI interrupt is enabled by setting the EWI bit in the WWDG configuration register (WWDG_CFR)

• The EWI interrupt is cleared by writing “0” to the EWIF bit in the WWDG status register (WWDG_SR).

image.png

image.png

void wwdg_init(void)
{
    /*Enable the peripheral clock WWDG*/
    RCC->APB1ENR |= RCC_APB1ENR_WWDGEN;

    /*Set prescaler to have a rollover each about ~2s*/
    WWDG->CFR |= (1U<<7);
    WWDG->CFR |= (1U<<8);

    /*Set window value to same value (~2s) as downcounter
      in order to be able to refresh the WWDG almost immediately*/
    WWDG->CFR &= ~WWDG_CFR_W;
    WWDG->CFR |= 0x7E;

    /*Set counter*/
    WWDG->CR &= ~WWDG_CR_T;
    WWDG->CR |= 0x7E;

    /*Enable WWDG*/
    WWDG->CR |= WWDG_CR_WDGA;
}

/**
 * @brief  System Clock Configuration
 * The system Clock is configured as follow :
 * System Clock source        = HSE
 * SYSCLK(Hz)                 = 8000000
 * HCLK(Hz)                   = 2000000
 * AHB Prescaler              = 4
 * APB1 Prescaler             = 1
 * APB2 Prescaler             = 1
 * HSE Frequency(Hz)          = 8000000
 * VDD(V)                     = 3.3
 * Main regulator output voltage = Scale1 mode
 * Flash Latency(WS)          = 0
 *
 * @param  None
 * @retval None
 */
void clock_config(void)
{
    /* Enable HSE oscillator */
    RCC->CR |= RCC_CR_HSEBYP;
    RCC->CR |= RCC_CR_HSEON;

    /*Wait till HSERDY goes LOW*/
    while((RCC->CR & RCC_CR_HSERDY) == (RCC_CR_HSERDY)){}

    /* Set FLASH latency */
    FLASH->ACR &= ~FLASH_ACR_LATENCY;
    FLASH->ACR |= FLASH_ACR_LATENCY_0WS;

    /* Sysclk activation on the main PLL */
    RCC->CFGR &= ~RCC_CFGR_HPRE;
    RCC->CFGR |= RCC_CFGR_HPRE_DIV4;

    /*Set clock source*/
    RCC->CFGR &= ~RCC_CFGR_SW;
    RCC->CFGR |= RCC_CFGR_SW_HSE;

    /*Wait till HSE is used as system clock*/
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE){}

    /* Set APB1 prescaler */
    RCC->CFGR &= ~RCC_CFGR_PPRE1;
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;

    /* Set APB2 prescaler */
    RCC->CFGR &= ~RCC_CFGR_PPRE2;
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
}