Arduino Zero/M0 timer capture and compare

Hello guys,
after a lot of searches i could not find something working on my M0 board.
I need to set 16 bit timer to capture and compare mode. I need to put the timer in free run mode. On capture event on input pin i would get the timer count to measure the period of the input signal by making the difference with the next timer count. At the same time i need to load the timer with a value to count up and when the timer reaches the compare with that value, generate a pulse output of 200us. The timer should be clocked at 1MHz to get resolution of 1us in the period count and delay generation.
I really appreciate if someone could help me.
thanks

I tried this code that i can manage for my purpose but does not works and i dont know why. I attached a 500Hz waveform on pin D12 but the serial monitor does not put anything out.

// Setup TCC0 to capture pulse-width and period
volatile boolean periodComplete;
volatile uint16_t isrPeriod;
volatile uint16_t isrPulsewidth;
uint16_t period;
uint16_t pulsewidth;

void setup()
{
  SerialUSB.begin(115200);                  // Send data back on the Zero's native port
  while(!SerialUSB);                        // Wait for the SerialUSB port to be ready

  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral

  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |    // Divide the 48MHz system clock by 3 = 16MHz
                    GCLK_GENDIV_ID(5);      // Set division on Generic Clock Generator (GCLK) 5
  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 5
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the clock source to 48MHz
                     GCLK_GENCTRL_ID(5);          // Set clock source on GCLK 5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization*/

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

  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO3;                                 // Enable event output on external interrupt 3
  attachInterrupt(12, NULL, HIGH);                                        // Attach interrupts to digital pin 12 (external interrupt 3)

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

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

  REG_TCC0_EVCTRL |= TCC_EVCTRL_MCEI1 |           // Enable the match or capture channel 1 event input
                     TCC_EVCTRL_MCEI0 |           //.Enable the match or capture channel 0 event input
                     TCC_EVCTRL_TCEI1 |           // Enable the TCC event 1 input
                     /*TCC_EVCTRL_TCINV1 |*/      // Invert the event 1 input         
                     TCC_EVCTRL_EVACT1_PPW;       // Set up the timer for capture: CC0 period, CC1 pulsewidth
                                        
  //NVIC_DisableIRQ(TCC0_IRQn);
  //NVIC_ClearPendingIRQ(TCC0_IRQn);
  NVIC_SetPriority(TCC0_IRQn, 0);      // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC0 to 0 (highest)
  NVIC_EnableIRQ(TCC0_IRQn);           // Connect the TCC0 timer to the Nested Vector Interrupt Controller (NVIC)

  REG_TCC0_INTENSET = TCC_INTENSET_MC1 |            // Enable compare channel 1 (CC1) interrupts
                      TCC_INTENSET_MC0;             // Enable compare channel 0 (CC0) interrupts

  REG_TCC0_CTRLA |= TCC_CTRLA_CPTEN1 |              // Enable capture on CC1
                    TCC_CTRLA_CPTEN0 |              // Enable capture on CC0
                    TCC_CTRLA_PRESCALER_DIV16 |     // Set prescaler to 16, 16MHz/16 = 1MHz
                    TCC_CTRLA_ENABLE;               // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                // Wait for synchronization
}

void loop()
{
  if (periodComplete)                             // Check if the period is complete
  {
    noInterrupts();                               // Read the new period and pulse-width
    period = isrPeriod;                   
    pulsewidth = isrPulsewidth;
    interrupts();
    SerialUSB.print(period);                      // Output the results
    SerialUSB.print(F("   "));
    SerialUSB.println(pulsewidth);
    periodComplete = false;                       // Start a new period
  }
}

void TCC0_Handler()                              // Interrupt Service Routine (ISR) for timer TCC0
{     
  // Check for match counter 0 (MC0) interrupt
  if (TCC0->INTFLAG.bit.MC0)             
  {   
    isrPeriod = REG_TCC0_CC0;                   // Copy the period 
    periodComplete = true;                       // Indicate that the period is complete
  }

  // Check for match counter 1 (MC1) interrupt
  if (TCC0->INTFLAG.bit.MC1)           
  {
    isrPulsewidth = REG_TCC0_CC1;               // Copy the pulse-width
  }
}

Also found this code as good starting point. I need to measure the time period between two rising edge on the input D12. How to do this?

// Setup timer TC4/TC5 to trigger on input event and set time delay for TCC0 output pulse, (TCC1 test input)
//  Input: D12, PWM Output: D3
void setup()
{
  // Generic Clocks (GCLK) //////////////////////////////////////////////////////////////////////////////
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |        // Enable generic clock
                      GCLK_CLKCTRL_GEN_GCLK0 |    // Select the 48MHz GCLK0
                      GCLK_CLKCTRL_ID_TCC0_TCC1;  // Set GCLK0 as a clock source for TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |        // Enable generic clock
                      GCLK_CLKCTRL_GEN_GCLK0 |    // Select the 48MHz GCLK0
                      GCLK_CLKCTRL_ID_TC4_TC5;    // Set GCLK0 as a clock source for TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Port Multiplexing //////////////////////////////////////////////////////////////////////////////////
  
  // Enable the port multiplexer D3 and D12
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
 // PORT->Group[g_APinDescription[8].ulPort].PINCFG[g_APinDescription[8].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
  
  // Set-up the pin multiplexers
  PORT->Group[g_APinDescription[3].ulPort].PMUX[g_APinDescription[3].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
 // PORT->Group[g_APinDescription[8].ulPort].PMUX[g_APinDescription[8].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
  PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg |= PORT_PMUX_PMUXO_A;

  // External Interrupt Controller (EIC) (Input) ///////////////////////////////////////////////////////////
  
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO3;         // Enable event output on external interrupt 3
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE3_HIGH;    // Set event detecting a HIGH level
  EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT3;        // Clear the interrupt flag on channel 3
  EIC->CTRL.reg |= EIC_CTRL_ENABLE;                // Enable EIC peripheral
  while (EIC->STATUS.bit.SYNCBUSY);                // Wait for synchronization

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

  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
  
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0);              // Set the event user (receiver) as timer TCC0, 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_3) |    // Set event generator (sender) as external interrupt 3
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0
  
  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_TC4_MCX_0) |       // Set event generator (sender) as TC4 Match Compare channel 0
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1

  TC4->COUNT32.EVCTRL.reg |= TC_EVCTRL_MCEO0 |               // Output event on Match Compare channel 0
                             TC_EVCTRL_TCEI |                // Enable the TC event input
                             //TC_EVCTRL_TCINV |             // Invert the event input 
                             TC_EVCTRL_EVACT_RETRIGGER;      // Set event to RETRIGGER timer TC4
  
  TCC0->EVCTRL.reg |= //TCC_EVCTRL_TCEI1 |                     // Enable the TCC event 1 input
                      TCC_EVCTRL_TCEI0 |                     // Enable the TCC event 0 input
                      //TCC_EVCTRL_TCINV1 |                    // Invert the event 1 input
                      //TCC_EVCTRL_TCINV0 |                   // Invert the event 0 input                     
                      TCC_EVCTRL_EVACT0_RETRIGGER;           // Set event 0 to count the incoming events

  // Timer TCC1 (Test Output at 250Hz, 25% duty-cycle) /////////////////////////////////////////////////////////
