How can I manage bits on the Portenta H7?

I want to put my Portenta H7 in sleepmode. I noticed something about power modes on page 294 of this datasheet, but I'm having trouble knowing what to do with it. There seems to be a control register (described on page 331) managing this, but I'm not sure how to use it. Could someone provide a sample sketch of how I can use said register to put the arduino to sleep, and then reawaken it with an interrupt?

As I understand the MCU:

  • the instruction set has an instruction as WFI - wait for interrupt: it will stop there the execution and waits for an INT
  • but in order to bring the chip in a sleep mode: you have to go to several registers and bring all devices - actually domains - into "retention mode" (STOP), also the memories. It means often to use the power mode registers, on all the domains, including memories.
  • it is called STOP mode

Usually, before you go to sleep mode - you also lower the MCU core clock.
And: you can also lower the voltage for the MCU (via the onboard PMIC chip which has three different profiles to manage low-power modes).

It is not so easy as writing a single register (there is not really such single register for sleep).
It is a "complex" procedure to bring the chip down to sleep mode (actually retention mode).

Also to bear in mind: not any INT can wakeup the MCU again. In low power mode (clock and voltage reduced) there are just few Wakeup-INTs possible, I guess PA0 is such one, potentially also UART pins. But I guess: not all pins can act as wake-up.
And there are some devices, e.g. Low Power Timers, LPTIM1, ... which can change from STOP mode to RUN mode, when their timer expires. Check devices/peripherals with LP in name.

Datasheet says:
"The EXTI controller performs interrupt and event management. In addition, it can wake up the processors, power domains and/or D3 domain from Stop mode."

So, you bring domains to STOP mode, before you execute WFI instruction. If INT comes, it brings the domain back to RUN mode. But when you have lowered voltage and clock speed - you have to re-configure again for fast speed.

Thank you for the in-depth breakdown! So, as I understand it:

  1. Lower MCU clock and voltage by choosing a mode through a PMIC register
  2. You execute the WFI command
  3. Assign all power mode registers to retention mode
  4. Enable a wake-up interrupt on PA0
How can I assign bits to the registers if the arduino is in WFI-mode? This is my first time venturing into registers, I'm a little lost on the "domains" and "memories", what are these? How do I define an interrupt as a wake-up int?

If it's not too much to ask, I would love an example sketch doing specifically this, and/or some guidance as to how I can learn it. My knowledge of this is limited to what I know of basic digital logic and what I learned from this introductory video. It gives a great idea of the concept, but it doesn't help much in my case.

BTW: all what you have listed as action - all before the WFI instruction will be done:

  • bring domains to STOP mode
  • enable a wakeup INT (or LPTIM)
  • optional is:
    • lower the clock speed for MCU
    • lower the voltage for MCU (via PMIC, needs I2C, or an external signal - there is one from MCU to PMIC)

All has to be in the right order! For instance: when you lower the voltage first (via PMIC) but the clock change just afterwards to slower clock - it will fail!
Now your MCU has less voltage but the clock is fast - this is a "conflict".

I would suggest to play with the STOP mode first:

  • configure a wakeup event (INT, device)
  • bring a domain to STOP mode (do not use it anymore when done)
  • do a WFI and wait for wakeup INT
  • roll back all: potentially, some domains come back automatically with wakeup, but make sure
    it is true, maybe a domain is not "coupled" with the wakeup

When this works, you can add features like lowering the clock, lowering the voltage. For lowering the clock - I think there is a clock config call where you have a flag to provide as "low power": it will change the MCU clock config.
For lowering the voltage: you need the PMIC datasheet and a clue how to use this external signal from MCU to PMIC.

It is a tough topic. Hard to debug. It can happen, when you lower the clock, when you bring domains into STOP mode, esp. when you lower the voltage - a debugger would not be able to connect properly (or keep the debug connection).
The debugger might need also a config set as "connect in low power mode" or as "connect under reset".

