SAMD21 TC4 counter ...missing ticks ?

I'm trying to detect the oscillation frequency of an LC tank.
Code is based on the ingenious work of MartinL : D1 outputs a fixed PWM that triggers the A2 pin interrupt and the ISR captures the TC4 count from D6.

The problem is that if my tank oscillates over 150/160 khz the counter starts acting funny and counts down to zero and then stops.

[....]
Frequency  = 85325 Hz
Frequency  = 84850 Hz
Frequency  = 83725 Hz
Frequency  = 83350 Hz
Frequency  = 83125 Hz
Frequency  = 83850 Hz
Frequency  = 81600 Hz
Frequency  = 81950 Hz
Frequency  = 83425 Hz
Frequency  = 81900 Hz
Frequency  = 73450 Hz
Frequency  = 82375 Hz
Frequency  = 79900 Hz
Frequency  = 81975 Hz
Frequency  = 77400 Hz
Frequency  = 80050 Hz
Frequency  = 77925 Hz
Frequency  = 82150 Hz
Frequency  = 77800 Hz
Frequency  = 77025 Hz
Frequency  = 74775 Hz
Frequency  = 77575 Hz
Frequency  = 76900 Hz
Frequency  = 70650 Hz
Frequency  = 70975 Hz
Frequency  = 65950 Hz
Frequency  = 69650 Hz
[...]
Frequency  = 65250 Hz
Frequency  = 55275 Hz
[...]
Frequency  = 45725 Hz
Frequency  = 37100 Hz
Frequency  = 26800 Hz
Frequency  = 47250 Hz
Frequency  = 40600 Hz
Frequency  = 36975 Hz
Frequency  = 36950 Hz
Frequency  = 23250 Hz
Frequency  = 50075 Hz
[...]
Frequency  = 29225 Hz
[...]
Frequency  = 0 Hz
Frequency  = 0 Hz
Frequency  = 0 Hz

Code is the following :

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;  // Switch on the event system peripheral

  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) |  // Divide the 48MHz system clock by 3 = 16MHz
                     GCLK_GENDIV_ID(4);     // Set division on Generic Clock Generator (GCLK) 4

  GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC |          // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |        // Enable GCLK 4
                      GCLK_GENCTRL_SRC_DFLL48M |  // Set the clock source to 48MHz
                      GCLK_GENCTRL_ID(4);         // Set clock source on GCLK 4
  while (GCLK->STATUS.bit.SYNCBUSY)
    ;  // Wait for synchronization

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |      // Route GCLK4 to TC4 and TC5
                      GCLK_CLKCTRL_GEN_GCLK4 |  // GCLK0 at 48MHz
                      GCLK_CLKCTRL_ID_TC4_TC5;

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |  // Route GCLK4 to TCC0 and TCC1
                      GCLK_CLKCTRL_GEN_GCLK4 |
                      GCLK_CLKCTRL_ID_TCC0_TCC1;
  while (GCLK->STATUS.bit.SYNCBUSY)
    ;  // Wait for synchronization

  // Enable the port multiplexer on digital pin D1
  PORT->Group[PORTA].PINCFG[23].bit.PMUXEN = 1;
  
  // Set-up the pin as an TCC0 PWM output on D1
  PORT->Group[PORTA].PMUX[23 >> 1].reg |= PORT_PMUX_PMUXO_F;

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;  // Enable Normal PWM (NPWM) mode
  while (TCC0->SYNCBUSY.bit.WAVE)
    ;  // Wait for synchronization

  TCC0->PER.reg = 19999; 
  while (TCC0->SYNCBUSY.bit.PER)
    ;  // Wait for synchronization

  TCC0->CC[1].reg = 5000; 
  while (TCC0->SYNCBUSY.bit.CC1)
    ;  // Wait for synchronization

  TCC0->CTRLA.reg = TCC_CTRLA_PRESCSYNC_PRESC |  // Reload timer on the next prescaler clock
                    TCC_CTRLA_PRESCALER_DIV16;   // Set prescaler to 16, 16MHz/16 = 1MHz

  TCC0->CTRLA.bit.ENABLE = 1;  // Enable timer TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);  // Wait for synchronization

  // Enable the port multiplexer on digital pin D6
  PORT->Group[PORTA].PINCFG[20].bit.PMUXEN = 1;
  // Set-up the pin as an EIC (interrupt) peripheral on D6
  PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_A;

  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO4;       // Enable event output on external interrupt 4
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE4_HIGH;  // 
  EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT4;      // Clear the interrupt flag on channel 4
  EIC->CTRL.reg |= EIC_CTRL_ENABLE;              // Enable EIC peripheral
  while (EIC->STATUS.bit.SYNCBUSY)
    ;  // Wait for synchronization

  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                  // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU);  // Set the event user (receiver) as timer TC4, event 1

  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |              // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                 // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_4) |  // Set event generator (sender) as external interrupt 4
                       EVSYS_CHANNEL_CHANNEL(0);                         // Attach the generator (sender) to channel 0

  TC4->COUNT32.EVCTRL.reg |= TC_EVCTRL_TCEI |        // Enable TC4 event input
                             TC_EVCTRL_EVACT_COUNT;  //.Enable the match or capture channel 0 event input
  
  NVIC_SetPriority(TC4_IRQn, 0);      // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);

  TC4->COUNT32.CTRLA.reg = TC_CTRLA_MODE_COUNT32;  // Chain TC4 with TC5 to create a 32-bit timer

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

  TC4->COUNT32.READREQ.reg = TC_READREQ_RCONT |  // Enable a continuous read request
                             TC_READREQ_ADDR(0x10);
  /////
  attachInterrupt(A2, trigger, RISING);

Then I have the void trigger :

void trigger()                             
{    
    frequency = ((TC4->COUNT32.COUNT.reg) * 25);
    TC4->COUNT32.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;    // Reset the TC4 timer
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);
}

I've already tried retriggering also TCC0, stopping and restarting the EIC and removing the prescaler with no luck.
Anyone know what I'm missing ? I'd really appreciate your help!

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