Browse Source

nRF52840 Power Management Stage 1 (Boot Lock) - v2.3 preparation work

This commit lays some of the ground work for the upcoming power management fix that allows users to configure the boot lock behaviour.

It is *not* the full fix yet. The goal is to break up the larger commit into smaller chunks so it is easier to review and softer to merge.

Improvements:
- MainBoard: Add bool to check if power management has been initialised (later to be consumed by CommonCLI to validate we can safely configure the settings)
- MainBoard: Add bool to validate LPCOMP is supported on the board (gates LPCOMP config so it doesnt wedge on an unsupported board)
- NRF52Board: Rename initPowerMgr() -> pwrmgtInit() to align with the "pwrmgt" prefix being used by any power management related functions in the upcoming new version
- NRF52Board: Add a shutdown reason for "None" so the `get pwrmgt.bootreason` command doesnt erroneously return "Unknown"
- NRF52Board: Add gate in configureVoltageWake to not arm LPCOMP when power management isn't initialised or the board doesn't support LPCOMP (i.e. we havent defined the AIN pin)
- NRF52Board: Reference the LPCOMP AIN pin directly instead of from the per-board definitions to align to the upcoming deprecation of the per-board static configs
- NRF52Board: Drop LPCOMP hysteresis as it broadens the wake voltage too much and makes the board less likely to self-recover
- NRF52Board: Separate LPCOMP and VBUS wake arm into their own functions
pull/3002/head
entr0p1 1 week ago
parent
commit
7982ac7fb3
  1. 2
      src/MeshCore.h
  2. 47
      src/helpers/NRF52Board.cpp
  3. 11
      src/helpers/NRF52Board.h

2
src/MeshCore.h

@ -69,8 +69,10 @@ public:
virtual bool isLoRaFemLnaEnabled() const { return false; } virtual bool isLoRaFemLnaEnabled() const { return false; }
// Power management interface (boards with power management override these) // Power management interface (boards with power management override these)
virtual bool isPwrMgtInitialised() const { return false; }
virtual bool isExternalPowered() { return false; } virtual bool isExternalPowered() { return false; }
virtual uint16_t getBootVoltage() { return 0; } virtual uint16_t getBootVoltage() { return 0; }
virtual bool getWakeLpcompSupported() const { return false; }
virtual uint32_t getResetReason() const { return 0; } virtual uint32_t getResetReason() const { return 0; }
virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; } virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; }
virtual uint8_t getShutdownReason() const { return 0; } virtual uint8_t getShutdownReason() const { return 0; }

47
src/helpers/NRF52Board.cpp

