#include "watchdog.h" #include "stm32f7xx_hal.h" #include /* ================================================================ * IWDG Hardware Configuration * ================================================================ */ /* LSI frequency: approximately 32 kHz (typical, 20-40 kHz) */ #define LSI_FREQUENCY_HZ 32000 /* IWDG prescaler values */ #define IWDG_PSC_4 0 /* Divider = 4 */ #define IWDG_PSC_8 1 /* Divider = 8 */ #define IWDG_PSC_16 2 /* Divider = 16 */ #define IWDG_PSC_32 3 /* Divider = 32 */ #define IWDG_PSC_64 4 /* Divider = 64 */ #define IWDG_PSC_128 5 /* Divider = 128 */ #define IWDG_PSC_256 6 /* Divider = 256 */ /* Reload register range: 0-4095 */ #define IWDG_RELOAD_MIN 1 #define IWDG_RELOAD_MAX 4095 /* ================================================================ * Watchdog State * ================================================================ */ typedef struct { bool is_initialized; /* Whether watchdog has been initialized */ bool is_running; /* Whether watchdog is currently active */ uint32_t timeout_ms; /* Configured timeout in milliseconds */ uint8_t prescaler; /* IWDG prescaler value */ uint16_t reload_value; /* IWDG reload register value */ } WatchdogState; static WatchdogState s_watchdog = { .is_initialized = false, .is_running = false, .timeout_ms = 0, .prescaler = IWDG_PSC_32, .reload_value = 0 }; static IWDG_HandleTypeDef s_iwdg_handle = {0}; /* ================================================================ * Helper Functions * ================================================================ */ /* Calculate prescaler and reload values for desired timeout */ static bool watchdog_calculate_config(uint32_t timeout_ms, uint8_t *out_prescaler, uint16_t *out_reload) { if (timeout_ms < 1 || timeout_ms > 32000) { return false; /* Out of reasonable range */ } /* Try prescalers from smallest to largest */ const uint8_t prescalers[] = {IWDG_PSC_4, IWDG_PSC_8, IWDG_PSC_16, IWDG_PSC_32, IWDG_PSC_64, IWDG_PSC_128, IWDG_PSC_256}; const uint16_t dividers[] = {4, 8, 16, 32, 64, 128, 256}; for (int i = 0; i < 7; i++) { uint16_t divider = dividers[i]; /* timeout_ms = (reload * divider * 1000) / LSI_FREQUENCY_HZ */ uint32_t reload = (timeout_ms * LSI_FREQUENCY_HZ) / (divider * 1000); if (reload >= IWDG_RELOAD_MIN && reload <= IWDG_RELOAD_MAX) { *out_prescaler = prescalers[i]; *out_reload = (uint16_t)reload; return true; } } return false; /* No suitable prescaler found */ } /* ================================================================ * Public API * ================================================================ */ bool watchdog_init(uint32_t timeout_ms) { if (s_watchdog.is_initialized) { return false; /* Already initialized */ } /* Validate and calculate prescaler/reload values */ uint8_t prescaler; uint16_t reload; if (!watchdog_calculate_config(timeout_ms, &prescaler, &reload)) { return false; } s_watchdog.prescaler = prescaler; s_watchdog.reload_value = reload; s_watchdog.timeout_ms = timeout_ms; /* Configure and start IWDG */ s_iwdg_handle.Instance = IWDG; s_iwdg_handle.Init.Prescaler = prescaler; s_iwdg_handle.Init.Reload = reload; s_iwdg_handle.Init.Window = reload; /* Window == Reload means full timeout */ HAL_IWDG_Init(&s_iwdg_handle); s_watchdog.is_initialized = true; s_watchdog.is_running = true; return true; } void watchdog_kick(void) { if (s_watchdog.is_running) { HAL_IWDG_Refresh(&s_iwdg_handle); /* Reset IWDG counter */ } } uint32_t watchdog_get_timeout(void) { return s_watchdog.timeout_ms; } bool watchdog_is_running(void) { return s_watchdog.is_running; } bool watchdog_was_reset_by_watchdog(void) { /* Check RCC reset source flags */ /* IWDG reset sets the IWDGRSTF flag in RCC_CSR */ uint32_t reset_flags = RCC->CSR; /* IWDGRSTF is bit 29 of RCC_CSR */ bool was_iwdg_reset = (reset_flags & RCC_CSR_IWDGRSTF) != 0; /* Clear the flag by writing to RMVF (Bit 24) */ if (was_iwdg_reset) { RCC->CSR |= RCC_CSR_RMVF; /* Clear reset flags */ } return was_iwdg_reset; }