Go Down

Topic: SAMD21 RTC internal oscillator extreme drift, but not for millis()? (Read 817 times) previous topic - next topic

jazzar

This is in part in reference to:

@MartinL
https://forum.arduino.cc/index.php?topic=684501.0

Using the code in Posts #3 and #4, with a change to the source of the clock signal:
GCLK_GENCTRL_SRC_XOSC32K -> GCLK_GENCTRL_SRC_OSC32K

The RTC experiences a horrible amount of drift, about 37 seconds loss in one hour. However, funny enough, I used millis() to print the time of the RTC every minute, and according to the log, the millis() is accurate to within 0.1sec. for the same hour. Compared to my PC RTC anyway, however those differences are huge! So I suspect there is something untoward happening.

Any ideas what could cause this behaviour?

jazzar

Complete Code Part 1/2:
Code: [Select]
// Set-up RTC and timer TC4 to generate a timestamp down to microsecond accuracy

union {                                              // Timestamp union/structure
  struct {
    uint32_t second : 6;
    uint32_t minute : 6;
    uint32_t hour : 5;
    uint32_t day : 5;
    uint32_t month : 4;
    uint32_t year : 6;
  } bit;
  uint32_t reg;
} timeStamp;

unsigned long ms;

void setup(){
  // Initialise native serial port, event system and LED output //////////////////////
  
  ms = millis();
  
  Serial.begin(115200);                           // Initialise the native serial port
  while (!Serial);                                // Wait for the console to open
  Serial.println("UP");
 
 
  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;             // Switch on the event system peripheral
  PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].DIRSET.reg = 1 << g_APinDescription[LED_BUILTIN].ulPin;    // Set digital pin LED_BUILTIN to an OUTPUT
 
  // RTC Generic Clock (GCLK) Configuration //////////////////////////////////////////
  GCLK->GENDIV.reg =  GCLK_GENDIV_DIV(4) |          // Select clock divisor to divide by 32 (2 ^ (4 + 1))
                      GCLK_GENDIV_ID(4);            // Select GLCK4
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
 
  GCLK->GENCTRL.reg = //GCLK_GENCTRL_RUNSTDBY |       // Enable run standby mode
                      GCLK_GENCTRL_IDC |            // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |          // Enable GCLK
                      GCLK_GENCTRL_DIVSEL |         // Set GLCK divisor as 2 to the power of (divisor) value
                      GCLK_GENCTRL_SRC_OSC32K |    // Select GCLK source as external 32.768kHz crystal (XOSC32K)
                      GCLK_GENCTRL_ID(4);           // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
    
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |          // Enable GCLK
                      GCLK_CLKCTRL_ID_RTC |         // Connect to the RTC at 1.024kHz (32.768kHz/32)
                      GCLK_CLKCTRL_GEN_GCLK4;       // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TC4 Generic Clock (GCLK) Configuration //////////////////////////////////////////
  GCLK->GENDIV.reg =  GCLK_GENDIV_DIV(3) |          // Select clock divisor to divide by 3 48MHz/3=16MHz
                      GCLK_GENDIV_ID(5);            // Select GLCK5
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization

  GCLK->GENCTRL.reg = //GCLK_GENCTRL_RUNSTDBY |       // Enable run standby mode
                      GCLK_GENCTRL_IDC |            // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |          // Enable GCLK
                      GCLK_GENCTRL_SRC_DFLL48M |    // Select GCLK source as the 48MHz DFLL (DFLL48M)
                      GCLK_GENCTRL_ID(5);           // Select GCLK5
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
 
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |          // Enable GCLK
                      GCLK_CLKCTRL_ID_TC4_TC5 |     // Connect to timers TC4 an TC5
                      GCLK_CLKCTRL_GEN_GCLK5;       // Select GCLK5
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
 
  // Timer TC4 configuration ////////////////////////////////////////////////////
  TC4->COUNT32.CTRLA.reg = TC_CTRLA_PRESCALER_DIV16 |    // Set the timer prescaler to divide by 16: 16MHz/16=1MHz
                           TC_CTRLA_PRESCSYNC_PRESC |    // Reload timer on next prescaler clock
                           TC_CTRLA_MODE_COUNT32;        // Set the TC4 timer to 32-bit mode in conjuction with timer TC5

  // RTC configuration //////////////////////////////////////////////////////////
  RTC->MODE2.CTRL.bit.SWRST = 1;                       // Software reset the RTC
  while (RTC->MODE2.STATUS.bit.SYNCBUSY);              // Wait for synchronization ...
  while(RTC->MODE2.CTRL.bit.ENABLE);                   // Wait for the RTC to become disabled

  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

  NVIC_SetPriority(RTC_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for the RTC
  NVIC_EnableIRQ(RTC_IRQn);         // Connect Event System to Nested Vector Interrupt Controller (NVIC)

  RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0;        // Set RTC ALARM0 interrupt

  RTC->MODE2.CLOCK.reg = 0x516AE000;                          // Initialise the RTC: May 21st 2020, 14:00 hours, ref year: 2000
  while (RTC->MODE2.STATUS.bit.SYNCBUSY);                     // Wait for synchronization

jazzar

Complete Code Part 2/2:
Code: [Select]
// Event System configuration //////////////////////////////////////////////////
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU);                // Set the event user (receiver) as timer TC4
                  
  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_RTC_CMP_0) |       // Set event generator (sender) as external RTC compare 0
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0

  RTC->MODE2.EVCTRL.reg = RTC_MODE2_EVCTRL_ALARMEO0;                       // Set RTC to generate an event output on ALARM0

  TC4->COUNT32.EVCTRL.reg = TC_EVCTRL_TCEI |                               // Enable asynchronous input events
                            TC_EVCTRL_EVACT_RETRIGGER;                     // Retrigger the TC4 timer upon receiving an event

  // Enable RTC and Timer TC4 ////////////////////////////////////////////////////////
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;                   // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);            // Wait for synchronization

  TC4->COUNT32.READREQ.reg = TC_READREQ_RCONT |        // Enable a continuous read request
                             TC_READREQ_ADDR(0x10);    // Offset of the 32-bit COUNT register
  while(TC4->COUNT32.STATUS.bit.SYNCBUSY);             // Wait for (read) synchronization

  RTC->MODE2.CTRL.bit.ENABLE = 1;                      // Enable the RTC
  while (RTC->MODE2.STATUS.bit.SYNCBUSY);              // Wait for synchronization
}

