Martin, this is exactly what I was looking for! Thank you very much!
MartinL:
Hi fostac,
Here's an example of how to use the SAMD51's RTC in mode 1, calling the RTC_Handler() interrupt service routine every 10 seconds and toggling the LED on D13 (or LED_BUILTIN). It also puts the microcontrolller into deep sleep during intervening time, this is disables the USB and requires a double press of the reset button to recover for reprogramming.
There's also the option to drive the RTC either from it's internal Ultra Low Power 32kHz oscillator:
OSC32KCTRL->OSCULP32K.bit.EN1K = 1; // Enable ULPOSC32K 1KHz clock output
OSC32KCTRL->RTCCTRL.reg |= OSC32KCTRL_RTCCTRL_RTCSEL_ULP1K; // Select the 1kHz Ultra Low Power (ULP) for the RTC
...or alternatively from its more accurate external 32kHz crystal, (if one's present on your board?):
OSC32KCTRL->XOSC32K.bit.EN1K = 1; // Enable XOSC32K 1kHz clock output
OSC32KCTRL->RTCCTRL.reg |= OSC32KCTRL_RTCCTRL_RTCSEL_XOSC1K; // Select the 1kHz external crystal for the RTC
Here's the code:
// Put SAMD51 into deep sleep and RTC interrupts set to trigger every 10s (PER)
void setup(){
// Output to D13 to check that the ISR is being called every 10 seconds
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].DIRSET.reg = 1 << g_APinDescription[LED_BUILTIN].ulPin;
// RTC configuration (rtc.h)----------------------------------------------------
RTC->MODE1.CTRLA.bit.ENABLE = 0; // Disable the RTC
while (RTC->MODE1.SYNCBUSY.bit.ENABLE); // Wait for synchronization
RTC->MODE1.CTRLA.bit.SWRST = 1; // Software reset the RTC
while (RTC->MODE1.SYNCBUSY.bit.SWRST); // Wait for synchronization
OSC32KCTRL->OSCULP32K.bit.EN1K = 1; // Enable ULPOSC32K 1KHz clock output
OSC32KCTRL->RTCCTRL.reg |= OSC32KCTRL_RTCCTRL_RTCSEL_ULP1K; // Select the 1kHz Ultra Low Power (ULP) for the RTC
//OSC32KCTRL->XOSC32K.bit.EN1K = 1; // Enable XOSC32K 1kHz clock output
//OSC32KCTRL->RTCCTRL.reg |= OSC32KCTRL_RTCCTRL_RTCSEL_XOSC1K; // Select the 1kHz external crystal for the RTC
RTC->MODE1.CTRLA.reg |= RTC_MODE1_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024
RTC_MODE1_CTRLA_MODE_COUNT16; // Set RTC to mode 1, 16-bit timer
RTC->MODE1.PER.reg = RTC_MODE1_PER_PER(9); // Interrupt time 10s: 1Hz/(9 + 1)
while (RTC->MODE1.SYNCBUSY.bit.PER); // Wait for synchronization
// Configure RTC interrupts ----------------------------------------------------
NVIC_SetPriority(RTC_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for RTC
NVIC_EnableIRQ(RTC_IRQn); // Connect RTC to Nested Vector Interrupt Controller (NVIC)
RTC->MODE1.INTENSET.reg = RTC_MODE1_INTENSET_OVF; // Enable RTC overflow interrupts
// Set-up Deep Sleep Mode--------------------------------------------------------
PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_STANDBY; // Set up the SAMD51 to go into STANDBY mode during sleep
while(PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_STANDBY_Val); // Wait the STANDBY bitfield to be set
// On the SAMD51 it's necessary to deactivate the RUNSTDBY (Run Standby) bit on the native USB
USB->DEVICE.CTRLA.bit.ENABLE = 0; // Disable the USB peripheral
while(USB->DEVICE.SYNCBUSY.bit.ENABLE); // Wait for synchronization
USB->DEVICE.CTRLA.bit.RUNSTDBY = 0; // Deactivate run on standby
USB->DEVICE.CTRLA.bit.ENABLE = 1; // Enable the USB peripheral
while(USB->DEVICE.SYNCBUSY.bit.ENABLE); // Wait for synchronization
// Enable RTC--------------------------------------------------------------------
RTC->MODE1.CTRLA.bit.ENABLE = 1; // Enable the RTC
while (RTC->MODE1.SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop()
{
__DSB(); // Complete outstanding memory operations - not required for SAMD21 ARM Cortex M0+
__WFI(); // Put the SAMD51 into deep sleep, Zzzzzzzz...
}
void RTC_Handler()
{
// Note that these two lines have to be in this order - clear interrupt flag then toggle LED
RTC->MODE1.INTFLAG.bit.OVF = 1; // Reset the overflow interrupt flag
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTTGL.reg = 1 << g_APinDescription[LED_BUILTIN].ulPin; // Toggle the LED_BUILTIN
}