Arduino ZERO timer interrupts

i have red the ATSAMD21 datasheet and some example codes for the arduino DUE. do you have any idea what is wrong with my approach?

/*

  • Blink__Interrupt_.c
  • Created: 24-6-2015 13:15:52
  • Author: Dell Pc
    */

#include "sam.h"

/**

  • \brief Application entry point.
  • \return Unused (ANSI-C compatibility).
    */

int main(void)
{
/* Initialize the SAM system */
SystemInit();

REG_PORT_DIRSET0 |= (1 << (17%32));//Pin 13 is now an output

REG_PORT_OUTSET0 |= (1 << (17%32));//Pin 13 == High

/setup timer:/

REG_PM_APBCMASK |= (1 << (8%32));//Enable CLK_TCC0_APB

REG_TCC0_CTRLA |= (1 << (3%32));//set MODE[1:0] to 0x2 for 32 bit counting (TOP = 468741, log2(468741) = 18.8384)

REG_TCC0_CTRLA |= (1 << (5%32));//set WAVEGEN[1:0] to 0x1 for matching the frequency operation

REG_TCC0_CTRLA |= (1 << (8%32))|(1 << (9%32))|(1 << (10%32));//set PRESCALER[2:0] to 0x7, prescaler == 1024

REG_TCC0_CTRLA |= (1 << (12%32));//set PRESYNC[1:0] to 0x1 to Reload or reset the counter on next prescaler clock (PRESC).

REG_TCC0_CC0 = 468741;//Reload or reset the counter on next prescaler clock thus TOP(1Hz) = ((4810^6)/(11024))-1 = 468741 (TOP=f_clock/(f_PWM×Prescaler)-1).

REG_TCC0_INTENSET |= (1 << (0x00));//Enable Overflow/Underflow interrupt by writing bit 0 (OVF) to 1

NVIC_EnableIRQ(TCC0_IRQn);//Enable TCC0 Interrupt

REG_TCC0_CTRLA |= (1 << (1%32));//Enable and start the timer

while (1)
{
/wacht op een interrupt/
//REG_PORT_OUTTGL0 |= (1 << (17%32));//Toggle Pin 13
}
}

void TCC0_Handler()
{
REG_PORT_OUTTGL0 |= (1 << (17%32));//Toggle Pin 13
}

Hi RRLAN,

SAMD21 and SAM3X belongs to different families: even if both are CPU based on a Cortex-M core, the devices (PORT, TIMERS, SPI, UARTS, ecc.) are different and low-level code cannot be transported as is from SAM3X to SAMD21.

For example in your sketch:

   REG_TCC0_CTRLA |= (1 << (3%32));//set MODE[1:0] to 0x2 for 32 bit counting (TOP = 468741, log2(468741) = 18.8384)
   REG_TCC0_CTRLA |= (1 << (5%32));//set WAVEGEN[1:0] to 0x1 for matching the frequency operation

if you read carefully the datasheet of the SAMD21 (chapter 30.8.1) you'll never find the MODE[1:0] bits or the WAVEGEN[1:0] bits in the TCC0.CTRLA register.

That is the reason why it is always preferred to use the Arduino API for this kind of operation if you want to make portable code (or any other specialized library if the functions provided by the Arduino Core are not powerful enough) instead of writing directly on the registers of the CPU.

thanks a lot, i was looking at chapter 29 instead of 30.

Hello cmaglie

I would like to see how it looks like with the Arduino API.
Would it be possible for you to post a working piece of code?

Greetings
Markus

I'm curious about this as well. I'm versed in avr-gcc, and how to use avr-gcc code to set timers in Arduino for the boards based on the AVR platform, but I'm not certain what cmaglie is referring to if he does not mean using the CMSIS library. Thanks.

moresun:
Hello cmaglie

I would like to see how it looks like with the Arduino API.
Would it be possible for you to post a working piece of code?

Greetings
Markus

Hi RRLAN

At least would it be possible to get your final working example?

Greetings

Hi RRLAN,

I'm also trying to use a timer interrupt on the zero.

I have copied your code and when uploaded to the Zero the function SystemInit() causes the arduino to hang.
When I comment out SystemInit() the arduino doesn't hang, but ISR is never called.

Any chance you or anyone else could give us an example of a working Arduino Zero timer interrupt configuration?

OMac:
Any chance you or anyone else could give us an example of a working Arduino Zero timer interrupt configuration?

Oooh, another very interesting and reasonable question without answer. Come on, Arduino guys...your documentation sucks

HI

After a vacation and some work I got it :slight_smile: .
I have two versions one for the TC timer chapter 29 and an other one for the TCC chapter 30 of the SAMD21 handbook.

Markus