void loop()
{
  
  if((millis() - ms) >= 60000)
  {
    ms = millis();
    uint32_t microseconds = TC4->COUNT32.COUNT.reg;      // Read the TC4 timer's COUNT register
    uint32_t milliseconds = microseconds / 1000;         // Calculate the elapsed milliseconds
    microseconds %= 1000;                                // Calculate the reamaining elapsed microseconds
    while (RTC->MODE2.STATUS.bit.SYNCBUSY);              // Wait for read synchronization (6ms delay)
    timeStamp.reg = RTC->MODE2.CLOCK.reg;                // Read the RTC calendar clock
  //  Serial.print(F("Year: "));                        // Display the timestamp results
  //  Serial.println(2000 + timeStamp.bit.year);        // Use reference year: 2000
  //  Serial.print(F("Month: "));
  //  Serial.println(timeStamp.bit.month);
  //  Serial.print(F("Day: "));
  //  Serial.println(timeStamp.bit.day);
  //  Serial.print(F("Hour: "));
  //  Serial.println(timeStamp.bit.hour);
  //  Serial.print(F("Minute: "));
    Serial.print(timeStamp.bit.minute);
    Serial.print(F(":"));
  //  Serial.print(F("Second: "));
    Serial.print(timeStamp.bit.second);
  //  Serial.print(F("Millisecond: "));
    Serial.print(F("."));
    Serial.println(milliseconds);
  //  Serial.print(F("Microsecond: "));
  //  Serial.println(microseconds);
  //  Serial.println();
  }
}

void RTC_Handler(void)                                  // Event System interrupt handler
{
  if (RTC->MODE2.INTFLAG.bit.ALARM0)                    // Check if an ALARM0 interrupt has occured
  {
    RTC->MODE2.INTFLAG.bit.ALARM0 = 1;                  // Clear the interrupt flag
    PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTTGL.reg = 1 << g_APinDescription[LED_BUILTIN].ulPin; // Toggle the LED_BUILTIN
    if (!RTC->MODE2.STATUS.bit.SYNCBUSY)                // Test if synchronization isn't in operation
    {
      RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND = (RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND + 1) % 60;   // Increment the ALARM0 compare register
    }
  }
}

jazzar

