ref: e5b73212f6addcfdb5e306df63d7135e543c4f8d
src/drivers/Watchdog.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
#include "drivers/Watchdog.h" #include <mdk/nrf.h> using namespace Pinetime::Drivers; 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; /// 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); } /// 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; } /// 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 = NRF_WDT->RREN | 1; } /// 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; 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::Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour) { SetBehaviours(sleepBehaviour, haltBehaviour); SetTimeout(timeoutSeconds); EnableFirstReloadRegister(); resetReason = ::GetResetReason(); } void Watchdog::Start() { // Write 1 in the START task to start the watchdog NRF_WDT->TASKS_START = 1; } 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* Pinetime::Drivers::ResetReasonToString(Watchdog::ResetReason reason) { switch (reason) { case Watchdog::ResetReason::ResetPin: return "Reset pin"; case Watchdog::ResetReason::Watchdog: return "Watchdog"; case Watchdog::ResetReason::DebugInterface: return "Debug interface"; case Watchdog::ResetReason::LpComp: return "LPCOMP"; case Watchdog::ResetReason::SystemOff: return "System OFF"; case Watchdog::ResetReason::CpuLockup: return "CPU Lock-up"; case Watchdog::ResetReason::SoftReset: return "Soft reset"; case Watchdog::ResetReason::NFC: return "NFC"; case Watchdog::ResetReason::HardReset: return "Hard reset"; default: return "Unknown"; } } |