Demo: Configuring An Artemis for Deep Sleep

I don’t think there is anything extra to do for the RAM on wakeup.

If you change your power down statement to retain all RAM and leave everything else exactly the same, do things work? If so, then your system is probably using RAM outside of the first 64K. Can you check your linker output file to make sure that every single bit of RAM your system is using is within the first 64K? I just checked the system I am working on and I see that by default, my Segger build creates a small stack right at the very top of RAM. That would get blown away if I ever powered down that uppermost block of RAM.

Also, have you tried setting a debugger breakpoint on the instruction after your deep sleep instruction and single-stepping through your wakeup code after the processor comes to life again? It might be that your peripheral wakeup code is responsible for things going astray, and the single-stepping would show you right where it happened.

Hi Robin,

Enabling all the SRAM did the trick! The code must not be retaining the SRAM that’s actually in use.

am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_ALL);
am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_SRAM_384K);

Considering that the current draw of powering up 32K vs 384K SRAM is ~0.6 uA, is there any reason not to retain all of it?

Also, I’m afraid I’m not familiar with linker output files. Can you provide any additional information on where to find them? I’m using the Arduino IDE but have set my build path in preferences.txt and can see the files used to compile the code.

Cheers,

Adam

I think that the disabling of RAM during sleep is really only of value to people who are sweating every single hour of battery life.

My opinion: Arduino is like popping the hood on a modern car and seeing a bunch of plastic covers. It engine bay looks nice and simple, but all those covers just make it harder to get at what is underneath.

