Timer interrupt on arduino zero board

Hello all,

I am having problem of setting timer interrupt frequency in M0 board. I read the timer function in SAMD21 datasheet and on the internet i found the following example code which generates interrupt on specific time. But i dont know this calculation. How to generate an interrupt at every one seconds using this.

long x;

void setup()
{
  //Serial5.begin(9600);
  delay(100);
  REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | 0x1B) ;
  while (GCLK->STATUS.bit.SYNCBUSY == 1); // wait for sync 

  // The type cast must fit with the selected timer mode
  TcCount16* TC = (TcCount16*) TC3; // get timer struct

  TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;   // Disable TC
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 

  TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16;  // Set Timer counter Mode to 16 bits
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 
  TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_NFRQ; // Set TC as normal Normal Frq
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 

  TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV256;   // Set perscaler
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 
  
  // TC->PER.reg = 0xFF;   // Set counter Top using the PER register but the 16/32 bit timer counts allway to max  
  // while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 

  TC->CC[0].reg = 250;
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 
  
  // Interrupts 
  TC->INTENSET.reg = 0;              // disable all interrupts
//TC->INTENSET.bit.OVF = 1;          // enable overfollow
  TC->INTENSET.bit.MC0 = 1;          // enable compare match to CC0

  // Enable InterruptVector
  NVIC_EnableIRQ(TC3_IRQn);

  // Enable TC
  TC->CTRLA.reg |= TC_CTRLA_ENABLE;
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 
}

void loop() {
  // put your main code here, to run repeatedly:

}

void TC3_Handler()
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
  
  if (TC->INTFLAG.bit.MC0 == 1)
  {
   
      ++x;
 
    //Serial5.println(TARGETSTEPS1);
    TC->INTFLAG.bit.MC0 = 1;    // writing a one clears the flag ovf flag
  }
}

Hi jigisha,

The following code calls the TC4_Handler() function every second:

// Set timer TC4 to call the TC4_Handler every second
void setup() {
  // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  REG_TC4_COUNT16_CC0 = 0xB71A;                   // Set the TC4 CC0 register as the TOP value in match frequency mode
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  //NVIC_DisableIRQ(TC4_IRQn);
  //NVIC_ClearPendingIRQ(TC4_IRQn);
  NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

  REG_TC4_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
  REG_TC4_INTENSET = TC_INTENSET_OVF;             // Enable TC4 interrupts
  // REG_TC4_INTENCLR = TC_INTENCLR_OVF;          // Disable TC4 interrupts
 
  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1024 |   // Set prescaler to 1024, 48MHz/1024 = 46.875kHz
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode 
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
}

void loop() {}

void TC4_Handler()                              // Interrupt Service Routine (ISR) for timer TC4
{     
  // Check for overflow (OVF) interrupt
  if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF)             
  {
    // Put your timer overflow (OVF) code here:     
    // ...
   
    REG_TC4_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag
  }
}

The code puts timer TC4 into match frequency (MFRQ) mode. In this mode the timer counts up to the value in the CC0 register before overflowing and resetting the timer back to 0.

I've set the generic clock 4 (GCLK4) to 48MHz and the timer prescaler to 1024. Therefore the timer's being clocked at 46.875kHz.

Using the formula:

timer frequency = generic clock frequency / (N * (CC0 + 1))

where:
N = timer prescaler
CC0 = value in the CC0 register

...we can calculate CC0 for a timer frequency of 1Hz.

So,

CC0 = (48MHz / 1024) - 1 = 46874 = 0xB71A (hex)

This will cause the timer to overflow every second.

Hi MartinL,

Thank you so much for this information.Its working .

Hi,

I am interested in your sketch to generate 40 KHz stable frequency with my M0PRO, on a dedicated PIN output.

I tried to compile your code, as it is, and I got this error message, could you help ?
And could you tell me what I have to adjust to get an interrupt every 25 µs ?

Many thanks,

Daniel

Arduino : 1.7.9 (Windows XP), Carte : "Arduino M0 Pro (Programming Port)"

Gene_frequence.ino: In function 'void setup()':

Gene_frequence.ino:19:22: error: 'GCLK_CLKCTRL_ID_TC4_TC5' was not declared in this scope

Erreur lors de la compilation.

MartinL:
Hi jigisha,

The following code calls the TC4_Handler() function every second:

// Set timer TC4 to call the TC4_Handler every second

