Difference between revisions of "STM32 Watchdogs"
Line 30: | Line 30: | ||
== STM32 Reset Cause == | == STM32 Reset Cause == | ||
− | When experimenting with watchdogs, it could be a good idea to add a bit of code to display/check the reset cause on restart. The following was lifted off of | + | When experimenting with watchdogs, it could be a good idea to add a bit of code to display/check the reset cause on restart. The following was lifted off of a [https://stackoverflow.com/questions/34196663/stm32-how-to-get-last-reset-status Stackoverflow Reply]: |
<pre> | <pre> |
Revision as of 02:52, 4 November 2024
From the point of view of embedded systems, a watchdog is a device which monitors a system and is able to restart the system if or when something goes wrong. STM32 MCUs are equipped with two watchdogs: the Independent Watchdog (IWDG) and a Window Watchdog (WWDG). These watchdogs will be described in the following sections.
Independent Watchdog (IWDG)
The IWDG is, as the name implies, an independent device which watches over the MCU. It can be illustrated like this:
In the STM32, the IWDG is built-into the MCU itself, but it is still a completely independent device. So much in fact that it is not possible to read the counter value during run-time.
Calculating Time
The IWDG is clocked from the internal (or external) low speed timer. By default, without an external crystal, this is running at 32000 kHz. The formula to calculate the reset time is like this:
The counter is a 12-bit value, so possible to configure from 1 - 4096 and the prescaler can be configured to: 4, 8, 16, 32, 64, 128 or 256.
The calculation therefore is quite simple. If we want, for example, a reset time of 1.5 seconds, we can calculate the counter value like this:
C = 1.5 * 32000 / 16 = 3000
Notice that by default the low speed oscillator is a simple built-in RC oscillator which is far less precise than a crystal would be. It is therefore important to leave some room for drive. If we set the reset time at 1.5 seconds, we should probably kick the watchdog once every second or so.
Window Watchdog (WWDG)
To be added
STM32 Reset Cause
When experimenting with watchdogs, it could be a good idea to add a bit of code to display/check the reset cause on restart. The following was lifted off of a Stackoverflow Reply:
/// @brief Possible STM32 system reset causes typedef enum reset_cause_e { RESET_CAUSE_UNKNOWN = 0, RESET_CAUSE_LOW_POWER_RESET, RESET_CAUSE_WINDOW_WATCHDOG_RESET, RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET, RESET_CAUSE_SOFTWARE_RESET, RESET_CAUSE_POWER_ON_POWER_DOWN_RESET, RESET_CAUSE_EXTERNAL_RESET_PIN_RESET, RESET_CAUSE_BROWNOUT_RESET, } reset_cause_t; /// @brief Obtain the STM32 system reset cause /// @param None /// @return The system reset cause reset_cause_t reset_cause_get(void) { reset_cause_t reset_cause; if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST)) { reset_cause = RESET_CAUSE_LOW_POWER_RESET; } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) { reset_cause = RESET_CAUSE_WINDOW_WATCHDOG_RESET; } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) { reset_cause = RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET; } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) { // This reset is induced by calling the ARM CMSIS // `NVIC_SystemReset()` function! reset_cause = RESET_CAUSE_SOFTWARE_RESET; } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) { reset_cause = RESET_CAUSE_POWER_ON_POWER_DOWN_RESET; } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) { reset_cause = RESET_CAUSE_EXTERNAL_RESET_PIN_RESET; } // Needs to come *after* checking the `RCC_FLAG_PORRST` flag in order to // ensure first that the reset cause is NOT a POR/PDR reset. See note // below. else if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) { reset_cause = RESET_CAUSE_BROWNOUT_RESET; } else { reset_cause = RESET_CAUSE_UNKNOWN; } // Clear all the reset flags or else they will remain set during future // resets until system power is fully removed. __HAL_RCC_CLEAR_RESET_FLAGS(); return reset_cause; } // Note: any of the STM32 Hardware Abstraction Layer (HAL) Reset and Clock // Controller (RCC) header files, such as // "STM32Cube_FW_F7_V1.12.0/Drivers/STM32F7xx_HAL_Driver/Inc/stm32f7xx_hal_rcc.h", // "STM32Cube_FW_F2_V1.7.0/Drivers/STM32F2xx_HAL_Driver/Inc/stm32f2xx_hal_rcc.h", // etc., indicate that the brownout flag, `RCC_FLAG_BORRST`, will be set in // the event of a "POR/PDR or BOR reset". This means that a Power-On Reset // (POR), Power-Down Reset (PDR), OR Brownout Reset (BOR) will trip this flag. // See the doxygen just above their definition for the // `__HAL_RCC_GET_FLAG()` macro to see this: // "@arg RCC_FLAG_BORRST: POR/PDR or BOR reset." <== indicates the Brownout // Reset flag will *also* be set in the event of a POR/PDR. // Therefore, you must check the Brownout Reset flag, `RCC_FLAG_BORRST`, *after* // first checking the `RCC_FLAG_PORRST` flag in order to ensure first that the // reset cause is NOT a POR/PDR reset. /// @brief Obtain the system reset cause as an ASCII-printable name string /// from a reset cause type /// @param[in] reset_cause The previously-obtained system reset cause /// @return A null-terminated ASCII name string describing the system /// reset cause const char* reset_cause_get_name(reset_cause_t reset_cause) { const char *reset_cause_name = "TBD"; switch (reset_cause) { case RESET_CAUSE_UNKNOWN: reset_cause_name = "UNKNOWN"; break; case RESET_CAUSE_LOW_POWER_RESET: reset_cause_name = "LOW_POWER_RESET"; break; case RESET_CAUSE_WINDOW_WATCHDOG_RESET: reset_cause_name = "WINDOW_WATCHDOG_RESET"; break; case RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET: reset_cause_name = "INDEPENDENT_WATCHDOG_RESET"; break; case RESET_CAUSE_SOFTWARE_RESET: reset_cause_name = "SOFTWARE_RESET"; break; case RESET_CAUSE_POWER_ON_POWER_DOWN_RESET: reset_cause_name = "POWER-ON_RESET (POR) / POWER-DOWN_RESET (PDR)"; break; case RESET_CAUSE_EXTERNAL_RESET_PIN_RESET: reset_cause_name = "EXTERNAL_RESET_PIN_RESET"; break; case RESET_CAUSE_BROWNOUT_RESET: reset_cause_name = "BROWNOUT_RESET (BOR)"; break; } return reset_cause_name; }
We can now check the reset cause during startup:
reset_cause_t reset_cause = reset_cause_get(); printf("The system reset cause is \"%s\"\n", reset_cause_get_name(reset_cause));
Miscellaneous Links
To be added