Terminal Output that shows time drift:
Format min:sec.millisec
Code: [Select]
12:11:41.652 -> UP
12:12:38.923 -> 0:56.637
12:13:38.912 -> 1:56.1
12:14:38.931 -> 2:55.382
12:15:38.913 -> 3:54.763
12:16:38.935 -> 4:54.126
12:17:38.922 -> 5:53.488
12:18:38.918 -> 6:52.846
12:19:38.943 -> 7:52.217
12:20:38.933 -> 8:51.601
12:21:38.924 -> 9:50.981
12:22:38.916 -> 10:50.351
12:23:38.942 -> 11:49.743
12:24:38.932 -> 12:49.121
12:25:38.925 -> 13:48.509
12:26:38.947 -> 14:47.887
12:27:38.939 -> 15:47.263
12:28:38.931 -> 16:46.653
12:29:38.948 -> 17:46.29
12:30:38.939 -> 18:45.412
12:31:38.930 -> 19:44.801
12:32:38.945 -> 20:44.178
12:33:38.936 -> 21:43.556
12:34:38.960 -> 22:42.931
12:35:38.951 -> 23:42.298
12:36:38.943 -> 24:41.663
12:37:38.966 -> 25:41.26
12:38:38.963 -> 26:40.403
12:39:38.957 -> 27:39.780
12:40:38.945 -> 28:39.154
12:41:38.971 -> 29:38.531
12:42:38.967 -> 30:37.905
12:43:38.957 -> 31:37.272
12:44:38.957 -> 32:36.650
12:45:38.956 -> 33:36.16
12:46:38.980 -> 34:35.395
12:47:38.960 -> 35:34.765
12:48:38.981 -> 36:34.126
12:49:38.976 -> 37:33.509
12:50:38.970 -> 38:32.896
12:51:38.967 -> 39:32.262
12:52:38.963 -> 40:31.638
12:53:38.992 -> 41:30.1001
12:54:38.985 -> 42:30.355
12:55:38.981 -> 43:29.718
12:56:38.973 -> 44:29.67
12:57:38.998 -> 45:28.434
12:58:38.986 -> 46:27.801
12:59:39.002 -> 47:27.164
13:00:38.993 -> 48:26.536
13:01:38.987 -> 49:25.901
13:02:39.010 -> 50:25.263
13:03:38.996 -> 51:24.633
13:04:38.989 -> 52:23.1003
13:05:39.018 -> 53:23.364
13:06:39.009 -> 54:22.728
13:07:39.005 -> 55:22.79
13:08:39.024 -> 56:21.452
13:09:39.017 -> 57:20.820
13:10:39.006 -> 58:20.173
13:11:39.032 -> 59:19.525

MartinL

Hi jazzar,

The SAMD21's 32.768kHz internal oscillator (OSC32K) isn't very precise. If used as a clock source for the RTC, expect an accuracy of ±20 minutes per day. It looks like your internal oscillator is losing around 14.8 minutes/day. By contrast the 32.768kHz ±20ppm external crystal (XOSC32K) is accurate to around ±1.7 seconds per day.

If you're using a board with an 32.768kHz external crystal, the Arduino core will by default use this as the clock source (through the 48MHz DFLL) for the SAMD21's system tick timer that generates the millis() and micros() timing functions. This means that these timing functions over the long term will be more accurate than the RTC peripheral clocked from the internal 32kHz oscillator.

jazzar

Hi MartinL,

I used a MKR wifi 1010, which after checking the schematics has an external oscillator, which the itsybitsy 
m0 what I switched to now, doesn't have. I somehow assumed the mkr wifi was also using the internal crystal hence my confusion as to why the same code suddenly drifts in time.

Thanks for the explanation!

MartinL

Hi jazzar,

As you mention, the Adafruit Itsy Bitsy M0 is crystalless, as are the Itsy Bitsy M4 and Arduino Nano 33 IoT.

The external crystal (or lack of) is hardly ever mentioned in the board specs, but if you happen to be measuring absolute time with an RTC, it can be essential.

borland

#7
Dec 19, 2020, 02:54 am Last Edit: Dec 20, 2020, 10:18 pm by borland Reason: problem solved.
To avoid using UNIX time stamp, I modified the above code. And wondering how to configure the RTC's clock for 12 hours with AM/PM, I went a little further.  Looked at github library RTCZero, but it seems like they only implement the library for 24 hour clock.

Here's Part 1 of 2
Code: [Select]
// Set-up RTC and timer TC4 to generate a timestamp down to microsecond accuracy
// Modified for 12/24 hour clocks

union {                                              // Timestamp union/structure
  struct {
    uint32_t second : 6;
    uint32_t minute : 6;
    uint32_t hour : 4;
    uint32_t _AMPM : 1;
    uint32_t day : 5;
    uint32_t month : 4;
    uint32_t year : 6;
  } bit;
  uint32_t reg;
} timeStamp;

// Initialize Real Time Counter Clock/Calendar

const byte seconds = 0;
const byte minutes = 59;
const byte hours = 11; // 1-12 (12 hour clock)
const byte _ampm = 0;  // 0 = AM, 1 = PM
const byte day = 20;
const byte month = 12;
const byte year = 2020;

unsigned long ms;

void setup(){
  // Initialise native serial port, event system and LED output //////////////////////
 
  ms = millis();
 
  SerialUSB.begin(115200);                           // Initialise the native serial port
  while (!SerialUSB);                                // Wait for the console to open
  SerialUSB.println("STARTUP");
 
 
  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;             // Switch on the event system peripheral
  PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].DIRSET.reg = 1 << g_APinDescription[LED_BUILTIN].ulPin;    // Set digital pin LED_BUILTIN to an OUTPUT
 
  // RTC Generic Clock (GCLK) Configuration //////////////////////////////////////////
  GCLK->GENDIV.reg =  GCLK_GENDIV_DIV(4) |          // Select clock divisor to divide by 32 (2 ^ (4 + 1))
                      GCLK_GENDIV_ID(4);            // Select GLCK4
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
 
  GCLK->GENCTRL.reg = //GCLK_GENCTRL_RUNSTDBY |       // Enable run standby mode
                      GCLK_GENCTRL_IDC |            // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |          // Enable GCLK
                      GCLK_GENCTRL_DIVSEL |         // Set GLCK divisor as 2 to the power of (divisor) value
                      GCLK_GENCTRL_SRC_XOSC32K |    // Select GCLK source as external 32.768kHz crystal (XOSC32K)
                      GCLK_GENCTRL_ID(4);           // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
   
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |          // Enable GCLK
                      GCLK_CLKCTRL_ID_RTC |         // Connect to the RTC at 1.024kHz (32.768kHz/32)
                      GCLK_CLKCTRL_GEN_GCLK4;       // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TC4 Generic Clock (GCLK) Configuration //////////////////////////////////////////
  GCLK->GENDIV.reg =  GCLK_GENDIV_DIV(3) |          // Select clock divisor to divide by 3 48MHz/3=16MHz
                      GCLK_GENDIV_ID(5);            // Select GLCK5
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization

  GCLK->GENCTRL.reg = //GCLK_GENCTRL_RUNSTDBY |       // Enable run standby mode
                      GCLK_GENCTRL_IDC |            // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |          // Enable GCLK
                      GCLK_GENCTRL_SRC_DFLL48M |    // Select GCLK source as the 48MHz DFLL (DFLL48M)
                      GCLK_GENCTRL_ID(5);           // Select GCLK5
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
 
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |          // Enable GCLK
                      GCLK_CLKCTRL_ID_TC4_TC5 |     // Connect to timers TC4 an TC5
                      GCLK_CLKCTRL_GEN_GCLK5;       // Select GCLK5
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization
 
  // Timer TC4 configuration ////////////////////////////////////////////////////
  TC4->COUNT32.CTRLA.reg = TC_CTRLA_PRESCALER_DIV16 |    // Set the timer prescaler to divide by 16: 16MHz/16=1MHz
                           TC_CTRLA_PRESCSYNC_PRESC |    // Reload timer on next prescaler clock
                           TC_CTRLA_MODE_COUNT32;        // Set the TC4 timer to 32-bit mode in conjuction with timer TC5

  // RTC configuration //////////////////////////////////////////////////////////
  RTC->MODE2.CTRL.bit.SWRST = 1;                       // Software reset the RTC
 
  while (RTC->MODE2.STATUS.bit.SYNCBUSY);              // Wait for synchronization ...
  // Why this DISABLED check after RTC RESET???
  while(RTC->MODE2.CTRL.bit.ENABLE);                   // Wait for the RTC to become disabled

  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

  NVIC_SetPriority(RTC_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for the RTC
  NVIC_EnableIRQ(RTC_IRQn);         // Connect Event System to Nested Vector Interrupt Controller (NVIC)

  RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0;  // Set RTC ALARM0 interrupt

  //RTC->MODE2.CLOCK.reg = 0x516AE000; // Initialise the RTC: May 21st 2020, 14:00 hours, ref year: 2000
  //*
  RTC->MODE2.CTRL.bit.CLKREP = 1; // 0 for 24hr, 1 for 12hr (AM/PM) clock
 
  RTC->MODE2.CLOCK.bit.DAY = day;
  RTC->MODE2.CLOCK.bit.MONTH = month;
  RTC->MODE2.CLOCK.bit.YEAR = year - 2000; // reference year 2000 is a leap year
  RTC->MODE2.CLOCK.bit.SECOND = seconds;
  RTC->MODE2.CLOCK.bit.MINUTE = minutes;
  RTC->MODE2.CLOCK.bit.HOUR = ( _ampm << 4 ) + hours;
  //*/
  while (RTC->MODE2.STATUS.bit.SYNCBUSY); // Wait for synchronization


Code part 2 of 2 below.

borland

Code Part 2 of 2

Code: [Select]
// Event System configuration //////////////////////////////////////////////////
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU);                // Set the event user (receiver) as timer TC4
                  
  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_RTC_CMP_0) |       // Set event generator (sender) as external RTC compare 0
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0

  RTC->MODE2.EVCTRL.reg = RTC_MODE2_EVCTRL_ALARMEO0;                       // Set RTC to generate an event output on ALARM0

  TC4->COUNT32.EVCTRL.reg = TC_EVCTRL_TCEI |                               // Enable asynchronous input events
                            TC_EVCTRL_EVACT_RETRIGGER;                     // Retrigger the TC4 timer upon receiving an event

  // Enable RTC and Timer TC4 ////////////////////////////////////////////////////////
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;                   // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);            // Wait for synchronization

  TC4->COUNT32.READREQ.reg = TC_READREQ_RCONT |        // Enable a continuous read request
                             TC_READREQ_ADDR(0x10);    // Offset of the 32-bit COUNT register
  while(TC4->COUNT32.STATUS.bit.SYNCBUSY);             // Wait for (read) synchronization

  RTC->MODE2.CTRL.bit.ENABLE = 1;                      // Enable the RTC
  while (RTC->MODE2.STATUS.bit.SYNCBUSY);              // Wait for synchronization
}

void loop()
{
  if((millis() - ms) >= 60000)
  {
    ms = millis();
    uint32_t microseconds = TC4->COUNT32.COUNT.reg;      // Read the TC4 timer's COUNT register
    uint32_t milliseconds = microseconds / 1000;         // Calculate the elapsed milliseconds
    microseconds %= 1000;                                // Calculate the reamaining elapsed microseconds
    //while (RTC->MODE2.STATUS.bit.SYNCBUSY);              // Wait for read synchronization (6ms delay)
    timeStamp.reg = RTC->MODE2.CLOCK.reg;                // Read the RTC calendar clock
  //  SerialUSB.print(F("Year: "));                        // Display the timestamp results
  //  SerialUSB.println(2000 + timeStamp.bit.year);        // Use reference year: 2000
  //  SerialUSB.print(F("Month: "));
  //  SerialUSB.println(timeStamp.bit.month);
  //  SerialUSB.print(F("Day: "));
  //  SerialUSB.println(timeStamp.bit.day);
  //  SerialUSB.print(F("Hour: "));
    SerialUSB.print(timeStamp.bit.hour);
  //  SerialUSB.print(F("Minute: "));    
    SerialUSB.print(F(":"));
    SerialUSB.print(timeStamp.bit.minute < 10 ? "0" : "");
    SerialUSB.print(timeStamp.bit.minute);
    SerialUSB.print(F(":"));
  //  SerialUSB.print(F("Second: "));
    SerialUSB.print(timeStamp.bit.second < 10 ? "0" : "");
    SerialUSB.print(timeStamp.bit.second);
    SerialUSB.print( timeStamp.bit._AMPM == 0 ? "AM" : "PM");
  //  SerialUSB.print(F("Millisecond: "));
    SerialUSB.print(F(" -> "));
    SerialUSB.println(milliseconds);
  //  SerialUSB.print(F("Microsecond: "));
  //  SerialUSB.println(microseconds);
  //  SerialUSB.println();
  }
}

void RTC_Handler(void)                                  // Event System interrupt handler
{
  if (RTC->MODE2.INTFLAG.bit.ALARM0)                    // Check if an ALARM0 interrupt has occured
  {
    RTC->MODE2.INTFLAG.bit.ALARM0 = 1;                  // Clear the interrupt flag
    PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTTGL.reg = 1 << g_APinDescription[LED_BUILTIN].ulPin; // Toggle the LED_BUILTIN
    if (!RTC->MODE2.STATUS.bit.SYNCBUSY)                // Test if synchronization isn't in operation
    {
      RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND = (RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND + 1) % 60;   // Increment the ALARM0 compare register
    }
  }
}


Output:
Code: [Select]
STARTUP
11:59:58AM -> 57591
12:00:58PM -> 57601
12:01:58PM -> 57609



Hope that helps someone.

Go Up