From 8fa38bb25ba601ac4830f070301fc06e36416185 Mon Sep 17 00:00:00 2001 From: Steven Cartmell Date: Tue, 28 Nov 2017 14:00:56 +0000 Subject: [PATCH] Add Watchdog driver API --- drivers/ResetReason.h | 2 +- drivers/Watchdog.cpp | 68 +++++++++ drivers/Watchdog.h | 137 ++++++++++++++++++ hal/watchdog_api.h | 53 +++++-- .../TARGET_MCU_K64F/watchdog.c | 31 ++-- targets/TARGET_STM/TARGET_STM32F4/watchdog.c | 25 ++-- 6 files changed, 278 insertions(+), 38 deletions(-) create mode 100644 drivers/Watchdog.cpp create mode 100644 drivers/Watchdog.h diff --git a/drivers/ResetReason.h b/drivers/ResetReason.h index cd30c6c798..7c9f85422a 100644 --- a/drivers/ResetReason.h +++ b/drivers/ResetReason.h @@ -40,7 +40,7 @@ public: * const reset_reason_t reason = ResetReason::get(); * * if (reason == RESET_REASON_WATCHDOG) { - * std::cout << "Watchdog reset" << std::endl; + * printf("Watchdog reset\n"); * rollback(); * } * @endcode diff --git a/drivers/Watchdog.cpp b/drivers/Watchdog.cpp new file mode 100644 index 0000000000..358b41003a --- /dev/null +++ b/drivers/Watchdog.cpp @@ -0,0 +1,68 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef DEVICE_WATCHDOG + +#include "Watchdog.h" + +namespace mbed +{ + +watchdog_status_t Watchdog::start(const uint32_t timeout, const bool enable_sleep) +{ + return start(timeout, 0, enable_sleep); +} + + +watchdog_status_t Watchdog::start(const uint32_t timeout, const uint32_t window, const bool enable_sleep) +{ + watchdog_config_t config; + config.timeout_ms = timeout; + config.window_ms = window; + config.enable_window = (window != 0); + config.enable_sleep = enable_sleep; + + return hal_watchdog_init(&config); +} + + +void Watchdog::kick() +{ + hal_watchdog_kick(); +} + + +watchdog_status_t Watchdog::stop() +{ + return hal_watchdog_stop(); +} + + +uint32_t Watchdog::reload_value() const +{ + return hal_watchdog_get_reload_value(); +} + + +uint32_t Watchdog::max_timeout() +{ + const watchdog_features_t features = hal_watchdog_get_platform_features(); + + return features.max_timeout; +} + +} // namespace mbed + +#endif // DEVICE_WATCHDOG diff --git a/drivers/Watchdog.h b/drivers/Watchdog.h new file mode 100644 index 0000000000..dca16740bc --- /dev/null +++ b/drivers/Watchdog.h @@ -0,0 +1,137 @@ +/** \addtogroup hal */ +/** @{*/ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_WATCHDOG_H +#define MBED_WATCHDOG_H + +#ifdef DEVICE_WATCHDOG + +#include "watchdog_api.h" + +#include + + +namespace mbed { +/** \addtogroup drivers */ +/** @{*/ +/** A system timer that will reset the system in the case of system failures or + * malfunctions. + * + * Example: + * @code + * + * Watchdog watchdog = Watchdog(); + * watchdog.set_timeout(2000); + * watchdog.start(); + * + * while (true) { + * watchdog.kick(); + * + * // Application code + * } + * @endcode + * + */ +class Watchdog +{ +public: + Watchdog() {} + +public: + /** Start an independent watchdog timer with specified parameters + * + * @param timeout Timeout of the watchdog in milliseconds + * @param enable_sleep Sets sleep mode behaviour. When enabled the watchdog + * will continue to run in sleep mode. When disable the + * watchdog will be paused. This feature is not + * supported on all platforms. + * + * @return status WATCHDOG_STATUS_OK if the watchdog timer was started + * successfully. WATCHDOG_INVALID_ARGUMENT if one of the input + * parameters is out of range for the current platform. + * WATCHDOG_NOT_SUPPORTED if one of the enabled input + * parameters is not supported by the current platform. + */ + watchdog_status_t start(const uint32_t timeout, const bool enable_sleep = true); + + + /** Start a windowed watchdog timer with specified parameters + * + * @param timeout Timeout of the watchdog in milliseconds + * @param window Time period of the window of the watchdog + * @param enable_sleep Sets sleep mode behaviour. When enabled the watchdog + * will continue to run in sleep mode. When disable the + * watchdog will be paused. This feature is not + * supported on all platforms. + * + * @return status WATCHDOG_STATUS_OK if the watchdog timer was started + * successfully. WATCHDOG_INVALID_ARGUMENT if one of the input + * parameters is out of range for the current platform. + * WATCHDOG_NOT_SUPPORTED if one of the enabled input + * parameters is not supported by the current platform. + */ + watchdog_status_t start(const uint32_t timeout, const uint32_t window, const bool enable_sleep = true); + + + /** Refreshes the watchdog timer. + * + * This function should be called periodically before the watchdog times out. + * Otherwise, the system is reset. + * + * If the watchdog timer is not currently running this function does nothing + */ + void kick(); + + + /** Stops the watchdog timer + * + * Calling this function will attempt to disable any currently running + * watchdog timers if supported by the current platform. + * + * @return Returns WATCHDOG_STATUS_OK if the watchdog timer was succesfully + * stopped, or if the timer was never started. Returns + * WATCHDOG_STATUS_NOT_SUPPORTED if the watchdog cannot be disabled + * on the current platform. + */ + watchdog_status_t stop(); + + + /** Get the watchdog timer refresh value + * + * This function returns the refresh timeout of the watchdog timer. + * + * @return Reload value for the watchdog timer in milliseconds. + */ + uint32_t reload_value() const; + + + /** Get the maximum refresh value for the current platform in milliseconds + * + * @return Maximum refresh value supported by the watchdog for the current + * platform in milliseconds + */ + static uint32_t max_timeout(); +}; + +} // namespace mbed + +/**@}*/ +/**@}*/ + +#endif // DEVICE_WATCHDOG +#endif // MBED_WATCHDOG_H diff --git a/hal/watchdog_api.h b/hal/watchdog_api.h index 8950efab2a..e403fcaedf 100644 --- a/hal/watchdog_api.h +++ b/hal/watchdog_api.h @@ -21,10 +21,6 @@ #if DEVICE_WATCHDOG -#if !(DEVICE_RESET_REASON) - #error "Watchdog feature depends on reset reason API also being implemented" -#endif - #include #include @@ -53,7 +49,10 @@ typedef struct /** * Refresh value for the watchdog in milliseconds. The maximum value of this * setting is platform dependent, to find the maximum value for the current - * platform call hal_watchdog_get_max_timeout(void) + * platform call hal_watchdog_get_features() and check the timeout value + * member. The minimum valid value for this setting is 1, attempting to + * initialise the watchdog with a timeout of 0ms will return + * WATCHDOG_STATUS_INVALID_ARGUMENT. */ uint32_t timeout_ms; /** @@ -81,6 +80,36 @@ typedef struct } watchdog_config_t; +typedef struct +{ + /** + * Maximum timeout value for the watchdog in milliseconds. + */ + uint32_t max_timeout; + /** + * Maximum timeout value for the watchdog in milliseconds during window + * operation mode + */ + uint32_t max_timeout_window_mode; + /** + * Watchdog timer supports window mode operation + */ + bool window_mode; + /** + * Watchdog configuration can be updated after the watchdog has been started + */ + bool update_config; + /** + * Watchdog can be stopped after it is started without a reset + */ + bool disable_watchdog; + /** + * Watchdog can be paused while the core is in sleep mode + */ + bool pause_during_sleep; +} watchdog_features_t; + + typedef enum { WATCHDOG_STATUS_OK, WATCHDOG_STATUS_NOT_SUPPORTED, @@ -143,18 +172,12 @@ watchdog_status_t hal_watchdog_stop(void); */ uint32_t hal_watchdog_get_reload_value(void); -/** Checks if the last system reset was caused by a watchdog timer. +/** Get information on the current platforms supported watchdog functionality * - * @return True if last reset was triggered by a watchdog, False if not. + * @return watchdog_feature_t indicating supported watchdog features on the + * current platform */ -bool hal_watchdog_caused_last_reset(void); - -/** Get the maximum refresh value for the current platform in milliseconds - * - * @return Maximum refresh value supported by the watchdog for the current - * platform in milliseconds - */ -uint32_t hal_watchdog_get_max_timeout(void); +watchdog_features_t hal_watchdog_get_platform_features(void); /**@}*/ diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/watchdog.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/watchdog.c index e2fd1a7936..d0e540789e 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/watchdog.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/watchdog.c @@ -7,12 +7,12 @@ // Platform specific watchdog definitions #define LPO_CLOCK_FREQUENCY 1000 #define MAX_PRESCALER 8 -#define MAX_TIMEOUT 0xFFFFFFFF +#define MAX_TIMEOUT 0xFFFFFFFFULL // 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 -#define MAX_TIMEOUT_MS ((MAX_TIMEOUT / TICKS_PER_MS) * MAX_PRESCALER) +const uint64_t max_timeout_ms = ((MAX_TIMEOUT / TICKS_PER_MS) * MAX_PRESCALER); // Maximum supported watchdog timeout for given prescaler value #define CALCULATE_MAX_TIMEOUT_MS(scale) \ @@ -28,11 +28,11 @@ static uint32_t calculate_prescaler_value(const uint32_t timeout_ms) { - if (timeout_ms > MAX_TIMEOUT_MS) { + if (timeout_ms > max_timeout_ms) { return 0; } - for (uint32_t scale = 1; scale < MAX_PRESCALER; ++scale) { + for (uint32_t scale = 1; scale <= MAX_PRESCALER; ++scale) { if (timeout_ms < CALCULATE_MAX_TIMEOUT_MS(scale)) { return scale; } @@ -49,11 +49,15 @@ watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) return WATCHDOG_STATUS_INVALID_ARGUMENT; } - if (config->timeout_ms > MAX_TIMEOUT_MS) { + if (config->timeout_ms == 0) { return WATCHDOG_STATUS_INVALID_ARGUMENT; } - if (config->window_ms > MAX_TIMEOUT_MS) { + if (config->timeout_ms > max_timeout_ms) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + if (config->window_ms > max_timeout_ms) { return WATCHDOG_STATUS_INVALID_ARGUMENT; } @@ -114,12 +118,15 @@ uint32_t hal_watchdog_get_reload_value(void) return ((timeout / TICKS_PER_MS) * (prescaler + 1)); } -bool hal_watchdog_caused_last_reset(void) -{ - return (hal_reset_reason_get() == RESET_REASON_WATCHDOG); -} -uint32_t hal_watchdog_get_max_timeout(void) +watchdog_features_t hal_watchdog_get_max_timeout(void) { - return MAX_TIMEOUT_MS; + watchdog_features_t features; + features.max_timeout = max_timeout_ms; + features.max_timeout_window_mode = max_timeout_ms; + features.update_config = true; + features.disable_watchdog = true; + features.pause_during_sleep = true; + + return features; } diff --git a/targets/TARGET_STM/TARGET_STM32F4/watchdog.c b/targets/TARGET_STM/TARGET_STM32F4/watchdog.c index a9ef3375fc..95c394a817 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/watchdog.c +++ b/targets/TARGET_STM/TARGET_STM32F4/watchdog.c @@ -8,12 +8,13 @@ // Platform specific watchdog definitions #define LPO_CLOCK_FREQUENCY 40000 #define MAX_PRESCALER 256 -#define MAX_TIMEOUT 0xFFF +#define MAX_TIMEOUT 0xFFFULL // 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 -#define MAX_TIMEOUT_MS ((MAX_TIMEOUT / TICKS_PER_MS) * MAX_PRESCALER) +const uint64_t max_timeout_ms = ((MAX_TIMEOUT / TICKS_PER_MS) * MAX_PRESCALER); + // Maximum supported watchdog timeout for given prescaler value #define CALCULATE_MAX_TIMEOUT_MS(scale) \ ((MAX_TIMEOUT / TICKS_PER_MS) * scale) @@ -44,6 +45,10 @@ watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) return WATCHDOG_STATUS_INVALID_ARGUMENT; } + if (config->timeout_ms == 0) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + if (config->timeout_ms > MAX_TIMEOUT_MS) { return WATCHDOG_STATUS_INVALID_ARGUMENT; } @@ -103,14 +108,14 @@ uint32_t hal_watchdog_get_reload_value(void) return ((timeout / TICKS_PER_MS) * prescaler); } - -bool hal_watchdog_caused_last_reset(void) +watchdog_features_t hal_watchdog_get_max_timeout(void) { - return (hal_reset_reason_get() == RESET_REASON_WATCHDOG); -} + watchdog_features_t features; + features.max_timeout = max_timeout_ms; + features.max_timeout_window_mode = max_timeout_ms; + features.update_config = true; + features.disable_watchdog = false; + features.pause_during_sleep = true; - -uint32_t hal_watchdog_get_max_timeout(void) -{ - return MAX_TIMEOUT_MS; + return features; }