So lets’s pop the hood and start removing engine covers:

  • - Go to Arduino/File/Preferences and check both ‘show verbose output’ boxes
  • - build your sketch
  • - look at the second last line in all that verbose output to find out where your build actually got done. Here is an example from my build: "C:\\Users\\robin\\AppData\\Local\\Arduino15\\packages\\SparkFun\\tools\\arm-none-eabi-gcc\\8-2018-q4-major/bin/arm-none-eabi-size" -A "C:\\Users\\robin\\AppData\\Local\\Temp\\arduino_build_183998/ReflowController.ino.axf"
  • - cd to the temp directory that Arduino is using: "cd C:\\Users\\robin\\AppData\\Local\\Temp", fixing up the slashes for whatever you are using to get to that directory
  • - you should see a map file there. Mine was named 'ReflowController.ino.map'. Open it with your favorite editor.
  • It’s a big, big file. Use your editor to find where all the symbols are assigned to RAM. Look for the highest RAM address you can find. Remember that RAM addresses always start with 0x100xxxxx. Here is my map file for my project showing the largest RAM address:

    .heap           0x10021214        0x0 load address 0x0002c594
                    0x10021214                __end__ = .
                    0x10021214                PROVIDE (end = .)
     *(.heap*)
                    0x10021214                __HeapLimit = .
    

    It looks like the HeapLimit is the last variable in RAM at 0x10021214. I can’t actually tell you if that is where the Heap starts or ends (but I bet that it is the first address of the Heap). That is another complication. If your code uses the heap, then how much is it allocating? I am betting that any allocations my sketch makes will be using space after 0x10021214 in my example build. But again, who knows how Arduino does it. I find it better to use a mechanism where I define where the heap is and how big it is so that I know its limits. So maybe with Arduino, it would be easiest to just leave all the RAM turned on. If you decide to use a different development environment in the future, well, they are more complex, but they sure give you control over everything. Plus, nothing beats a hardware debugger. I use the Segger Embedded Studio. Here is the output from its map file:

    ***********************************************************************************************
    ***                                                                                         ***
    ***                                      LINK SUMMARY                                       ***
    ***                                                                                         ***
    ***********************************************************************************************
    
    Memory breakdown:
    
       67 690 bytes read-only  code
        6 046 bytes read-only  data
      150 701 bytes read-write data
    
    Region summary:
    
      Name        Range                    Size                Used              Unused      Alignment Loss
      ----------  -----------------  ----------  ------------------  ------------------  ------------------
      FLASH       00010000-0007ffff     458 752      73 740  16.07%     385 000  83.92%          12   0.00%
      RAM         10000000-1005ffff     393 216     150 701  38.33%     242 515  61.67%           0   0.00%
    

    That’s a nice summary. If you want details, it has them all too. Here is the overview of the RAM space:

      0x10000000  __RAM1_segment_start__                        ----  Gb  [ Linker created ]
      0x10000000  __RAM1_segment_used_start__
                                                                ----  Gb  [ Linker created ]
      0x10000000  __RAM_segment_start__                         ----  Gb  [ Linker created ]
      0x10000000  __RAM_segment_used_start__
                                                                ----  Gb  [ Linker created ]
      0x100048B0  __heap_start__                                ----  Gb  [ Linker created ]
      0x100248B0  __heap_end__                                  ----  Gb  [ Linker created ]
      0x10060000  __RAM1_segment_end__                          ----  Gb  [ Linker created ]
      0x10060000  __RAM1_segment_used_end__                     ----  Gb  [ Linker created ]
      0x10060000  __RAM_segment_end__                           ----  Gb  [ Linker created ]
      0x10060000  __RAM_segment_used_end__                      ----  Gb  [ Linker created ]
      0x10060000  __stack_end__                                 ----  Gb  [ Linker created ]
    

    The beauty of this little section is that all of those symbols are available to my program. So if it wanted, it could take a look at heap_end and then calculate what pages of RAM need to have their power retained during deep sleep.

    Arduino is a great way to get started fast, but a sub-optimal way to develop a complex application. That’s probably enough opinions for one day though. I think there are lots of development systems with hardware debugging now, but I still use Segger. I like it!

    Thanks, Robin,

    This super helpful!

    I had specified a path to output the build files, so it was quite simple to locate the map file. So while we don’t know if the heap starts or stops at 0x1001857c, we do know that it’s at least 99708 bytes into the total SRAM memory, which explains why retaining the lowest 64K wouldn’t work.

    .heap           0x000000001001857c        0x0 load address 0x00000000000137e4
                    0x000000001001857c                __end__ = .
                    0x000000001001857c                PROVIDE (end = .)
     *(.heap*)
                    0x000000001001857c                __HeapLimit = .
    

    And if I’m interpreting the output of your Segger map file correctly, your heap starts at the 0x10000000 and ends at 0x100248B0, or 149680 bytes, meaning that you could safely retain 160K of SRAM?

    am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_SRAM_160K);
    

    While I’m quite enthusiastic about low-power operation, I think for my applications I could happily keep all of the SRAM powered. I’m currently using the MicroMod Artemis Processor, which due to a design flaw in its op-amp circuit, has a quiescent draw of 150-200 uA. I don’t think an extra microamp is going to make a big difference!

    Appreciate all your help and I’ll definitely look into Segger Embedded Studio. Can it easily be used with the SparkFun board definitions and libraries?

    Cheers,

    Adam

    I had to go read the Artemis MicroMod schematic to see what you meant by op amp design flaw. The bottom line is that the Apollo3 ADC reads between 0 and 2V. It was probably an attempt for MicroMod compatibility on Sparkfun’s part that they chose to rescale the ADC inputs with some op-amps so that the ADC range would appear to be 0 to 3.3V. The quad opamp Sparkfun chose takes 120 uA, which is OK, but not great. On a different project, I needed a quad opamp and used a MCP6424 which is 14 uA total. My speed requirements were not that high though. Maybe the Sparkfun opamp runs faster. Anyway, I agree that it is not worth worrying about saving 0.6 uA by powering down SRAM with that opamp in the circuit.

    I treat the Segger IDE more like a bare metal environment. I don’t care about board definitions because I make my own. I steer clear of the confusingly complex Arduino pin numbering indirection scheme and strictly use Apollo3 pad numbering. I have a fake Arduino compatibility layer to make it slightly easier to import Arduino libraries. By that, I mean that I have definitions for things like millis() and delay() and things that are likely for an Arduino library to use. My latest project includes the Arduino Adafruit GFX library for driving SPI LCDs that has been attached to fprintf(). I finally have a nice, easy to use LCD driver. It lets me do this:

    Adafruit_ST7735* lcd; 
    …
    fprintf(lcd, “Hello, world!”);
    

    which does just what you would think. So the whole setup is more complex and took some time, but in the end, it is exactly what I want. And debuggable!

    Morning Robin,

    Yes, the goal was to make the Artemis 0-3.3V compatible, but I believe the placement of the op-amp resulted in a limited range of 0-2.9 V and an increased current draw since it’s always being powered. Nate did a good job of describing the issue here: https://github.com/sparkfun/MicroMod_Ar … -742868563. My hope is that the processor board will see a hardware revision, but I suspect there’s a large stock to go through first and it’ll be some time.

    Thanks also for the additional info on the Segger IDE. After I started researching it, I suspect that was the case. Once I have some free time I think it’ll be a nice rabbit hole to dive down.

    It’s a coincidence that you mentioned the Adafruit GFX library and LCDs. I’ve been trying to troubleshoot why my SparkFun OLEDs (https://www.sparkfun.com/products/17153) are spitting out garbage pixels after a time with the Artemis (v1.x & v2.x). While these OLEDs communicate over I2C, is there any general reason why reason the OLEDs wouldn’t like the Artemis?

    Cheers,

    Adam

    Cheers,

    Adam

    The Artemis I2C interface should work just fine. I have used it a lot with zero problems. I use SPI for displays because it is so much faster. It doesn’t matter so much with a monochrome 128x32 display, but for a 320x240 color LCD, there are 153K bytes to move when redrawing the entire screen. I2C won’t cut it, but SPI at 16 MHz will do the trick.

    If you ever want to get started with Segger, check this thread: viewtopic.php?p=222142#p222142. There is a version 1.8 document attached that explains how to get Segger version 4 to work with Artemis. Note that Segger version 5 is NOT compatible with the instructions in the doc. However, it is not hard to migrate from V4 to V5. So if you want to use the document, download the latest V4 version of Segger Embedded Studio from Segger, and have at it. Once it all works, you can upgrade to V5 at your leisure. Segger documents the migration operation themselves.

    Tried running Robin’s code as well as the sample ones on an Artemis Dev Kit. I’ve desoldered all of the leds as well as the microphone, but the lowest I can get it to sleep at is 12.1mA. I am using an Otii Arc to provide power and measure the draw by feeding 3.7v into the 3.3v and GND pins on the board as there is no MEAS on the Dev Kit.

    Is there anything I might have missed?

    TIA. Kevin

    Is there a typo: was your measurement 12.1 mA or uA?

    I don’t see how you are isolating the power measurement to just the Artemis. From your description, it sounds like the USB interface chip must also be powered, which is probably why the current draw is so high. If you want to measure just the Artemis, I could cut the 3.3V trace in the backside location shown by the green ‘X’ in the attached picture, then feed in 3.3V power through wires plugged into the QWIIC connector. The power going into the QWIIC connector will only be powering the Artemis, plus the 1.8V linear regulator. You could remove the 1.8V regulator if needed because I think that the camera needs it, but nothing else.

    Sorry maybe I missed the point of the measuring. I’m trying to get the dev kit in total measured, not just the Artemis. The end goal is to use it with the Himax hm01b0 camera, but I was trying to get a benchmark on the board first. The 12.1mA was not a typo. The board power draw started at +25mA in sleep mode before I removed the microphone and leds. I do not have the USB cable plugged in while measuring. Just running the power and ground from the meter to the board as pictured.

    So maybe I’m off in thinking the Dev Kit board in total can get any lower in draw?

    OK, now I understand.

    I think you are right, and that might be as low as it will go. In sleep mode, the Artemis will be responsible for basically no power consumption at all. That means that most of the power you are measuring must be going into the the USB interface chip and potentially the camera (it looks like you have a camera present in the photo). I have the 3.3V traces highlighted in the photo I attached, and you can see that the USB chip gets 3.3V from where you are supplying it, even with no USB cable plugged in.

    I’m trying to get a similar system going where the Artemis can be put to sleep by either waiting long enough, or pressing a button. That same button should also be able to wake it up. Problem is that as soon as the code reaches the attachInterrupt line in setup, it immediately jumps into the interrupt and then proceeds to hardfault (blinking S.O.S. message). I double checked with an multimeter and oscilloscope and didn’t see any signs of oscillations that could trigger a falling edge signal. Additionally, I put in some debug lines to show the button status when the interrupt triggers and held the button down before resetting the Artemis. Sure enough, the signal reads as high when left alone and low when the button is held down, but it still enters the interrupt regardless. Any idea what might be going on?

    E.HP.S:
    Problem is that as soon as the code reaches the attachInterrupt line in setup, it immediately jumps into the interrupt and then proceeds to hardfault (blinking S.O.S. message).

    The hardfault seems to have just been a mistake I made with the debug code, trying to print something to the serial monitor when the UART wasn’t set up (sleep mode). Either way, it seems like the interrupt is being triggered constantly without any input from the button. It’s pulled up both internally and externally, and has a filtering capacitor for debounce, but I don’t even have the chance for debounce to even factor in it seems

    Have a look at https://github.com/paulvha/apollo3/tree … PowerCntrl. Make sure to read the deepsleep.odt (a document you can open with a large number of word processors (Word, Openoffice etc).

    Also look at the RTC example6

    RTC example 6 works great for the RTC timer, but I also want it to be button operated, which is especially where I’m having trouble. It also doesn’t explain why the interrupt would trigger as soon as it’s attached to the pin, regardless of button status.

    How did you connect the button ? (small schematic, is there a pull-up / pull-down)

    Can you share the sketch you use so I can have a look?

    I’ll try to get a picture. In the meantime, it’s connected to pin 4 on the Artemis Nano (D18 in the .BRD file), and has a 10k pullup resistor, as well as a 1k resistor between the pullup and the button pin, and a 0.1uF capacitor between the pull-up and ground.

    paulvha:
    How did you connect the button ? (small schematic, is there a pull-up / pull-down)

    Can you share the sketch you use so I can have a look?

    I actually forgot I posted most of what you were asking for on a separate topic! You can find it below.

    viewtopic.php?f=169&t=60400

    Let me know if I’m missing anything else you might need

    I see you define SLEEP_INTERRUPT as 4, but you disable that GPIO (but exclude 18?)

    Do you have a pull-up resistor on line between the switch and Nano ? Else the line is floating.