/*
  TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC1 timer counter to normal PWM mode (NPWM)
  while (TCC1->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC1->CC[0].reg = 48000;                            // Set duty cycle 25% with 1ms pulse
  while (TCC1->SYNCBUSY.bit.CC0);                     // Wait for synchronization

  TCC1->PER.reg = 191999;                             // Set period to 4ms
  while (TCC1->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC1->CTRLA.bit.ENABLE = 1;                         // Enable TCC1
  while (TCC1->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
*/
  // Timer TC4/TC5 (Delay Timer) /////////////////////////////////////////////////////////////////////////////

  TC4->COUNT32.CTRLA.reg |= TC_CTRLA_WAVEGEN_NFRQ |   // Set TC4 to normal frequency mode (NFRQ)
                            TC_CTRLA_MODE_COUNT32;    // Enable 32-bit timer mode (in conjuction with TC5)

  TC4->COUNT32.CC[0].reg = 480;//47999;               // Set the delay to 1ms Delay (us)= 48MHz/1000000
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);           // Wait for synchronization
  
  TC4->COUNT32.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;    // Enable oneshot operation
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);           // Wait for synchronization
  
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;                  // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);           // Wait for synchronization

  // Timer TCC0 (PWM Output) /////////////////////////////////////////////////////////////////////////////////

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC0 timer counter to normal PWM mode (NPWM)
  while (TCC0->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC0->CC[1].reg = 4799;                            // Set duty cycle 100% with 100us pulse on channel 1
  while (TCC0->SYNCBUSY.bit.CC1);                     // Wait for synchronization

  TCC0->PER.reg = 4799;                              // Set period to 100us
  while (TCC0->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC0->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT;          // Enable oneshot operation
  while (TCC0->SYNCBUSY.bit.CTRLB);                   // Wait for synchronization

  TCC0->DRVCTRL.reg |= TCC_DRVCTRL_NRE1;              // Set the non-recoverable state output to 0 when inactive
                                                                                      
  TCC0->CTRLA.bit.ENABLE = 1;                         // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
}

void loop() {}

Hi @ekjk

Here's a more up-to-date version of the code that uses TCC0 timer pulsewidth and period capture on port pin PA20, (digital pin D6 on the Arduino Zero):

// Setup TCC0 to capture pulse-width and period on D6
volatile boolean periodComplete;
volatile uint32_t isrPeriod;
volatile uint32_t isrPulsewidth;
uint32_t period;
uint32_t pulsewidth;

void setup()
{
  SerialUSB.begin(115200);                   // Configure the native USB port
  while(!SerialUSB);                         // Wait for the console to be ready
 
  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) 5 

  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 TCC0 and TCC1
                      GCLK_CLKCTRL_GEN_GCLK4 |     
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   

  // 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;                            // Set event detecting a HIGH level
  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_TCC0_EV_1);              // Set the event user (receiver) as timer TCC0, 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

  TCC0->EVCTRL.reg |= TCC_EVCTRL_MCEI1 |           // Enable the match or capture channel 1 event input
                      TCC_EVCTRL_MCEI0 |           //.Enable the match or capture channel 0 event input
                      TCC_EVCTRL_TCEI1 |           // Enable the TCC event 1 input
                      /*TCC_EVCTRL_TCINV1 |*/      // Invert the event 1 input         
                      TCC_EVCTRL_EVACT1_PPW;       // Set up the timer for capture: CC0 period, CC1 pulsewidth
                                       
  NVIC_SetPriority(TCC0_IRQn, 0);      // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC0 to 0 (highest)
  NVIC_EnableIRQ(TCC0_IRQn);           // Connect the TCC0 timer to the Nested Vector Interrupt Controller (NVIC)
 
  TCC0->INTENSET.reg = TCC_INTENSET_MC1 |            // Enable compare channel 1 (CC1) interrupts
                       TCC_INTENSET_MC0;             // Enable compare channel 0 (CC0) interrupts
 
  TCC0->CTRLA.reg |= TCC_CTRLA_CPTEN1 |              // Enable capture on CC1
                     TCC_CTRLA_CPTEN0 |              // Enable capture on CC0
                     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 TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                 // Wait for synchronization
}

void loop()
{
  if (periodComplete)                             // Check if the period is complete
  {
    noInterrupts();                               // Read the new period and pulse-width
    period = isrPeriod;                   
    pulsewidth = isrPulsewidth;
    interrupts();
    SerialUSB.print(F("PW: "));                   // Output the results
    SerialUSB.print(pulsewidth);
    SerialUSB.print(F("   "));
    SerialUSB.print(F("P:" ));
    SerialUSB.println(period);                         
    periodComplete = false;                       // Start a new period
  }
}

void TCC0_Handler()                              // Interrupt Service Routine (ISR) for timer TCC0
{     
  // Check for match counter 0 (MC0) interrupt
  if (TCC0->INTFLAG.bit.MC0)             
  {   
    isrPeriod = TCC0->CC[0].reg;                 // Copy the period
    periodComplete = true;                       // Indicate that the period is complete
  }

  // Check for match counter 1 (MC1) interrupt
  if (TCC0->INTFLAG.bit.MC1)           
  {
    isrPulsewidth = TCC0->CC[1].reg;             // Copy the pulse-width
  }
}

Hi @MartinL,
thanks for the quick reply.
I got your code working to measure the period. Now i would like to implement also those additional features:
At the rising edge of the input signal on D6, measure the period (already done) and generate a pulse of width 100us (pulse width variable if possible) with a settable delay from the input edge with resolution of 1us or about.

Do you think is possible? Thanks

@ekjk Yes, it's possible. One way is to route the same input event on D6 to anonther timer, set to start counting upon receiving the event. Upon timing the delay (match compare), this timer sends out an event to yet another timer set to oneshot mode that outputs a single pulse of a specified length.

I've written something similar on the Arduino forum here: https://forum.arduino.cc/t/pwm-generation-for-a-very-low-duty-cycle-and-phase-shifting-capabiliy-on-samd21/644736/7.

I have been trying to merge the code of the link you posted with the code of the period measure, but without success. I cant find out the way to feed the timer as you mention.
I would also keep the micros() function working (i dont know if the timers used affects this) and leave the spi gpio free to communicate with a spi flash memory. I have done it in past with atmega328p using just one timer to do all.
Now, translate the old code into the new one is brain bursting for me :frowning:
Could you gentle draft some code lines? thanks in advance

