Demo: Configuring An Artemis for Deep Sleep

It has taken me a while, but I am finally getting the Artemis module on my Blackboard achieve the deepsleep current consumption levels promised by the Ambiq datasheet. To help others interested in this process, I have created an Arduino sketch that shows the steps I took. This sketch is really just a starting point. Different systems will have different requirements for what peripherals need to be active during deepsleep, or how much memory they need powered during runtime or deepsleep, or if they can tolerate a ‘cold’ cache after waking from deepsleep, just to name a few things.

It is worth noting that deepsleep for any system is a very tricky thing. When you are talking about currents in the microamp and sub-microamp range, all kinds of things start to become measurable effects that might have been previously ignorable. For example, my test board is sleeping right now at 2.5 uA. If I pick the board up off my desk , the power drops to 2.3 uA. If I let go of the board, the power goes back up to 2.5 uA. It is a very repeatable effect. Who knows why, but there you are.

Another class of problems involves the software you might be using: after updating my Arduino system from 1.8.9 to 1.8.10, the deep sleep current jumped from 2.5 uA to 55 uA. I traced that back to what appeared to be another GPIO being configured in a fashion that caused it to suck more current. Changing the appropriate #if in the demo source code (provided below) to invoke the “big hammer” to disable all the GPIOs brought the current back down to 2.5 uA. But to prove that I knew what was going on, I did a search of the GPIOs to see which one it was. And log and behold, it appeared to be none of them. So I changed the source code back to the way it was before. And guess what, the power stayed the same at 2.5 uA, even after a cold start power cycle. So why did it go up in the first place? Again, I don’t have an answer to that one. All I know for sure is that deepsleep is affected by subtle things.

Here’s a final example, and then I’ll shut up: Environment factors also matter. If I exhale a nice, long breath onto my Blackboard, the power consumption goes up over 3 uA and then drifts back down to 2.5 uA over a few seconds as the humid air dissipates.

The bottom line is that there are lots of subtleties involved in any system that is trying to minimize power consumption, and it is a hard problem to truly know that you are on top of all of them. You need to be both careful and ruthless managing your power drains.

