SAMD51 timers and interrupts

Hi
I'm trying to generate interrupts from a SAMD51 timer - don't mind which one - but I'm failing to get anything to happen in the interrupt routine.
I have used code from MartinL and Microchip but it doesn't seem to do what I expect. It may be that I'm not understanding what the timer can do but it seems to be able to do what I want except it hasn't so far.
I'm generating a square wave using a timer and would like an interrupt to happen every compare match.
The Microchip site here implies the code will work but it doesn't on my Adafruit M4 Express Feather - it needs editing before it will compile in Arduino: (Configuring SAM D21 Interrupts - Developer Help). I know it SAMD21- based but the registers all look the same to me :slight_smile:

volatile bool check = false;
volatile bool LED_state = false;

void setup() {
    // put your setup code here, to run once:

    pinMode(13, OUTPUT);
  /* Enable TC3 Overflow Interrupt Signal */
  TC0->COUNT16.INTENSET.bit.OVF = 1;

  /* Enable TC3 Error Interrupt Signal */
  TC0->COUNT16.INTENSET.bit.ERR = 1;

  // Configure asynchronous clock source
  // TC0 setup for PWM on pin A1
  // 38kHz (+- 100Hz)
  MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC0;           // Activate timer TC0
 
  // Set up the generic clock (GCLK7) used to clock timers
  GCLK->GENCTRL[7].reg = GCLK_GENCTRL_DIV(0) |       // Divide the 48MHz clock source by divisor 1: 48MHz
                         GCLK_GENCTRL_IDC |          // Set the duty cycle to 50/50 HIGH/LOW
                         GCLK_GENCTRL_GENEN |        // Enable GCLK7
                         GCLK_GENCTRL_SRC_DFLL;      // Generate from 48MHz DFLL clock source
  while (GCLK->SYNCBUSY.bit.GENCTRL7);               // Wait for synchronization 

  GCLK->PCHCTRL[9].reg = GCLK_PCHCTRL_CHEN |         // Enable perhipheral channel
                         GCLK_PCHCTRL_GEN_GCLK7;     // Connect generic clock 7 to TC0

  // Enable the peripheral multiplexer on pin A1
  PORT->Group[g_APinDescription[A1].ulPort].PINCFG[g_APinDescription[A1].ulPin].bit.PMUXEN = 1;
 
  // Set A1 the peripheral multiplexer to peripheral E(4): TC0, Channel 1
  PORT->Group[g_APinDescription[A1].ulPort].PMUX[g_APinDescription[A1].ulPin >> 1].reg |= PORT_PMUX_PMUXO(4);
 

  // Configure Count Mode (16-bit)
  TC0->COUNT16.CTRLA.bit.MODE = 0x0;

  // Configure Prescaler for divide by 2 (500kHz clock to COUNT)
  TC0->COUNT16.CTRLA.bit.PRESCALER = 0x1;

  // Configure TC0 Compare Mode for compare channel 0
  TC0->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;            // "Match Frequency" operation

  // Initialize compare value for 100mS @ 500kHz
  TC0->COUNT16.CC[0].reg = 50000;
  

  // Enable TC0 compare mode interrupt generation
  TC0->COUNT16.INTENSET.bit.MC0 = 0x1;    // Enable match interrupts on compare channel 0 

  // Enable TC0
  TC0->COUNT16.CTRLA.bit.ENABLE = 1;

  /* Set TC3 Interrupt Priority to Level 3 */
  NVIC_SetPriority(TC0_IRQn, 3);
  NVIC_EnableIRQ(TC3_IRQn);

  __enable_irq();
}

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

  if(check)
  {
    Serial.print("Interrupt fired");
    check = false;
  }
}

void TC0_Handler(void)
{

  // Interrupt processing code

  // Acknowledge the interrupt (clear MC0 interrupt flag to re-arm)
  TC0->COUNT16.INTFLAG.reg |= 0b00010000;  // edited for clarity following westfw's question
  check = true;
  if(LED_state)
    digitalWrite(13, HIGH);
  else
    digitalWrite(13, LOW);

  LED_state = !LED_state;
}

I've added stuff to make the LED turn on and off for my benefit but the LED doesn't turn on.
It looks like it should be straightforward but I'm getting nothing at the moment. What am I doing wrong?

Thanks in advance.

Max

Are you using TC0 or TC3 ?

Hi westfw
Thanks for the reply. Trying to use TC0 and I get the point of the question. I posted last thing last night and made a silly error.
However, even if the interrupt flag doesn't get cleared, it should fire at least once...
Just tried again with the correct timer register flag being cleared and it hasn't made any difference.
I have two real questions from this:

  1. Am I reading the datasheet/examples correctly and should the overflow interrupt fire on Compare match every time?
  2. If so, what have I done wrong? Something silly, probably, but I find the datasheet difficult to read for this.

Thanks in advance.
Max

Hi @max.booker

As @westfw mentions, looking at your code, you'll need to also enable interrupts for TC0 rather than TC3 as well:

NVIC_EnableIRQ(TC0_IRQn);

Furthermore, you might have to synchronize the writes to the CC0 and CTRLA ENABLE bit registers:

// Enable TC0
TC0->COUNT16.CTRLA.bit.ENABLE = 1;           // Enable TC0
while (TC0->COUNT16.SYNCBUSY.bit.ENABLE);    // Wait for synchronization
TC0->COUNT16.CC[0].reg = 50000;              // Set the Counter Compare for channel 0
while (TC0->COUNT16.SYNCBUSY.bit.CC0);       // Wait for synchronization

The while() loops wait for the MPU's main clock (MCLK) and timer's generic clock (GCLK7) to synchronize and therefore the register write to complete, before continuing.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.