Arduino Zero TCC Capture

Hi Daniel,

1 / Has AttachInterrupt with NULL being repaired in the IDE 1.8.5 ? If not I need to put a callback routine as previously isn't it ?

I raised the issue with the Arduino SAMD core development team and they've flagged it as a bug, so I expect it might be fixed in a subsequent Arduino Zero build.

In the DMAC example code above, I commented out attachInterrupt() function and replaced it by setting the interrupt registers directly.

2/ How can I know that a new period has been measured in your sketch, since there is no more "void TCC0_Handler()" ?

The "dmacPeriod" and "dmacPulsewidth" variables now hold the latest measurements. The DMAC is reading them instead of the CPU having service the TCC0_Handler() each time. The DMAC is effectively handling the TCC0 timer for the CPU.

3/ I also need "Count" data for my application. Can I declare isrCount variable and put isrCount++; in the void loop, but as said in 2/ above, how can I increment it at the end of each period measurement ?

I'll have to think about this one....

It might be possible to use the event system to get another of the SAMD21's timers to count the number of TCC0 periods that have elapsed.

Kind regards,
Martin

Hi parnal,

I am facing a problem to set an event (the purpose of this will be to make them work like Arduino Due's function of clock chaining):

  1. I want to use a pair of TC4 - TC5 working in 32 bit mode.
  2. I read that to set an event 2 parameters are important EVGEN (Event Generator) and USER (Event user).
    So TC4 should act as a event generator and TC5 as a user.
    which isTC4 should count upto the maximum value and then it should trigger TC5 to start counting i.e clock
    chaining.
  3. My understanding goes like: initialisation path ( PM -> APBCMASK ->EVSYS -> EVGEN ->USER)
  4. The problem is to setup this connection. What all registers should be initialised to make it work
    and how ?

Looks like your issue is similar to the Daniel's requirement in point (3) above. I'll see if I can come up with a solution using the TC timers in 32-bit mode.

Kind regards,
Martin

Hi Daniel and parnal,

I've added the counter using the TC3 and TC4 timers in 32-bit mode. In this mode, TC4 acts as the master and TC3 the slave, this just means that the registers are accessed through TC4's interface.

The TC4 timer in 32-bit mode is set-up to increment each time it receives an event from interrupt pin on the event system.

Using 32-bits with a 40kHz input frequency will allow the TC timer to count for almost 30 hours before rolling over back to 0.

If you require longer than this then it'll be necessary to get TC4 to generate an overflow event to the 16-bit TC5 timer. Using TC5 as well will give you 223 years before it rolls over! Probably enough for most projects, in any case we won't be around when this happens.

Please find the code attached. Unfortunately it exceeds the 9000 character limit, so I can't display it in this message.

Kind regards,
Martin

TCCCapture_DMAC_TCCount.ino (8.95 KB)

Hi parnal,

Here's a cut down version of the above code, that only counts the number of incoming interrupt pulses on D12 using timer TC4 in 32-bit mode, (but doesn't use the DMAC or TCC0):

// Setup TC4 in 32-bit mode to count interrupt pulses using the event system on digital pin D12
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(1) |    // Divide the 48MHz system clock by 1 = 48MHz
                    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_TCC2_TC3;    // Feed the GCLK5 to TCC2 and TC3
  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_TC4_TC5;     // Feed the GCLK5 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer on digital pin D12
  PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
  // Set-up the pin as an EIC (interrupt) peripheral on D12
  PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg |= PORT_PMUX_PMUXO_A;

  //attachInterrupt(12, NULL, HIGH);                                        // Attach interrupts to digital pin 12 (external interrupt 3)
  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO3;                                 // Enable event output on external interrupt 3
  REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE3_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_TC4_EVU);                // Set the event user (receiver) as timer TC4
  
  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                                    

  REG_TC4_EVCTRL |= TC_EVCTRL_TCEI |              // Enable asynchronous events on the TC timer
                    TC_EVCTRL_EVACT_COUNT;        // Increment the TC timer each time an event is received

  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                   TC_CTRLA_MODE_COUNT32 |        // Set the TC4 timer to 32-bit mode in conjuction with timer TC3
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  REG_TC4_READREQ = TC_READREQ_RCONT |            // Enable a continuous read request
                    TC_READREQ_ADDR(0x10);        // Offset of the 32-bit COUNT register
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for (read) synchronization
}

void loop()
{
  SerialUSB.println(REG_TC4_COUNT32_COUNT);       // Output the results
}

Thank you so much.
The only part where I have a problem is this :

// Enable the port multiplexer on digital pin D12
PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) peripheral on D12
PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg |= PORT_PMUX_PMUXO_A;