@MartinL to give you more infos, the 32bit timer to measure the period is ok. For delay and pulse generation a 16bit timer is enough for generate delay from few us to 10ms: this one should be clocked at 1MHz so it increment every 1us. Hope you can help me.
Thanks

Hi @MartinL,
looking at the code of the delay i would try to to the same thing but with timer TC3.
Here the modified code. I will try this. I don't understand how to connect on the same input pin the trigger of the timer TC3.

// Setup timer TC3 to trigger on input event and set time delay for TCC0 output pulse, 
// Input: D12, PWM Output: D3
void setup()
{
  // Generic Clocks (GCLK) //////////////////////////////////////////////////////////////////////////////
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |        // Enable generic clock
                      GCLK_CLKCTRL_GEN_GCLK0 |    // Select the 48MHz GCLK0
                      GCLK_CLKCTRL_ID_TCC0_TCC1;  // Set GCLK0 as a clock source for TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization


REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                              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

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |        // Enable generic clock
                      GCLK_CLKCTRL_GEN_GCLK4 |     // Select the 48MHz GCLK4
                      GCLK_CLKCTRL_ID_TCC2_TC3;    // Set GCLK4 as a clock source for TC3

  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization


  // Port Multiplexing //////////////////////////////////////////////////////////////////////////////////
  
  // Enable the port multiplexer D3, D12
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
  
  // Set-up the pin multiplexers
  PORT->Group[g_APinDescription[3].ulPort].PMUX[g_APinDescription[3].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
  PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg |= PORT_PMUX_PMUXO_A;

  // External Interrupt Controller (EIC) (Input) ///////////////////////////////////////////////////////////
  
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO3;         // Enable event output on external interrupt 3
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE3_HIGH;    // Set event detecting a HIGH level
  EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT3;        // Clear the interrupt flag on channel 3
  EIC->CTRL.reg |= EIC_CTRL_ENABLE;                // Enable EIC peripheral
  while (EIC->STATUS.bit.SYNCBUSY);                // Wait for synchronization

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

  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TC3_EVU);                // Set the event user (receiver) as timer TC3 event
  
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0);              // Set the event user (receiver) as timer TCC0, 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_3) |    // Set event generator (sender) as external interrupt 3
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0
  
  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_TC3_MCX_0) |       // Set event generator (sender) as TC3 Match Compare channel 0
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1

  TC3->COUNT16.EVCTRL.reg |= TC_EVCTRL_MCEO0 |               // Output event on Match Compare channel 0
                             TC_EVCTRL_TCEI |                // Enable the TC event input
                             //TC_EVCTRL_TCINV |             // Invert the event input 
                             TC_EVCTRL_EVACT_RETRIGGER;      // Set event to RETRIGGER timer TC3
  
  TCC0->EVCTRL.reg |= //TCC_EVCTRL_TCEI1 |                     // Enable the TCC event 1 input
                      TCC_EVCTRL_TCEI0 |                     // Enable the TCC event 0 input
                      //TCC_EVCTRL_TCINV1 |                    // Invert the event 1 input
                      //TCC_EVCTRL_TCINV0 |                   // Invert the event 0 input                     
                      TCC_EVCTRL_EVACT0_RETRIGGER;           // Set event 0 to count the incoming events

 
  // Timer TC3 (Delay Timer) /////////////////////////////////////////////////////////////////////////////

  TC3->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_NFRQ |   // Set TC3 to normal frequency mode (NFRQ)
                            TC_CTRLA_PRESCALER_DIV16 |  // Set prescaler to 16, 16MHz/16 = 1MHz 
                            TC_CTRLA_MODE_COUNT16;    // Enable 16-bit timer mode

  TC3->COUNT16.CC[0].reg = 1000;                     // Set the delay to 1000us
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);           // Wait for synchronization
  
  TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;    // Enable oneshot operation
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);           // Wait for synchronization
  
  TC3->COUNT16.CTRLA.bit.ENABLE = 1;                  // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);           // Wait for synchronization

  // Timer TCC0 (PWM Output) /////////////////////////////////////////////////////////////////////////////////

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC0 timer counter to normal PWM mode (NPWM)
  while (TCC0->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC0->CC[1].reg = 4799;                            // Set duty cycle 100% with 100us pulse on channel 1
  while (TCC0->SYNCBUSY.bit.CC1);                     // Wait for synchronization

  TCC0->PER.reg = 4799;                              // Set period to 100us
  while (TCC0->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC0->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT;          // Enable oneshot operation
  while (TCC0->SYNCBUSY.bit.CTRLB);                   // Wait for synchronization

  TCC0->DRVCTRL.reg |= TCC_DRVCTRL_NRE1;              // Set the non-recoverable state output to 0 when inactive
                                                                                      
  TCC0->CTRLA.bit.ENABLE = 1;                         // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
}

void loop() {}

Hello.
i got to code working. the first based on 32bit timer to count the period of input wave on pin D6. here the code

// Setup TC4 to capture pulse-width and period on input D6
volatile boolean periodComplete;
volatile uint32_t isrPeriod;
volatile uint32_t isrPulsewidth;
uint32_t period;
uint32_t pulsewidth;

void setup()   {                
  SerialUSB.begin(115200);                         // Initialise the native serial port
  while(!SerialUSB);                               // Wait for the console to open
  //pinMode(12,OUTPUT);
  
  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 |         // Enable the generic clock...
                      GCLK_CLKCTRL_GEN_GCLK4 |     // ....on GCLK4
                      GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5


  // 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;                            // Set event detecting a HIGH level
  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

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

  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

  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
  
  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_TC4_MCX_0) |       // Set event generator (sender) as TC4 Match Compare channel 0
                       EVSYS_CHANNEL_CHANNEL(1);  
                       
  TC4->COUNT32.EVCTRL.reg |= TC_EVCTRL_MCEO0 |               // Output event on Match Compare channel 0
                             TC_EVCTRL_TCEI |              // Enable the TC event input
                             //TC_EVCTRL_TCINV |             // Invert the event input
                             TC_EVCTRL_EVACT_PPW;          // Set up the timer for capture: CC0 period, CC1 pulsewidth
  
  TCC0->EVCTRL.reg |= //TCC_EVCTRL_TCEI1 |                     // Enable the TCC event 1 input
                      TCC_EVCTRL_TCEI0 |                     // Enable the TCC event 0 input
                      //TCC_EVCTRL_TCINV1 |                    // Invert the event 1 input
                      //TCC_EVCTRL_TCINV0 |                   // Invert the event 0 input                     
                      TCC_EVCTRL_EVACT0_RETRIGGER;           // Set event 0 to count the incoming events
                                        
  TC4->COUNT32.CTRLC.reg = //TC_CTRLC_CPTEN1 |               // Enable capture on CC1
                           TC_CTRLC_CPTEN0;                // Enable capture on CC0
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);                // Wait for (write) synchronization

  NVIC_SetPriority(TC4_IRQn, 0);      // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);           // Connect the TC4 timer to the Nested Vector Interrupt Controller (NVIC)
 
  TC4->COUNT32.INTENSET.reg = //TC_INTENSET_MC1 |            // Enable compare channel 1 (CC1) interrupts
                              TC_INTENSET_MC0;             // Enable compare channel 0 (CC0) interrupts
  
  TC4->COUNT32.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Overflow on precaler clock, (rather than the GCLK)
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_MODE_COUNT32;    // Enable 32-bit timer mode (in conjuction with TC5)

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



}