Stepping back from the subtleties for a moment, the critical factors that affect deep sleep power consumption are:

  • - Disabling power to all peripherals that you don’t need operating during deep sleep
  • - Making sure that the HFRC (the 48 MHz main RC oscillator) is not being used by any peripheral. This is an issue with the Arduino environment: it configures STIMER to be clocked by the HFRC. That will cost about 60 uA in deepsleep!
  • - Make sure that you know what every single GPIO is doing in your system. If you don't need a GPIO, make sure it is disabled. If you do need one, make sure it is not doing something dumb like supplying a weak pullup to external circuitry that is pulling the pin down during deepsleep. Even if the pullup is not being pulled down, the external circuitry that is connected to the pullup will be loading that pin, which will cause a power drain.
  • Finally, here is an Arduino sketch that puts my Blackboard into a deep sleep at 2.5 uA. Cut and paste the whole mess into your own sketch. Play with it. Try changing memory sizes, or clock sources, and see what changes. You will need a good current meter though. A lot of the changes result in sub-microamp differences in current.

    See the photo showing my Blackboard powered from 3.3V being directly injected from an external power supply through my meter and into the measurement connector on the board. I am using Arduino 1.8.10, so who knows if future changes to the Arduino code itself or the Sparkfun Arduino package will affect things.

    // An Arduino sketch for a Sparkfun Artemis Blackboard that demonstrates how to 
    // configure the board for deep sleep.
    
    #define LED_ON              HIGH
    #define LED_OFF             LOW
    
    const char* peripheralNames[] = {
      "NONE",
      "IOS",
      "IOM0",
      "IOM1",
      "IOM2",
      "IOM3",
      "IOM4",
      "IOM5",
      "UART0",
      "UART1",
      "ADC",
      "SCARD",
      "MSPI",
      "PDM",
      "BLEL",
      "MAX"
      };
    
    void flash(uint32_t count)
    {
      while (count > 0) {
        digitalWrite(LED_BUILTIN, LED_ON);
        delay(50);
        digitalWrite(LED_BUILTIN, LED_OFF);
        delay(100);
        count--;
      }
    }
    
    void panic()
    {
      while (1) {
        flash(1);
      }
    }
    
    // For some reason, I need this in the VS Code IDE even though I don't in the Arduino IDE
    extern Uart Serial1;
    
    // Disable powering all peripherals except UART0 (used by the Arduino Serial object)
    void disablePeripherals()
    {
      // Find out what peripherals are enabled at this point and disable them all
      // except UART0 (Arduino 'Serial') which we will still use for a bit:
      Serial.println("Initial peripheral power enable states:");
      for (uint32_t id = (AM_HAL_PWRCTRL_PERIPH_NONE + 1); id < AM_HAL_PWRCTRL_PERIPH_MAX; id++) {
        Serial.print("  ");
        Serial.print(id);
        Serial.print("[");
        Serial.print(peripheralNames[id]);
        Serial.print("]: ");
        uint32_t enabled;
        am_hal_pwrctrl_periph_enabled((am_hal_pwrctrl_periph_e)id, &enabled);
        Serial.println(enabled ? "ENABLED" : "disabled");
        if (enabled && (id != AM_HAL_PWRCTRL_PERIPH_UART0)) {
          am_hal_pwrctrl_periph_disable((am_hal_pwrctrl_periph_e)id);
        }
      }
      Serial.println("All peripherals except UART0 have been powered down.");
    }
    
    // Configure the memories for the desired normal runtime and deepsleep operation.
    void configureMemories()
    {
      // The Apollo3 processor can be configured to only power part of the Flash and SRAM
      // in order to save power during normal operation.
      // For example, if an application doesn't need all of the SRAM, it can arrange to power off the unused parts.
      // A further optimization allows the system to power down even more of the SRAM during deep sleep.
      // 
      // In this example, we will ask to only use 1/2 of the flash during normal operation, 
      // but we will power all 384K of SRAM.
      am_hal_pwrctrl_memory_enable(AM_HAL_PWRCTRL_MEM_FLASH_512K);
      am_hal_pwrctrl_memory_enable(AM_HAL_PWRCTRL_MEM_SRAM_384K);
    
      // In deepsleep mode, the chip can be configured to turn off even more memory.  Note that if 
      // runmode enables (above) turn some part of the memory off, the sleepmode cannot turn it on.
      // These sleepmode settings only allow you to power down memories that runmode had powered up.
      // Uncommenting the next line would tell the system to power down all of SRAM except the low 32K of RAM.
      // This would take less power, but it would require the codebase to make sure that any information 
      // that it expected to survive a deepsleep would have to be stored in the low 32K of SRAM.
      //PWRCTRL->MEMPWDINSLEEP_b.SRAMPWDSLP = PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER32K;
    
      // The Flash memory can also be powered down during deep sleep.
      // changing FLASH0 from being disabled during deep sleep to enabled during sleep adds about 40 uA
      // Testing proves that asking flash1 to be enabled during sleep does not increase the power if runmode has already disabled flash1.
      PWRCTRL->MEMPWDINSLEEP_b.FLASH0PWDSLP = 1;  // '1' indicates that the specified memory will have its power gated OFF during deepsleep
      PWRCTRL->MEMPWDINSLEEP_b.FLASH1PWDSLP = 1;
    
      // The cache can also be shut down during deepsleep.  This means that when the processor wakes,
      // the cache will be 'empty' and everything will be a miss until the cache refills.
      // The tradeoff is that turning the cache off will cause the system to be a bit slower to wake up.
      PWRCTRL->MEMPWDINSLEEP_b.CACHEPWDSLP = 1;
    
    }
    
    void configureStimer()
    {
      // The default Arduino environment runs the System Timer (STIMER) off the 48 MHZ HFRC clock source.
      // The HFRC appears to take over 60 uA when it is running, so this is a big source of extra 
      // current consumption in deep sleep.
      // For systems that might want to use the STIMER to generate a periodic wakeup, it needs to be left running.
      // However, it does not have to run at 48 MHz.  If we reconfigure STIMER (system timer) to use the 32768 Hz 
      // XTAL clock source instead the measured deepsleep power drops by about 64 uA.
      CTIMER->STCFG &= ~0xF;
      #if 1
        CTIMER->STCFG |= 0x3;    // selects 32768 Hz via crystal osc. This appears to cost about 0.1 uA versus selecting "no clock"
      #else
        // This option would be available to systems that don't care about passing time, but might be set
        // to wake up on a GPIO transition interrupt.
        CTIMER->STCFG |= 0x0;    // selects "no clock" (disables STIMER)
      #endif
    }
    
    void dumpRegs()
    {
      Serial.println("\nDumping Registers");
      // Display which devices currently are powered
      Serial.print("PWRCTRL->DEVPWREN: $");
      Serial.print((uint32_t)&(PWRCTRL->DEVPWREN), HEX);
      Serial.print(": $");
      Serial.println((uint32_t)PWRCTRL->DEVPWREN, HEX);
    
      // This register enables power to various memories during runmode.
      // If a memory is not enabled in runnmode, it will not be powered in deep sleep either!
      Serial.print("PWRCTRL->MEMPWREN: $");
      Serial.print((uint32_t)&(PWRCTRL->MEMPWREN), HEX);
      Serial.print(": $");
      Serial.println((uint32_t)PWRCTRL->MEMPWREN, HEX);
    
      // For every memory that is powered in runmode, this register defines whether the specific
      // memory will remain powered during sleepmode.
      Serial.print("PWRCTRL->MEMPWDINSLEEP: $");
      Serial.print((uint32_t)&(PWRCTRL->MEMPWDINSLEEP), HEX);
      Serial.print(": $");
      Serial.println((uint32_t)PWRCTRL->MEMPWDINSLEEP, HEX);
     
      // Show the MISC register contents
      Serial.print("PWRCTRL->MISC: $");
      Serial.print((uint32_t)&(PWRCTRL->MISC), HEX);
      Serial.print(": $");
      Serial.println((uint32_t)PWRCTRL->MISC, HEX);
      
      Serial.print("BLE Buck ON: ");
      Serial.println(PWRCTRL->SUPPLYSTATUS_b.BLEBUCKON);
    
      // Note in Apollo3 manual: "The SIMO buck cannot be dynamically enabled/disabled after initial device reset."
      Serial.print("SIMO Buck ON: ");
      Serial.println(PWRCTRL->SUPPLYSTATUS_b.SIMOBUCKON);
    
      // The CLOCKENSTAT registers are important because they show you if there are any parts of the processor
      // that might be responsible for not letting the HFRC oscillator shut down.
      // Show the CLOCKENSTAT register contents
      Serial.print("CLKGEN->CLOCKENSTAT: $");
      Serial.print((uint32_t)&(CLKGEN->CLOCKENSTAT), HEX);
      Serial.print(": $");
      Serial.println((uint32_t)CLKGEN->CLOCKENSTAT, HEX);
    
      // Show the CLOCKEN2STAT register contents
      Serial.print("CLKGEN->CLOCKEN2STAT: $");
      Serial.print((uint32_t)&(CLKGEN->CLOCKEN2STAT), HEX);
      Serial.print(": $");
      Serial.println((uint32_t)CLKGEN->CLOCKEN2STAT, HEX);
      
      // Show the CLOCKEN3STAT register contents
      Serial.print("CLKGEN->CLOCKEN3STAT: $");
      Serial.print((uint32_t)&(CLKGEN->CLOCKEN3STAT), HEX);
      Serial.print(": $");
      Serial.println((uint32_t)CLKGEN->CLOCKEN3STAT, HEX);
    }
    
    
    // The last step is to disable any GPIOs that might be the source of 
    // stray or leaking current going in or out of the Apollo3.
    void disableGpios()
    {
      #if 1
        // Use a big hammer: disable every single GPIO
        for (int i = 0; i <= 49; i++) {
          am_hal_gpio_pinconfig(i, g_AM_HAL_GPIO_DISABLE);
        }
      #else
        // Disable the GPIOs that we know are in use:
        
        // Disabling the debugger GPIOs saves about 1.2 uA total:
        am_hal_gpio_pinconfig(20 /* SWDCLK */, g_AM_HAL_GPIO_DISABLE);
        am_hal_gpio_pinconfig(21 /* SWDIO */,  g_AM_HAL_GPIO_DISABLE);
    
        // These two GPIOs are critical: the TX/RX connections between the Artemis module and the CH340S on the Blackboard
        // are prone to backfeeding each other. To stop this from happening, we must reconfigure those pins as GPIOs 
        // and then disable them completely:
        am_hal_gpio_pinconfig(48 /* TXO-0 */, g_AM_HAL_GPIO_DISABLE);
        am_hal_gpio_pinconfig(49 /* RXI-0 */, g_AM_HAL_GPIO_DISABLE);
      #endif
    }
    
    void setup()
    {
      // We will use the Serial object while we get set up, then shut it down during our deep sleep test
      Serial.begin(115200);
      delay(1000);
      Serial.print("DeepSleep Testing V1.3 ");
      Serial.println(__DATE__ " " __TIME__);
    
      // Shut down the Arduino Serial1 which uses the Apollo3 peripheral UART1
      Serial1.end();
    
      // The Arduino environment has almost certainly already done this, but it is important and
      // calling it again is harmless: Configure the Apollo3 power control block for low power.
      am_hal_pwrctrl_low_power_init();
    
      disablePeripherals();
      configureMemories();
      configureStimer();
      
      // Before we shut down, dump the state of some of the CPU registers involved with power control
      // to verify the results of the memory configuration requests.
      dumpRegs();
      
      // Print one last message, allow the serial output to drain, shut down the Arduino Serial object,
      // and finally, disable power to the hardware UART0 peripheral
      Serial.println("\nEntering sleep mode");
      delay(100);
      Serial.end();
      am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_UART0);
      
      // Last thing: disable GPIOs so that they don't leak current or get backfed by external circuitry
      // This step is critical to avoid interactions with the CH340 and its TX/RX LEDs from 
      // interacting with the Apollo3 processor.
      disableGpios();
    }
    
    
    void loop()
    {
    #if 1
      // On my Artemis Blackboard, this results in a power consumption of 2.5 uA
      am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
    #else
      // On my Artemis Blackboard, this results in a power consumption of 68.6 uA
      am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_NORMAL);
    #endif
    
      // We never intend to get here, so blink the LED forever if we do as a panic indication.
      // It turns out that the Arduino environment is using the STIMER, probably as its basic timer for 
      // managing millis() and things like that.  When this timer rolls over, it appears to cause an interrupt that will 
      // take the system out of sleep again.  Given that we have reconfigured the STIMER to clock at 32 KHz instead
      // of 48 MHz (for the purposes of power testing), we won't wake up for a long time.  Also, note that because
      // we have messed with the Arduino environment before going to sleep, we can't expect the Arduino environment to function 
      // properly when we wake up again.  To really make use of deepsleep modes, a system would need to reconfigure
      // itself after waking up.
      pinMode(LED_BUILTIN, OUTPUT);
      panic();
    }
    

    I hope the community finds this useful!

    I guess I should be extremely clear about 1 more thing: the demo only puts the system into deep sleep. A real system needs to be able to wake from deep sleep effectively, too. For example, the demo program changes how the STIMER is clocked before going to sleep. Instead of being clocked at 48 MHz, the timer will be clocked at 32786 Hz during deep sleep. The Arduino system will not be aware of that, so if you were to make a millis() call after waking from deep sleep, it would appear as though a lot less time had gone past. Of course, you could calculate how many ticks elapsed at the slower clock rate during sleep,use that info to update the arduino counters used by millis(), and reconfigure the STIMER to clock at 48 MHz again. The users of millis() would probably be none the wiser. Going to these lengths is pretty normal. They just represent a potential incremental cost in terms of time and battery energy consumed in order to enter and exit a deep sleep which gets factored into a system-wide cost/benefit tradeoff analysis. For example, it may be that if your desired rate of periodically waking up is fast enough (meaning that the actual sleep period is short enough), it might not be worth the energy cost involved in putting the system to its deepest possible sleep and waking it up again for the short amount of time the system would be able to spend in deep sleep.

    Engineering is all about managing tradeoffs, and a system deep sleep mode represents one of the more challenging sets of choices.

    Oops, make that being clear about 2 more things…

    To get down to 2.5 uA, you have to unpower the microphone. An easy way to do that is detailed in this post: viewtopic.php?p=205882#p205882

    Fantastic work Robin. Will be trying your examples soon.

    Regards,

    Kevin

    Hi Robin - Thank you very much for this work. It helped me find my issue (not turning off HFRC).

    I’ve pushed two examples to the core: https://github.com/sparkfun/Arduino_Apollo3/pull/77

    I’m measuring ~2.4uA on an Edge 2. I’m not sure how much lower we can go with the datasheet spec’ing 1-2uA at 3.3V.

    Please have a look and please comment if you have any additional thoughts.

    Cheers,

    -Nathan

    The best I have been able to do on my Blackboard is 1.63 uA at 3.3V. That is based on:

  • - microphone power trace cut
  • - All peripherals off
  • - All gpios disabled
  • - CTimer OFF
  • - STIMER configured for 1KHz RX clock
  • - All FLASH powered off in deep sleep
  • - Cache powered OFF in deep sleep
  • - Only 8K of the SRAM remains powered in deep sleep
  • See this post viewtopic.php?p=208415#p208415 to see a table of power consumption data.

    It’s all moot though because none of the A1 processors can reliably reach these current levels. As mentioned in this post viewtopic.php?p=208325#p208325, the A1 processors have a bug that comes into play when they are in deep sleep. According the Ambiq, the SIMOBUCK voltage regulator on an A1 can lock up and fail, eventually causing the ASIC to suffer a supply voltage brownout reset. The published workaround from Ambiq is to never let the A1 chip go into deep sleep. That is accomplished by leaving the PDM unit turned on because it takes the least current of any peripheral on the chip. You can see that inside the HAL routine am_hal_pwrctrl_low_power_init() where the code explicitly tests if the processor is an A1 with the SIMOBUCK turned on and if so, turns on the PDM to avoid triggering the bug.

    On my Blackboard with its A1 silicon, it means that the deep sleep current I have measured is more like 38.0 uA, as per my current consumption chart mentioned earlier in this post. The only way to get a reliable 1.63 uA deep sleep is via B0 silicon. I have asked if there is a plan to release boards with B0 silicon on them in this post viewtopic.php?p=208325#p208325, but have not heard back from Sparkfun yet. Maybe you can ask around for us!!

    Hi Nathan,

    Thanks for providing the examples. I recognize much of the code from the Ambiq SDK deepsleep_wake.c example, but for those of us still familiarizing ourselves with the HAL, it can be really beneficial to see the code rewritten for a specific application. I find that using the HAL functions to place the Artemis in deep sleep is very reminiscent of using the <avr/power.h> and <avr/sleep.h> functions to reduce the power consumption of an ATmega328P.

    A question I have is: what happens when the Artemis module wakes up from deep sleep? Given that the ADC, SRAM, and Flash1 are manually disabled/turned off, when the system wakes back up from deep sleep, will it automatically re-enable/turn on all of the disabled on-board peripherals?

    Cheers,

    Adam

    The only things that have automatic power control when the module wakes from deepsleep are memory-related: Flash, SRAM and Cache. The flash will remember what it contained (obviously), but any flash that was unpowered during deepsleep takes some extra latency to get started up after the wake event. Any cache or SRAM that was configured to be unpowered during deepsleep will lose their contents as the result of a deepsleep.

    In contrast, the individual peripherals must be powered off manually before a deepsleep, and must be manually powered up again after a deepsleep. Powering a peripheral off has the side effect of destroying its register configuration. After exiting deepsleep, the system software can’t just repower the peripheral, but it will need to completely reinitialize the peripheral’s internal registers and IO pin configurations before it can be used again.

    If I remember right, one of the Ambiq examples showed a method of dealing with a peripheral across a deep sleep power down. Before deepsleeping, they copied the state of the existing UART configuration into a RAM structure, disabled the GPIO pins associated with the UART to avoid leaking power in or out of the IO pads, powered down the UART, then finally went into deepsleep. After waking from deepsleep, they powered the UART peripheral back up and used the saved peripheral configuration to reinitialize the UART peripheral. That is the basic process for any peripheral that you want powered down during a deep sleep.

    Great post, I’m currently starting some deep sleep tests on an Redboard Artemis Nano, so this (and the other contributions) will surely be of great help.

    David.

    Hi,

    Just a quick update that I was also able to achieve a quiescent draw of ~2.64 uA with an Edge 2 (B0 silicon confirmed) using the LowPower_WithWake example that has now been pushed to the SparkFun Apollo3 board definitions.

    However, I feel it’s important to restate what Robin mentioned earlier about his code:

    robin_hodgson:
    I guess I should be extremely clear about 1 more thing: the demo only puts the system into deep sleep. A real system needs to be able to wake from deep sleep effectively, too.

    While neither Robin’s code above, nor the SparkFun Advanced examples bring the system back out of deep sleep, they are really good starting points to learn from. I imagine that a library will eventually be written to simplify the sleep functionality, but in the meantime it would still be great to see an example of how this can be effectively accomplished.

    Cheers,

    Adam

    I’ve been working on that for a while now. From a silicon perspective, it pretty trivial. Follow the basic steps from the original Arduino example (above), and you are good to go to sleep. When an event or interrupt occurs that causes the processor to start running again, the call to am_hal_sysctrl_sleep() will return, the next line of code executes, and off you go. There is nothing you have to do when the system wakes up, unless you want to undo some things that you did before deep sleeping. But those things to be undone are not silicon requirements, they represent your own specific software system requirements. For example, maybe your system uses the ADC to sample some sensor while the system is awake. Even without considering deep sleep, your software system would have required some form of an adc_init() routine that makes sure the ADC peripheral is powered and properly configured before use. In order to minimize system power, you would power down the ADC peripheral before calling am_hal_sysctrl_sleep(). After waking, you would just call your adc init() routine and you are good to go. It really is that simple. Something like this:

    ...
    
    // Power down the ADC
    am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_ADC);
    
    // Go to sleep
    am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
    
    // Power up and reconfigure the ADC before use!
    adc_init();
    

    The complexities arise due to the requirements of your specific system. For example, it you want to turn off RAM during deepsleep, that’s easy. But your software system needs to be written with the understanding that any time it executes am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP), any unpowered SRAM gets erased. That could end up being a very complicated problem. Another example of a complexity is when you add an RTOS to the mix. The RTOS will sleeping whenever it has no tasks that are ready to run, as it should. But when the RTOS puts the processor to sleep, it only knows that it has nothing immediate to do. The issue is that the RTOS has no innate knowledge of your system’s overall power states. So the RTOS can’t tell the difference between a situation where the only running task asks for a 1 millisecond sleep delay and the situation where your entire system wants to go into the deepest possible sleep for the next hour. That makes it a much trickier problem to figure out when to shut peripherals down, and how to explain all that to the RTOS. I’m still working on that one!

    My test case to try and work through these issues uses a FreeRTOS program to drive a current sensor board that measures the AC current being drawn by whatever is plugged into a specific power outlet. While no AC current is being drawn, the system remains in deep sleep. The current sensor hardware is designed to present an analog voltage trigger to the Apollo3 Voltage Comparator unit. The Apollo3 comparator is set up so that whenever the current sensor board sees a current draw of about 1 Amp, it generates an interrupt that wakes the processor from deep sleep. This causes an RTOS task to run that powers up an I2C interface so it can start logging. The task logs 16-bit current readings at 10Hz. During the entire logging process, the RTOS itself will be deep sleeping periodically as the logging software starts ADC conversions and waits for results. But these deep sleeps are different in that I don’t want the system turning off my I2C interface for example. Logging continues until the logging task observes the current draw drop below 0.1A for more than a second. At that point, the task shuts down the I2C interface, primes the Voltage Comparator unit to look for the next 1A current event, and arranges for the lowest-possible deep power to be entered the next time the RTOS goes into deep sleep. It’s a pretty simple system, but it has served to expose a lot of the issues that I mentioned. FWIW, the system works, but I think it is telling me that I don’t have a general solution yet on how to integrate things like this with the RTOS.

    Hi,

    I am using your exact code on a new Artemis Thing Plus (new, without any hardware alterations) and in deep sleep

    I measure 1,8 mA (which is considerbly higher than then then expected value of < 250 uA).

    I use a battery-pack 3,7 V with the suplied connector, not connecting the usb).

    The red power led is on (how can I turn that off?).

    What am I doing wrong. Help is appreciated,

    Regards, Jur

    Hi Jur,

    Unfortunately, the SparkFun Artemis Thing Plus does not have a jumper that you can cut to disable the power LED. I experienced the same problem with the Artemis Nano (viewtopic.php?f=172&t=50789#p207655). If you want to reduce the quiescent draw, you will either need to delicately cut the trace to the LED or remove it with a hot air soldering rework station. Given how close all of the LEDs are, I’d suggest cutting the trace.

    Cheers,

    Adam

    I am experimenting with deep sleep on the Artemis Nano, and am learning quite a bit. Thank you to everyone who has shared all of the valuable info above. I have two related questions so far…

    1. When running the associated code examples in the core, “lowPower” and “lowPower_WithWake”, I don’t get any successful Serial output, just a few “???” in the terminal window. My terminal and serial connection are definitely setup correctly, correct baud rate, etc. For example with the code Nathan provided to check the silicon revision on the Nano boards, I get successful output confirming B0 silicon on all of my boards. But with these two lowPower sketches I get nothing. Is anyone else seeing this, or is something peculiar about my setup? I’ve tested it on three different rev B0 Nano boards with the same result. I was hoping to modify these sketches with additional serial output to learn more about what’s going on within them.

    2. On one of my boards I have connected a switch across the PSWC pins. However it seems that when using this switch to disable the power on the board, the power draw remains in the ~0.3mA range. Clearly in a ‘low power’ application to have the board drawing more power in the “power off” state than a deep sleep state doesn’t make much sense. Is this expected behavior, or am I doing something incorrectly? If normal behavior, it seems that if I want to be able to truly stop the board from drawing power, my switch needs to go between the 3.7v LiPo and the board instead (which is what I likely would have done initially had I not seen the option of the PSWC pins as an attractive alternative to cutting and soldering one of battery leads into my switch…making it infinitely harder to swap out battery packs using the onboard JST connector).

    Thanks in advance.

    Nevermind on question #1, I realized the RX/TX pins had been disabled in setup. Commenting those two lines out temporarily restores the serial output.

    After studying this sketch further though, I now realize what others meant when they said the device is never truly woken from sleep in the ‘withWake’ example. It essentially stays in sleep mode and merely performs a periodic task via RTC interrupt. Has anyone managed to produce an example of a more conventional sketch that while performing a set of tasks reaches a condition that results in deep sleep, and then by way of interrupt or timer, wakes from deep sleep and resumes those original tasks? As a novice programmer, that would be an invaluable example to learn from. While I mostly understand what’s going on the in examples provided thus far, I am having a hard time making the leap between these examples and the type of sleep/wake routine that could function within a more conventional sketch. Even a rough outline of how a sketch like that would function would be a great start.

    Any help or guidance is very much appreciated! TIA.

    The answer is both simple and complicated. The simple part is that putting the processor into deep sleep is as simple as calling the appropriate HAL routine:

    am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
    

    Waking up is just as simple. Just remember that you can’t put the processor to sleep without defining some sort of mechanism to wake it again. The mechanism needs to generate an interrupt, like a timer timing out or some external event creating a GPIO interrupt, or an incoming serial character, or something. The interrupt that wakes the process from deep sleep will cause the interrupt handler to get executed, after which execution will return from am_hal_sysctrl_sleep() and start running whatever code followed the sleep call. So the wakeup is easy too. To answer your question about setting up the interrupt to wake from deep sleep, you literally just have to put your code following the call the am_hal_sysctrl_sleep() that put the processor to sleep. It’s that easy. In an Arduino-style world, you could do something like this:

    void loop()
    {
      // Do normal loop stuff performing all required tasks
    
      if (nothing-left-to-do) {
        // We have decided that there is nothing to do, so we can sleep.
        // First, make sure that some interrupt exists that will wake us.
        setUpWakeInterrupts();
    
        am_hal_sysctrl_sleep(DEEP_SLEEP);
    
        // When the interrupt completes, we get here.
        // As written, we just let this iteration of loop() complete.
        // The Arduino driver code will reinvoke loop() just like normal.
      }
    }
    

    The trickier part is that to save maximum power, it is required to turn off various unused bits of the processor before going to deep sleep. That means that you have to turn them on again after waking. One way to do that is to create some sort of centralized software sleep manager that knows what parts of your system to shut down before sleeping and what parts to restart after waking up. I didn’t like that approach, because it seemed like a hypothetical sleep manager would need to have intimate knowledge of all kinds of areas of the system, including how to interpret their operating state. It gets complicated fast, and hard to maintain.

    What worked for me was a much simpler approach. My system only powers its peripherals when it is using them, otherwise they are powered down. For example, I will turn on an I2C peripheral to do a transfer, then shut it down again after the transfer was complete. The power on/off stuff is built into my transfer mechanism so that it is automatic from a caller’s point of view. When I call my I2C transfer routine, it makes sure that the IOM gets powered up, the transfer gets performed, and the IOM gets shut down again. The caller is not even aware that it is happening. The important part is that my system is free to invoke a call to am_hal_sysctrl_sleep() whenever it feels like, and there is nothing to do before shutting down, or after waking up. All peripherals that can be powered off will be powered off when the deep sleep happens. Peripherals that are in use will remain powered across the deep sleep, but that’s perfect since they are being used. If they weren’t in use, they would have been powered off. I can still have parts of the system that may need to manage their power states across deep sleep requests, but those specific parts of the system know what they need to do, and they do it without requiring a complex centralized sleep manager.

    If that sort of approach would work for you, I can attest that it is effective. I am using FreeRTOS and have things set up so that FreeRTOS calls deep sleep when it has nothing to do. Since my unneeded peripherals are already shut down, the deep sleep represents the lowest possible power for my system. But it means that FreeRTOS is aggressively deep sleeping whenever there are no tasks ready to run. System-wise, a very simple approach.

    Thanks a lot for that Robin, that was the missing info I needed to get me to the next step. I now have a basic but functioning sleep/wake routine integrated into my sketch. After an activity timeout period, the unit goes into deep sleep, and I have 3 hardware interrupts (2 buttons and a vibration sensor) that can all successfully wake the unit from sleep. What I am experimenting with now is which peripherals I can disable permanently (in setup code) without affecting my sketch, versus what I’m going to need to disable just before sleep and re-enable just after waking.

    What I’m stuck on at the moment is how to re-enable the STIMER to work off the default 48 MHZ HFRC clock after waking. Because I am not waking from sleep via timer/clock (only from GPIO interrupts), I’ve elected to disable the clock before sleep using: ```
    am_hal_stimer_config(AM_HAL_STIMER_NO_CLK);

    
    However I'm having a hard time finding the correct syntax to re-enable the STIMER to its default state after waking (and my sketch certainly won't resume functioning without a running system timer). I took a few shots in the dark, but no luck so far. Was thinking it might be along the lines of...
    
    

    am_hal_stimer_config(AM_HAL_STIMER_HFRC_48MHZ);

    
    Can anyone lend a hand? I'm sure there is a reference source for this, but I clearly don't know where to look. TIA.

    I’m looking at page 663 of the Apollo3 data sheet rev DS-A3-0p10p0. The CLK_SEL field in that register has no 48 MHz setting. The fastest it goes is HFRC divide-by-16, or 3 MHz. I would guess that the default Arduino setting is divide-by-16, but its just a guess. Why not have your program read the setting in that field before going to sleep, save the value, select “no clock” during sleep, and just restore the original setting after wakeup.

    Don’t forget that with the timers turned off, you can’t do time calculations based on millis() that cross a sleep boundary. If that matters…

    Ah thanks, 3MHZ did the trick. Was going off the comments denoting 48Mhz, wasn’t aware of the divide-by-16. Good thought to read/save it and restore.

    Yeah thanks for the reminder, fortunately I don’t need to track time across the sleep cycle. I’m resetting all my millis() timing markers upon waking.

    So, i have so far identified the following items from the lowerPower examples that I can include in my sketch’s setup() code without affecting any of my functionality. I am going to continue to experiment to see what else I can disable/shutdown for even more power savings. I’m curious though if some of these items might need to go into my sleep routine so that they are run each time I’m initiating sleep, instead of just once at the beginning of the sketch. For example if the FLASH, Cache, or SRAM reset to their defaults upon coming out of sleep, then I may need to disable them again each time before going back into sleep to retain the power savings of them being disabled?

        //Turn off ADC
        power_adc_disable();
    
        // Stop the XTAL.
        am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_STOP, 0);
    
        // Disable the RTC.
        am_hal_rtc_osc_disable();
    
        // Set the clock frequency.
        am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
    
        // Set the default cache configuration
        am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
        am_hal_cachectrl_enable();
    
        // Initialize for low power in the power control block
        am_hal_pwrctrl_low_power_init();
    
        // Disabling the debugger GPIOs saves about 1.2 uA total:
        am_hal_gpio_pinconfig(20 /* SWDCLK */, g_AM_HAL_GPIO_DISABLE);
        am_hal_gpio_pinconfig(21 /* SWDIO */, g_AM_HAL_GPIO_DISABLE);
    
        
        // These two GPIOs are critical: the TX/RX connections between the Artemis module and the CH340S on the Blackboard
        // are prone to backfeeding each other. To stop this from happening, we must reconfigure those pins as GPIOs
        // and then disable them completely
        am_hal_gpio_pinconfig(48 /* TXO-0 */, g_AM_HAL_GPIO_DISABLE);
        am_hal_gpio_pinconfig(49 /* RXI-0 */, g_AM_HAL_GPIO_DISABLE);
    
        //Turn OFF Flash1
        if (am_hal_pwrctrl_memory_enable(AM_HAL_PWRCTRL_MEM_FLASH_512K))
        {
            while (1)
                ;
        }
    
        // Power down SRAM
        PWRCTRL->MEMPWDINSLEEP_b.SRAMPWDSLP = PWRCTRL_MEMPWDINSLEEP_SRAMPWDSLP_ALLBUTLOWER32K;