void setup() {
 // Set up the generic clock (GCLK4) used to clock timers
 REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                   GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
 while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                    GCLK_GENCTRL_GENEN |         // Enable GCLK4
                    GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                    GCLK_GENCTRL_ID(4);          // Select GCLK4
 while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

// Feed GCLK4 to TC4 and TC5
 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                    GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                    GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
 while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

REG_TC4_COUNT16_CC0 = 0xB71A;                   // Set the TC4 CC0 register as the TOP value in match frequency mode
 while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

//NVIC_DisableIRQ(TC4_IRQn);
 //NVIC_ClearPendingIRQ(TC4_IRQn);
 NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
 NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

REG_TC4_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
 REG_TC4_INTENSET = TC_INTENSET_OVF;             // Enable TC4 interrupts
 // REG_TC4_INTENCLR = TC_INTENCLR_OVF;          // Disable TC4 interrupts

REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1024 |   // Set prescaler to 1024, 48MHz/1024 = 46.875kHz
                  TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode
                  TC_CTRLA_ENABLE;               // Enable TC4
 while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
}

void loop() {}

void TC4_Handler()                              // Interrupt Service Routine (ISR) for timer TC4
{    
 // Check for overflow (OVF) interrupt
 if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF)            
 {
   // Put your timer overflow (OVF) code here:    
   // ...
 
   REG_TC4_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag
 }
}

I tried running your code on my Arduino Zero and it didn't work for me. Could it be outdated?

Any help would be appreciated, thanks

Hi sukalo98,

I just tested the above code with my Arduino Zero, toggling the D8 output every time the TC4's interrupt service routine is called and it works just fine, generating a 0.5Hz square wave output. That means the ISR is being called every second.

Here's the code toggling the output on D8, (using the alternative register pointer notation):

// Set timer TC4 to call the TC4_Handler every second
void setup() {
  PORT->Group[PORTA].DIRSET.reg = PORT_PA06;       // Set-up digital pin D8 as an output
  
  // Set up the generic clock (GCLK4) used to clock timers
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);             // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |         // Enable GCLK4
                      GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                      GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Feed GCLK4 to TC4 and TC5
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                      GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                      GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  TC4->COUNT16.CC[0].reg = 0xB71A;                 // Set the TC4 CC0 register as the TOP value in match frequency mode
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  //NVIC_DisableIRQ(TC4_IRQn);
  //NVIC_ClearPendingIRQ(TC4_IRQn);
  NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

  TC4->COUNT16.INTFLAG.bit.OVF = 1;                        // Clear the interrupt flags
  TC4->COUNT16.INTENSET.bit.OVF = 1;                       // Enable TC4 interrupts
 
  TC4->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1024 |   // Set prescaler to 1024, 48MHz/1024 = 46.875kHz
                            TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode
                            TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
}

void loop() {}

void TC4_Handler()                                 // Interrupt Service Routine (ISR) for timer TC4
{     
  // Check for overflow (OVF) interrupt
  if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF)             
  {
    // Put your timer overflow (OVF) code here:     
    // ...
    PORT->Group[PORTA].OUTTGL.reg = PORT_PA06;     // Toggle the D8 pin HIGH LOW
    TC4->COUNT16.INTFLAG.bit.OVF = 1;              // Clear the OVF interrupt flag
  }
}

Hm, maybe it's my alterations to the code then?

bool test = false;

// Set timer TC4 to call the TC4_Handler every second
void setup()
{
  Serial.begin(115200);

  // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_TC4_COUNT16_CC0 = 0xB71A;                   // Set the TC4 CC0 register as the TOP value in match frequency mode
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  //NVIC_DisableIRQ(TC4_IRQn);
  //NVIC_ClearPendingIRQ(TC4_IRQn);
  NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

  REG_TC4_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
  REG_TC4_INTENSET = TC_INTENSET_OVF;             // Enable TC4 interrupts
  // REG_TC4_INTENCLR = TC_INTENCLR_OVF;          // Disable TC4 interrupts

  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1024 |   // Set prescaler to 1024, 48MHz/1024 = 46.875kHz
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
}

void loop()
{
  if (test) Serial.println("TRUE");
}

void TC4_Handler()                              // Interrupt Service Routine (ISR) for timer TC4
{
  // Check for overflow (OVF) interrupt
  if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF)
  {
    // Put your timer overflow (OVF) code here:
    test = true;

    REG_TC4_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag
  }
}

I tried setting a flag with the OVF is triggered, triggering a statement to print in the main loop and that didn't work. In a different alteration, I also tried printing a statement directly in the TC4_handler function and setting the onboard LED to high when the OVF was triggered, still no success.

Could it be due to the fact I have an XBee shield hooked up to the Arduino Zero? (I doubt this is the cause, but just a thought)

Thanks,
Suky

Hi Suky,

As the "test" variable is used in both the main loop() and the interrupt service routine TC4_Handler() it needs to be declared as volatile:

volatile bool test = false;

Here's a link to the Arduino webpage that explains (better than I can) what's going on: https://www.arduino.cc/reference/en/language/variables/variable-scope--qualifiers/volatile/.

Kind regards,
Martin