Hi hussat,
I've modified the code so that it should work with D6 as the input on the Xiao. I've tested the code on my custom SAMD21 board with 200kHz pulses, but using a different input pin.
The code receives the incoming pulses on D6 and routes them to the TCC0 timer via the EIC (External Interrupt Controller) and the Event System, (a 12-channel peripheral-to-peripheral highway). The TCC0 is configured to simply count the number of input events or pulses.
The TCC2 timer meanwhile is clocked from the on-board 32.768kHz external crystal and uses this to time a period of 1 second, over which the number of pulses are counted.
Calling the startConversion() function retriggers both timers. When the 1 second has elapsed, the TCC2's interrupt service routine is called and number of pulses counted by TCC0 is displayed on the Serial console.
I appreciate that this code is somewhat different from your AVR code. It's just that the SAMD21 operates in a completely different way.
Here's the code:
// Count the number of pulses on pin D6 (PB08) over a 1 second period
void setup()
{
SerialUSB.begin(115200); // Initialise the native serial port
while(!SerialUSB); // Wait for the console to open
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS; // Switch on the event system peripheral
////////////////////////////////////////////////////////////////////////////////////////
// Generic Clock Initialisation
////////////////////////////////////////////////////////////////////////////////////////
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) | // Select clock divisor to 1
GCLK_GENDIV_ID(4); // Select GLCK4
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK
GCLK_GENCTRL_SRC_XOSC32K | // Select GCLK source as external 32.768kHz crystal (XOSC32K)
GCLK_GENCTRL_ID(4); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable generic clock
GCLK_CLKCTRL_GEN_GCLK0 | // GCLK0 at 48MHz
GCLK_CLKCTRL_ID_TCC0_TCC1; // As a clock source for TCC0 and TCC1
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable generic clock
GCLK_CLKCTRL_GEN_GCLK4 | // GCLK4 at 32.768kHz
GCLK_CLKCTRL_ID_TCC2_TC3; // As a clock source for TCC2 and TC3
//////////////////////////////////////////////////////////////////////////////////////////////
// TCC2 Initialisation - reference timer: measures a 1s period
//////////////////////////////////////////////////////////////////////////////////////////////
TCC2->PER.reg = 32767; // Set the period (PER) register for a PWM period of 1s
while (TCC2->SYNCBUSY.bit.PER); // Wait for synchronization
TCC2->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Set timer to Normal PWM mode (NPWM)
while (TCC2->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC2->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT; // Enable oneshot operation
while(TCC2->SYNCBUSY.bit.CTRLB); // Wait for synchronization
NVIC_SetPriority(TCC2_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC2 to 0 (highest)
NVIC_EnableIRQ(TCC2_IRQn); // Connect TCC2 to Nested Vector Interrupt Controller (NVIC)
TCC2->INTENSET.reg = TCC_INTENSET_OVF; // Enable overflow (OVF) interrupts on TCC2
TCC2->CTRLA.bit.ENABLE = 1; // Enable TCC2
while (TCC2->SYNCBUSY.bit.ENABLE); // Wait for synchronization
////////////////////////////////////////////////////////////////////////////////////////
// TCC0 Initialisation - measurement counter: counts the number of incoming of pulses
////////////////////////////////////////////////////////////////////////////////////////
PORT->Group[PORTB].PINCFG[8].bit.PMUXEN = 1; // Enable the port multiplexer on port pin PB08 (D6)
PORT->Group[PORTB].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_A; // Set-up PB08 (D6) as an EIC (interrupt)
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO8; // Enable event output on external interrupt 8
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE0_HIGH; // Set interrupt to detect a HIGH level
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT8; // Clear the interrupt flag on channel 8
EIC->CTRL.bit.ENABLE = 1; // 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_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_8) | // Set event generator (sender) as external interrupt 8
EVSYS_CHANNEL_CHANNEL(0); // Attach the generator (sender) to channel 0
TCC0->EVCTRL.reg = TCC_EVCTRL_TCEI0 | // Enable TCC0 event 0 inputs
TCC_EVCTRL_EVACT0_INC; // Increment the TCC0 counter on receiving an event 0
TCC0->CTRLA.bit.ENABLE = 1; // Enable TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop()
{
startConversion(); // Start a conversion over the 1 second integration window
delay(2000); // Wait for 1 second
}
void startConversion()
{
TCC2->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger the timer TCC2 to start the integration window
while (TCC2->SYNCBUSY.bit.CTRLB); // Wait for synchronization
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger the timer TCC0 to start the count
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
void TCC2_Handler()
{
TCC2->INTFLAG.bit.OVF = 1; // Clear the TCC2 overflow interrupt flag
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC; // Trigger a read synchronization on the COUNT register
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for the CTRLB register write synchronization
while (TCC0->SYNCBUSY.bit.COUNT); // Wait for the COUNT register read sychronization
SerialUSB.println(TCC0->COUNT.reg); // Print the result
}