void loop() { 
  if (periodComplete)                             // Check if the period is complete
  {
    noInterrupts();                               // Read the new period and pulse-width
    period = isrPeriod;                  
    interrupts();
    SerialUSB.print("P us : ");
    SerialUSB.println(period);
    periodComplete = false;                       // Start a new period
  }
}

void TC4_Handler()                                // Interrupt Service Routine (ISR) for timer TC4
{    
  // Check for match counter 0 (MC0) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC0)            
  {//digitalWrite(12,1);
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x18);      // Offset address of the CC0 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPeriod = TC4->COUNT32.CC[0].reg;                    // Copy the period  
    periodComplete = true;                                 // Indicate that the period is complete
    
    
    //digitalWrite(12,0);
  }
/*
  // Check for match counter 1 (MC1) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC1)          
  {
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x1A);      // Offset address of the CC1 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPulsewidth = TC4->COUNT32.CC[1].reg;                // Copy the pulse-width
  }*/
}

The second one for generate the delayed pulse on D3 by a 16bit timer triggered by pin D6. here the code

// Setup timer TC3 to trigger on input event and set time delay for TCC0 output pulse, 
// Input: D6 PA20, PWM Output: D3
void setup()
{
  // Generic Clocks (GCLK) //////////////////////////////////////////////////////////////////////////////
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |        // Enable generic clock
                      GCLK_CLKCTRL_GEN_GCLK0 |    // Select the 48MHz GCLK0
                      GCLK_CLKCTRL_ID_TCC0_TCC1;  // Set GCLK0 as a clock source for TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization


  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |        // Enable generic clock
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select the 48MHz GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Set GCLK4 as a clock source for TC3
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization


  // Port Multiplexing //////////////////////////////////////////////////////////////////////////////////
  
  // Enable the port multiplexer D3
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  // Set-up the pin multiplexers
  PORT->Group[g_APinDescription[3].ulPort].PMUX[g_APinDescription[3].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;

  // 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;                            // Set event detecting a HIGH level
  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);

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

  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TC3_EVU);                // Set the event user (receiver) as timer TC3 event
  
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0);              // Set the event user (receiver) as timer TCC0, 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 3
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0
  
  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_TC3_MCX_0) |       // Set event generator (sender) as TC3 Match Compare channel 0
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1

  TC3->COUNT16.EVCTRL.reg |= TC_EVCTRL_MCEO0 |               // Output event on Match Compare channel 0
                             TC_EVCTRL_TCEI |                // Enable the TC event input
                             //TC_EVCTRL_TCINV |             // Invert the event input 
                             TC_EVCTRL_EVACT_RETRIGGER;      // Set event to RETRIGGER timer TC3
  
  TCC0->EVCTRL.reg |= //TCC_EVCTRL_TCEI1 |                     // Enable the TCC event 1 input
                      TCC_EVCTRL_TCEI0 |                     // Enable the TCC event 0 input
                      //TCC_EVCTRL_TCINV1 |                    // Invert the event 1 input
                      //TCC_EVCTRL_TCINV0 |                   // Invert the event 0 input                     
                      TCC_EVCTRL_EVACT0_RETRIGGER;           // Set event 0 to count the incoming events

 
  // Timer TC3 (Delay Timer) /////////////////////////////////////////////////////////////////////////////

  TC3->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_NFRQ |   // Set TC3 to normal frequency mode (NFRQ)
                            TC_CTRLA_PRESCALER_DIV16 |  // Set prescaler to 16, 16MHz/16 = 1MHz 
                            TC_CTRLA_MODE_COUNT16;    // Enable 16-bit timer mode

  TC3->COUNT16.CC[0].reg = 1;                     // Set the delay in us
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);           // Wait for synchronization
  
  TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;    // Enable oneshot operation
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);           // Wait for synchronization
  
  TC3->COUNT16.CTRLA.bit.ENABLE = 1;                  // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);           // Wait for synchronization

  // Timer TCC0 (PWM Output) /////////////////////////////////////////////////////////////////////////////////

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC0 timer counter to normal PWM mode (NPWM)
  while (TCC0->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC0->CC[1].reg = 4799;                            // Set duty cycle 100% with 100us pulse on channel 1
  while (TCC0->SYNCBUSY.bit.CC1);                     // Wait for synchronization

  TCC0->PER.reg = 4799;                              // Set period to 100us
  while (TCC0->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC0->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT;          // Enable oneshot operation
  while (TCC0->SYNCBUSY.bit.CTRLB);                   // Wait for synchronization

  TCC0->DRVCTRL.reg |= TCC_DRVCTRL_NRE1;              // Set the non-recoverable state output to 0 when inactive
                                                                                      
  TCC0->CTRLA.bit.ENABLE = 1;                         // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
}

void loop() {}

Now i have to merge both. I tried but is not working. Can someone help me please?
Thanks

Hi @ekjk

Sure, I'll have a look at merging the code from you last post and get back to you tomorrow.

Thank you very MUCH, @MartinL

Hi @MartinL, i add an important detail for the implementation.
I do not need to start both timers (period+delay) with the same input pin, but the delay timer shall start ONLY when the "period" timer has produced the period measure.
I want this behavior since the delay calculation is done based on the period calculation and thus only when the period calc is ready i then calculate the delay to feed into TC3 and start it to produce a delayed single pulse.

Thanks

@ekjk I'll take these requirements into account.

@ekjk Here's the code. It generates a test PWM output of 1ms pulses at 10ms spacing on PA06 (D8), input pin on PA20 (D6), delayed output pulse of 1.5ms on PA21 (D7). The delay toggles between 1ms and 2ms can can be set in an Interrupt Service Routine (ISR).

I've used timer TCC1 to generate the test PWM output, timer TC4/TC5 in 32-bit mode to measure the period and pulsewidth of the incoming pulses from the test output. TC3 sets the delay of the output pulse and is triggered by a retrigger command software in TC4's ISR. After the specified delay, TC3 generates an event on the event system that automatically triggers TCC0 to ouput a single (onshot) pulse:

// Setup timer TC4/TC5 to measure pulse width and period, TC3 to calculate time delay and TCC0 output the delayed pulse, (PWM test output generated by TCC1)
// Test PWM output: PA06 (D8), Input: PA20 (D6), Pulse Output: PA21 (D7)
volatile boolean periodComplete;
volatile uint32_t isrPeriod;
volatile uint32_t isrPulsewidth;
uint32_t period;
uint32_t pulsewidth;

void setup()
{
  // Native Serial Port ////////////////////////////////////////////////////////////////////////////////

  SerialUSB.begin(115200);                         // Initialise the native serial port
  while(!SerialUSB);                               // Wait for the console to open
  
  // Generic Clocks (GCLK) //////////////////////////////////////////////////////////////////////////////
  
  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) 5 

  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 TCC0 and TCC1
                      GCLK_CLKCTRL_GEN_GCLK4 |     
                      GCLK_CLKCTRL_ID_TCC0_TCC1;
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Route GCLK4 to TCC2 and TC3
                      GCLK_CLKCTRL_GEN_GCLK4 |    
                      GCLK_CLKCTRL_ID_TCC2_TC3; 
 
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Route GCLK4 to TC4 and TCC5
                      GCLK_CLKCTRL_GEN_GCLK4 |    
                      GCLK_CLKCTRL_ID_TC4_TC5;    

  // Port Multiplexing //////////////////////////////////////////////////////////////////////////////////
  
  // Enable the port multiplexer on PA06 (D8)(Test output), PA20 (D6)(Input) and PA21(D7)(Pulse output)
  PORT->Group[PORTA].PINCFG[6].bit.PMUXEN = 1;
  PORT->Group[PORTA].PINCFG[20].bit.PMUXEN = 1;
  PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
  
  // Set-up the pin multiplexers
  PORT->Group[PORTA].PMUX[6 >> 1].reg |= PORT_PMUX_PMUXE_E;
  PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_A;
  PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;

  // External Interrupt Controller (EIC) (Input) ///////////////////////////////////////////////////////////
  
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO4;               // Enable event output on external interrupt 4
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE4_HIGH;          // Set event detecting a HIGH level
  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

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

  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
  
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0);              // Set the event user (receiver) as timer TCC0, event 0

  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
  
  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_TC3_MCX_0) |       // Set event generator (sender) as TC3 Match Compare channel 0
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1

  TC3->COUNT16.EVCTRL.reg = TC_EVCTRL_MCEO0;                // Output event on Match Compare channel 0                       
                           
  TC4->COUNT32.EVCTRL.reg = TC_EVCTRL_TCEI |                // Enable the TC event input
                            //TC_EVCTRL_TCINV |             // Invert the event input 
                            TC_EVCTRL_EVACT_PPW;            // Set up the timer for capture: CC0 period, CC1 pulsewidth
  
  TCC0->EVCTRL.reg = //TCC_EVCTRL_TCEI1 |                     // Enable the TCC event 1 input
                     TCC_EVCTRL_TCEI0 |                     // Enable the TCC event 0 input
                     //TCC_EVCTRL_TCINV1 |                    // Invert the event 1 input
                     //TCC_EVCTRL_TCINV0 |                    // Invert the event 0 input                     
                     TCC_EVCTRL_EVACT0_RETRIGGER;           // Set event 0 to count the incoming events

  // Timer TCC1 (Test Output at 250Hz (4ms period), 25% duty-cycle) ////////////////////////////////////////

  TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC1 timer counter to normal PWM mode (NPWM)
  while (TCC1->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC1->CC[0].reg = 1000;                             // Set duty cycle 25% with 1ms pulse width
  while (TCC1->SYNCBUSY.bit.CC0);                     // Wait for synchronization

  TCC1->PER.reg = 9999;                               // Set period to 10ms
  while (TCC1->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC1->CTRLA.reg = TCC_CTRLA_PRESCSYNC_PRESC |       // Reload timer on the next prescaler clock
                    TCC_CTRLA_PRESCALER_DIV16;        // Set prescaler to 16, 16MHz/16 = 1MHz
  
  TCC1->CTRLA.bit.ENABLE = 1;                         // Enable TCC1
  while (TCC1->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization

  // Timer TC4 (Period Timer) //////////////////////////////////////////////////////////////////////////////////

  TC4->COUNT32.CTRLC.reg = TC_CTRLC_CPTEN1 |               // Enable capture on CC1
                           TC_CTRLC_CPTEN0;                // Enable capture on CC0
  while (TC4->COUNT32.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 the TC4 timer to the Nested Vector Interrupt Controller (NVIC)
 
  TC4->COUNT32.INTENSET.reg = TC_INTENSET_MC1 |            // Enable compare channel 1 (CC1) interrupts
                              TC_INTENSET_MC0;             // Enable compare channel 0 (CC0) interrupts
  
  TC4->COUNT32.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reload timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_WAVEGEN_NFRQ |         // Set TC4 to normal frequency mode (NFRQ)
                           TC_CTRLA_MODE_COUNT32;          // Set TC4/TC5 to 32-bit timer mode
                          
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;                       // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TC3 (Delay Timer) //////////////////////////////////////////////////////////////////////////////////

  TC3->COUNT16.CC[0].reg = 1000;                           // Set the delay to 1ms
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;         // Enable oneshot operation
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC3->COUNT16.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reload timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_WAVEGEN_NFRQ |         // Set TC4 to normal frequency mode (NFRQ)
                           TC_CTRLA_MODE_COUNT16;          // Set TC3 to 16-bit timer mode
  
  TC3->COUNT16.CTRLA.bit.ENABLE = 1;                       // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TCC0 (Pulse Output) /////////////////////////////////////////////////////////////////////////////////

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC0 timer counter to normal PWM mode (NPWM)
  while (TCC0->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC0->CC[3].reg = 1500;                             // Set pulse width to 1.5ms on channel 3
  while (TCC0->SYNCBUSY.bit.CC1);                     // Wait for synchronization

  TCC0->PER.reg = 0xFFFFFF;                           // Set period to the timer's maximum value (2^24)
  while (TCC0->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC0->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT;          // Enable oneshot operation
  while (TCC0->SYNCBUSY.bit.CTRLB);                   // Wait for synchronization

  TCC0->DRVCTRL.reg |= TCC_DRVCTRL_NRE1;              // Set the non-recoverable state output to 0 when inactive
                                                                                      
  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 TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
}

void loop() 
{
  if (periodComplete)                             // Check if the period is complete
  {
    noInterrupts();                               // Read the new period and pulse-width
    period = isrPeriod;                  
    pulsewidth = isrPulsewidth;
    interrupts();
    SerialUSB.print("PW: ");
    SerialUSB.print(pulsewidth);
    SerialUSB.print(F("   "));
    SerialUSB.print("P: ");
    SerialUSB.println(period);
    periodComplete = false;                       // Start a new period
  }
}

void TC4_Handler()                                // Interrupt Service Routine (ISR) for timer TC4
{    
  static boolean toggle = false;                           // Initialise the toggle variable
  
  // Check for match counter 0 (MC0) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC0)            
  {
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x18);      // Offset address of the CC0 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPeriod = TC4->COUNT32.CC[0].reg;                    // Copy the period  

    /////////////////////////////////////////////////////////////////////////////////////////////
    
    // Calculate the output pulse delay here... 
    if (toggle)
    {
      TC3->COUNT16.CC[0].reg = 1000;                       // Set the delay to 1ms
      while (TC3->COUNT16.STATUS.bit.SYNCBUSY);            // Wait for synchronization
    }
    else
    {
      TC3->COUNT16.CC[0].reg = 2000;                       // Set the delay to 2ms
      while (TC3->COUNT16.STATUS.bit.SYNCBUSY);            // Wait for synchronization
    }
    toggle = !toggle;                                      // Flip the toggle

    /////////////////////////////////////////////////////////////////////////////////////////////
    
    TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TC3, pulse automatically output on TCC0 after delay
    while (TC3->COUNT16.STATUS.bit.SYNCBUSY);              // Wait for synchronization
    
    periodComplete = true;                                 // Indicate that the period is complete
  }

  // Check for match counter 1 (MC1) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC1)          
  {
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x1A);      // Offset address of the CC1 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPulsewidth = TC4->COUNT32.CC[1].reg;                // Copy the pulse-width
  }
}

Hi @MartinL, i just try it and is not fully working.
I do not use the test output on D8 but i put directly on D6 my signal generator with a square wave with a settable frequency. The input frequency is from 10 to 500Hz. from 50Hz the output pulse starts to blink on my oscilloscope. It means not every input edge of the signal generate the delayed out pulse. If i increase the frequency this behavior increases.
Any thoughts about that?
thanks

Hi @ekjk

The code toggles the delay between 1ms and 2ms after each input period has been retrieved. This is to test setting the programmable delay. On my scope the output pulse wiggles back and forth as expected, but I'm not seeing any missed output pulses.

Might I ask what duty-cycle you're using for the test pulse ouput on your signal generator?

I mean, without toggling the delay (i modified the code to make a fixed delay, let say 100us and 200us pulse width) and input signal with duty 50% (from 10Hz to 500Hz) i see missing out pulses.
Here the modified code

// Setup timer TC4/TC5 to trigger on input event and set time delay for TCC0 output pulse, (TCC1 test output)
// Test PWM output: PA06 (D8), Input: PA20 (D6), Pulse Output: PA21 (D7)
volatile boolean periodComplete;
volatile uint32_t isrPeriod;
volatile uint32_t isrPulsewidth;
uint32_t period;
uint32_t pulsewidth;

void setup()
{
  // Native Serial Port ////////////////////////////////////////////////////////////////////////////////

  SerialUSB.begin(115200);                         // Initialise the native serial port
  while(!SerialUSB);                               // Wait for the console to open
  
  // Generic Clocks (GCLK) //////////////////////////////////////////////////////////////////////////////
  
  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) 5 

  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 TCC0 and TCC1
                      GCLK_CLKCTRL_GEN_GCLK4 |     
                      GCLK_CLKCTRL_ID_TCC0_TCC1;
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Route GCLK4 to TCC2 and TC3
                      GCLK_CLKCTRL_GEN_GCLK4 |    
                      GCLK_CLKCTRL_ID_TCC2_TC3; 
 
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Route GCLK4 to TC4 and TCC5
                      GCLK_CLKCTRL_GEN_GCLK4 |    
                      GCLK_CLKCTRL_ID_TC4_TC5;    

  // Port Multiplexing //////////////////////////////////////////////////////////////////////////////////
  
  // Enable the port multiplexer on PA06 (D8)(Test output), PA20 (D6)(Input) and PA21(D7)(Pulse output)
  PORT->Group[PORTA].PINCFG[6].bit.PMUXEN = 1;
  PORT->Group[PORTA].PINCFG[20].bit.PMUXEN = 1;
  PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
  
  // Set-up the pin multiplexers
  PORT->Group[PORTA].PMUX[6 >> 1].reg |= PORT_PMUX_PMUXE_E;
  PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_A;
  PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;

  // External Interrupt Controller (EIC) (Input) ///////////////////////////////////////////////////////////
  
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO4;               // Enable event output on external interrupt 4
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE4_HIGH;          // Set event detecting a HIGH level
  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

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

  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
  
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0);              // Set the event user (receiver) as timer TCC0, event 0

  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
  
  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_TC3_MCX_0) |       // Set event generator (sender) as TC3 Match Compare channel 0
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1

  TC3->COUNT16.EVCTRL.reg = TC_EVCTRL_MCEO0;                // Output event on Match Compare channel 0                       
                           
  TC4->COUNT32.EVCTRL.reg = TC_EVCTRL_TCEI |                // Enable the TC event input
                            //TC_EVCTRL_TCINV |             // Invert the event input 
                            TC_EVCTRL_EVACT_PPW;            // Set up the timer for capture: CC0 period, CC1 pulsewidth
  
  TCC0->EVCTRL.reg = //TCC_EVCTRL_TCEI1 |                     // Enable the TCC event 1 input
                     TCC_EVCTRL_TCEI0 |                     // Enable the TCC event 0 input
                     //TCC_EVCTRL_TCINV1 |                    // Invert the event 1 input
                     //TCC_EVCTRL_TCINV0 |                    // Invert the event 0 input                     
                     TCC_EVCTRL_EVACT0_RETRIGGER;           // Set event 0 to count the incoming events

  // Timer TCC1 (Test Output at 250Hz (4ms period), 25% duty-cycle) ////////////////////////////////////////
/*
  TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC1 timer counter to normal PWM mode (NPWM)
  while (TCC1->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC1->CC[0].reg = 1000;                             // Set duty cycle 25% with 1ms pulse width
  while (TCC1->SYNCBUSY.bit.CC0);                     // Wait for synchronization

  TCC1->PER.reg = 9999;                               // Set period to 10ms
  while (TCC1->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC1->CTRLA.reg = TCC_CTRLA_PRESCSYNC_PRESC |       // Reload timer on the next prescaler clock
                    TCC_CTRLA_PRESCALER_DIV16;        // Set prescaler to 16, 16MHz/16 = 1MHz
  
  TCC1->CTRLA.bit.ENABLE = 1;                         // Enable TCC1
  while (TCC1->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
*/
  // Timer TC4 (Period Timer) //////////////////////////////////////////////////////////////////////////////////

  TC4->COUNT32.CTRLC.reg = TC_CTRLC_CPTEN1 |               // Enable capture on CC1
                           TC_CTRLC_CPTEN0;                // Enable capture on CC0
  while (TC4->COUNT32.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 the TC4 timer to the Nested Vector Interrupt Controller (NVIC)
 
  TC4->COUNT32.INTENSET.reg = TC_INTENSET_MC1 |            // Enable compare channel 1 (CC1) interrupts
                              TC_INTENSET_MC0;             // Enable compare channel 0 (CC0) interrupts
  
  TC4->COUNT32.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reload timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_WAVEGEN_NFRQ |         // Set TC4 to normal frequency mode (NFRQ)
                           TC_CTRLA_MODE_COUNT32;          // Set TC4/TC5 to 32-bit timer mode
                          
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;                       // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TC3 (Delay Timer) //////////////////////////////////////////////////////////////////////////////////

  TC3->COUNT16.CC[0].reg = 100;                           // Set the delay to 100us
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;         // Enable oneshot operation
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC3->COUNT16.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reload timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_WAVEGEN_NFRQ |         // Set TC3 to normal frequency mode (NFRQ)
                           TC_CTRLA_MODE_COUNT16;          // Set TC3 to 16-bit timer mode
  
  TC3->COUNT16.CTRLA.bit.ENABLE = 1;                       // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TCC0 (Pulse Output) /////////////////////////////////////////////////////////////////////////////////

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC0 timer counter to normal PWM mode (NPWM)
  while (TCC0->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC0->CC[3].reg = 200;                             // Set pulse width to 200us on channel 3
  while (TCC0->SYNCBUSY.bit.CC1);                     // Wait for synchronization

  TCC0->PER.reg = 0xFFFFFF;                           // Set period to the timer's maximum value (2^24)
  while (TCC0->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC0->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT;          // Enable oneshot operation
  while (TCC0->SYNCBUSY.bit.CTRLB);                   // Wait for synchronization

  TCC0->DRVCTRL.reg |= TCC_DRVCTRL_NRE1;              // Set the non-recoverable state output to 0 when inactive
                                                                                      
  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 TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
}

void loop() 
{
  if (periodComplete)                             // Check if the period is complete
  {
    noInterrupts();                               // Read the new period and pulse-width
    period = isrPeriod;                  
    pulsewidth = isrPulsewidth;
    interrupts();
    SerialUSB.print("PW: ");
    SerialUSB.print(pulsewidth);
    SerialUSB.print(F("   "));
    SerialUSB.print("P: ");
    SerialUSB.println(period);
    periodComplete = false;                       // Start a new period
  }
}

void TC4_Handler()                                // Interrupt Service Routine (ISR) for timer TC4
{    
 // static boolean toggle = false;                           // Initialise the toggle variable
  
  // Check for match counter 0 (MC0) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC0)            
  {
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x18);      // Offset address of the CC0 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPeriod = TC4->COUNT32.CC[0].reg;                    // Copy the period  

    /////////////////////////////////////////////////////////////////////////////////////////////
    
    // Calculate the output pulse delay here... 
 //   if (toggle)
    {
      TC3->COUNT16.CC[0].reg = 100;                       // Set the delay to 100us
      while (TC3->COUNT16.STATUS.bit.SYNCBUSY);            // Wait for synchronization
    }
/*    else
    {
      TC3->COUNT16.CC[0].reg = 2000;                       // Set the delay to 2ms
      while (TC3->COUNT16.STATUS.bit.SYNCBUSY);            // Wait for synchronization
    }
    toggle = !toggle;                                      // Flip the toggle
*/
    /////////////////////////////////////////////////////////////////////////////////////////////
    
    TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TC3, pulse automatically output on TCC0 after delay
    while (TC3->COUNT16.STATUS.bit.SYNCBUSY);              // Wait for synchronization
    
    periodComplete = true;                                 // Indicate that the period is complete
  }

  // Check for match counter 1 (MC1) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC1)          
  {
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x1A);      // Offset address of the CC1 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPulsewidth = TC4->COUNT32.CC[1].reg;                // Copy the pulse-width
  }
}

@ekjk I set up an input PWM test signal on an Arduino Due instead, since its signals are generated asynchronously to that of the M0's, now I'm seeing the missed pulses that you observe.

I'm looking into it.

@ekjk The timing issue was with the TC3's oneshot functionality.

The solution was to set the TC3 timer to Match Frequency (MFRQ) mode:

  TC3->COUNT16.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reload timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_WAVEGEN_MFRQ |         // Set TC4 to match frequency mode (MFRQ)
                           TC_CTRLA_MODE_COUNT16;          // Set TC3 to 16-bit timer mode

... and to get it to generate an event upon overflow:

  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_TC3_OVF) |         // Set event generator (sender) as TC3 overflow
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1

  TC3->COUNT16.EVCTRL.reg = TC_EVCTRL_OVFEO;                // Output event on overflow

