Reducing power consumption

Hey everyone,

I want to reduce my board's power consumption down to the bare minimum. I can't find where in SAMD core all of the peripheral clocks / power management bits are setup initially. For my usage I don't need the USB or ADC enabled so I'd like to disable them. Does anyone know where I should be looking?

Here is an app note from Atmel that discusses configurations for low power operation for the SAML and SAMD series. This is a good place to start Application Notes | Microchip Technology

I have already read that document and that's why I am wondering where in the core all of the peripherals are "turned on". I have this feeling that everything in the chip is running and sucking down power.

When I'm looking for such kind of information I usually use the GitHub searchbar into the Samd core. Keywords like "ENABLE" and "CTRLA" could be some good try.

To reduce the power consumption a lot, disabling some peripheral is not enough : you have to put the microcontroller into sleep mode, and wake up sometimes to do what you need.

You could try something like this :

void setup() 
{
//Be careful here : there is no way to wake up the board with this code,
//so we need a delay to allow uploading new code
//!!! DO NOT REMOVE THIS DELAY UNLESS YOU KNOW WHAT YOU'RE DOING!!!
  delay(10000);
  
  
  // Set sleep mode to deep sleep 
  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

  //Disable USB port (to disconnect correctly from host
  USB->DEVICE.CTRLA.reg &= ~USB_CTRLA_ENABLE;
  
  //Enter sleep mode and wait for interrupt (WFI)
  __WFI();
  
  //seelping ZZZZZZZ....
  
  //Re-enable USB (should never get there because no wakeup interrupt is configured)
  USB->DEVICE.CTRLA.reg |= USB_CTRLA_ENABLE;
}

void loop() 
{
}

If you set up the RTC to wakeup the microcontroller each 1s or 10 or whatever and then go back to sleep, you'll be able to save A LOT of power.

I'll take a look for "ENABLE" and "CTRLA" in the core... I do know Atmel's documentation says to disable the clocks to peripherals as well. For my particular usage I need to keep the MCU awake continuously but there's no need to have USB and 4x SERCOMs enabled if I am not using them.

if you sketch only needs to respond to interrupts, you can set SLEEPONEXIT and it will sleep when exiting an interrupt handler rather than going into thread mode

Well I disabled just about every peripheral with no major change in current draw... I tried with both a Sparkfun SAMD21 Dev board and an Adafruit Feather M0. Both boards drew 15mA before and after while I'd expect current draw to be less than 5mA per page 955 of the SAMD21 datasheet.

Here's my code...

void setup() {
  
  SERCOM0->USART.CTRLA.bit.ENABLE=0;
  SERCOM1->USART.CTRLA.bit.ENABLE=0;
  SERCOM2->USART.CTRLA.bit.ENABLE=0;
  SERCOM3->USART.CTRLA.bit.ENABLE=0;
  SERCOM4->USART.CTRLA.bit.ENABLE=0;
  SERCOM5->USART.CTRLA.bit.ENABLE=0;
  
  I2S->CTRLA.bit.ENABLE=0;
  
  ADC->CTRLA.bit.ENABLE=0;
  
  DAC->CTRLA.bit.ENABLE=0;
  
  AC->CTRLA.bit.ENABLE=0;
  
  TCC0->CTRLA.bit.ENABLE=0;
  TCC1->CTRLA.bit.ENABLE=0;
  TCC2->CTRLA.bit.ENABLE=0;
  
  RTC->MODE0.CTRL.bit.ENABLE=0;
  RTC->MODE1.CTRL.bit.ENABLE=0;
  RTC->MODE2.CTRL.bit.ENABLE=0;
  
  USB->HOST.CTRLA.bit.ENABLE=0;
  USB->DEVICE.CTRLA.bit.ENABLE=0;
  
  
}

void loop() {

while(1){}
  
}

try this instead. rather than marking the register "not enabled" consider turning off the clock to the peripheral in question

PM->APBBMASK.reg &= ~PM_APBBMASK_PORT;
PM->APBBMASK.reg &= ~PM_APBBMASK_DMAC;
PM->APBCMASK.reg &= ~PM_APBCMASK_SERCOM0;
PM->APBCMASK.reg &= ~PM_APBCMASK_SERCOM1;

and so on.
here's the power manager's bus bitmasks:

AHBMASK:  CLK_HPBA_AHB CLK_HPBB_AHB CLK_HPBC_AHB CLK_DSU_AHB CLK_NVMCTRL_AHB CLK_DMAC_AHB CLK_USB_AHB
APBAMASK:  CLK_PAC0_APB CLK_PM_APB CLK_SYSCTRL_APB CLK_GCLK_APB CLK_WDT_APB CLK_RTC_APB CLK_EIC_APB
APBBMASK:  CLK_PAC1_APB CLK_DSU_APB CLK_NVMCTRL_APB CLK_PORT_APB CLK_DMAC_APB CLK_USB_APB
APBCMASK:  CLK_SERCOM0_APB CLK_SERCOM1_APB CLK_SERCOM2_APB CLK_SERCOM3_APB CLK_SERCOM4_APB CLK_SERCOM5_APB CLK_TCC0_APB CLK_TCC1_APB CLK_TCC2_APB CLK_TC3_APB CLK_TC4_APB CLK_TC5_APB CLK_ADC_APB CLK_DAC_APB