29. TC – Timer/Counter

/**
 * @author Markus Bader
 * @brief this program shows how to use the TC timer with interrupts on an Arduino Zero board
 * @email markus.bader@tuwien.ac.at
 */
 
int pin_ovf_led = 13;  // debug pin for overflow led 
int pin_mc0_led = 5;  // debug pin for compare led 
unsigned int loop_count = 0;
unsigned int irq_ovf_count = 0;

void setup() {

  pinMode(pin_ovf_led, OUTPUT);   // for debug leds
  digitalWrite(pin_ovf_led, LOW); // for debug leds
  pinMode(pin_mc0_led, OUTPUT);   // for debug leds
  digitalWrite(pin_mc0_led, LOW); // for debug leds

  
  // Enable clock for TC 
  REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3) ;
  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 = 0xFFF;
  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() {
  // dummy
  delay(250);
}

void TC3_Handler()
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
  if (TC->INTFLAG.bit.OVF == 1) {  // A overflow caused the interrupt
    digitalWrite(pin_ovf_led, irq_ovf_count % 2); // for debug leds
    digitalWrite(pin_mc0_led, HIGH); // for debug leds
    TC->INTFLAG.bit.OVF = 1;    // writing a one clears the flag ovf flag
    irq_ovf_count++;                 // for debug leds
  }
  
  if (TC->INTFLAG.bit.MC0 == 1) {  // A compare to cc0 caused the interrupt
    digitalWrite(pin_mc0_led, LOW);  // for debug leds
    TC->INTFLAG.bit.MC0 = 1;    // writing a one clears the flag ovf flag
  }
}

30. TCC – Timer/Counter for Control Applications

/**
 * @author Markus Bader
 * @brief this program shows how to use the TCC timer with interrupts on an Arduino Zero board
 * @email markus.bader@tuwien.ac.at
 */
 
int pin_ovf_led = 13; // debug pin for overflow led 
int pin_mc0_led = 5;  // debug pin for compare led 
unsigned int loop_count = 0;
unsigned int irq_ovf_count = 0;

void setup() {

  pinMode(pin_ovf_led, OUTPUT);   // for debug leds
  digitalWrite(pin_ovf_led, LOW); // for debug leds
  pinMode(pin_mc0_led, OUTPUT);   // for debug leds
  digitalWrite(pin_mc0_led, LOW); // for debug leds

  
  // Enable clock for TC 
  REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC0_TCC1) ;
  while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync 


  // The type cast must fit with the selected timer 
  Tcc* TC = (Tcc*) TCC0; // get timer struct
  
  TC->CTRLA.reg &= ~TCC_CTRLA_ENABLE;   // Disable TC
  while (TC->SYNCBUSY.bit.ENABLE == 1); // wait for sync 


  TC->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV256;   // Set perscaler


  TC->WAVE.reg |= TCC_WAVE_WAVEGEN_NFRQ;   // Set wave form configuration 
  while (TC->SYNCBUSY.bit.WAVE == 1); // wait for sync 

  TC->PER.reg = 0xFFFF;              // Set counter Top using the PER register  
  while (TC->SYNCBUSY.bit.PER == 1); // wait for sync 

  TC->CC[0].reg = 0xFFF;
  while (TC->SYNCBUSY.bit.CC0 == 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(TCC0_IRQn);

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

}

void loop() {
  // dummy
  delay(250);
}

void TCC0_Handler()
{
  Tcc* TC = (Tcc*) TCC0;       // get timer struct
  if (TC->INTFLAG.bit.OVF == 1) {  // A overflow caused the interrupt
    digitalWrite(pin_ovf_led, irq_ovf_count % 2); // for debug leds
    digitalWrite(pin_mc0_led, HIGH); // for debug leds
    TC->INTFLAG.bit.OVF = 1;    // writing a one clears the flag ovf flag
    irq_ovf_count++;                 // for debug leds
  }
  
  if (TC->INTFLAG.bit.MC0 == 1) {  // A compare to cc0 caused the interrupt
    digitalWrite(pin_mc0_led, LOW);  // for debug leds
    TC->INTFLAG.bit.MC0 = 1;    // writing a one clears the flag ovf flag
  }
}

Great!
Could this code be "packaged" as a library? (à la GitHub - ivanseidel/DueTimer: ⏳ Timer Library fully implemented for Arduino DUE for Due)
This would be fantastic.
Thanks!

Hi

There is no interface, it is just a demo

Hi

I placed some demo programs on github:

moresun:
Hi

I placed some demo programs on github:
GitHub - maxbader/arduino_tools: Arduino libraries and samples

Your demo programs are great - any advice on how to set the timer frequency? I can change it manually by adjusting TC->PER.reg and TC->CC.reg but I'd love to know how to set the interval to a specific time duration like I can with the TimerOne library.