//attachInterrupt(12, NULL, HIGH); // Attach interrupts to digital pin 12 (external interrupt 3)
REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO3; // Enable event output on external interrupt 3
REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE3_HIGH; // Set event detecting a HIGH level
REG_EIC_CTRL |= EIC_CTRL_ENABLE; // Enable EIC peripheral
while (EIC->STATUS.bit.SYNCBUSY); // Wait for synchronization

Why is it necessary to attach an external interrupt, cant it be done using internal interrupt ?
In simple words when the TC4 counts to its maximum count it displays the count and again starts over .

Hi Parnal,

In the earlier builds of the Arduino core code, it was possible to set-up a pin as an interrupt by using the attachInterrupt() function and passing a NULL argument as the function callback pointer:

attachInterrupt(12, NULL, HIGH);           // Attach interrupts to digital pin 12 (external interrupt 3)

However, in the later builds this was changed so that attachInterrupt() would skip pin set-up if a NULL pointer was detected.

This means at the moment it's necessary to either use attachInterrupt() with a dummy (empty) callback function, or set-up the pin using regsiter manipulation.

I've raised the isse on Github with the Arduino SAMD core developers, as this changes reduces the flexibility of this function.

The first section of code just switches the pin from GPIO to the peripheral multiplexer and then selects the peripheral as the External Interrupt Controller (EIC):

// Enable the port multiplexer on digital pin D12
PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) peripheral on D12
PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg |= PORT_PMUX_PMUXO_A;

The following lines then just activate the interrupt on D12, which is on EIC channel 3 and set the pin sensing to interrupt on a HIGH level:

REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO3;       // Enable event output on external interrupt 3
REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE3_HIGH;           // Set event detecting a HIGH level
REG_EIC_CTRL |= EIC_CTRL_ENABLE;                            // Enable EIC peripheral
while (EIC->STATUS.bit.SYNCBUSY);                             // Wait for synchronization

Hi parnal,

I think I probably got the wrong end of the stick with regard to your requirements, it's just that this thread was dealing with capturing an external signal and for this it's necessary to use an external interrupt.

If you're application doesn't require an external interrupt then it's possible to just use TC timer in 32-bit mode with internal interrupts and events.

Hello Martin,

Thank you again, but that is where my problem is how to fed TC3 to TC4 internally ? :frowning:

Hi parnal,

Setting the TC4 timer to 32-bit mode automatically feeds one timer from another. Essentially it turns timer TC4 into a 32-bit timer. The SAMD21's hardware does this for you, chaining TC4 to TC3 in the background.

Reading the TC4's 32-bit count register gives you a seamless 32-bit result.

It's also possible to set-up CCx counter compare and overflow interrupts in 32-bits.

Hi Martin,

So ideally if i just remove the port and external interrupt commands, it should work right?

Well I did that and it shows TC4 value is zero on the serial monitor.

Hi parnal,

Well I did that and it shows TC4 value is zero on the serial monitor.

That's because with this code the TC timer is set up to read a high speed external interrupt.

How the TC timer is initialised depends on what you're trying to achieve. It can be set up in number of different ways depending on your requirement.

If you're just requiring a regular internal interrupt then the normal (NFRQ) or match frequency (MFRQ) modes are suitable. If however you require an external PWM output signal then normal (NPWM) or match (MPWM) PWM modes are probably best. There's also the event system, where one peripheral can be used to trigger another without CPU intervention.

How do you intend to use the TC timer?

Many thanks Martin I'll try it on my application and be back to you soon

Kind Regards

Daniel

parnal:
Hi Martin,

This is how my general TC4 counter code looks like:

void setup() {

Serial.begin(9600);

REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TC4_TC5) ; 
 
  TcCount32* TC = (TcCount32*) TC4;              // The type cast must fit with the selected timer mode
 
 
  TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT32;        // Set Timer counter Mode to 32 bits
 
  TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_NFRQ;      // Set TC as normal Frequency
 
  TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV256;  // Set perscaler
   
  TC->CTRLA.reg |= TC_CTRLA_ENABLE;            // Enable TC

REG_TC4_READREQ = TC_READREQ_RCONT |            // Enable a continuous read request
                    TC_READREQ_ADDR(0x10);        // Offset of the 16 bit COUNT register
    while (TC4->COUNT32.STATUS.bit.SYNCBUSY);              // Wait for synchronization
}