@ -38,7 +38,8 @@ static void __attribute__((constructor(101))) nrf52_early_reset_capture() {
g_nrf52_shutdown_reason = NRF_POWER->GPREGRET2; g_nrf52_shutdown_reason = NRF_POWER->GPREGRET2;
} }
void NRF52Board::initPowerMgr() { void NRF52Board::pwrmgtInit() {
if (pwrmgt_initialised) return;
// Copy early-captured register values // Copy early-captured register values
reset_reason = g_nrf52_reset_reason; reset_reason = g_nrf52_reset_reason;
shutdown_reason = g_nrf52_shutdown_reason; shutdown_reason = g_nrf52_shutdown_reason;
@ -65,6 +66,7 @@ void NRF52Board::initPowerMgr() {
MESH_DEBUG_PRINTLN("PWRMGT: Reset = %s (0x%lX)", MESH_DEBUG_PRINTLN("PWRMGT: Reset = %s (0x%lX)",
getResetReasonString(reset_reason), (unsigned long)reset_reason); getResetReasonString(reset_reason), (unsigned long)reset_reason);
} }
pwrmgt_initialised = true;
} }
const char* NRF52Board::getResetReasonString(uint32_t reason) { const char* NRF52Board::getResetReasonString(uint32_t reason) {
@ -89,6 +91,7 @@ const char* NRF52Board::getResetReasonString(uint32_t reason) {
const char* NRF52Board::getShutdownReasonString(uint8_t reason) { const char* NRF52Board::getShutdownReasonString(uint8_t reason) {
switch (reason) { switch (reason) {
case SHUTDOWN_REASON_NONE: return "None";
case SHUTDOWN_REASON_LOW_VOLTAGE: return "Low Voltage"; case SHUTDOWN_REASON_LOW_VOLTAGE: return "Low Voltage";
case SHUTDOWN_REASON_USER: return "User Request"; case SHUTDOWN_REASON_USER: return "User Request";
case SHUTDOWN_REASON_BOOT_PROTECT: return "Boot Protection"; case SHUTDOWN_REASON_BOOT_PROTECT: return "Boot Protection";
@ -97,7 +100,7 @@ const char* NRF52Board::getShutdownReasonString(uint8_t reason) {
} }
bool NRF52Board::checkBootVoltage(const PowerMgtConfig* config) { bool NRF52Board::checkBootVoltage(const PowerMgtConfig* config) {
initPowerMgr(); pwrmgtInit();
// Read boot voltage // Read boot voltage
boot_voltage_mv = getBattMilliVolts(); boot_voltage_mv = getBattMilliVolts();
@ -164,24 +167,30 @@ void NRF52Board::enterSystemOff(uint8_t reason) {
NVIC_SystemReset(); NVIC_SystemReset();
} }
void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) { void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t lpcomp_refsel) {
pwrmgtWakeArmVbus();
if (!isPwrMgtInitialised()) {
MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake not armed. Reason: Power Management not initialised");
return;
}
if (!getWakeLpcompSupported()) {
MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake not armed. Reason: LPCOMP unsupported on variant");
return;
}
// LPCOMP is not managed by SoftDevice - direct register access required // LPCOMP is not managed by SoftDevice - direct register access required
// Halt and disable before reconfiguration // Halt and disable before reconfiguration
NRF_LPCOMP->TASKS_STOP = 1; NRF_LPCOMP->TASKS_STOP = 1;
NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Disabled; NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Disabled;
// Select analog input (AIN0-7 maps to PSEL 0-7) // Select analog input (AIN0-7 maps to PSEL 0-7)
NRF_LPCOMP->PSEL = ((uint32_t)ain_channel << LPCOMP_PSEL_PSEL_Pos) & LPCOMP_PSEL_PSEL_Msk; NRF_LPCOMP->PSEL = ((uint32_t)PWRMGT_LPCOMP_AIN << LPCOMP_PSEL_PSEL_Pos) & LPCOMP_PSEL_PSEL_Msk;
// Reference: REFSEL (0-6=1/8..7/8, 7=ARef, 8-15=1/16..15/16) // Reference: REFSEL (0-6=1/8..7/8, 7=ARef, 8-15=1/16..15/16)
NRF_LPCOMP->REFSEL = ((uint32_t)refsel << LPCOMP_REFSEL_REFSEL_Pos) & LPCOMP_REFSEL_REFSEL_Msk; NRF_LPCOMP->REFSEL = ((uint32_t)lpcomp_refsel << LPCOMP_REFSEL_REFSEL_Pos) & LPCOMP_REFSEL_REFSEL_Msk;
// Detect UP events (voltage rises above threshold for battery recovery) // Detect UP events (voltage rises above threshold for battery recovery)
NRF_LPCOMP->ANADETECT = LPCOMP_ANADETECT_ANADETECT_Up; NRF_LPCOMP->ANADETECT = LPCOMP_ANADETECT_ANADETECT_Up;
// Enable 50mV hysteresis for noise immunity
NRF_LPCOMP->HYST = LPCOMP_HYST_HYST_Hyst50mV;
// Clear stale events/interrupts before enabling wake // Clear stale events/interrupts before enabling wake
NRF_LPCOMP->EVENTS_READY = 0; NRF_LPCOMP->EVENTS_READY = 0;
NRF_LPCOMP->EVENTS_DOWN = 0; NRF_LPCOMP->EVENTS_DOWN = 0;
@ -195,23 +204,22 @@ void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) {
NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Enabled; NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Enabled;
NRF_LPCOMP->TASKS_START = 1; NRF_LPCOMP->TASKS_START = 1;
// Wait for comparator to settle before entering SYSTEMOFF // Wait for comparator to settle
for (uint8_t i = 0; i < 20 && !NRF_LPCOMP->EVENTS_READY; i++) { for (uint8_t i = 0; i < 20 && !NRF_LPCOMP->EVENTS_READY; i++) {
delayMicroseconds(50); delayMicroseconds(50);
} }
if (refsel == 7) { if (lpcomp_refsel == 7) {
MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake configured (AIN%d, ref=ARef)", ain_channel); MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=ARef)", PWRMGT_LPCOMP_AIN);
} else if (refsel <= 6) { } else if (lpcomp_refsel <= 6) {
MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake configured (AIN%d, ref=%d/8 VDD)", MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=%d/8 VDD)", PWRMGT_LPCOMP_AIN, lpcomp_refsel + 1);
ain_channel, refsel + 1);
} else { } else {
uint8_t ref_num = (uint8_t)((refsel - 8) * 2 + 1); uint8_t ref_num = (uint8_t)((lpcomp_refsel - 8) * 2 + 1);
MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake configured (AIN%d, ref=%d/16 VDD)", MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake armed (AIN%d, ref=%d/16 VDD)", PWRMGT_LPCOMP_AIN, ref_num);
ain_channel, ref_num);
} }
}
// Configure VBUS (USB power) wake alongside LPCOMP void NRF52Board::pwrmgtWakeArmVbus() {
uint8_t sd_enabled = 0; uint8_t sd_enabled = 0;
sd_softdevice_is_enabled(&sd_enabled); sd_softdevice_is_enabled(&sd_enabled);
if (sd_enabled) { if (sd_enabled) {
@ -220,8 +228,7 @@ void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) {
NRF_POWER->EVENTS_USBDETECTED = 0; NRF_POWER->EVENTS_USBDETECTED = 0;
NRF_POWER->INTENSET = POWER_INTENSET_USBDETECTED_Msk; NRF_POWER->INTENSET = POWER_INTENSET_USBDETECTED_Msk;
} }
MESH_DEBUG_PRINTLN("PWRMGT: VBUS wake armed");
MESH_DEBUG_PRINTLN("PWRMGT: VBUS wake configured");
} }
#endif #endif