Hi,

I don't know if this is what you need, but here is an example sketch that shows how the clock frequency for the timers can be set for an M0 Pro. (Note only TC3 and TC5 are available because TC4 is used internally for the micros() routine in the M0 Pro - I am not sure about the Zero). If you want to set the top-count and generate interrupts when this is reached you might be better of with using the TCC. This is because they are designed to be used in this mode for PWM control and H-bridge motor drivers.

For my part, I have been trying to read the TCC counter values for use as a 24-bit live-time clock. This will reduce latency due to interrupt servicing compared to 16 bit timer counters. Sadly, despite a months of evenings testing things and reading the manual, I did not manage to get this to work. It seems it only works using the event system.

I wish you success!

Harry J. Whitlow

/////////////////////////////////////////////////////////////////////////
//
//
//   Real time clock on TC3
//
//
//
/////////////////////////////////////////////////////////////////////////

volatile uint32_t realTcount = 0x0 ;   // Counter for superticks (overflow interrupts)


void setup() {
Serial.begin(9600);
setTC3clock();
}


/*
This is test code that counts to 10 s then resets and waits 3s to test 
reset, start and stop functions on TC3. 
*/
void loop() {
  
  delay(250);
  
    Serial.print(realTime(), 7);
    Serial.println();
    double val = realTime();
   
    if( int(val) >= 10)
    {
  resetStartTC3();  // reset so never gets above 10 s
      stopTC3();
    delay(3000); //wait 3 sec
    startTC3();
    }
    
}





void TC3_Handler()  // Interrupt on overflow
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
    realTcount++;                    // Increment the supertick register
    TC->INTFLAG.bit.OVF = 1;    // writing a one clears the ovf flag
//  }
}

/*
Get the real time in seconds.
*/
double realTime() 
{   
double  realTime =  (realTcount * 1.2288E-2) + (REG_TC3_COUNT16_COUNT * 1.875E-7) ;;
return realTime;
}


/*
  Setup the Generic clock register
*/


void setTC3clock()
{
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC2_TC3 ));
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
  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_DIV8;   // Set perscaler
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
  // Interrupts
  TC->INTENSET.reg = 0;              // disable all interrupts
  TC->INTENSET.bit.OVF = 1;          // enable overfollow interrup
  // Enable InterruptVector
  NVIC_EnableIRQ(TC3_IRQn);
  // Enable TC
  TC->CTRLA.reg |= TC_CTRLA_ENABLE;
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
}



void resetStartTC3()
{
   TcCount16* TC = (TcCount16*) TC3; // get timer struct
   realTcount = 0x00;                // Zero superclicks
    TC->CTRLBSET.reg |= TC_CTRLBCLR_CMD_RETRIGGER;   // restart
}

void stopTC3()
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
    TC->CTRLBSET.reg |= TC_CTRLBSET_CMD_STOP;   // Stop counter
}


void startTC3()
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
    TC->CTRLBSET.reg |= TC_CTRLBSET_CMD_RETRIGGER;   //  Start
}

OK, I think I have this figured out. I'm using the TCC0 example from moresun's site. In order to change the timer count in the middle of the program you have to invoke Tcc* TC = (Tcc*) TCC0;

Here's my function for changing the counter:

