Interrupt running at wrong frequency

I am trying to get an interrupt working using TC3. The below code works fine, except for the fact that it appears to run at only .5MHz instead of the intended 48MHz. CC[0] is set to 4166. My oscilloscope, connected to PA15, shows a period of 8300ms, so that works about to be half a megahertz.

Looking at the code, I am pretty sure no prescaling is being done and that the clock should run at 48MHz, but I guess I must be missing something.

Does anybody here have any idea?

#include "sam.h"

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

	const uint16_t minimum_interrupt_delay = 48000000UL / (8 * 90 * (1 << 4));

	GCLK->GENCTRL.bit.ID = 0x4;
	GCLK->GENCTRL.bit.OE = 0x1;

	// Generic clock config for TC3
	GCLK->CLKCTRL.bit.ID |= 0x1B;				// Clock selection (TC3)
	GCLK->CLKCTRL.bit.GEN = 0x4;				// Clock Generator
	GCLK->CLKCTRL.bit.CLKEN = 0x1;				// Enable

	// Enable APB Clock for TC3
	PM->APBCMASK.bit.TC3_ = 0x1;

	TC3->COUNT16.INTENCLR.bit.MC0 = 0x1;
	TC3->COUNT16.CTRLA.bit.ENABLE = 0x0;
	TC3->COUNT16.CTRLA.bit.PRESCSYNC = 0x1;		// 48MHz
	TC3->COUNT16.CTRLA.bit.MODE = 0x0;			// 16-bit timer
	TC3->COUNT16.CTRLA.bit.WAVEGEN = 0x1;		// Normal
	TC3->COUNT16.CTRLA.bit.PRESCALER = 0x00;	// 1:1 prescaler
	TC3->COUNT16.EVCTRL.bit.MCEO0 = 0x1;
	TC3->COUNT16.CC[0].bit.CC = minimum_interrupt_delay;

	// Set up interrupt for TC3
	TC3->COUNT16.INTENSET.bit.MC0 = 0x1;
	NVIC_ClearPendingIRQ(TC3_IRQn);
	NVIC_SetPriority(TC3_IRQn, 1);
	NVIC_EnableIRQ(TC3_IRQn);
		
	while (TC3->COUNT16.STATUS.bit.SYNCBUSY);
		
	TC3->COUNT16.CTRLA.bit.ENABLE = 0x1;

	PORT->Group[0].PMUX[7].bit.PMUXO = 0x4; // PA15 Mux TC3/WO[1] (E)
	PORT->Group[0].PINCFG[15].bit.PMUXEN = 0x1; // PA15 Mux ON	
}

Hi rsscpl,

Your TC timer is running at 48MHz, however you've set the timer to match frequency mode (MFRQ) with the line:

TC3->COUNT16.CTRLA.bit.WAVEGEN = 0x1; // Normal

In match frequency mode the timer frquency is given by the formula:

TC_Frequency = GCLK_Frequency / (TC_Prescaler * CC0 + 1)

The CC0 register acts as the TOP value of your timer cycle.

However, in match frequency mode you also have to take into account that the fact the output pin is toggled, this effectively further divides your output frequency by 2.

At what frequency were you intending to trigger the TC interrupt?

Hi MartinL,

Thanks for your response and for pointing me in the right direction.

I am trying to get it to trigger the interrupt at 48MHz (/4166, so about 11.5K times per second). I will study the data sheet some more tonight and see if I can figure it out.

Hi rsscp1,

Here's some example code:

// Set timer TC3 to call the TC3_Handler every 86.8us
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 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 the GCLK4 to TCC2 and TC3
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  // Enable output to test, should be a frequency of 5760Hz
  // Enable the port multiplexer for the digital pin D10
  PORT->Group[g_APinDescription[10].ulPort].PINCFG[g_APinDescription[10].ulPin].bit.PMUXEN = 1;

  // Connect the TC3 timer to the port output D10 - port pins are paired odd PMUXO and even PMUXE
  // In this case peripheral E specifies the TC3/WO[0] timer output
  PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg = /*PORT_PMUX_PMUXO_E |*/ PORT_PMUX_PMUXE_E;
  
  REG_TC3_COUNT16_CC0 = 4166;                     // Set the TC3 CC0 register as the TOP value in match frequency mode
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  NVIC_SetPriority(TC3_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest)
  NVIC_EnableIRQ(TC3_IRQn);         // Connect TC3 to Nested Vector Interrupt Controller (NVIC)

  REG_TC3_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
  REG_TC3_INTENSET = TC_INTENSET_OVF;             // Enable TC3 interrupts
  // REG_TC3_INTENCLR = TC_INTENCLR_OVF;          // Disable TC3 interrupts
 
  REG_TC3_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC3 into match frequency (MFRQ) mode 
                   TC_CTRLA_ENABLE;               // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
}

void loop() {}

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

Thank you so much for the sample code! It does exactly what I want and my oscilloscope shows the correct frequency on the test pin.