oneshot TC4 interrupts without driving pin

I'm trying to configure TC4 as one-shot timer with an interrupt at MC0 (5ms) and OVF at 16-bit (65535) top value (=around 10ms). Is this possible with TC_CTRLA_WAVEGEN_NPWM without driving external pin?

Furthermore I assume it's possible to retrigger the oneshot even when the timer is still counting?

So far:

/ 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

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 | TC_INTFLAG_MC0;    // Clear the interrupt flags
TC4->COUNT16.INTENSET.reg = TC_INTENSET_MC0 | TC_INTENSET_OVF;	// Enable MC0 and MC1 interrupts

TC4->COUNT16.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;		// Enable Oneshot operation on TC4
	
REG_TC4_CTRLA = TC_CTRLA_PRESCALER_DIV1 |								
TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_NPWM;			// Put the timer TC4 into normal PWM mode
	
REG_TC4_COUNT16_CC0 = 30000;				// half period of 100Hz (5ms)
while (TC4->COUNT16.STATUS.bit.SYNCBUSY);		// Wait for synchronization

TC4->COUNT16.CTRLA.bit.ENABLE = 1;			// Enable TC4
while (TC4->COUNT16.STATUS.bit.SYNCBUSY);		// Wait for synchronization

The retrigger command:

TC4->COUNT16.CTRLBSET.reg |= TC_CTRLBSET_CMD_RETRIGGER;
while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

The ISR:

 void TC4_Handler() 
 {
	if(TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF) 
	{
		REG_TC4_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag
	}

	if(TC4->COUNT16.INTFLAG.bit.MC0 && TC4->COUNT16.INTENSET.bit.MC0) 
	{
		REG_TC4_INTFLAG = TC_INTFLAG_MC0;         // Clear the OVF interrupt flag
	}
 }

I've configured GCLK_CLKCTRL_GEN_GCLK4 as 48MHz/DIV8.

It seems the re-trigger doesn't work, when I instead, disable and enable the TC4 timer via the CTRLA register, it works OK.

Hi LeCrAm,

Here's an example of using timer TC4 in oneshot mode to generate an interrupt 5ms after the software trigger. To measure the "time to interrupt", the code uses GPIO to generate sets digital pin D7 HIGH prior to the retrigger and clears to LOW in the interrupt:

// Set timer TC4 to call the TC4_Handler 5ms after oneshot software re-trigger
void setup() {
  PORT->Group[PORTA].DIRSET.reg = PORT_PA21;               // Set D7 as a digital output
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |                 // Enable GCLK0
                      GCLK_CLKCTRL_GEN_GCLK0 |             // Select GCLK0 at 48MHz
                      GCLK_CLKCTRL_ID_TC4_TC5;             // Connecto to timers TC4 and TC5
 
  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.INTENSET.reg = TC_INTENSET_MC0;             // Enable TC4 match conpare 0 (MC0) interrupt

  TC4->COUNT16.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;         // Set the TC4 timer to Oneshot mode
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC4->COUNT16.CC[0].reg = 29999;                          // Set timer TC4 to call the TC4_Handler after 5ms: (8 * (29999 + 1)) / 48MHz = 5ms
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC4->COUNT16.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reset timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV8;        // Set prescaler to 8, 48MHz/8 = 6MHz                        

  TC4->COUNT16.CTRLA.bit.ENABLE = 1;                       // Enable the TC4 timer
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
}

void loop() 
{
  PORT->Group[PORTA].OUTSET.reg = PORT_PA21;               // Set the D7 output to HIGH
  TC4->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;   // Retrigger the TC4 timer
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  //delay(3);                                                // Wait for 3 milliseconds to test if retriggering works before interrupt called  
  //TC4->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;   // Retrigger the TC4 timer
  //while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  delay(1000);                                             // Wait for 1 second
}

void TC4_Handler()                                         // Interrupt Service Routine (ISR) for timer TC4
{       
  TC4->COUNT16.INTFLAG.bit.MC0 = 1;                        // Clear the MC0 interrupt flag
  PORT->Group[PORTA].OUTCLR.reg = PORT_PA21;               // Clear the D7 output to LOW - should generate 5ms pulse output
}

Just to test the retriggering before the interrupt occurs, I added the commented out lines. This code waits for 3ms before initiating a second retrigger. As anticipated, this extends the pulse by a further 3ms (to 8ms), indicating that the second retrigger has indeed reset the timer.

Hi LeCrAm,

There appears to be problem retriggering two times in quick succession with TC4, as it sometimes appears to randomly miss an interrupt every minute or so.

I've tried the same sketch on TC3 and it by contrast performs faultlessly.

To switch to TC3 instead, just change the generic clock control register to:

GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |                 // Enable GCLK0
                    GCLK_CLKCTRL_GEN_GCLK0 |             // Select GCLK0 at 48MHz
                    GCLK_CLKCTRL_ID_TCC2_TC3;            // Connect to timers TCC2 and TC3

...and change all instances of TC4 to TC3.