I'm trying to build a random pulse counter (rising edge) for the SAMD51/M4.
The input pulses are ~30ns in width, and can be anything up to 10mhz (10 million random pulses per second).
I am using a Feather M4 Express board.
The idea is to use external interrupt pin A0 (PA02 = EXT_INT_2) as input, so it counts without the MCU intervention (asynchronous), and store the counts in TCC0 for random reading at any time. The input signal could be another pin and another counter (but need 24 bit counter or as large as possible).
AFAIK the EVSYS is needed for this task to avoid loading the MCU (ie trying to count using traditional interrupts which have an upper limit of ~1mhz counting bandwidth). Eventually I need to make several counters from different input pins, on the same MCU (use TCC0 & TCC1 for 24 bit, and then use 16 bit TC counters together to form 32bit counters).
I've come up with the code below based mostly on posts by MartinL in this thread (also this thread was somewhat useful).
The first link above is for a pulse counter which works perfectly on SAMD21/M0. The trick is to port this to SAMD51 which seems to be quite different in setting up the event system and the counters to pick up the rising edges of the input signal.
Not sure what I'm missing, any help would be greatly appreciated!
//Board: Adafruit Feather M4 Express
//Below is an example that sets up TCC0 (24 bit) to count input events on A0(PA02 = EXT_INT_2).
//The TCC0 counter value is output to the console:
void setup()
{
Serial.begin(115200);
delay(1000);
MCLK->APBBMASK.reg |= MCLK_APBBMASK_EVSYS; //// or: MCLK->APBBMASK.bit.EVSYS_ = 1; // Switch on the event system peripheral
MCLK->APBAMASK.reg |= MCLK_APBAMASK_EIC; //not needed?
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC0; //not needed?
// Set up the generic clock (GCLK7) to clock timer TCC0
GCLK->GENCTRL[7].reg = GCLK_GENCTRL_DIV(1) | // Divide the 48MHz clock source by divisor 1: 48MHz/1 = 48MHz
GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK7
GCLK_GENCTRL_SRC_DFLL; // Select 48MHz DFLL clock source
//GCLK_GENCTRL_SRC_DPLL1; // Select 100MHz DPLL clock source
//GCLK_GENCTRL_SRC_DPLL0; // Select 120MHz DPLL clock source
while (GCLK->SYNCBUSY.bit.GENCTRL7); // Wait for synchronization
GCLK->PCHCTRL[TCC0_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | // Enable the TCC0/TCC1 peripheral channel - PCHCTRL[25] is TCC0, TCC1
GCLK_PCHCTRL_GEN_GCLK7; // Connect generic clock 7 to TCC0/TCC1
// Enable the port multiplexer on A0 input
PORT->Group[g_APinDescription[A0].ulPort].PINCFG[g_APinDescription[A0].ulPin].bit.PULLEN = 0;
PORT->Group[g_APinDescription[A0].ulPort].PINCFG[g_APinDescription[A0].ulPin].bit.INEN = 1;
PORT->Group[g_APinDescription[A0].ulPort].PINCFG[g_APinDescription[A0].ulPin].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) peripheral D2 A(0)
PORT->Group[g_APinDescription[A0].ulPort].PMUX[g_APinDescription[A0].ulPin >> 1].reg = PORT_PMUX_PMUXE(0) | PORT_PMUX_PMUXO(0);
//attachInterrupt(A0, NULL, RISING); // Attach interrupts to digital pin 2 (external interrupt 2)
EIC->CTRLA.bit.ENABLE = 0;
//EIC->INTENCLR.bit.EXTINT = (1<<2);
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO(2); // Enable event output on external interrupt 2
EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE2_HIGH; // Set event on detecting a HIGH level
EIC->CTRLA.bit.ENABLE = 1; // Enable EIC peripheral
while (EIC->SYNCBUSY.bit.ENABLE); // Wait for synchronization
EVSYS->USER[EVSYS_ID_USER_TCC0_EV_0].reg = EVSYS_USER_CHANNEL(1); // Attach the event consumer (receiver) to channel 0 (n + 1)
EVSYS->Channel[0].CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT | // No event edge detection is required in asynchronous mode
EVSYS_CHANNEL_PATH_ASYNCHRONOUS | // Set event path as asynchronous
EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_2); // Set event generator (sender) as external interrupt 2
//| EVSYS_CHANNEL_CHANNEL(0); implied above??? // Attach the generator (sender) to channel 0
TCC0->EVCTRL.reg |= TCC_EVCTRL_TCEI0 | // Enable the TCC event 0 input
TCC_EVCTRL_EVACT0_COUNTEV; // Set event 0 to count on incoming events
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NFRQ; // Set the TCC0 timer counter to normal frequency mode
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC0->CTRLA.bit.ENABLE = 1; // Enable TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop()
{
//read TCC0 continuously
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
Serial.println(TCC0->COUNT.reg); // Output the TCC0 COUNT register
delay(100);
}