void loop() {
 
  TcCount32* TC = (TcCount32*) TC4;              // The type cast must fit with the selected timer mode  TcCount32* TC = (TcCount32*) TC3;              // The type cast must fit with the selected timer mode
 
 
  Serial.println(REG_TC4_COUNT32_COUNT);
  Serial.println("---");
  delay(100);

}




Now I would just like the counter (when rolls over to zero ) it should go to TC5 from TC4 and should not start counting itself from zero. I know I am asking the same thing but I am still there. so,if you can help how can this connection be achieved.


This sounds to be my solution , but I need help in initialization :
**There's also the event system, where one peripheral can be used to trigger another without CPU intervention.** 

EVGEN(Event Generator) TC3_MC0 should generate an event to the USER_TC4

Hi parnal,

No problem, hopefully I can help with the initialization.

First of all I need to know the frequency you intend to clock the timers. I notice in your code you're using a 256 prescaler divider.

My questions are:

  1. Are you intending to clock the timers at 187.5kHz (48MHz/256)?

  2. Do you wish the timers to free run and overflow at their maximum (2^32)?

Regarding the TC timer chaining, setting the TC timers to 32-bit mode is effectively the same as TC timer chaining on the Arduino Due, like in ard_newbie's code:

TC0->TC_BMR = TC_BMR_TC1XC1S_TIOA0;                     // Timer Counter 0 channel 1 is internally clocked by TIOA0

  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_XC1            // External clock XC1 selected
                              | TC_CMR_WAVE                // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC        // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA1 on RA compare match
                              | TC_CMR_ACPC_SET;           // Set TIOA1 on RC compare match

It's just that the SAMD21 requires less input from the programmer, as its internal hardware handles this for you. It means that you don't have to use the event system or interrupts to chain the timers.

Hi MartinL

I'm using your "TCCCapture_DMAC_TCCount.ino".

What I need is to "restart" the TCCount timer (TC4 & slave TC3) to 0 when the value of REG_TC4_COUNT32_COUNT rises a dedicated number (i.e lenght of bit measurement in my application).

Then how can I reset the TC timer to 0 and let it count again ?

Kind Regards

Daniel

Hi Daniel,

You can do this by setting the TC4 (plus TC3) to match frquency (MFRQ) mode.

In MFRQ mode the timer period can be set by the CC0 (counter compare 0) register, instead of the timer's maximum value:

REG_TC4_COUNT32_CC0 = 1919;                     // Set the TC4 CC0 register as the TOP value in match frequency mode
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for synchronization

The timer will return to 0 when it reaches the value in the CC0 register.

The timer is set to MFRQ mode in the CTRLA register:

REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                 TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode                   
                 TC_CTRLA_MODE_COUNT32 |        // Set the TC4 timer to 32-bit mode in conjuction with timer TC3
                 TC_CTRLA_ENABLE;               // Enable TC4
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for synchronization

Kind regards,
Martin

Hii Martin,

This is my code for clock chaining in Due and I wish to do the same on Zero.

uint32_t *TC_CV = (uint32_t *)(0x40084010); //for timer 1 channel 0 (see datasheet p.888)
uint32_t startTime;
uint32_t wantedTime;
uint32_t endTime;
uint32_t addup = 0;


void setup() {

pmc_set_writeprotect(false);

/*************  Timer Counter 1 Channel 0 to generate PWM pulses thru MCK  ************/

PMC->PMC_PCER0 |= PMC_PCER0_PID30;                          //PID30 from the peripheral identifiers for Timer 1 channel 0, the command enables PWM
TC1->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1      // MCK/2, clk on rising edge
                              | TC_CMR_WAVE                 // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC         // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR           // Clear TIOA0 on RA compare match
                              | TC_CMR_ACPC_SET   ;          // Set TIOA0 on RC compare match
                                            
// Frequency VARIANT_MCK is 84MHz, counter clocked with MCK/2 = 42MHz:
TC1->TC_CHANNEL[0].TC_RC =  VARIANT_MCK / 1000000 / 2;      //  count to 42 --> 1MHz on TIOA0
TC1->TC_CHANNEL[0].TC_RA = VARIANT_MCK / 1000000 / 4;       //  half value 21 (50% duty cycle on TIOA0)


TC1->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN;    // Software trigger TC0 counter and enable CLOCK


/*************  Timer Counter 1 Channel 1 to generate PWM pulses thru TIOA0  ************/

PMC->PMC_PCER0 |= PMC_PCER0_PID31;                        //PID31 from peripheral identifier from Timer 1 channel 1
TC1->TC_BMR = TC_BMR_TC1XC1S_TIOA0;                      // Timer Counter 1  XC1 is internally clocked by TIOA0
TC1->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_XC1            // External clock XC1 (i.e. TIOA0) selected
                              | TC_CMR_WAVE                // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC;        // UP mode with automatic trigger on RC Compare

TC1->TC_CHANNEL[1].TC_CV = 0;
TC1->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN;   // Software trigger TC1 counter and enable CLOCK


Serial.begin(250000);

}

