Example6_LowPower_Alarm compile error with Sparkfun Apollo3 2.0.5

Hello -

I am using…

Win10

Arduino 1.8.12

Sparkfun Apollo3 Core 2.0.5

I am starting a project that will use Deep Sleep (or as low as it will go) and so am running thru the Apollo/RTC examples.

I started with the Artemis ATP and received an error, then tried the Artemis Dev Kit and finally the Artemis RedBoard Nano - each with the same error.

Is this an issue with 2.0.5 that can be fixed?

The error below is from the Nano –

c:/users/frede/appdata/local/arduino15/packages/sparkfun/tools/arm-none-eabi-gcc/8-2018-q4-major/bin/…/lib/gcc/arm-none-eabi/8.2.1/…/…/…/…/arm-none-eabi/bin/ld.exe: core\core.a(main.cpp.o): in function `main’:

C:\Users\frede\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.0.5\cores\arduino\mbed-bridge/main.cpp:12: undefined reference to `setup’

c:/users/frede/appdata/local/arduino15/packages/sparkfun/tools/arm-none-eabi-gcc/8-2018-q4-major/bin/…/lib/gcc/arm-none-eabi/8.2.1/…/…/…/…/arm-none-eabi/bin/ld.exe: C:\Users\frede\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.0.5\cores\arduino\mbed-bridge/main.cpp:15: undefined reference to `loop’

collect2.exe: error: ld returned 1 exit status

exit status 1

Error compiling for board RedBoard Artemis Nano.

Thanks

what does the sketch look like ? It seems you are missing BOTH setup() and loop() functions.

The Sketch I am using is from File/Examples/(Examples for Redboard Artemis ATP) RTC/Example6_LowPower_Alarm. I have made no changes to the Sketch. The Sketch has both Setup () and Loop () functions.

Just checked. No problem on 1.2.1, but in 2.0.5 it gives the same error message.

The issue is related to a check #ifdef I_WANT_TO_BRICK_MY_BOARD. Not sure why that check is there, but it is expecting somewhere a

#define I_WANT_TO_BRICK_MY_BOARD 1

You can try to add that in your sketch before the #ifdef line, but I don’t know whether this sketch will brick-your-board. Never worked with this sketch before.

At least you know the issue of failing linking

paulvha:
Just checked. No problem on 1.2.1, but in 2.0.5 it gives the same error message.

The issue is related to a check #ifdef I_WANT_TO_BRICK_MY_BOARD. Not sure why that check is there, but it is expecting somewhere a

#define I_WANT_TO_BRICK_MY_BOARD 1

You can try to add that in your sketch before the #ifdef line, but I don’t know whether this sketch will brick-your-board. Never worked with this sketch before.

At least you know the issue of failing linking

Hi folks,

I wrote the RTC deep sleep example, which was based on Nathan’s low power examples in v1.x of the Apollo3 Core. When Owen rewrote the RTC library for v2.x, he added that #ifdef statement. I’ve been waiting to learn why: https://github.com/sparkfun/Arduino_Apollo3/issues/337

These examples, and library, were based on the Ambiq HAL, and I suspect that v2.x of the core has broken some functionality. I just tried testing the code on v2.0.5 with an Artemis Redboard but it doesn’t appear that the system ever wakes from sleep.

Cheers,

Adam

There are a couple of issues:

  1. In the Sketch, line 99 :
am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_SRAM_64K_DTCM); // Turn off everything but lower 64k

the program is 121K so we need to comment this out:

//am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_SRAM_64K_DTCM); // Turn off everything but lower 64k
  1. The whole handling of the UART has changed. Much more complex given that Mbed is in between, but the real problem is the fact that NO “deinitialize” is happening. Serial.end() does nothing in 2.0.5, opposite to 1.2.1.

In the Sketch we turn off the power as well as disable the UART0 pins. In wakeup() it will call Serial.begin(), which will lead to an MBED call in unbufferdserial.cpp, which in turn call MBED Serialbase.cpp and from there Serial_api.c. In serial.init() function in Serial_api.c it will ONLY turn on power and set the pins if this was NOT done previous ( e.g. there is no valid handle yet.). BUT as the handle is still valid because as was never de-initialized, power and pins will not be set correctly again.

To overcome, we will have to change the sketch to NOT disable the TX and RX pins and to re-enable the UART power.

I have applied all the necessary changes in the attached sketch and tested that it works on an ATP.

Example6_LowPower_Alarm.zip (2.04 KB)

Hi Paul,

Thanks very much! My biggest challenge with Mbed OS has been not knowing what I don’t know. The handling of UART is a perfect example of this.

Regarding the changes to the code:

the program is 121K so we need to comment this out:

It makes complete sense to need to keep the lower 512K powered up. I didn't realize how significant the sketch size difference is between v1.x and v2.x. Example 6 on v1.x is approximately 14,000 bytes but 121,000 bytes on v2.x. I assume this is due to it needing to include the Mbed core?

The whole handling of the UART has changed.

Thanks for the heads up. I was unaware of this change. In your code, you modified the range of GPIO pins that are disabled to 0 - 47. I assume this is because GPIO 48/49 are the UART0 TX/RX pins. I've tested your code on a SparkFun Redboard Artemis and an Edge 2 and a side effect I've noticed of this is that the TX LED is now constantly ON when the Artemis is in deep sleep.
// Disable all pads (do NOT reassign UART TX and RX)
for (int x = 0 ; x < 48 ; x++)
  am_hal_gpio_pinconfig(x, g_AM_HAL_GPIO_DISABLE);

Could we not explicitly re-enable the TX/RX pins in wakeUp() before powering back up UART0 and reinitializing Serial?

Happy to get your thoughts on the best way to approach this. I use the Ambiq HAL because it’s what I know. If there’s a better way to do it using Mbed OS, I’m all ears.

Cheers,

Adam

I took the simple approach to not disable the pins. Of course, one can disable all and enable TX/RX pins when waking up. Mbed UnbufferedSerial.cpp does not have an end /de-init function. So that would require a change in Mbed. Best in Serial_Api.cpp, to rework the init function. There is nothing against trying re-applying power and re-setting the pins correctly if you already have a handle. You just have to skip am_hal_uart_initialize() and skip setting the default serial_format() if you have a handle. All the rest (FIFO etc) can be done in my mind with a handle available

Paul - Thanks for your rework of the RTC/Example #6!!! I have an example to follow and learn from now. You and Adam are way above my pay grade!

Hi Paul,

Reading up on the Mbed OS memory model (https://os.mbed.com/handbook/Memory-Model), and it looks like program code is stored in flash and static variables, the heap, and the stack are stored in RAM. The Ambiq HAL low power code maintains 512 KB of flash memory and 64 KB of SRAM. Should this not be sufficient for this example, which requires 118 KB program storage space and 28 KB of dynamic memory?

  am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_FLASH_512K); // Turn off everything but lower 512k
  am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_SRAM_64K_DTCM); // Turn off everything but lower 64k

Thinking in terms of low-power operation, it looks like the HAL provides numerous options for the amount of RAM/flash that remains enabled.

typedef enum
{
    AM_HAL_PWRCTRL_MEM_NONE,
    AM_HAL_PWRCTRL_MEM_SRAM_8K_DTCM,
    AM_HAL_PWRCTRL_MEM_SRAM_32K_DTCM,
    AM_HAL_PWRCTRL_MEM_SRAM_64K_DTCM,
    AM_HAL_PWRCTRL_MEM_SRAM_96K,
    AM_HAL_PWRCTRL_MEM_SRAM_128K,
    AM_HAL_PWRCTRL_MEM_SRAM_160K,
    AM_HAL_PWRCTRL_MEM_SRAM_192K,
    AM_HAL_PWRCTRL_MEM_SRAM_224K,
    AM_HAL_PWRCTRL_MEM_SRAM_256K,
    AM_HAL_PWRCTRL_MEM_SRAM_288K,
    AM_HAL_PWRCTRL_MEM_SRAM_320K,
    AM_HAL_PWRCTRL_MEM_SRAM_352K,
    AM_HAL_PWRCTRL_MEM_SRAM_384K,
    AM_HAL_PWRCTRL_MEM_FLASH_512K,
    AM_HAL_PWRCTRL_MEM_FLASH_1M,
    AM_HAL_PWRCTRL_MEM_CACHE,
    AM_HAL_PWRCTRL_MEM_ALL,
    AM_HAL_PWRCTRL_MEM_MAX
} am_hal_pwrctrl_mem_e;

Cheers,

Adam

hi Adam,

This time I did a deep dive and the more I learned the more I realized how incorrect this example is. I have studied the Sparkfun source code, the datasheet and SDK code

  1. Calling am_hal_pwrctrl_memory_deepsleep_powerdown()

This will ENABLE powerdown in deepsleep for the memory that is provided. So calling

am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_FLASH_512K); // Turn off everything but lower 512k

will do exactly the opposite of what the comment states. It will enable power down for the first 512K flash memory during deep sleep. Nothing will be lost as this is flash memory that will retain the content without power.

same for calling

am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_SRAM_64K_DTCM); // Turn off everything but lower 64k

this will enable the lower 64k to power down during deep sleep.

In wakeUp() routine in the sketch the call :

am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_MAX);

This would enable ALL memory to power down at deep sleep HOWEVER the wrong argument is used. The source code will check and if the provided argument is higher or equal to AM_HAL_PWRCTRL_MEM_MAX it will fail.

Actually, this call is not needed at all. Whether the memory area will be powered up again after deep sleep is done with the enable call. The Artemis will use the MEMPWREN register which is set during boot.

**Now what ??**

There is a call am_hal_pwrctrl_memory_deepsleep_retain(). This call will DISABLE the indicated memory area for going power down in deep sleep

So before enabling deep sleep, first enable power down for ALL memory. Then exclude the first 32K by calling am_hal_pwrctrl_memory_deepsleep_retain() for the selected area. No need to maintain the power for the flash…

  //Power down Flash, SRAM, cache
  am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_ALL);    // enable powerdown of all memory during deepsleep
  am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_SRAM_32K_DTCM); // disable powerdown of the first 32K memory during deepsleep

Next we remove the am_hal_pwrctrl_memory_deepsleep_powerdown() from wakeUp(). We still need to enable UART0 after wakeUp as that was disabled before AND because of how this driver is integrated with Mbed, it will not re-enable itself.

Attached the adjusted sketch (tested on ATP)

Example6_LowPower_Alarm_new.zip (2.02 KB)

Hi Paul,

Thanks for looking into this. I assume you must have found the Ambiq SDK deepsleep_wake.c example? I’ll try to do some current measurements with a SparkFun Edge 2 board to see if I can achieve the same 2.5 uA draw with v2.x of the Apollo3 Core.

The v1.2.1 low power examples were originally written by Nathan and I believe stemmed from Robin Hodgson’s efforts: viewtopic.php?f=169&t=50904

I’d be keen to get Robin’s thoughts on how to correctly achieve optimal deep sleep currents with the new Mbed OS/Ambiq HAL Apollo3 Core v2.x .

Cheers,

Adam

Hi Paul,

You are completely right. The previous low-power examples are incorrect. I believe everyone (myself included) just assumed the code was working correctly because we achieved the desirable current consumption.

I was able to confirm a sleep current of 2.27 uA on a SparkFun Edge 2 using on v2.0.6 of the Apollo3 Core and using the following sleep and wake functions. I disable the UART pins and explicitly reenable them. If you omit them from the GPIO disable function, the sleep current jumps to 521 uA.

Are the calls then to Serial.begin() and Serial.end() even necesssary?

Edit: Deep sleep current of 2.08 uA when only 32K of SRAM is retained.

Cheers,

Adam

void goToSleep()
{
  // Disable UART0
  Serial.end();

  // Disable ADC
  powerControlADC(false);

  // Force the peripherals off
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM0);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM1);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM2);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM3);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM4);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM5);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_ADC);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_UART0);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_UART1);

  // Disable all pads
  for (int x = 0 ; x < 50 ; x++)
    am_hal_gpio_pinconfig(x, g_AM_HAL_GPIO_DISABLE);

  // Power down Flash, SRAM, cache
  am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_ALL);    // Powerdown all memory during deepsleep
  am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_SRAM_64K_DTCM); // Powerup first 64K of SRAM during deepsleep

  // Keep the 32kHz clock running for RTC
  am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
  am_hal_stimer_config(AM_HAL_STIMER_XTAL_32KHZ);

  am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); // Sleep forever

  // And we're back!
  wakeUp();
}

// Power up gracefully
void wakeUp()
{
  // Go back to using the main clock
  am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
  am_hal_stimer_config(AM_HAL_STIMER_HFRC_3MHZ);

  // Reconfigure UART0 pins and reenable power to interface
  am_hal_gpio_pinconfig(48, g_AM_BSP_GPIO_COM_UART_TX);
  am_hal_gpio_pinconfig(49, g_AM_BSP_GPIO_COM_UART_RX);
  am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_PERIPH_UART0);

  // Enable ADC
  initializeADC();

  // Enable Serial
  Serial.begin(115200);
}

thanks Adam. I have read the link to Robin’s entry and I found we are in sync. Unfortunately, the Serial.end() does not do anything in the current version (up to 2.0.6). It is an empty routine and that is part of the problem that we need to re-enable UART after waking-up. The Serial.begin() will set for the right speed, bits and parity, just in case it is different than default values.

Hi Paul,

A quick note that the following code does not work on Apollo3 Core v1.2.1. The watchdog timer is rendered inoperable and the system enters into a reset loop.

  // Power down flash, SRAM, cache
  am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_ALL); // Power down all memory during deepsleep
  am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_FLASH_512K); // Retain lower 512K of CACHE
  am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_SRAM_64K_DTCM); // Retain lower 64K of SRAM

However, if I switch to AM_HAL_PWRCTRL_MEM_MAX instead of AM_HAL_PWRCTRL_MEM_ALL, the system works as intended.

am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_MAX); // Power down all memory during deepsleep

Could it be that we’re inadvertently powering down memory that is critical to the overall operation?

Cheers,

Adam

AM_HAL_PWRCTRL_MEM_MAX is NOT accepted and return an error. So nothing is happening…

(from am_hal_powercntrl.c) :

am_hal_pwrctrl_memory_deepsleep_powerdown(am_hal_pwrctrl_mem_e eMemConfig)
{
    if ( eMemConfig >= AM_HAL_PWRCTRL_MEM_MAX )
    {
        return AM_HAL_STATUS_FAIL;
    }

    //
    // Power down the required memory.
    //
    PWRCTRL->MEMPWDINSLEEP |= am_hal_pwrctrl_memory_control[eMemConfig].ui32PwdSlpEnable;

    return AM_HAL_STATUS_SUCCESS;
}

I have not tested on 1.2.1 (stopped using that long ago) but will try in the coming days.

Hi Paul,

Good catch!

I am also concurrently testing v2.x of the Apollo3 Core, but it’s still too unstable for use with real-world projects.

Cheers,

Adam

what errors do you get on 2.0.6 ?

Hi Paul,

In addition to the low-power code, I’m dealing with RTC alarm matches not working and the WDT ISR is not triggering correctly (or at all), resulting in endless reset loops. I wrote WDT library for v2.x and I have this sneaking suspicion that I’ve missed something obvious. If I knew how, I’d try to make use of the Mbed OS WDT functionality, but I haven’t cracked that nut yet.

https://github.com/sparkfun/Arduino_Apollo3/pull/363

Cheers,

Adam

Hi Paul,

To give a quick update, the issues I was experiencing with the WDT reset loops were due to the fact the program was being stored beyond the range of the lowest 64K SRAM. Robin was very helpful in troubleshooting this.

viewtopic.php?f=169&t=50904&p=224374#p224374

Considering the power consumption difference between retaining 32K vs 384K is less than a microamp, I believe it would be best for all of SparkFun’s examples to power down all the CACHE and flash, but retain all of the SRAM. When I have time I’ll work on a PR for the v1/v2 Cores.

Cheers,

Adam