void setTimer(long period) {

  // The type cast must fit with the selected timer

 // must do this or you can't touch the timer

  Tcc* TC = (Tcc*) TCC0; // get timer struct



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

TC->PER.reg = period;              // Set counter Top using the PER register  
  while (TC->SYNCBUSY.bit.PER == 1); // wait for sync 

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

Changing the prescaler in the middle of the program crashes my application, so I need to figure out what's up with that. There's also a weird spot in the timer top count: if I set the timer with a period of less than 4185 the speed goes DOWN instead of up, and over a certain point I have to feed it HALF the period to get the right output speed. I've got a hack in my application now to do this but it's odd.

But thanks to moresun for the examples, very helpful.

Hi pharaohamps,

It's also possible to change the frequency of generic clock (GCLK) that feeds the timers. There are 8 generic clocks: 0..3 are used by the Zero's core, but 4..7 are free.

This is done by setting the Generic Clock Generator Division register (GENDIV). GCLKs 3..8 allow an 8 bit divisor from 1 to 255.

So for example to feed a timer TC3 with a 16MHz clock from generic clock 4:

// Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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 TCC2 (and TC3)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 (and TC3)
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 (and TC3)
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

The timer's own prescaler (CTRLA register) can then be used to divide the frequency down further, for instance a prescaler divsor of 8 will set the timer at 2MHz.

Hello,

With an earlier Arduino I set up 2 timer interrupts like this...

TCCR1A = 0;
TCCR1B = (1<<WGM12)|(1<<CS12);
OCR1A = 255;
TCNT1 = 0;

TCCR2A = (1<<WGM21);
TCCR2B = (1<<CS22)|(1<<CS21);
OCR2A = 255;
TCNT2 = 0;

TIMSK1 |= (1<<OCIE1A);
TIMSK2 |= (1<<OCIE2A);

And then these timers call functions on interrupt...

ISR(TIMER1_COMPA_vect)
ISR(TIMER2_COMPA_vect)

Can someone help me configure the same timers for the Zero.

Thanks

Hi markab,

Here's an example of using Timer Counter 4 (TC4) in 8-bit mode on the SAMD21:

void setup() {
   // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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_CTRLA |= TC_CTRLA_MODE_COUNT8;           // Set the counter to 8-bit mode
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization 

  REG_TC4_COUNT8_CC0 = 0x55;                      // Set the TC4 CC0 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
  REG_TC4_COUNT8_CC1 = 0xAA;                      // Set the TC4 CC1 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
  REG_TC4_COUNT8_PER = 0xFF;                      // Set the PER (period) register to its maximum value
  while (TC4->COUNT8.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_MC1 | TC_INTFLAG_MC0 | TC_INTFLAG_OVF;        // Clear the interrupt flags
  REG_TC4_INTENSET = TC_INTENSET_MC1 | TC_INTENSET_MC0 | TC_INTENSET_OVF;     // Enable TC4 interrupts
  // REG_TC4_INTENCLR = TC_INTENCLR_MC1 | TC_INTENCLR_MC0 | TC_INTENCLR_OVF;     // Disable TC4 interrupts
  
  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV64 |     // Set prescaler to 64, 16MHz/64 = 256kHz
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
}

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

}

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

  // Check for match counter 0 (MC0) interrupt
  if (TC4->COUNT8.INTFLAG.bit.MC0 && TC4->COUNT8.INTENSET.bit.MC0)             
  {
    // Put your counter compare 0 (CC0) code here:
    // ...
    
    REG_TC4_INTFLAG = TC_INTFLAG_MC0;         // Clear the MC0 interrupt flag 
  }

  // Check for match counter 1 (MC1) interrupt
  if (TC4->COUNT8.INTFLAG.bit.MC1 && TC4->COUNT8.INTENSET.bit.MC1)            
  {
    // Put your counter compare 1 (CC1) code here:
    // ...
    
    REG_TC4_INTFLAG = TC_INTFLAG_MC1;        // Clear the MC1 interrupt flag 
  }
}

perfect thanks

Thanks moresun, MartinL and others for contributing here. You have got me moving in the right direction. I downloaded moresun's examples and have been playing around with them and want to add a few things I discovered.

First, setting the "top" value of the counter is different depending on if you get a pointer to a TcCount16 or TcCount8 struct. For TcCount16 (and I would assume TcCount32) you use the CC member:

//  create a struct to help access TC3's registers for a 16 bit timer counter
TcCount16* TC = (TcCount16*) TC3;

(other setup details covered in examples above)

//  set the top value to 32767
TC->CC[0].reg = 0x7FFF;
while (TC->STATUS.bit.SYNCBUSY == 1);

But for 8 bit counters where you get a pointer to a TcCount8 struct you use the PER member:

//  create a struct to help access TC3's registers for a 16 bit timer counter
TcCount8* TC = (TcCount8*) TC3;

(other setup details covered in examples above)

//  set the top value to 128
TC->PER.reg = 0x7F;
while (TC->STATUS.bit.SYNCBUSY == 1);

Full disclosure, I never got an 8 bit counter running while using a pointer to a TcCount8 struct. I probably missed something simple... I did get an 8 bit counter running using TcCount16:

//  create a struct to help access TC3's registers for a 16 bit timer counter
TcCount16* TC = (TcCount16*) TC3;

TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT8
while (TC->STATUS.bit.SYNCBUSY == 1); 

(other setup details covered in examples above)

//  set the top value to 128
TC->CC[0].reg = 0x7F;

The second point I would like to clarify is that you must sett the TC wave generation mode to TC_CTRLA_WAVEGEN_MFRQ if you want the TC to honor your top value. Setting it to TC_CTRLA_WAVEGEN_NFRQ will cause the counter to ignore the "top" value and always count to the maximum for that type. For example 65535 for a 16 bit counter. You can set CC[0] but it will be ignored.

Lastly, while I couldn't get it to compile since it couldn't find all of the required header files I did find the Adafruit_ZeroTimer which looks promising. Just looking at the code and the included ASF source files helped me.

I hope this helps.