Go Down

Topic: independent Timer Interrupts (Read 569 times) previous topic - next topic

gab27

Hi

I have done a Timer Interrupt on the Arduino Zero.

To enable a clock for a Timer Interrupt I did this:

Code: [Select]
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TC4_TC5

I'm unsure about this last command ID_TC4_TC5. With this I have now a TC4_Handler and a TC5_Handler. That worked. But they are not independent? So for example they have the same period, then first TC4 is handled and afterwards the TC5? So they are dependent on each other?

And second I have this four commands GCLK_CLKCTRL_ID_TCC0_TCC1, GCLK_CLKCTRL_ID_TCC2_TC3, GCLK_CLKCTRL_ID_TC4_TC5, GCLK_CLKCTRL_ID_TC7_TC8. So at the end I have only four timer interrupts?

Thanks you

MartinL

Hi gab27,

The SAMD21 used on the Arduino Zero has a number of clock sources including the 32.768kHz external crystal and the 48MHz generated by the chip's digital frequency locked loop (DFLL).

These clock sources can be routed to the SAMD21's on-board peripherals, such as the TCC/TC timers via the generic clock controller. The generic clock controller has 8 generic clocks channels in total, GCLK0 to GCLK3 are used by the Arduino core code, leaving channels GCLK4 to GCLK7 free.

The generic clock controller can route a selected clock source though a generic clock channel to a timer pair. The pairs being TCC0/TCC1, TCC2/TC3 and TC4/TC5. (Timers TC6 and TC7 are only available on the larger 64-pin SAMD21J variant).

Apart from sharing the same generic clock, the timer pairs are able to operate completely independently from one another. The timers' frequency being controlled by the their respective period (PER) registers.

Furthermore, each timer has its own interrupt service routine and set of match compare (MCx)/overflow (OVF) interrupts.


gab27

Hi Martin

thanks for your answer. Everything seems clear, but I think that timer4 is handled before timer5.

Code: [Select]
// timer freq = generic clock freq / (prescaler * (CC0+1))
// example: timer freq=1Hz -> CC0 = (48MHz/1024)-1 = 0xB71A, pre 1024
// now: CC0 = 48MHz/(256*10) - 1 = 0x493D, frequenz of 10Hz


volatile bool ledState = LOW; // ledState used to set the LED

unsigned long start_time;

// Set timer TC4 to call the TC4_Handler every second
void setup() {
  SerialUSB.begin(9600);
 
 
//  // 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 GCLK0 to TCC0 and TCC1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK0 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0
                     GCLK_CLKCTRL_ID_TCC0_TCC1;     // Feed the GCLK0 to TCC2 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  // Feed GCLK0 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

 

 
 
 
 
  //4
  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

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

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

  REG_TC5_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
  REG_TC5_INTENSET = TC_INTENSET_OVF;             // Enable TC5 interrupts
  // REG_TC5_INTENCLR = TC_INTENCLR_OVF;          // Disable TC5 interrupts
 
  REG_TC5_CTRLA |= TC_CTRLA_PRESCALER_DIV1024 |   // Set prescaler to 1024, 48MHz/1024 = 46.875kHz
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC5 into match frequency (MFRQ) mode
                   TC_CTRLA_ENABLE;               // Enable TC5
  while (TC5->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)             
  {
    start_time = micros();
   
    // Put your timer overflow (OVF) code here:     
    // ...
    ledState =! ledState;
    digitalWrite(LED_BUILTIN, ledState ? HIGH : LOW);
   
    SerialUSB.print("TC4_Handler: ");
    SerialUSB.println(millis());

   
    REG_TC4_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag

   
  }
}

void TC5_Handler()                              // Interrupt Service Routine (ISR) for timer TC5
{     
  // Check for overflow (OVF) interrupt
  if (TC5->COUNT16.INTFLAG.bit.OVF && TC5->COUNT16.INTENSET.bit.OVF)             
  {
    // Put your timer overflow (OVF) code here:     
    // ...
    unsigned long elapsed_time = micros() - start_time;
    SerialUSB.print("time between start of timer 4 and start of timer 5: ");
    SerialUSB.print(elapsed_time);
    SerialUSB.println("us");
    SerialUSB.print("TC5_Handler: ");
    SerialUSB.println(millis());
   
    REG_TC5_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag

  }
}




I have defined TC4 and TC5 and I have defined a time from the start of TC4_Handler to the start of TC5_Handler. It's about 80us, but always different.
And if I changed the priority bit for Timer4 to 1, then first Timer5 will be handled. So one depends on the other? And that is not so good ;D

Thanks

MartinL

Hi gab27,

The TC4 and TC5 timers work completely independently from one another.

The priority bit has nothing to do with the timer peripherals themselves, but instead is an ARM processor feature that indicates to the CPU the relative priorities of the interrupt service routines (ISR). In your case the TC4_Handler() and TC5_Handler() functions.

This means that an ISR with their priority set to 0 is executed before, and is able to interrupt, an ISR with its priority set to 1.

As it stands, the timers are operating asynchronously from one another. If you're attempting to synchronize them you could use the SAMD21's event system. The event system is a 12 channel communications highway between peripherals and can allow one timer to set a trigger to activate another.

gab27

Hi martin,

thanks for your informative answers.

But can you explain why the elapsed_time I have defined in TC5_Handler is about 80us. And if I write more lines of code into TC4_Handler, then the time grows.
So the CPU can only handle one timer interrupt in exact the same moment?

Thanks

david_prentice

Be realistic.   The MCU has only one core.   It can only service one ISR() at a time even if it can interrupt one to service another ISR() with higher priority.

It is often easier to specify exactly what you want to do in English.
Then you might receive advice on how to implement it.

ARM code works pretty fast but you still have to obey the general rules for ISR()s e.g  avoid wait-loops and avoid unnecessary calls to external functions.

David.

MartinL

Hi gab27,

Quote
So the CPU can only handle one timer interrupt in exact the same moment?
Yes that's right, the timer interrupts can occur at the same time, but as David says the CPU only has a single core, so the ISRs are executed sequentially one after another.

gab27

ok that sounds ok. Thanks for the answers.

Go Up