Go Down

Topic: RTC interrupt initialization in Mode 1 without using RTCZero library (Read 385 times) previous topic - next topic

parsec326

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?

Code: [Select]

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
    }
}


MartinL

Hi parsec326,

Managed to get the RTC_Hander() function working with the following code:

Code: [Select]
// 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.

parsec326

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 (https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/startup.c#L139) 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.

MartinL

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):

Code: [Select]
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):

Code: [Select]
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:

Code: [Select]
// 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
  }
}

parsec326

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:
1. RTC->MODE1.CTRL.reg has some bits that are synchronized (RTC->MODE1.CTRL.bit.ENABLE) and the rest that are unsynchronized.

2. 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.

3. The RTC->MODE1.CTRL.bit.SWRST is a one-way reset bit, ie you can't unreset it by setting it zero.

4. Its a good idea to ~RTC->MODE1.CTRL.bit.ENABLE before configuring the RTC.

5. 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!!

Go Up