void loop() {
  
    while (true){
    startTime = TC1->TC_CHANNEL[1].TC_CV; //*TC_CV
    //while(TC1->TC_CHANNEL[1].TC_CV == startTime)addup+=1;
    for(int i=0; i<5; i++);
    endTime=TC1->TC_CHANNEL[1].TC_CV;
    
    Serial.println(startTime);
    Serial.println(endTime);
    Serial.println(endTime-startTime);
    Serial.println(TC1->TC_CHANNEL[0].TC_RC);
  
    Serial.println("--");
    delay(500);
    }
}

and I wish to do this : "2) Do you wish the timers to free run and overflow at their maximum (2^32)?"

Hi parnal,

Ok, so this is ard_newbie's Arduino Due timer chaining code from here: Arduino Due - Clock Chaining - Arduino Due - Arduino Forum.

If you tell me what this code does, I'll implement for you on the SAMD21.

Kind regards,
Martin

Hii Martin,

The program is:

  1. Here I wanted to use Timer 1 which is having 3 channels (T1_CH0; T1_CH1; T1_CH2).
  2. I initialised 2 channels channel 0 and 1.
  3. I have set: channel 0 in waveform mode with master clock (MCK/2 which is 96/2 for due) with trigger on RC compare. After Timer 1 channel 0 is done counting it generates a output TIOA0 and this is fed input to Timer 1 channel 1 .
  4. Set Channel 1 with same parameters as channel 0 (waveform mode, rc compare ) then set the input as TIOA0 instead of master clock.
  5. Then simply print the counter value. (check pg. 861 Clock chaining diagram from datasheet SAM3X / SAM3A Series )

Hi parnal,

The following sets up the 16-bit TC3 timer at 48MHz and gets it to overflow every microsecond (1MHz). On each overflow the timer outputs an event that is received by the 32-bit TC4 (with TC5 as the slave). TC4 is set up to increment each time it receives an event, in other words it's clocked at 1MHz.

There appears to be an error in the SAMD21 datasheet. It says that TC4 is chained with TC3 for 32-bit mode, in reality however it's chained to TC5.

This code does the same as the Arduino Due TC timers:

// Setup TC3 to clock TC4/TC5 in 32-bit mode at 1MHz using the event system 

uint32_t timeMicros;

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(1) |    // Divide the 48MHz system clock by 1 = 48MHz
                    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_TCC2_TC3;    // Feed the GCLK5 to TCC2 and TC3
  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_TC4_TC5;     // Feed the GCLK5 to TC4 and TC5
  while (GCLK->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_TC4_EVU);                // Set the event user (receiver) as timer TC4
  
  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_TC3_OVF) |         // Set event generator (sender) as TC3 overflow
                      EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0                                

  REG_TC4_EVCTRL |= TC_EVCTRL_TCEI |              // Enable asynchronous input events on the TC timer
                    TC_EVCTRL_EVACT_COUNT;        // Increment the TC timer each time an event is received

  REG_TC3_EVCTRL |= TC_EVCTRL_OVFEO;              // Enable an event output on overflow of the TC3 timer

  REG_TC3_COUNT16_CC0 = 47;                       // Set the TC3 CC0 register to overflow every 1us (1MHz)
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
  
  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                   TC_CTRLA_MODE_COUNT32 |        // Set the TC4 timer to 32-bit mode in conjuction with timer TC5
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  REG_TC4_READREQ = TC_READREQ_RCONT |            // Enable a continuous read request
                    TC_READREQ_ADDR(0x10);        // Offset of the 32-bit COUNT register
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for (read) synchronization

  REG_TC3_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC3 into match frequency (MFRQ) mode   
                   TC_CTRLA_ENABLE;               // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  // Test timer TC3
  //REG_TC3_READREQ = TC_READREQ_RCONT |            // Enable a continuous read request
  //                  TC_READREQ_ADDR(0x10);        // Offset of the 32-bit COUNT register
  //while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for (read) synchronization

  timeMicros = micros();
}

void loop()
{
  SerialUSB.print(REG_TC4_COUNT32_COUNT);       // Output the results
  SerialUSB.print(F("   "));
  SerialUSB.println(micros() - timeMicros);
  
  //SerialUSB.println(REG_TC3_COUNT32_COUNT);     // Test timer TC3
}