@ekjk Here's the corrected code:

// Setup timer TC4/TC5 to trigger on input event and set time delay for TCC0 output pulse, (TCC1 test output)
// Test PWM output: PA06 (D8), Input: PA20 (D6), Pulse Output: PA21 (D7)
volatile boolean periodComplete;
volatile uint32_t isrPeriod;
volatile uint32_t isrPulsewidth;
uint32_t period;
uint32_t pulsewidth;

void setup()
{
  // Native Serial Port ////////////////////////////////////////////////////////////////////////////////

  SerialUSB.begin(115200);                         // Initialise the native serial port
  while(!SerialUSB);                               // Wait for the console to open
  
  // Generic Clocks (GCLK) //////////////////////////////////////////////////////////////////////////////
  
  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) 5 

  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 TCC0 and TCC1
                      GCLK_CLKCTRL_GEN_GCLK4 |     
                      GCLK_CLKCTRL_ID_TCC0_TCC1;
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Route GCLK4 to TCC2 and TC3
                      GCLK_CLKCTRL_GEN_GCLK4 |    
                      GCLK_CLKCTRL_ID_TCC2_TC3; 
 
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Route GCLK4 to TC4 and TCC5
                      GCLK_CLKCTRL_GEN_GCLK4 |    
                      GCLK_CLKCTRL_ID_TC4_TC5;    

  // Port Multiplexing //////////////////////////////////////////////////////////////////////////////////
  
  // Enable the port multiplexer on PA06 (D8)(Test output), PA20 (D6)(Input) and PA21(D7)(Pulse output)
  PORT->Group[PORTA].PINCFG[6].bit.PMUXEN = 1;
  PORT->Group[PORTA].PINCFG[20].bit.PMUXEN = 1;
  PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
  
  // Set-up the pin multiplexers
  PORT->Group[PORTA].PMUX[6 >> 1].reg |= PORT_PMUX_PMUXE_E;
  PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_A;
  PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;

  // External Interrupt Controller (EIC) (Input) ///////////////////////////////////////////////////////////
  
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO4;               // Enable event output on external interrupt 4
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE4_HIGH;          // Set event detecting a HIGH level
  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

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

  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
  
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0);              // Set the event user (receiver) as timer TCC0, event 0

  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
  
  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_TC3_OVF) |         // Set event generator (sender) as TC3 overflow
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1

  TC3->COUNT16.EVCTRL.reg = TC_EVCTRL_OVFEO;                // Output event on overflow                     
                           
  TC4->COUNT32.EVCTRL.reg = TC_EVCTRL_TCEI |                // Enable the TC event input
                            //TC_EVCTRL_TCINV |             // Invert the event input 
                            TC_EVCTRL_EVACT_PPW;            // Set up the timer for capture: CC0 period, CC1 pulsewidth
  
  TCC0->EVCTRL.reg = //TCC_EVCTRL_TCEI1 |                     // Enable the TCC event 1 input
                     TCC_EVCTRL_TCEI0 |                     // Enable the TCC event 0 input
                     //TCC_EVCTRL_TCINV1 |                    // Invert the event 1 input
                     //TCC_EVCTRL_TCINV0 |                    // Invert the event 0 input                     
                     TCC_EVCTRL_EVACT0_RETRIGGER;           // Set event 0 to count the incoming events

  // Timer TCC1 (Test Output at 250Hz (4ms period), 25% duty-cycle) ////////////////////////////////////////

  TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC1 timer counter to normal PWM mode (NPWM)
  while (TCC1->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC1->CC[0].reg = 10000;                             // Set duty cycle 25% with 1ms pulse width
  while (TCC1->SYNCBUSY.bit.CC0);                     // Wait for synchronization

  TCC1->PER.reg = 19999;                              // Set period to 10ms
  while (TCC1->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC1->CTRLA.reg = TCC_CTRLA_PRESCSYNC_PRESC |       // Reload timer on the next prescaler clock
                    TCC_CTRLA_PRESCALER_DIV16;        // Set prescaler to 16, 16MHz/16 = 1MHz
  
  TCC1->CTRLA.bit.ENABLE = 1;                         // Enable TCC1
  while (TCC1->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization

  // Timer TC4 (Period Timer) //////////////////////////////////////////////////////////////////////////////////

  TC4->COUNT32.CTRLC.reg = TC_CTRLC_CPTEN1 |               // Enable capture on CC1
                           TC_CTRLC_CPTEN0;                // Enable capture on CC0
  while (TC4->COUNT32.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 the TC4 timer to the Nested Vector Interrupt Controller (NVIC)
 
  TC4->COUNT32.INTENSET.reg = TC_INTENSET_MC1 |            // Enable compare channel 1 (CC1) interrupts
                              TC_INTENSET_MC0;             // Enable compare channel 0 (CC0) interrupts
  
  TC4->COUNT32.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reload timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_WAVEGEN_NFRQ |         // Set TC4 to normal frequency mode (NFRQ)
                           TC_CTRLA_MODE_COUNT32;          // Set TC4/TC5 to 32-bit timer mode
                          
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;                       // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TC3 (Delay Timer) //////////////////////////////////////////////////////////////////////////////////

  TC3->COUNT16.CC[0].reg = 100;                            // Set the delay to 1ms
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_ONESHOT;         // Enable oneshot operation
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  
  TC3->COUNT16.CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC |      // Reload timer on the next prescaler clock
                           TC_CTRLA_PRESCALER_DIV16 |      // Set prescaler to 16, 16MHz/16 = 1MHz
                           TC_CTRLA_WAVEGEN_MFRQ |         // Set TC4 to match frequency mode (MFRQ)
                           TC_CTRLA_MODE_COUNT16;          // Set TC3 to 16-bit timer mode
  
  TC3->COUNT16.CTRLA.bit.ENABLE = 1;                       // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Timer TCC0 (Pulse Output) /////////////////////////////////////////////////////////////////////////////////

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;             // Set the TCC0 timer counter to normal PWM mode (NPWM)
  while (TCC0->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  TCC0->CC[3].reg = 200;                              // Set pulse width to 1.5ms on channel 3
  while (TCC0->SYNCBUSY.bit.CC3);                     // Wait for synchronization

  TCC0->PER.reg = 0xFFFFFF;                           // Set period to the timer's maximum value (2^24)
  while (TCC0->SYNCBUSY.bit.PER);                     // Wait for synchronization

  TCC0->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT;          // Enable oneshot operation
  while (TCC0->SYNCBUSY.bit.CTRLB);                   // Wait for synchronization

  TCC0->DRVCTRL.reg |= TCC_DRVCTRL_NRE7;              // Set the non-recoverable state output to 0 when inactive
                                                                                      
  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 TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
}

void loop() 
{
  if (periodComplete)                             // Check if the period is complete
  {
    noInterrupts();                               // Read the new period and pulse-width
    period = isrPeriod;                  
    pulsewidth = isrPulsewidth;
    interrupts();
    SerialUSB.print("PW: ");
    SerialUSB.print(pulsewidth);
    SerialUSB.print(F("   "));
    SerialUSB.print("P: ");
    SerialUSB.println(period);
    periodComplete = false;                       // Start a new period
  }
}

void TC4_Handler()                                // Interrupt Service Routine (ISR) for timer TC4
{    
  //static boolean toggle = false;                           // Initialise the toggle variable
  
  // Check for match counter 0 (MC0) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC0)            
  {
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x18);      // Offset address of the CC0 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPeriod = TC4->COUNT32.CC[0].reg;                    // Copy the period  

    /////////////////////////////////////////////////////////////////////////////////////////////
    
    // Calculate the output pulse delay here... 
    /*if (toggle)
    {
      TC3->COUNT16.CC[0].reg = 1000;                       // Set the delay to 1ms
      while (TC3->COUNT16.STATUS.bit.SYNCBUSY);            // Wait for synchronization
    }
    else
    {
      TC3->COUNT16.CC[0].reg = 2000;                       // Set the delay to 2ms
      while (TC3->COUNT16.STATUS.bit.SYNCBUSY);            // Wait for synchronization
    }
    toggle = !toggle;                                      // Flip the toggle*/

    /////////////////////////////////////////////////////////////////////////////////////////////
    
    TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TC3, pulse automatically output on TCC0 after delay
    while (TC3->COUNT16.STATUS.bit.SYNCBUSY);              // Wait for synchronization
    
    periodComplete = true;                                 // Indicate that the period is complete
  }

  // Check for match counter 1 (MC1) interrupt
  if (TC4->COUNT32.INTFLAG.bit.MC1)          
  {
    TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |           // Enable a read request
                               TC_READREQ_ADDR(0x1A);      // Offset address of the CC1 register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for (read) synchronization
    isrPulsewidth = TC4->COUNT32.CC[1].reg;                // Copy the pulse-width
  }
}