11
src/helpers/NRF52Board.h

@ -25,15 +25,15 @@ struct PowerMgtConfig {
#endif #endif
class NRF52Board : public mesh::MainBoard { class NRF52Board : public mesh::MainBoard {
#ifdef NRF52_POWER_MANAGEMENT private:
void initPowerMgr(); bool pwrmgt_initialised = false;
#endif
protected: protected:
uint8_t startup_reason; uint8_t startup_reason;
char *ota_name; char *ota_name;
#ifdef NRF52_POWER_MANAGEMENT #ifdef NRF52_POWER_MANAGEMENT
void pwrmgtInit();
uint32_t reset_reason; // RESETREAS register value uint32_t reset_reason; // RESETREAS register value
uint8_t shutdown_reason; // GPREGRET value (why we entered last SYSTEMOFF) uint8_t shutdown_reason; // GPREGRET value (why we entered last SYSTEMOFF)
uint16_t boot_voltage_mv; // Battery voltage at boot (millivolts) uint16_t boot_voltage_mv; // Battery voltage at boot (millivolts)
@ -41,6 +41,7 @@ protected:
bool checkBootVoltage(const PowerMgtConfig* config); bool checkBootVoltage(const PowerMgtConfig* config);
void enterSystemOff(uint8_t reason); void enterSystemOff(uint8_t reason);
void configureVoltageWake(uint8_t ain_channel, uint8_t refsel); void configureVoltageWake(uint8_t ain_channel, uint8_t refsel);
void pwrmgtWakeArmVbus();
virtual void initiateShutdown(uint8_t reason); virtual void initiateShutdown(uint8_t reason);
#endif #endif
@ -63,6 +64,10 @@ public:
uint8_t getShutdownReason() const override { return shutdown_reason; } uint8_t getShutdownReason() const override { return shutdown_reason; }
const char* getResetReasonString(uint32_t reason) override; const char* getResetReasonString(uint32_t reason) override;
const char* getShutdownReasonString(uint8_t reason) override; const char* getShutdownReasonString(uint8_t reason) override;
bool isPwrMgtInitialised() const override { return pwrmgt_initialised; }
#ifdef PWRMGT_LPCOMP_AIN
bool getWakeLpcompSupported() const override { return true; }
#endif
#endif #endif
}; };

Loading…
Cancel
Save