|
|
@ -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 |
|
|
|
|
|
|
|
|
|