Zero RTC - getting a 1Hz interrupt

Hi Phil-D,

The trick to getting the RTC interrupt to fire every second (1Hz) in clock/calendar mode (Mode 2), is to increment the ALARM0 compare register (SECOND bitfield) each time the RTC_Hander() interrupt service routine is called:

RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND = (RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND + 1) % 60;   // Increment the ALARM0 compare register

In this way, the ALARM0 compare register is set one second ahead of the clock/canlendar counter and the RTC interrupt is therefore called every second, each time the counter increments. The modulo 60 resets the compare register back to 0 after 60 seconds have elapsed.

Here's the code:

// Set-up RTC interrupt to fire every second (1Hz) in clock/calendar mode (Mode 2)
void setup(){
  SerialUSB.begin(115200);
  while (!SerialUSB);
  
  PORT->Group[PORTA].DIRSET.reg = PORT_PA17;  // Set digital pin D13 to an OUTPUT
  
// Configure clock source and clock generators (gclk.h)------------------------    
  GCLK->GENDIV.reg =  GCLK_GENDIV_ID(4) |   // Select GLCK4
                      GCLK_GENDIV_DIV(4);   // Select clock divisor to divide by 32 (2 ^ (4 + 1))
  while (GCLK->STATUS.bit.SYNCBUSY);        // Wait for synchronization
 
  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(4) |        // Select GCLK4
                      GCLK_GENCTRL_SRC_XOSC32K |  // Select GCLK source as the external 32.768kHz crystal                      
                      GCLK_GENCTRL_IDC |          // Improve duty cycle for odd div factors
                      GCLK_GENCTRL_RUNSTDBY |     // Enable run standby mode  
                      GCLK_GENCTRL_DIVSEL |       // Set GLCK divisor as 2 to the power of (divisor) value                        
                      GCLK_GENCTRL_GENEN;         // Enable GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
     
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_GEN_GCLK4 |  // Select GCLK4
                      GCLK_CLKCTRL_ID_RTC |     // Connect to the RTC
                      GCLK_CLKCTRL_CLKEN;       // Enable GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);            // Wait for synchronization

// RTC configuration (rtc.h)--------------------------------------------------                                              
  RTC->MODE2.CTRL.reg |= RTC_MODE2_CTRL_PRESCALER_DIV1024 |     // Set prescaler to 1024
                         RTC_MODE2_CTRL_MODE_CLOCK;             // Set RTC to mode 2, clock/calendar mode

  RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND = RTC_MODE2_ALARM_SECOND(0);   // Set the ALARM0 compare register
  while (RTC->MODE2.STATUS.bit.SYNCBUSY)                                   // Wait for synchronization
  
  RTC->MODE2.Mode2Alarm[0].MASK.bit.SEL = RTC_MODE2_MASK_SEL_SS;  // Set the RTC clock/calender interrupt mask to match seconds only
  while (RTC->MODE2.STATUS.bit.SYNCBUSY)                          // 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->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0;            // Set Mode 2 alarm interrupt                            

// Enable RTC--------------------------------------------------------------
  RTC->MODE2.CLOCK.reg = 0x0000000;                               // Set the RTC counter to zero
  while (RTC->MODE2.STATUS.bit.SYNCBUSY);                         // Wait for synchronization
  
  RTC->MODE2.CTRL.reg |= RTC_MODE2_CTRL_ENABLE;                   // Turn on the RTC
  while (RTC->MODE2.STATUS.bit.SYNCBUSY);                         // Wait for synchronization
  SerialUSB.println(F("Setup complete..."));
}

void loop(){}

void RTC_Handler(void)
{
  if (RTC->MODE2.INTFLAG.bit.ALARM0 && RTC->MODE2.INTENSET.bit.ALARM0)    // Check for ALARM0 interrupt
  {  
     SerialUSB.print(F("RTC Handler  "));
     SerialUSB.println(RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND);
     PORT->Group[PORTA].OUTTGL.reg = PORT_PA17;                           // Toggle digital pin D13
     RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM0;                   // Reset interrupt flag     
     RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND = (RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND + 1) % 60;   // Increment the ALARM0 compare register
     while (RTC->MODE2.STATUS.bit.SYNCBUSY);                              // Wait for synchronization
  }
}

The code toggles the Zero's LED on digtial pin 13, so it'll go on for 1 second, off for 1 second and so on... The ALARM0 compare register value is also output on the console.