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