mirror of https://github.com/ARMmbed/mbed-os.git
STM32F4: watchdog HAL: Fix issues found with tests
Fix WATCHDOG_STATUS_INVALID_ARGUMENT for timeout values from 1 ms to 407 ms (fix inability to set prescaler bits to zero). Fix timeout <-> IWDG registers conversions. Fix read & write access to IWDG_PR and IWDG_RLR registers. Fix LSI RC frequency setting. Limit MAX_TIMEOUT_MS to UINT32_MAX.pull/10657/head
parent
8baf16760d
commit
d26dba40b2
|
@ -24,88 +24,118 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
// Platform specific watchdog definitions
|
||||
#define LPO_CLOCK_FREQUENCY 40000
|
||||
#define MAX_PRESCALER 256
|
||||
#define MAX_TIMEOUT 0xFFFULL
|
||||
#define LSI_RC_HZ 32000 // Frequency of the low-speed internal RC oscillator that drives IWDG
|
||||
#define MAX_IWDG_PR 0x6 // Max value of Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR)
|
||||
#define MAX_IWDG_RL 0xFFFUL // Max value of Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR).
|
||||
|
||||
// Number of decrements in the timeout register per millisecond
|
||||
#define TICKS_PER_MS (LPO_CLOCK_FREQUENCY / 1000)
|
||||
// Maximum timeout that can be specified in milliseconds
|
||||
const uint64_t max_timeout_ms = ((MAX_TIMEOUT / TICKS_PER_MS) * MAX_PRESCALER);
|
||||
// Convert Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR) to a prescaler_divider value.
|
||||
#define PR2PRESCALER_DIV(PR_BITS) \
|
||||
(4UL << (PR_BITS))
|
||||
|
||||
// Maximum supported watchdog timeout for given prescaler value
|
||||
#define CALCULATE_MAX_TIMEOUT_MS(scale) \
|
||||
((MAX_TIMEOUT / TICKS_PER_MS) * scale)
|
||||
// Convert Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR)
|
||||
// and Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR)
|
||||
// to a timeout value [ms].
|
||||
#define PR_RL2UINT64_TIMEOUT_MS(PR_BITS, RL_BITS) \
|
||||
((PR2PRESCALER_DIV(PR_BITS)) * (RL_BITS) * 1000ULL / (LSI_RC_HZ))
|
||||
|
||||
// Convert Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR) and a timeout value [ms]
|
||||
// to Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR)
|
||||
#define PR_TIMEOUT_MS2RL(PR_BITS, TIMEOUT_MS) \
|
||||
((TIMEOUT_MS) * (LSI_RC_HZ) / (PR2PRESCALER_DIV(PR_BITS)) / 1000UL)
|
||||
|
||||
static uint32_t calculate_prescaler_value(const uint32_t timeout_ms)
|
||||
{
|
||||
if (timeout_ms > max_timeout_ms) {
|
||||
return 0;
|
||||
}
|
||||
#define MAX_TIMEOUT_MS_UINT64 PR_RL2UINT64_TIMEOUT_MS(MAX_IWDG_PR, MAX_IWDG_RL)
|
||||
#if (MAX_TIMEOUT_MS_UINT64 > UINT32_MAX)
|
||||
#define MAX_TIMEOUT_MS UINT32_MAX
|
||||
#else
|
||||
#define MAX_TIMEOUT_MS (MAX_TIMEOUT_MS_UINT64 & 0xFFFFFFFFUL)
|
||||
#endif
|
||||
|
||||
for (uint32_t scale = 0; scale < 7; ++scale) {
|
||||
const uint32_t prescaler = (4U << scale);
|
||||
#define INVALID_IWDG_PR ((MAX_IWDG_PR) + 1) // Arbitrary value used to mark an invalid PR bits value.
|
||||
|
||||
if (timeout_ms < CALCULATE_MAX_TIMEOUT_MS(prescaler)) {
|
||||
return scale;
|
||||
// Pick a minimal Prescaler_divider bits (PR) value suitable for given timeout.
|
||||
static uint8_t pick_min_iwdg_pr(const uint32_t timeout_ms) {
|
||||
for (uint8_t pr = 0; pr <= MAX_IWDG_PR; pr++) {
|
||||
// Check that max timeout for given pr is greater than
|
||||
// or equal to timeout_ms.
|
||||
if (PR_RL2UINT64_TIMEOUT_MS(pr, MAX_IWDG_RL) >= timeout_ms) {
|
||||
return pr;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return INVALID_IWDG_PR;
|
||||
}
|
||||
|
||||
|
||||
watchdog_status_t hal_watchdog_init(const watchdog_config_t *config)
|
||||
{
|
||||
const uint32_t prescaler = calculate_prescaler_value(config->timeout_ms);
|
||||
|
||||
if (prescaler == 0) {
|
||||
const uint8_t pr = pick_min_iwdg_pr(config->timeout_ms);
|
||||
if (pr == INVALID_IWDG_PR) {
|
||||
return WATCHDOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
const uint32_t rl = PR_TIMEOUT_MS2RL(pr, config->timeout_ms);
|
||||
|
||||
// Enable write access to Prescaler(IWDG_PR) and Reload(IWDG_RLR) registers
|
||||
IWDG->KR = 0x5555;
|
||||
// Set the Key_value bits (KEY) of Key_register (IWDG_KR) to 0x5555
|
||||
// in order to enable write access to IWDG_PR and IWDG_RLR registers.
|
||||
MODIFY_REG(IWDG->KR, IWDG_KR_KEY_Msk, (IWDG_KR_KEY_Msk & (0x5555U << IWDG_KR_KEY_Pos)));
|
||||
|
||||
// Set the prescaler and timeout values
|
||||
IWDG->RLR = (TICKS_PER_MS * config->timeout_ms) / (4U << prescaler);
|
||||
IWDG->PR = prescaler;
|
||||
// Wait for the Watchdog_prescaler_value_update bit (PVU) of
|
||||
// Status_register (IWDG_SR) to be reset.
|
||||
while (READ_BIT(IWDG->SR, IWDG_SR_PVU)) {
|
||||
}
|
||||
// Set the Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR).
|
||||
MODIFY_REG(IWDG->PR, IWDG_PR_PR_Msk, (IWDG_PR_PR_Msk & (pr << IWDG_PR_PR_Pos)));
|
||||
|
||||
// Reload the Watchdog Counter.
|
||||
IWDG->KR = 0xAAAA;
|
||||
// Enable the Independent Watchdog.
|
||||
IWDG->KR = 0xCCCC;
|
||||
// Wait for the Watchdog_counter_reload_value_update bit (RVU) of
|
||||
// Status_register (IWDG_SR) to be reset.
|
||||
while (READ_BIT(IWDG->SR, IWDG_SR_RVU)) {
|
||||
}
|
||||
// Set the Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR).
|
||||
MODIFY_REG(IWDG->RLR, IWDG_RLR_RL_Msk, (IWDG_RLR_RL_Msk & (rl << IWDG_RLR_RL_Pos)));
|
||||
|
||||
// Set the Key_value bits (KEY) of Key_register (IWDG_KR) to 0xAAAA
|
||||
// in order to reload IWDG_RLR register value.
|
||||
MODIFY_REG(IWDG->KR, IWDG_KR_KEY_Msk, (IWDG_KR_KEY_Msk & (0xAAAAU << IWDG_KR_KEY_Pos)));
|
||||
|
||||
// Set the Key_value bits (KEY) of Key_register (IWDG_KR) to 0xCCCC
|
||||
// in order to start the watchdog.
|
||||
MODIFY_REG(IWDG->KR, IWDG_KR_KEY_Msk, (IWDG_KR_KEY_Msk & (0xCCCCU << IWDG_KR_KEY_Pos)));
|
||||
|
||||
return WATCHDOG_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
void hal_watchdog_kick(void)
|
||||
{
|
||||
IWDG->KR = 0xAAAA;
|
||||
// Set the Key_value bits (KEY) of Key_register (IWDG_KR) to 0xAAAA
|
||||
// in order to reload IWDG_RLR register value.
|
||||
MODIFY_REG(IWDG->KR, IWDG_KR_KEY_Msk, (IWDG_KR_KEY_Msk & (0xAAAAU << IWDG_KR_KEY_Pos)));
|
||||
}
|
||||
|
||||
|
||||
watchdog_status_t hal_watchdog_stop(void)
|
||||
{
|
||||
return WATCHDOG_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
uint32_t hal_watchdog_get_reload_value(void)
|
||||
{
|
||||
const uint32_t timeout = (IWDG->RLR & 0xFFF);
|
||||
const uint32_t prescaler = (4U << (IWDG->PR & 0x7));
|
||||
// Wait for the Watchdog_prescaler_value_update bit (PVU) of
|
||||
// Status_register (IWDG_SR) to be reset.
|
||||
while (READ_BIT(IWDG->SR, IWDG_SR_PVU)) {
|
||||
}
|
||||
// Read Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR).
|
||||
const uint8_t pr = (IWDG->PR & IWDG_PR_PR_Msk) >> IWDG_PR_PR_Pos;
|
||||
|
||||
return ((timeout / TICKS_PER_MS) * prescaler);
|
||||
// Wait for the Watchdog_counter_reload_value_update bit (RVU) of
|
||||
// Status_register (IWDG_SR) to be reset.
|
||||
while (READ_BIT(IWDG->SR, IWDG_SR_RVU)) {
|
||||
}
|
||||
// Read Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR).
|
||||
const uint32_t rl = (IWDG->RLR & IWDG_RLR_RL_Msk) >> IWDG_RLR_RL_Pos;
|
||||
|
||||
return PR_RL2UINT64_TIMEOUT_MS(pr, rl);
|
||||
}
|
||||
|
||||
|
||||
watchdog_features_t hal_watchdog_get_platform_features(void)
|
||||
{
|
||||
watchdog_features_t features;
|
||||
features.max_timeout = max_timeout_ms;
|
||||
features.max_timeout = MAX_TIMEOUT_MS;
|
||||
features.update_config = true;
|
||||
features.disable_watchdog = false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue