I want to setup an interrupt every 0.5s and 1s. I'm trying to setup the interrupt using the Real Time Counter since it is better suited for long time interrupts lasting seconds when compared to the Timer Counters (TC) or Timer Counter for Control (TCC).
I'm aware of the RTCZero library, however my requirement is I do not need a calendar clock in my application, just a counter that causes an interrupt every 0.5s and 1s.
RTCZero: https://github.com/MarkusLange/RTCZero
So, I'm trying to modify the RTCZero code to operate the RTC in 16-bit mode (MODE1) with an interrupt every 0.5s and 1s. I'm setting up the clock source (XOSC32K) and the generic clock (GCLK2) in the same way as the library. Only I'm running the clock in 16-bit mode with two interrupts, overflow (OVF) and compare0 (CMP0). Both the interrupts are not triggered. The RTC_Handler() is also not triggered. Any suggestions on what's wrong?
void rtc_init(){
// Configure clock source and clock generators (gclk.h)------------------------
SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_ONDEMAND |
SYSCTRL_XOSC32K_RUNSTDBY |
SYSCTRL_XOSC32K_EN32K |
SYSCTRL_XOSC32K_XTALEN |
SYSCTRL_XOSC32K_STARTUP(6) |
SYSCTRL_XOSC32K_ENABLE;
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | // Select GLCK2
GCLK_GENDIV_DIV(4); // Select prescalar 1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) | // Select GCLK2
GCLK_GENCTRL_SRC_XOSC32K | // Select GCLK SRC
GCLK_GENCTRL_IDC | // Improve duty cycle for odd div factors
GCLK_GENCTRL_RUNSTDBY |
GCLK_GENCTRL_GENEN; // Enable GCLK2
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_GEN_GCLK2 | // Select GCLK2
GCLK_CLKCTRL_ID_RTC |
GCLK_CLKCTRL_CLKEN; // Connect to RTC
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
// Configure power manager (pm.h)-------------------------------------------
PM->APBAMASK.reg = PM_APBAMASK_RTC; // Setup Power manager for ADC peripheral
PM->APBASEL.reg = PM_APBASEL_APBADIV_DIV1; // Select prescalar 1
// RTC configuration (rtc.h)--------------------------------------------------
RTC->MODE1.CTRL.reg = ~RTC_MODE1_CTRL_ENABLE; // Disable RTC for configuration
while (RTC->MODE1.STATUS.bit.SYNCBUSY); // Wait for sync
RTC->MODE1.CTRL.reg = RTC_MODE1_CTRL_SWRST;
while (RTC->MODE1.STATUS.bit.SYNCBUSY); // Wait for sync
RTC->MODE1.CTRL.reg = RTC_MODE1_CTRL_MODE_COUNT16; // RTC mode 1 count 16
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
RTC->MODE1.CTRL.reg = RTC_MODE1_CTRL_PRESCALER_DIV16; // RTC prescalar 16
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
RTC->MODE1.READREQ.reg = RTC_READREQ_RCONT;
RTC->MODE1.PER.reg = RTC_MODE1_PER_PER(0x0400); // Interrupt time 1s
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
RTC->MODE1.COMP[0].reg = RTC_MODE1_COMP_COMP(0x200); // Interrupt time 0.5s
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
// Configure RTC interrupts ------------------------------------------
RTC->MODE1.INTENCLR.reg = RTC_MODE1_INTENCLR_OVF |
RTC_MODE1_INTENCLR_CMP0; // Clear Mode 1 interrupts
RTC->MODE1.INTENSET.reg = RTC_MODE1_INTENSET_OVF |
RTC_MODE1_INTENSET_CMP0; // Set Mode 1 interrupts ; // Set result ready interrupt
NVIC_SetPriority(RTC_IRQn, 4); // Set the Nested Vector Interrupt Controller (NVIC) priority for RTC
NVIC_EnableIRQ(RTC_IRQn); // Connect RTC to Nested Vector Interrupt Controller (NVIC)
// Enable RTC--------------------------------------------------------------
RTC->MODE1.CTRL.reg = RTC_MODE1_CTRL_ENABLE;
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
RTC->MODE1.CTRL.reg = ~RTC_MODE1_CTRL_SWRST;
while (RTC->MODE1.STATUS.bit.SYNCBUSY); // Wait for sync
}
void RTC_Handler(void){
PORT->Group[PORTA].OUTTGL.reg = PORT_PA21;
if (RTC->MODE1.INTFLAG.bit.OVF && RTC->MODE1.INTENSET.bit.OVF) { // A overflow caused the interrupt
PORT->Group[PORTA].OUTTGL.reg = PORT_PA21;
RTC->MODE1.INTFLAG.reg = RTC_MODE1_INTFLAG_OVF; // reset interrupt flag
}
if (RTC->MODE1.INTFLAG.bit.CMP0 && RTC->MODE1.INTENSET.bit.CMP0) { // A compare caused the interrupt
PORT->Group[PORTA].OUTTGL.reg = PORT_PA11;
RTC->MODE1.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP0; // reset interrupt flag
}
}
Hi parsec326,
Managed to get the RTC_Hander() function working with the following code:
// RTC interrupts set to 1s (PER) and 0.5s (COMP0)
void setup(){
PORT->Group[PORTA].DIRSET.reg = PORT_PA17; // Set digital pin D13 as an output
PORT->Group[PORTA].DIRSET.reg = PORT_PA21; // Set digital pin D7 as an output
// Configure clock source and clock generators (gclk.h)------------------------
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | // Select GLCK2
GCLK_GENDIV_DIV(1); // Select clock divisor to 1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) | // Select GCLK2
GCLK_GENCTRL_SRC_XOSC32K | // Select GCLK 32.768kHz
GCLK_GENCTRL_IDC | // Improve duty cycle for odd div factors
GCLK_GENCTRL_RUNSTDBY | // Enable run standby mode
GCLK_GENCTRL_GENEN; // Enable GCLK2
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_GEN_GCLK2 | // Select GCLK2
GCLK_CLKCTRL_ID_RTC | // Connect to the RTC
GCLK_CLKCTRL_CLKEN; // Enable GCLK2
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
// RTC configuration (rtc.h)--------------------------------------------------
RTC->MODE1.PER.reg = RTC_MODE1_PER_PER(0x7FFF); // Interrupt time 1s
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
RTC->MODE1.COMP[0].reg = RTC_MODE1_COMP_COMP(0x3FFF); // Interrupt time 0.5s
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
// Configure RTC interrupts ------------------------------------------
RTC->MODE1.INTENSET.reg = RTC_MODE1_INTENSET_OVF |
RTC_MODE1_INTENSET_CMP0; // Set Mode 1 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)
// Enable RTC--------------------------------------------------------------
RTC->MODE1.CTRL.reg |= RTC_MODE1_CTRL_PRESCALER_DIV1 | // Set prescaler to 1
RTC_MODE1_CTRL_MODE_COUNT16 | // Set RTC to mode 1, 16-bit timer
RTC_MODE1_CTRL_ENABLE; // Turn on the RTC
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
}
void loop(){}
void RTC_Handler(void)
{
if (RTC->MODE1.INTFLAG.bit.OVF && RTC->MODE1.INTENSET.bit.OVF) { // A overflow caused the interrupt
PORT->Group[PORTA].OUTTGL.reg = PORT_PA21; // Toggle digital pin D7
RTC->MODE1.INTFLAG.reg = RTC_MODE1_INTFLAG_OVF; // reset interrupt flag
}
if (RTC->MODE1.INTFLAG.bit.CMP0 && RTC->MODE1.INTENSET.bit.CMP0) { // A compare caused the interrupt
PORT->Group[PORTA].OUTTGL.reg = PORT_PA17; // Toggle digital pin D13
RTC->MODE1.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP0; // reset interrupt flag
}
}
However, with this code there appears to be a strange interaction going on with the Arduino core code that causes the native USB port to cease working. This can only be recovered by double tapping the reset button to enter bootloader mode. I haven't quite yet figured out what's going on.
Thanks for the reply MartinL!
I tried out your code on my controller. I'm not seeing the RTC interrupt with your unmodified code. With your code PORT_PA17 is constantly high, PORT_PA21 is constantly low. After that I did try to make some changes like:
Changing GCLK -> GCLK6+ Interrupt priority 0 - No RTC interrupt
Changing Interrupt priority -> GCLK2 + Interrupt priority 8 - No RTC interrupt.
Regarding your point about native USB not working, I'm not seeing that either. I can still program without hardware reset. (I'm using M0 pro) But either way, I'm not sure if its safe to use the lower numbered GCLKs (GCLK0,GCLK1,GCLK2,GCLK3). All of my code uses GCLK4 and above.
All of my code also uses DFLL48M as the clock source and I get very reliable interrupts. I'm using XOSC32K only for RTC. I looked at the arduino startup code here (ArduinoCore-samd/startup.c at master · arduino/ArduinoCore-samd · GitHub) and it seems like XOSC32K is used for GCK1. RTCZero (https://github.com/MarkusLange/RTCZero/blob/master/src/RTCZero.cpp) uses GCLK2 for XOSC32K. For both these reasons, I didn't want to use GCLK2 or GCLK1 in my code.
So tl;dr is I'm not closer to figuring this out either.
Hi parsec326,
Finally solved it!
The issue was that I was setting the MODE bitfields and the ENABLE bit in the RTC's CTRL register at the same time.
By first setting the RTC to MODE1, (no synchronization required):
RTC->MODE1.CTRL.reg |= RTC_MODE1_CTRL_PRESCALER_DIV1 | // Set prescaler to 1
RTC_MODE1_CTRL_MODE_COUNT16; // Set RTC to mode 1, 16-bit timer
...then subsequently setting the enable bit, (synchronization required):
RTC->MODE1.CTRL.reg |= RTC_MODE1_CTRL_ENABLE; // Turn on the RTC
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
...allows the RTC and RTC_Handler() interrupt service routine to run correctly.
Here's the code, with the RTC in MODE1 running on GCLK4:
// RTC interrupts set to 1s (PER) and 0.5s (COMP0)
void setup(){
PORT->Group[PORTA].DIRSET.reg = PORT_PA17;
PORT->Group[PORTA].DIRSET.reg = PORT_PA21;
// Configure clock source and clock generators (gclk.h)------------------------
GCLK->GENDIV.reg = GCLK_GENDIV_ID(4) | // Select GLCK4
GCLK_GENDIV_DIV(1); // Select clock divisor to 1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(4) | // Select GCLK4
GCLK_GENCTRL_SRC_XOSC32K | // Select GCLK 32.768kHz
GCLK_GENCTRL_IDC | // Improve duty cycle for odd div factors
GCLK_GENCTRL_RUNSTDBY | // Enable run standby mode
GCLK_GENCTRL_GENEN; // Enable GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for SYNC
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 SYNC
// RTC configuration (rtc.h)--------------------------------------------------
RTC->MODE1.CTRL.reg |= RTC_MODE1_CTRL_PRESCALER_DIV1 | // Set prescaler to 1
RTC_MODE1_CTRL_MODE_COUNT16; // Set RTC to mode 1, 16-bit timer
RTC->MODE1.PER.reg = RTC_MODE1_PER_PER(0x7FFF); // Interrupt time 1s
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
RTC->MODE1.COMP[0].reg = RTC_MODE1_COMP_COMP(0x3FFF); // Interrupt time 0.5s
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
// Configure RTC interrupts ------------------------------------------
RTC->MODE1.INTENSET.reg = RTC_MODE1_INTENSET_OVF |
RTC_MODE1_INTENSET_CMP0; // Set Mode 1 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)
// Enable RTC--------------------------------------------------------------
RTC->MODE1.CTRL.reg |= RTC_MODE1_CTRL_ENABLE; // Turn on the RTC
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
}
void loop(){}
void RTC_Handler(void)
{
if (RTC->MODE1.INTFLAG.bit.OVF && RTC->MODE1.INTENSET.bit.OVF) { // A overflow caused the interrupt
PORT->Group[PORTA].OUTTGL.reg = PORT_PA21; // Toggle digital pin D7
RTC->MODE1.INTFLAG.reg = RTC_MODE1_INTFLAG_OVF; // reset interrupt flag
}
if (RTC->MODE1.INTFLAG.bit.CMP0 && RTC->MODE1.INTENSET.bit.CMP0) { // A compare caused the interrupt
PORT->Group[PORTA].OUTTGL.reg = PORT_PA17; // Toggle digital pin D13
RTC->MODE1.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP0; // reset interrupt flag
}
}
Thank you MartinL. Your RTC configuration is perfect!
A couple of subtle issues for anyone else trying to configure the RTC and finding out about this message thread:
-
RTC->MODE1.CTRL.reg has some bits that are synchronized (RTC->MODE1.CTRL.bit.ENABLE) and the rest that are unsynchronized.
-
All the unsynchronized bits have to be configured at the SAME time WITHOUT waiting for sync! I realize now that my old code was waiting for the RTC->MODE1.STATUS.bit.SYNCBUSY bit in some places when that bit was never enabled. And setting the bits at the SAME time is also important, both MODE and PRESCALAR must be configured in a SINGLE operation. I tried out setting it in two lines of C and it doesn't work.
-
The RTC->MODE1.CTRL.bit.SWRST is a one-way reset bit, ie you can't unreset it by setting it zero.
-
Its a good idea to ~RTC->MODE1.CTRL.bit.ENABLE before configuring the RTC.
-
I realize after seeing the interrupts on the scope that a CMP0 interrupt does NOT trigger with a shorter duration when compared to OVF interrupt. In fact the interrupt trig interval duration of both CMP0 and OVF interrupt will be same. CMP0 does however trigger EARLIER than OVF.
P.S: I don't know if there's a way to mark this thread [SOLVED], but either way my RTC problem is resolved!!
Hi All,
Thanks for the code. Using Arduino 1.8.10 and an Adafruit Feather M0 with Adafruit SAMD 1.5.9, the init code has already configured the RTC, so I needed the following just after the GCLK configuration:
RTC->MODE1.CTRL.reg |= RTC_MODE2_CTRL_SWRST;
while (RTC->MODE1.STATUS.bit.SYNCBUSY);
HTH.
david