aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/displayapp/screens/SystemInfo.cpp20
-rw-r--r--src/drivers/Watchdog.cpp168
-rw-r--r--src/drivers/Watchdog.h60
-rw-r--r--src/systemtask/SystemTask.cpp6
4 files changed, 186 insertions, 68 deletions
diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp
index 511ecf50..d6fa7365 100644
--- a/src/displayapp/screens/SystemInfo.cpp
+++ b/src/displayapp/screens/SystemInfo.cpp
@@ -101,24 +101,24 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
auto batteryPercent = batteryController.PercentRemaining();
const auto* resetReason = [this]() {
- switch (watchdog.ResetReason()) {
- case Drivers::Watchdog::ResetReasons::Watchdog:
+ switch (watchdog.GetResetReason()) {
+ case Drivers::Watchdog::ResetReason::Watchdog:
return "wtdg";
- case Drivers::Watchdog::ResetReasons::HardReset:
+ case Drivers::Watchdog::ResetReason::HardReset:
return "hardr";
- case Drivers::Watchdog::ResetReasons::NFC:
+ case Drivers::Watchdog::ResetReason::NFC:
return "nfc";
- case Drivers::Watchdog::ResetReasons::SoftReset:
+ case Drivers::Watchdog::ResetReason::SoftReset:
return "softr";
- case Drivers::Watchdog::ResetReasons::CpuLockup:
+ case Drivers::Watchdog::ResetReason::CpuLockup:
return "cpulock";
- case Drivers::Watchdog::ResetReasons::SystemOff:
+ case Drivers::Watchdog::ResetReason::SystemOff:
return "off";
- case Drivers::Watchdog::ResetReasons::LpComp:
+ case Drivers::Watchdog::ResetReason::LpComp:
return "lpcomp";
- case Drivers::Watchdog::ResetReasons::DebugInterface:
+ case Drivers::Watchdog::ResetReason::DebugInterface:
return "dbg";
- case Drivers::Watchdog::ResetReasons::ResetPin:
+ case Drivers::Watchdog::ResetReason::ResetPin:
return "rst";
default:
return "?";
diff --git a/src/drivers/Watchdog.cpp b/src/drivers/Watchdog.cpp
index d0907a65..6c2c963b 100644
--- a/src/drivers/Watchdog.cpp
+++ b/src/drivers/Watchdog.cpp
@@ -2,74 +2,148 @@
#include <mdk/nrf.h>
using namespace Pinetime::Drivers;
-void Watchdog::Setup(uint8_t timeoutSeconds) {
- NRF_WDT->CONFIG &= ~(WDT_CONFIG_SLEEP_Msk << WDT_CONFIG_SLEEP_Pos);
- NRF_WDT->CONFIG |= (WDT_CONFIG_HALT_Run << WDT_CONFIG_SLEEP_Pos);
+namespace {
+ /// The watchdog is always driven by a 32768kHz clock
+ constexpr uint32_t ClockFrequency = 32768;
+ /// Write this value in the reload register to reload the watchdog
+ constexpr uint32_t ReloadValue = 0x6E524635UL;
- NRF_WDT->CONFIG &= ~(WDT_CONFIG_HALT_Msk << WDT_CONFIG_HALT_Pos);
- NRF_WDT->CONFIG |= (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos);
+ /// Configures the behaviours (pause or run) of the watchdog while the CPU is sleeping or halted by the debugger
+ ///
+ /// @param sleepBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is sleeping
+ /// @param haltBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is halted by the debugger
+ void SetBehaviours(Watchdog::SleepBehaviour sleepBehaviour, Watchdog::HaltBehaviour haltBehaviour) {
+ // NRF_WDT->CONFIG : only the 1st and 4th bits are relevant.
+ // Bit 0 : Behavior when the CPU is sleeping
+ // Bit 3 : Behavior when the CPU is halted by the debugger
+ // O means that the CPU is paused during sleep/halt, 1 means that the watchdog is kept running
+ NRF_WDT->CONFIG = static_cast<uint32_t>(sleepBehaviour) | static_cast<uint32_t>(haltBehaviour);
+ }
- /* timeout (s) = (CRV + 1) / 32768 */
- // JF : 7500 = 7.5s
- uint32_t crv = (((timeoutSeconds * 1000u) << 15u) / 1000) - 1;
- NRF_WDT->CRV = crv;
+ /// Configure the timeout delay of the watchdog (called CRV, Counter Reload Value, in the documentation).
+ ///
+ /// @param timeoutSeconds Timeout of the watchdog, expressed in seconds
+ void SetTimeout(uint8_t timeoutSeconds) {
+ // According to the documentation:
+ // Clock = 32768
+ // timeout [s] = ( CRV + 1 ) / Clock
+ // -> CRV = (timeout [s] * Clock) -1
+ NRF_WDT->CRV = (timeoutSeconds * ClockFrequency) - 1;
+ }
- /* Enable reload requests */
- NRF_WDT->RREN = (WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos);
+ /// Enables the first reload register
+ ///
+ /// The hardware provides 8 reload registers. To reload the watchdog, all enabled
+ /// register must be refreshed.
+ ///
+ /// This driver only enables the first reload register.
+ void EnableFirstReloadRegister() {
+ // RRED (Reload Register Enable) is a bitfield of 8 bits. Each bit represent
+ // one of the eight reload registers available.
+ // In this case, we enable only the first one.
+ NRF_WDT->RREN |= 1;
+ }
- resetReason = ActualResetReason();
-}
+ /// Returns the reset reason provided by the POWER subsystem
+ Watchdog::ResetReason GetResetReason() {
+ /* NRF_POWER->RESETREAS
+ * -------------------------------------------------------------------------------------------------------------------- *
+ * Bit | Reason (if bit is set to 1)
+ * ----|---------------------------------------------------------------------------------------------------------------- *
+ * 0 | Reset from the pin reset
+ * 1 | Reset from the watchdog
+ * 2 | Reset from soft reset
+ * 3 | Reset from CPU lock-up
+ * 16 | Reset due to wake up from System OFF mode when wakeup is triggered from DETECT signal from GPIO
+ * 17 | Reset due to wake up from System OFF mode when wakeup is triggered from ANADETECT signal from LPCOMP
+ * 18 | Reset due to wake up from System OFF mode when wakeup is triggered from entering into debug interface mode
+ * 19 | Reset due to wake up from System OFF mode by NFC field detect
+ * -------------------------------------------------------------------------------------------------------------------- */
+ const uint32_t reason = NRF_POWER->RESETREAS;
+ NRF_POWER->RESETREAS = 0xffffffff;
-void Watchdog::Start() {
- NRF_WDT->TASKS_START = 1;
+ uint32_t value = reason & 0x01; // avoid implicit conversion to bool using this temporary variable.
+ if (value != 0) {
+ return Watchdog::ResetReason::ResetPin;
+ }
+
+ value = (reason >> 1u) & 0x01u;
+ if (value != 0) {
+ return Watchdog::ResetReason::Watchdog;
+ }
+
+ value = (reason >> 2u) & 0x01u;
+ if (value != 0) {
+ return Watchdog::ResetReason::SoftReset;
+ }
+
+ value = (reason >> 3u) & 0x01u;
+ if (value != 0) {
+ return Watchdog::ResetReason::CpuLockup;
+ }
+
+ value = (reason >> 16u) & 0x01u;
+ if (value != 0) {
+ return Watchdog::ResetReason::SystemOff;
+ }
+
+ value = (reason >> 17u) & 0x01u;
+ if (value != 0) {
+ return Watchdog::ResetReason::LpComp;
+ }
+
+ value = (reason >> 18u) & 0x01u;
+ if (value != 0) {
+ return Watchdog::ResetReason::DebugInterface;
+ }
+
+ value = (reason >> 19u) & 0x01u;
+ if (value != 0) {
+ return Watchdog::ResetReason::NFC;
+ }
+
+ return Watchdog::ResetReason::HardReset;
+ }
}
-void Watchdog::Kick() {
- NRF_WDT->RR[0] = WDT_RR_RR_Reload;
+void Watchdog::Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour) {
+ SetBehaviours(sleepBehaviour, haltBehaviour);
+ SetTimeout(timeoutSeconds);
+ EnableFirstReloadRegister();
+
+ resetReason = ::GetResetReason();
}
-Watchdog::ResetReasons Watchdog::ActualResetReason() const {
- uint32_t reason = NRF_POWER->RESETREAS;
- NRF_POWER->RESETREAS = 0xffffffff;
+void Watchdog::Start() {
+ // Write 1 in the START task to start the watchdog
+ NRF_WDT->TASKS_START = 1;
+}
- if (reason & 0x01u)
- return ResetReasons::ResetPin;
- if ((reason >> 1u) & 0x01u)
- return ResetReasons::Watchdog;
- if ((reason >> 2u) & 0x01u)
- return ResetReasons::SoftReset;
- if ((reason >> 3u) & 0x01u)
- return ResetReasons::CpuLockup;
- if ((reason >> 16u) & 0x01u)
- return ResetReasons::SystemOff;
- if ((reason >> 17u) & 0x01u)
- return ResetReasons::LpComp;
- if ((reason) &0x01u)
- return ResetReasons::DebugInterface;
- if ((reason >> 19u) & 0x01u)
- return ResetReasons::NFC;
- return ResetReasons::HardReset;
+void Watchdog::Reload() {
+ // Write the reload value 0x6E524635UL to the reload register to reload the watchdog.
+ // NOTE : This driver enables only the 1st reload register.
+ NRF_WDT->RR[0] = ReloadValue;
}
-const char* Watchdog::ResetReasonToString(Watchdog::ResetReasons reason) {
+const char* Pinetime::Drivers::ResetReasonToString(Watchdog::ResetReason reason) {
switch (reason) {
- case ResetReasons::ResetPin:
+ case Watchdog::ResetReason::ResetPin:
return "Reset pin";
- case ResetReasons::Watchdog:
+ case Watchdog::ResetReason::Watchdog:
return "Watchdog";
- case ResetReasons::DebugInterface:
+ case Watchdog::ResetReason::DebugInterface:
return "Debug interface";
- case ResetReasons::LpComp:
+ case Watchdog::ResetReason::LpComp:
return "LPCOMP";
- case ResetReasons::SystemOff:
+ case Watchdog::ResetReason::SystemOff:
return "System OFF";
- case ResetReasons::CpuLockup:
+ case Watchdog::ResetReason::CpuLockup:
return "CPU Lock-up";
- case ResetReasons::SoftReset:
+ case Watchdog::ResetReason::SoftReset:
return "Soft reset";
- case ResetReasons::NFC:
+ case Watchdog::ResetReason::NFC:
return "NFC";
- case ResetReasons::HardReset:
+ case Watchdog::ResetReason::HardReset:
return "Hard reset";
default:
return "Unknown";
diff --git a/src/drivers/Watchdog.h b/src/drivers/Watchdog.h
index 65a505cb..c075232e 100644
--- a/src/drivers/Watchdog.h
+++ b/src/drivers/Watchdog.h
@@ -1,24 +1,68 @@
#pragma once
#include <cstdint>
+#include <nrf52_bitfields.h>
namespace Pinetime {
namespace Drivers {
+ /// Low level driver for the watchdog based on the nRF52832 Product Specification V1.1
+ ///
+ /// This driver initializes the timeout and sleep and halt behaviours of the watchdog
+ /// in the method Watchdog::Setup().
+ ///
+ /// The watchdog can then be started using the method Watchdog::Start(). At this point, the watchdog runs
+ /// and will reset the MCU if it's not reloaded before the timeout elapses.
+ ///
+ /// The watchdog can be reloaded using Watchdog::Kick().
+ ///
+ /// The watchdog also provide the cause of the last reset (reset pin, watchdog, soft reset, hard reset,... See
+ /// Watchdog::ResetReasons).
class Watchdog {
public:
- enum class ResetReasons { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset };
- void Setup(uint8_t timeoutSeconds);
+ /// Indicates the reasons of a reset of the MCU
+ enum class ResetReason { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset };
+
+ /// Behaviours of the watchdog when the CPU is sleeping
+ enum class SleepBehaviour : uint8_t {
+ /// Pause watchdog while the CPU is sleeping
+ Pause = 0 << WDT_CONFIG_SLEEP_Pos,
+ /// Keep the watchdog running while the CPU is sleeping
+ Run = 1 << WDT_CONFIG_SLEEP_Pos
+ };
+
+ /// Behaviours of the watchdog when the CPU is halted by the debugger
+ enum class HaltBehaviour : uint8_t {
+ /// Pause watchdog while the CPU is halted by the debugger
+ Pause = 0 << WDT_CONFIG_HALT_Pos,
+ /// Keep the watchdog running while the CPU is halted by the debugger
+ Run = 1 << WDT_CONFIG_HALT_Pos
+ };
+
+ /// Configures the watchdog with a specific timeout, behaviour when sleeping and when halted by the debugger
+ ///
+ /// @param sleepBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is sleeping
+ /// @param haltBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is halted by the debugger
+ void Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour);
+
+ /// Starts the watchdog. The watchdog will reset the MCU when the timeout period is elapsed unless you call
+ /// Watchdog::Kick before the end of the period
void Start();
- void Kick();
- ResetReasons ResetReason() const {
+ /// Reloads the watchdog.
+ ///
+ /// Ensure that you call this function regularly with a period shorter
+ /// than the timeout period to prevent the watchdog from resetting the MCU.
+ void Reload();
+
+ /// Returns the reason of the last reset
+ ResetReason GetResetReason() const {
return resetReason;
}
- static const char* ResetReasonToString(ResetReasons reason);
-
private:
- ResetReasons resetReason;
- ResetReasons ActualResetReason() const;
+ ResetReason resetReason;
};
+
+ /// Converts a reset reason to a human readable string
+ const char* ResetReasonToString(Watchdog::ResetReason reason);
}
}
diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp
index 0621c6ef..b199d53f 100644
--- a/src/systemtask/SystemTask.cpp
+++ b/src/systemtask/SystemTask.cpp
@@ -99,9 +99,9 @@ void SystemTask::Process(void* instance) {
void SystemTask::Work() {
BootErrors bootError = BootErrors::None;
- watchdog.Setup(7);
+ watchdog.Setup(7, Drivers::Watchdog::SleepBehaviour::Run, Drivers::Watchdog::HaltBehaviour::Pause);
watchdog.Start();
- NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason()));
+ NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::ResetReasonToString(watchdog.GetResetReason()));
APP_GPIOTE_INIT(2);
spi.Init();
@@ -403,7 +403,7 @@ void SystemTask::Work() {
dateTimeController.UpdateTime(systick_counter);
NoInit_BackUpTime = dateTimeController.CurrentDateTime();
if (nrf_gpio_pin_read(PinMap::Button) == 0) {
- watchdog.Kick();
+ watchdog.Reload();
}
}
#pragma clang diagnostic pop