I tried your suggestion, small improvement but not much. The odd thing is that if I launch the bootloader via the reset double tap the boards only draw about 8mA. I am going to look into the bootloader code to see how it's setting up everything.

ok then consider structuring your sketch so its in standby mode until its woken by an interrupt

also, from an atmel appnote/tech sheet thingie:

"By default all peripherals will be connected to GCLK generator number zero (GCLKGEN[0]). As this generator is used to generate the main clock it will always be on in active mode as well as the idle modes. The clock from this generator will then be propagated further down the clock tree than necessary. This will cause higher power consumption.
In the power measurements that are presented in the datasheet all peripherals are connected to a GCLK generator that is not connected to a source oscillator. The only exception to this is the DFLL which has its reference clock connected to a 32kHz clock crystal. In the code distributed with this appnote code can be found in the function switch_gclkgen_to_peripherals(). Switching unused peripherals to a GCLK generator that is not setup with a source oscillator is something that will save power in all applications."

I started playing with the Atmel Start web tool, I'm going to try initializing the clocks with the Atmel generated clock code instead of the Arduino code.

So I did some testing with different configurations of boot loaders and start up code...

Atmel START generated code: 6.5mA
Sparkfun SAMD21 Dev Bootloader: 8.5mA
Adafruit Feather M0 Bootlaoder: 16.9mA
Arduino Zero Bootloader: 17.4mA

To account for LEDs these are the minimum current draw seen over 5 minutes on an Agilent 34461A. It seems like the Sparkfun version of the SAM-BA bootloader is somehow setting up the chip in a more power friendly way than the Arduino version.

the clocks are assigned and started in Arduino15/packages/arduino/hardware/samd/1.6.7/cores/arduino/startup.c
on a mac its in ~/Libary
on windows i think its in AppData/Local

also, use ZeroRegs.h to dump the registers and see what the difference is between how the boards set up the clocks.

I dumped all of the registers with an Atmel ICE, the only real difference is that the official bootloader and core are using the 48MHz Digital Frequency Locked Loop in closed loop mode while the Sparkfun bootloader bootloader runs the DFLL in open loop. I have no clue if that is the cause or not, I was hoping one of the Arudino developers would have jump in by now.

Try to remove the pin initialization in wiring.c

I tried removing the pin initialization and no success... I even tried without the Arudino bootloader being loaded on the board and as soon as the Arduino core starts to execute current draw jumps up to atleast 13mA. I can leave the Sparkfun bootloader running all day at ~8mA.

I have done a custom board design using the SAMD21. In the design I use the RTC to wake the processor every minute, and then put back to sleep.

In sleep I am consuming ~50uA with the RTC running.

There quite a few things I had to do get down this low, for example make sure all GPIOs are not floating and turning off the peripherals and clocks not used. The code was for a contract I did so I can not share but will try to get an example posted if it would help.

Trampas
misfittech.net

Any chance you have the example code for reducing power consumption?

Resurrecting the post from the dead because it is one of the first results when you search for M0 and power consumption.
It is indeed possible to reduce the power consumption of the microcontroller down to microamp levels.
The lowest I have achieved is ~3μA @ 3.3V, measured with a keysight B2900A precision source. This is for the microcontroller only, on a board where everything else is removed.
Unfortunately to do so easily, you need to use Atmel Studio and a programmer to flash the device without the arduino bootloader. (It would be possible to do it with the bootloader, but that would require reconfiguring everything and probably breaking most peripheral libraries in the process)
Then, as trampas mentioned, point all unused peripherals to dead clocks like so:

GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_USB | GCLK_CLKCTRL_GEN_GCLK4;
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_EVSYS_0 | GCLK_CLKCTRL_GEN_GCLK4;
...
..

In this case GCLK4 is unused and not connected to any oscillator.

And make sure that the clock system is optimally configured for low power consumption. To do so, have only one Generic clock generator active on standby, connected to the 32kHz ultra low power internal oscillator (OSCULP32K), and then connect only the RTC to that clock generator. Don't forget to enable Run in standby for that generator.
If that is the only thing powered on in standby, and no other peripherals are pointed to that generator, you can go down to few microamps.

I have attached an overview of the clock configuration on Atmel START

Why does this technique require the removal of the bootloader? Doesn't the bootloader run only once when the board is initially powered up, and from then on it's never executed again unless you press reset or the PC attempts to initiate a serial connection?

(I assume the code that performs that check is part of your sketch, not the bootloader itself.)