BTW: it can also happen, that a connected debugger brings the domains back. If debugger does anything - it can look like an event to power up again (release from STOP mode).
So, hard to debug. You need a clear "picture" about your "low power mode" state machine, how it works and which state transitions to do (when going to sleep and what when waking up).

I see, thank you. Looking through the datasheet I'm having some trouble locating the INT pins available for wakeup-calls. I'm looking at p795 (NVIC) and p804 (EXTI), but compared to the atmega328 datasheet, this is greek. How do I find pins available for this kind of interrupt? When I want to attach them I assume I just need to do attachInterrupt(<some pin>, <callback>, RISING), but I'm not sure what to fill in the callback function? I want to be able to execute various functions on various interrupt pins; does the interrupt itself automatically wake from STOP mode? In the case I do discover how to enable the interrupt, which registers do I write to in order to enable STOP mode? Where can I find this? I'm also uncertain how to enable WFI? According to p310, this would automatically enable cStop as well.

This is a rough sketch of what I'm trying to achieve (with placeholders where I'm uncertain what to do)

uint8_t intPin = <some pin>

void setup() 
{
    pinMode(LEDB, OUTPUT);
    attatchInterrupt(digitalPinToInterrupt(intPin), blinkISR, RISING);
    sleep();
}

void loop() 
{
    // Replace these with some instruction (which I don't know) to enable WFI for total sleep instead
    digitalWrite(LEDB, HIGH);
    delay(5000);
    digitalWrite(LEDB, LOW);
    delay(5000);
}

void blinkISR()
{
    // Do something to wake from STOP mode...

    for (int i = 0; i < 10; i++)
    {
        digitalWrite(LEDB, HIGH);
        delay(100);
        digitalWrite(LEDB, LOW);
        delay(100);
    }

    sleep();
}

void sleep()
{
    <SOME REGISTER FOR CPU> = (1 << <BIT TO CHANGE FOR STOP MODE>) & <SOME REGISTER FOR CPU>
    <SOME REGISTER FOR DOMAIN 1> = (1 << <BIT TO CHANGE FOR STOP MODE>) & <SOME REGISTER FOR DOMAIN 1>
    <SOME REGISTER FOR DOMAIN 2> = (1 << <BIT TO CHANGE FOR STOP MODE>) & <SOME REGISTER FOR DOMAIN 2>
}

Based on the reference manual for MCU, RM0399:
RM0399 - MCU reference manual
it says:

If the WFI instruction or Return from ISR was used to enter to low-power mode, any
peripheral interrupt acknowledged by the NVIC can wake up the system.

Also this:

The system can wake up from Stop mode by enabling an EXTI wakeup, without waking up a
CPU subsystem. In this case the system will operate in D3 autonomous mode.

See "PWR Control", 7.7.4 Exiting from low power modes, at page 311 in this reference manual.
There is also 14.2 Wakeup from low power modes, page 632:

The Extended interrupt and event controller module (EXTI) allows to wake up the system
from Stop mode and/or a CPU from CStop mode. Wakeup events are coming from
peripherals.

I think, when time left on my side, it would be a fun project to try low power modes (and to add support for low power config on Portenta H7). Not so easy, but curious how to do.
The info about low power modes, "wake up", is all over the place in this manual.

I think, one important issue to bear in mind:
which memories are used when the MCU enters low power mode? And which memories come back on wakeup? It might be important to know which memory the MCU will try to access, e.g. as stack region.
It sounds to me: the Domain D3, is used to do the retention for MCU data.
So, be careful when you bring another Domain into STOP mode but MCU would access or needs to access when back. Potentially, such a Domain, e.g. D1, needs an intervention of MCU to enable again. So, running on it - can be a dead lock. It needs careful memory assignments, which memory is used/needed by MCU during a wakeup.

I have started a new thread (and project):
Portenta H7 low power modes API