How to check the reason for wakeup from idle and GPIO interrupt unresponsive in idle

I am working on an ongoing project where some minor power management is implemented:

void exit_sleep(enum pm_state state)
{
	/* turn on gyroscope 5V power supply */
	set_5v_reg(true);
}

void enter_sleep(enum pm_state state)
{
	/* turn off gyroscope 5V power supply */
	set_5v_reg(false);
}

static pm_notifier notifier = {.state_entry = enter_sleep, .state_exit = exit_sleep};

void suspend()
{
	/* Information about the suspend state */
	pm_state_info suspend{PM_STATE_SUSPEND_TO_IDLE, 0, 0, 0};
	/* Forces power management to go to a suspend state */
	pm_state_force(0u, &suspend);
}

void init()
{
	pm_notifier_register(&notifier);
	set_5v_reg(true);
}

suspend is called when measurements go to idle. But while debugging the application behaves strangely. The system goes into suspend after power up as intended, deactivates a 5 V power line and waits for a GPIO trigger. But the application instead starts up again immediately. The GPIO interrupt generally works here as its callback is called when triggered. When it goes into suspend again after some time it stays in idle and the GPIO interrupt does not get triggered again.

As far as I know the power states are not defined in the device tree as I have seen in some documentations.

The first problem is now that I don't even know what triggered the wakeup from idle. Is there any way to check the wakeup reason? Debugging does not help here as the reason is obfuscated by the OS. Maybe a register value?

For the second problem I am not even sure how the GPIO interrupt is supposedly linked to the wakeup. Is there a resource with an appropriate example? Most examples with the interfaces used in the code only handle the power down but not the power up, or it is thread triggered and not by interrupt.

Many thanks,

Rico

Parents
  • Hi,

    The first problem is now that I don't even know what triggered the wakeup from idle. Is there any way to check the wakeup reason? Debugging does not help here as the reason is obfuscated by the OS. Maybe a register value?

    There are generally two power saving modes in nRF52833:

    Reason for wakeup from SystemOFF mode can be read from the RESETREAS register. 

    Since you are using PM_STATE_SUSPEND_TO_IDLE, the system will likely enter SystemON mode. In this mode, every HW event will wake the CPU and continue execution. Events will be visible in the EVENTS_* register in each peripheral, but if you do not know which peripheral caused the wakeup, it is not trivial to find the actual event. Are you only seeing this behavior when debugging the application, or does it also wakeup in normal operation mode?

    Technically it would be possible to enable interrupts for EVENTS_SLEEPEXIT event with high interrupt priority and step through the application to see what code is executed after wakeup, but I have not tested this in nRF Connect SDK.

    For the second problem I am not even sure how the GPIO interrupt is supposedly linked to the wakeup. Is there a resource with an appropriate example? Most examples with the interfaces used in the code only handle the power down but not the power up, or it is thread triggered and not by interrupt.

    The interrupt handling is primarily done in the lower layer driver implementations in Zephyr, and events are passed from driver to application handlers to take appropriate action.

    Best regards,
    Jørgen

  • For me the problem is what even qualifies as a wakeup from the SYSTEM ON idle. In the data sheet it just says "wake on any event", but what kind of event? If it were really any event, then the MCU would never keep sleeping, as something will always happen.

Reply Children
  • It is confusing, but literally every hardware event in the data sheet labelled "EVENTS_blahblah" (ie tons of them) will wake up from SYSTEM_ON if and only if the corresponding "ENABLE" register for the peripheral with the event is enabled and the conditions for the hardware event occurs. This has nothing to do with interrupts whether enabled or not; additionally a hardware event under active and enabled conditions can trigger an interrupt if and only if the corresponding interrupt is also enabled. A hardware Event can be triggered by hardware (eg uart rx character), implicitly by software (eg setting Tasks-Start and getting Events-Started, or explicitly by software (eg setting an Event in the EGU).

    Many hardware events once triggered in an enabled peripheral will keep generating interrupts (if enabled) and waking up from SYSTEM_ON until the hardware event is explicitly cleared (aka level-sensitive not edge-sensitive). If not enabling interrupts for a specific hardware event the hardware event must be cleared in foreground code otherwise remaining in SYSTEM_ON sleep is not possible. It is possible to write code which checks every peripheral for 1) is it enabled and 2) are there any active hardware events for that peripheral; yes a pain.

    This is effectively nothing to do with Zephyr, by the way, other than when Zephyr secretly clears the events for you. Normal practice is to clear the events within interrupts; which implies the interrupts corresponding to each possible enabled event must be enabled and embed the correct code.

Related