Hello everyone,
I am trying to get the power consumption of my barebone/baremetal SAMD21G16A down to the datasheet value's which is a typical consumption in standby of 4.6 uA and a maximum consumption of 15 uA. this is with the RTC enabled at 1 kHz. i am currently using a slightly modified RTCZero library with the a little bit of code. here are the files:
RTCZero.cpp:
#include <time.h>
#include "RTCZero.h"
// Default date & time after reset
#define DEFAULT_YEAR 2000 // 2000..2063
#define DEFAULT_MONTH 1 // 1..12
#define DEFAULT_DAY 1 // 1..31
#define DEFAULT_HOUR 0 // 1..23
#define DEFAULT_MINUTE 0 // 0..59
#define DEFAULT_SECOND 0 // 0..59
voidFuncPtr RTC_callBack = NULL;
RTCZero::RTCZero()
{
_configured = false;
}
void RTCZero::begin(bool resetTime)
{
uint16_t tmp_reg = 0;
PM->APBAMASK.reg |= PM_APBAMASK_RTC; // turn on digital interface clock
// If the RTC is in clock mode and the reset was
// not due to POR or BOD, preserve the clock time
// POR causes a reset anyway, BOD behaviour is?
bool validTime = false;
RTC_MODE2_CLOCK_Type oldTime;
if ((!resetTime) && (PM->RCAUSE.reg & (PM_RCAUSE_SYST | PM_RCAUSE_WDT | PM_RCAUSE_EXT))) {
if (RTC->MODE2.CTRL.reg & RTC_MODE2_CTRL_MODE_CLOCK) {
validTime = true;
oldTime.reg = RTC->MODE2.CLOCK.reg;
}
}
// Setup clock GCLK2 with OSC32K divided by 32
configureClock();
RTCdisable();
RTCreset();
tmp_reg |= RTC_MODE2_CTRL_MODE_CLOCK; // set clock operating mode
tmp_reg |= RTC_MODE2_CTRL_PRESCALER_DIV1024; // set prescaler to 1024 for MODE2
tmp_reg &= ~RTC_MODE2_CTRL_MATCHCLR; // disable clear on match
//According to the datasheet RTC_MODE2_CTRL_CLKREP = 0 for 24h
tmp_reg &= ~RTC_MODE2_CTRL_CLKREP; // 24h time representation
RTC->MODE2.READREQ.reg &= ~RTC_READREQ_RCONT; // disable continuously mode
RTC->MODE2.CTRL.reg = tmp_reg;
while (RTCisSyncing())
;
NVIC_EnableIRQ(RTC_IRQn); // enable RTC interrupt
NVIC_SetPriority(RTC_IRQn, 0x00);
RTC->MODE2.INTENSET.reg |= RTC_MODE2_INTENSET_ALARM0; // enable alarm interrupt
RTC->MODE2.Mode2Alarm[0].MASK.bit.SEL = MATCH_OFF; // default alarm match is off (disabled)
while (RTCisSyncing())
;
RTCenable();
RTCresetRemove();
// If desired and valid, restore the time value, else use first valid time value
if ((!resetTime) && (validTime) && (oldTime.reg != 0L)) {
RTC->MODE2.CLOCK.reg = oldTime.reg;
}
else {
RTC->MODE2.CLOCK.reg = RTC_MODE2_CLOCK_YEAR(DEFAULT_YEAR - 2000) | RTC_MODE2_CLOCK_MONTH(DEFAULT_MONTH)
| RTC_MODE2_CLOCK_DAY(DEFAULT_DAY) | RTC_MODE2_CLOCK_HOUR(DEFAULT_HOUR)
| RTC_MODE2_CLOCK_MINUTE(DEFAULT_MINUTE) | RTC_MODE2_CLOCK_SECOND(DEFAULT_SECOND);
}
while (RTCisSyncing())
;
_configured = true;
}
void RTC_Handler(void)
{
if (RTC_callBack != NULL) {
RTC_callBack();
}
RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM0; // must clear flag at end
}
void RTCZero::standbyMode()
{
// Entering standby mode when connected
// via the native USB port causes issues.
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__WFI();
}
/* Attach peripheral clock to 32k oscillator */
void RTCZero::configureClock() {
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2)|GCLK_GENDIV_DIV(4);
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
;
GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_DIVSEL );
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
;
GCLK->CLKCTRL.reg = (uint32_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | (RTC_GCLK_ID << GCLK_CLKCTRL_ID_Pos)));
while (GCLK->STATUS.bit.SYNCBUSY)
;
}
/*Private Utility Functions*/
/* Synchronise the CLOCK register for reading*/
inline void RTCZero::RTCreadRequest() {
if (_configured) {
RTC->MODE2.READREQ.reg = RTC_READREQ_RREQ;
while (RTCisSyncing())
;
}
}
/* Wait for sync in write operations */
inline bool RTCZero::RTCisSyncing()
{
return (RTC->MODE2.STATUS.bit.SYNCBUSY);
}
void RTCZero::RTCdisable()
{
RTC->MODE2.CTRL.reg &= ~RTC_MODE2_CTRL_ENABLE; // disable RTC
while (RTCisSyncing())
;
}
void RTCZero::RTCenable()
{
RTC->MODE2.CTRL.reg |= RTC_MODE2_CTRL_ENABLE; // enable RTC
while (RTCisSyncing())
;
}
void RTCZero::RTCreset()
{
RTC->MODE2.CTRL.reg |= RTC_MODE2_CTRL_SWRST; // software reset
while (RTCisSyncing())
;
}
void RTCZero::RTCresetRemove()
{
RTC->MODE2.CTRL.reg &= ~RTC_MODE2_CTRL_SWRST; // software reset remove
while (RTCisSyncing())
;
}
RTCZero.h:
#ifndef RTC_ZERO_H
#define RTC_ZERO_H
#include "Arduino.h"
typedef void(*voidFuncPtr)(void);
class RTCZero {
public:
enum Alarm_Match: uint8_t // Should we have this enum or just use the identifiers from /component/rtc.h ?
{
MATCH_OFF = RTC_MODE2_MASK_SEL_OFF_Val, // Never
};
RTCZero();
void begin(bool resetTime = false);
void standbyMode();
bool isConfigured() {
return _configured;
}
private:
bool _configured;
void configureClock(void);
void RTCreadRequest();
bool RTCisSyncing(void);
void RTCdisable();
void RTCenable();
void RTCreset();
void RTCresetRemove();
};
#endif // RTC_ZERO_H
The main code:
#include "RTCZero.h"
RTCZero rtc;
void setup(){
delay(5000);
rtc.begin();
rtc.standbyMode();
}
void loop(){}
I sometimes get a consumption of 150 uA for a few seconds and then it jumps up to 200 uA or sometimes even 250 uA. I am coding on platform io. i have tried pulling pins or and down which only led to increased power draw. i have an external crystal available, but from what i've gathered is the internal crystal better for saving power. this is also the main edit in the rtczero library code apart from removing functions which i won't be using. does anybody have suggestions on lowering the consumption? i read the following threads:
but haven't gotten much wiser.