Is the PMU keeping things powered on?

We have code that puts the HW to sleep. On a nRF52833.

The pseudo code would be something like this -

<shutdown SD, stop freertos scheduler, stop interrupts, power down all hardware blocks, put GPIOs into low power state (most inputs, some gate our HW>

If at this point we call a NRF_POWER->SYSTEMOFF = 1; we see the board consumption drop to ~6uA (which is about what we expect)

If however we try either a loop around nrf_pwr_mgmt_run(); or a basic

_disableirq(); while(1) { __WFI() }; // as expected this never returns

We see about 1.4mA after the main code has been running, and only 6uA if the sleep code is called immediately after HW init.

The assumption is that PMU is keeping lots of the device powered up and active.

Q. I cannot see any central method of determining the state of the PMU and which peripherals are powered up, is this correct?

To remove any ambiguity on our shutdown code I added in a simple function "force_all" see below

void force_all (void)
{
NRF_UART0->ENABLE = 0;
NRF_UARTE0->ENABLE = 0;
NRF_USBD->ENABLE = 0;
NRF_UARTE1->ENABLE = 0;
NRF_SPI0->ENABLE = 0;
NRF_SPIM0->ENABLE = 0;
NRF_SPIS0->ENABLE = 0;
NRF_SPI1->ENABLE = 0;
NRF_SPIM1->ENABLE = 0;
NRF_SPIS1->ENABLE = 0;
NRF_SAADC->ENABLE = 0;
NRF_PWM0->ENABLE = 0;
NRF_PDM->ENABLE = 0;
NRF_PWM1->ENABLE = 0;
NRF_PWM2->ENABLE = 0;
NRF_SPI2->ENABLE = 0;
NRF_SPIM2->ENABLE = 0;
NRF_SPIS2->ENABLE = 0;
NRF_RTC0->TASKS_STOP = 1;
NRF_RTC1->TASKS_STOP = 1;
NRF_RTC2->TASKS_STOP = 1;
NRF_I2S->ENABLE = 0;
NRF_PWM3->ENABLE = 0;
NRF_SPIM3->ENABLE = 0;
NRF_TIMER1->TASKS_STOP = 1;
NRF_TIMER2->TASKS_STOP = 1;
NRF_TIMER3->TASKS_STOP = 1;
NRF_TIMER4->TASKS_STOP = 1;
NRF_QDEC->ENABLE = 0;
NRF_COMP->ENABLE = 0;
NRF_LPCOMP->ENABLE = 0;
NRF_TWI1->ENABLE = 0;
NRF_TWIM1->ENABLE = 0;
NRF_TWIS1->ENABLE = 0;
NRF_TWI0->ENABLE = 0;
NRF_TWIM0->ENABLE = 0;
NRF_TWIS0->ENABLE = 0;
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
NRF_TIMER0->TASKS_STOP = 1;
NRF_RADIO->TASKS_DISABLE = 1;
NRF_NFCT->TASKS_DISABLE = 1;
NRF_GPIOTE->CONFIG[0] = 0;
NRF_GPIOTE->CONFIG[1] = 0;
NRF_GPIOTE->CONFIG[2] = 0;
NRF_GPIOTE->CONFIG[3] = 0;
NRF_GPIOTE->CONFIG[4] = 0;
NRF_GPIOTE->CONFIG[5] = 0;
NRF_GPIOTE->CONFIG[6] = 0;
NRF_GPIOTE->CONFIG[7] = 0;
NRF_NVMC->CONFIG=0;
NRF_MWU->REGIONENSET = 1;
NRF_TEMP->TASKS_STOP=1;
NRF_RNG->TASKS_STOP=1;
NRF_ECB->TASKS_STOPECB=1;
NRF_AAR->TASKS_STOP = 1;
NRF_EGU0->INTENCLR = 1;
NRF_EGU1->INTENCLR = 1;
NRF_EGU2->INTENCLR = 1;
NRF_EGU3->INTENCLR = 1;
NRF_EGU4->INTENCLR = 1;
NRF_EGU5->INTENCLR = 1;
NRF_EGU0->INTEN=0;
NRF_EGU1->INTEN=0;
NRF_EGU2->INTEN=0;
NRF_EGU3->INTEN=0;
NRF_EGU4->INTEN=0;
NRF_EGU5->INTEN=0;
NRF_CCM->TASKS_STOP;
NRF_PPI->TASKS_CHG[0].DIS = 1;
NRF_PPI->TASKS_CHG[1].DIS = 1;
NRF_PPI->TASKS_CHG[2].DIS = 1;
NRF_PPI->TASKS_CHG[3].DIS = 1;
NRF_PPI->TASKS_CHG[4].DIS = 1;
NRF_PPI->TASKS_CHG[5].DIS = 1;
NRF_PPI->CHEN = 0;
NRF_POWER->TASKS_LOWPWR=1;
}

and called it before the sleep, it had no effect.

So, to recap, if the power down code is called after all the HW is initialized we see the expect 6uA (with either a WFI() loop or a complete core shutdown) - if we call it after the application has been running we see 1.4mA on the WFI() loop and 6uA on the shutdown. Therefore I assume the issue is something still running on the device.

I assume there is a hole in my shutdown logic, or something is somehow being determined as "ON" by the PMU and is keeping everything else on. And 1.4mA is fairly large as things go.

I've been diving the forums for a few hours, and other that "check your peripherals are all off" (see force_all() above) there it not a huge amount of advice on debugging the PMU to see where the power is going. The FW has vanished into a WFI() and (according to the Segger has never returned - after all, all interrupts are disabled).

it would not be possible to recreate all of this as an example, but I can certainly include shutdown code.

I know the Segger being attached powers up a large amount of the core via the SWD - so I remove it when not checking if the WFI() is returning and I see a drop of about ~30uA only.

The intention is that once I have solved this power issue I'll re-enable a single RTC via the LFCLK to allow it to make decisions and come out of sleep.

Any advice welcome.


  • At this point I am pretty sure anything that has ever been touched by the code is turned off, including the SD - I still cannot turn of the HFCLK (so something is keeping it awake) and it still draws ~500uA. The attempt to turn of HFCLK fails every time - we have a XTAL LFCLK but it is running on the internal RC LFCLK.

    If I use the WFI() it never returns, if I use the WFE() it returns instantly but there are no pending interrupts in NVIC->ISPR so I have no idea what events it is waking on and there is no way of telling.

    // check everything
    ASSERT(nrf_sdh_is_enabled() == false);
    ASSERT(NRF_RADIO->STATE == 0);
    ASSERT(NRF_UART0->ENABLE == 0);
    ASSERT(NRF_UARTE0->ENABLE == 0);
    ASSERT(NRF_USBD->ENABLE == 0);
    ASSERT(NRF_UARTE1->ENABLE == 0);
    ASSERT(NRF_SPI0->ENABLE == 0);
    ASSERT(NRF_SPIM0->ENABLE == 0);
    ASSERT(NRF_SPIS0->ENABLE == 0);
    ASSERT(NRF_SPI1->ENABLE == 0);
    ASSERT(NRF_SPIM1->ENABLE == 0);
    ASSERT(NRF_SPIS1->ENABLE == 0);
    ASSERT(NRF_SAADC->ENABLE == 0);
    ASSERT(NRF_PWM0->ENABLE == 0);
    ASSERT(NRF_PDM->ENABLE == 0);
    ASSERT(NRF_PWM1->ENABLE == 0);
    ASSERT(NRF_PWM2->ENABLE == 0);
    ASSERT(NRF_SPI2->ENABLE == 0);
    ASSERT(NRF_SPIM2->ENABLE == 0);
    ASSERT(NRF_SPIS2->ENABLE == 0);
    ASSERT(NRF_I2S->ENABLE == 0);
    ASSERT(NRF_PWM3->ENABLE == 0);
    ASSERT(NRF_SPIM3->ENABLE == 0);
    ASSERT(NRF_QDEC->ENABLE == 0);
    ASSERT(NRF_COMP->ENABLE == 0);
    ASSERT(NRF_LPCOMP->ENABLE == 0);
    ASSERT(NRF_TWI1->ENABLE == 0);
    ASSERT(NRF_TWIM1->ENABLE == 0);
    ASSERT(NRF_TWIS1->ENABLE == 0);
    ASSERT(NRF_TWI0->ENABLE == 0);
    ASSERT(NRF_TWIM0->ENABLE == 0);
    ASSERT(NRF_TWIS0->ENABLE == 0);

    ASSERT(NRF_RTC0->EVTENCLR == 0);
    ASSERT(NRF_RTC1->EVTENCLR == 0);
    ASSERT(NRF_RTC2->EVTENCLR == 0);
    ASSERT(NRF_TIMER0->SHORTS == 0);
    ASSERT(NRF_TIMER0->INTENSET == 0);
    ASSERT(NRF_TIMER1->SHORTS == 0);
    ASSERT(NRF_TIMER1->INTENSET == 0);
    ASSERT(NRF_TIMER2->SHORTS == 0);
    ASSERT(NRF_TIMER2->INTENSET == 0);
    ASSERT(NRF_TIMER3->SHORTS == 0);
    ASSERT(NRF_TIMER3->INTENSET == 0);
    ASSERT(NRF_TIMER4->SHORTS == 0);
    ASSERT(NRF_TIMER4->INTENSET == 0);

    ASSERT(NRF_PPI->TASKS_CHG[0].EN == 0);
    ASSERT(NRF_PPI->TASKS_CHG[1].EN == 0);
    ASSERT(NRF_PPI->TASKS_CHG[2].EN == 0);
    ASSERT(NRF_PPI->TASKS_CHG[3].EN == 0);
    ASSERT(NRF_PPI->TASKS_CHG[4].EN == 0);
    ASSERT(NRF_PPI->TASKS_CHG[5].EN == 0);
    ASSERT(NRF_PPI->CHEN == 0);

    ASSERT(NRF_MWU->REGIONEN == 0);

    for (int i=0; i<=7; i++)
    {
    ASSERT(NRF_GPIOTE->EVENTS_IN[i] == 0);
    ASSERT(NRF_GPIOTE->CONFIG[i] == 0);
    }
    ASSERT(NRF_GPIOTE->INTENCLR == 0);

    for (int i=0; i<16; i++)
    {
    ASSERT(NRF_EGU0->EVENTS_TRIGGERED[i] == 0);
    ASSERT(NRF_EGU1->EVENTS_TRIGGERED[i] == 0);
    ASSERT(NRF_EGU2->EVENTS_TRIGGERED[i] == 0);
    ASSERT(NRF_EGU3->EVENTS_TRIGGERED[i] == 0);
    ASSERT(NRF_EGU4->EVENTS_TRIGGERED[i] == 0);
    ASSERT(NRF_EGU5->EVENTS_TRIGGERED[i] == 0);
    }
    ASSERT(NRF_EGU0->INTEN==0);
    ASSERT(NRF_EGU1->INTEN==0);
    ASSERT(NRF_EGU2->INTEN==0);
    ASSERT(NRF_EGU3->INTEN==0);
    ASSERT(NRF_EGU4->INTEN==0);
    ASSERT(NRF_EGU5->INTEN==0);

    ASSERT(NRF_MWU->REGIONEN == 0);

    // for some reason the HFCLK is still sometimes active after SD shutdown, if so explicitly kill it
    uint32_t temp = NRF_CLOCK->HFCLKSTAT;

    if (temp & 0x10000) // this is HFCLK bit in HFCLKSTAT
    {
    NRF_CLOCK->TASKS_HFCLKSTOP = 1;
    }

    while (1)
    {
    #if !defined(DISABLE_WATCHDOG)
    watchdog_feed(); // needed because the supervisor has been stopped by this point
    #endif
    nrf_pwr_mgmt_run();
    //__WFI();
    }
  • ***SOLVED*** - updating in case anybody else finds the same issue

    https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_Rev1%2FERR%2FnRF52832%2FRev1%2Flatest%2Ferr_832_new.html&cp=3_1_1_1_1

    We have an earlier Rev1 HW on our PCB - I pulled the details from FICR

    This from the Rev1 Errata details the increased power consumption on stopping SAADC - and this turned out to be the cause of ~470uA of additional current.

    And, of course, explains why shutting the core down removed the excess current.

    Been a vexing few days, but pleasing to have found it.

Related