[Solved] TCC0 Capture on rising edge of extenal signal

I would like to timestamp the rising edge of an external signal as fast as possible and without using CPU. In order to do that, the plan is to use EXTINT->EVSYS->TCC0 capture. The external interrupt creates an event that trigger the capture of TCC0.

So here is a first functional code, with EXTINT configured to trigger on HIGH state and not RISING. It's inspired by the very good topic Arduino Zero TCC Capture. Note that this code works for MKR wifi 1010 board (EXTINT6 corresponding to pin 0), and I'm using XOSC32K as it seems to be the most accurate oscillator available (ie with the lowest ppm).

#include "Arduino.h"
uint32_t syncreadTCC0();
uint32_t syncreadCC0();
void callback();

void setup() {
  Serial.begin(9600);
  while (!Serial) {
  }
  Serial.println("Debut config");

  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;


  // Reglage des horloges
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the system clock by 1
                    GCLK_GENDIV_ID(4);            // Set division on Generic Clock Generator (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 GCLK 4
                     GCLK_GENCTRL_SRC_XOSC32K |   // Set the clock source to external 32768Hz clock
                     GCLK_GENCTRL_ID(4);          // Set clock source on GCLK 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK4 |     // ....on GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed the GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  attachInterrupt(digitalPinToInterrupt(0), callback, HIGH);            // Attach interrupts to digital pin 0 (external interrupt 6)
  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO6;                                 // Enable event output on external interrupt 6
  REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE6_HIGH;                              // Set event detecting a HIGH level
  REG_EIC_CTRL |= EIC_CTRL_ENABLE;                                        // Enable EIC peripheral
  while (EIC->STATUS.bit.SYNCBUSY);                                       // Wait for synchronization

  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TCC0_MC_0);              // Set the event user (receiver) as timer TCC0, event MC 0

  REG_EVSYS_CHANNEL = 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_6) |    // Set event generator (sender) as external interrupt 6
                      EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0

  REG_TCC0_EVCTRL |= TCC_EVCTRL_MCEI0;

  REG_TCC0_CTRLA |= TCC_CTRLA_CPTEN0 |              // Enable capture on CC0
                    TCC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1
                    TCC_CTRLA_ENABLE;               // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                // Wait for synchronization
  Serial.println("Config OK");
}

uint32_t syncreadTCC0 () {
  REG_TCC0_CTRLBSET = TCC_CTRLBSET_CMD_READSYNC;  // Trigger a read synchronization on the COUNT register
  while (TCC0->SYNCBUSY.bit.CTRLB);               // Wait for the CTRLB register write synchronization
  while (TCC0->SYNCBUSY.bit.COUNT);               // Wait for the COUNT register read sychronization
  return REG_TCC0_COUNT;
}

uint32_t syncreadCC0 () {
  REG_TCC0_CTRLBSET = TCC_CTRLBSET_CMD_READSYNC;  // Trigger a read synchronization on the COUNT register
  while (TCC0->SYNCBUSY.bit.CTRLB);               // Wait for the CTRLB register write synchronization
  while (TCC0->SYNCBUSY.bit.COUNT);               // Wait for the COUNT register read sychronization
  return REG_TCC0_CC0;
}

void loop() {
    Serial.print("CC0 : ");
    Serial.println(syncreadCC0());
    Serial.print("COUNT : ");
    Serial.println(syncreadTCC0());
    delay(1000);
}

void callback () {};

This code print every second the value of TCC0 COUNT and CC0 registers, and CC0 is correctly modified when pin 0 is HIGH.

Here is my problem : I'm not able to get the same result by setting the interrupt to trigger at the RISING edge instead of HIGH state. Here is how the code changes :

-- attachInterrupt(digitalPinToInterrupt(0), callback, HIGH);
++ attachInterrupt(digitalPinToInterrupt(0), callback, RISING);

-- REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE6_HIGH;
++ REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE6_RISE;

With this code, the rising edge is never detected, and the value of CC0 is never changed. Where can this problem come from?

OK, I just figured out what was the problem, it is described in that topic : https://forum.arduino.cc/index.php?topic=410699.0

Just have to add the following code and it works for the rising-edge detection :

   // Set the XOSC32K to run in standby
   SYSCTRL->XOSC32K.bit.RUNSTDBY = 1;
   
   // Configure EIC to use GCLK1 which uses XOSC32K
   // This has to be done after the first call to attachInterrupt()
   GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_EIC) |
                       GCLK_CLKCTRL_GEN_GCLK1 |
                       GCLK_CLKCTRL_CLKEN;