I am currently looking for a way to redirect some GPIO inputs to TC counters without CPU intervention on a MKRGSM 1400, to calculate the frequency every second.
I am quite at a loss about how to do it, and I do not manage to make analogies between the SAMD21 datasheet and the code to use.
I found that this thread :
spoke about a code that seems to do similar things, but I cannot find the mentioned code.
It's possible to acquire the pulse width and period of incoming pulses with the SAMD21's TC timer on pin D12 of the MKR1400 GSM, using the following code:
// Setup TC4 to capture pulse-width and period on digital pin D12
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
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) 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 | // Route GCLK4 to TC4 and TC5
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TC4_TC5;
// Enable the port multiplexer on port pin PA09
PORT->Group[PORTA].PINCFG[9].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) on port pin PA09
PORT->Group[PORTA].PMUX[9 >> 1].reg |= PORT_PMUX_PMUXO_A;
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO9; // Enable event output on external interrupt 9
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE1_HIGH; // Set event detecting a HIGH level
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT9; // Disable interrupts on external interrupt 9
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_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_9) | // Set event generator (sender) as external interrupt 9
EVSYS_CHANNEL_CHANNEL(0); // Attach the generator (sender) to 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
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 | // Overflow on precaler clock, (rather than the GCLK)
TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16, 16MHz/16 = 1MHz
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
}
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
{
// 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
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
}
}
Thank you, what a well-explained piece of code!
I was looking to use the GCLKIN source but it seems to not be a good solution since it prevents any synchronisation when there's nothing coming on the GCLKIN port.
However, I seem to run into an odd problem: I made a simple circuit with a pulldown resistor to create the impulsions with a button. When I put the cable on the pin D12, my PC doesn't find the Arduino board anymore... (even before compiling and loading the code on it)
Can I change the pin freely?
Moreover, the Serial port does not show anything even after minutes of the code running, is that related to the aforementioned problem?
The MKR1400 GSM's D12 pin doubles as the I2C SCL line and is already pulled up with an on-board 4k7 resistor. An external pull-down resistor, depending on its value, may or may not pull the input down to the correct voltage.
To test that the above code is working OK, I added a test PWM output, 1500us pulses at 50Hz (20000us period) on D7. After compilation I connected D7 to D12.
Here's the additional test code (that I inserted after the TC4 and TC5 GCLK set-up):
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Route GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TCC0_TCC1;
while (GCLK->STATUS.bit.SYNCBUSY);
// Enable the port multiplexer on digital pin D7
PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) peripheral on D7
PORT->Group[g_APinDescription[7].ulPort].PMUX[g_APinDescription[7].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Enable Normal PWM (NPWM) mode
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC0->PER.reg = 19999; // Set the timer to output to 50Hz
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[3].reg = 1500; // Set CC3 duty-cycle to 1500us pulse width
while (TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
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 timer TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
The output on the console is as follows:
PW: 1499 P : 19999
PW: 1499 P : 19999
PW: 1499 P : 19999
PW: 1499 P : 19999
PW: 1499 P : 19999
PW: 1499 P : 19999
The above example measures microseconds, however it's possible to tweak both the GCLK4 divisor and/or the TC timer prescaler, to generate the required timer resolution for your application.
Could you please enlighten me about how you link D7 to D12? I used a simple wire, but this does not do anything. I still remain with a saddening blank Serial Monitor no matter how much I wait for something to display (and I am pretty sure I should not have to wait). Since your code works for you, I am absolutely at a loss right now.
Anyway, shouldn't it display something even if there was no input (like the equivalent of 0 frequency) ?
If I put a LED on the output of the D7 I can see that there is indeed an output (my LED is lit) but I get a quite similar result on the D12 pin, and tell me if I'm wrong but it seems like this is not normal if I understand the code well.
Strange that it's not working, it should just mean connecting D7 to D12 with a wire.
Do you have access to a digital multimeter (DMM), or even better an oscilloscope? If you change the TCC0 duty-cycle to 50%, or in other words change the CC3 register to 10000 (instead of 1500), your DMM should read 1.65V (or thereabouts) on the D7 output. This will at least prove that the PWM output is working.
The reason why you're not seeing any output, is because the TC4 timer interrupt is only triggered when it sees an incoming pulse. Otherwise it will just sit there waiting.
It's also possible to route the input signal to almost any other pin other than D12. If you have anonother pin free on your board, I can provide you with the code for that.
@diamondemon Here's the complete code that I'm using:
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
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) 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 | // Route GCLK4 to TC4 and TC5
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TC4_TC5;
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Route GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TCC0_TCC1;
while (GCLK->STATUS.bit.SYNCBUSY);
// Enable the port multiplexer on digital pin D7
//PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
// Set-up the pin as an TCC0 PWM output on D7
//PORT->Group[g_APinDescription[7].ulPort].PMUX[g_APinDescription[7].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Enable Normal PWM (NPWM) mode
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC0->PER.reg = 19999; // Set the timer to output a 50Hz
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[3].reg = 1500; // Set CC3 duty-cycle to 1500us pusle width
while (TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
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 timer TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
// Enable the port multiplexer on port pin PA09
PORT->Group[PORTA].PINCFG[9].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) on port pin PA09
PORT->Group[PORTA].PMUX[9 >> 1].reg |= PORT_PMUX_PMUXO_A;
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO9; // Enable event output on external interrupt 9
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE1_HIGH; // Set event detecting a HIGH level
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT9; // Disable interrupts on external interrupt 9
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_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_9) | // Set event generator (sender) as external interrupt 9
EVSYS_CHANNEL_CHANNEL(0); // Attach the generator (sender) to 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
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 | // Overflow on precaler clock, (rather than the GCLK)
TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16, 16MHz/16 = 1MHz
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
}
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
{
// 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
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
}
}
I managed to get my hands on a DMM and it seems like the D7 pin is working perfectly. However, I cannot explain the 1.08V value I find on the D12. It seems to me like an absolutely random value, which I do not understand.
Have you removed the external pull-down resistor on D12?
A value of 1.08V would indicate that an external 2k2 pull-down is still connected to the input. This would create a voltage divider with the MKR1400 GSM's on-board I2C 4k7 pull-up reistor.
Thank you.
Moreover, I cannot explain why but it turns out that the other MKR GSM 1400 I have available has the same behaviour about not displaying anything (but Serial communication works for sure).
It's always nice to have two boards to compare. The fact that they're displaying same behaviour usually means that it's the code and not the hardware that's at fault. I'm just testing the code...
@diamondemon I've moved the input from D12 to D6. If you upload the code below, connect D7 to D6 then open the console, you should see the pulse width and period output.
By the way, I made a small mistake that I've now corrected in the code above. The GCLK divisor was set to 1 instead of 3.
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) | // Divide the 48MHz system clock by 3 = 16MHz
It doesn't make any difference to the result, other than the fact that both timers ran 3 times faster than I anticipated. Anyway, that's now been corrected.
I've tested the code, but please let me know if this works for you (or not)?
Here's the code:
// Setup TC4 to capture pulse-width and period on digital pin D6, (test signal on D7)
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
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) 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 | // Route GCLK4 to TC4 and TC5
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TC4_TC5;
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Route GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TCC0_TCC1;
while (GCLK->STATUS.bit.SYNCBUSY);
// Enable the port multiplexer on digital pin D7
//PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
// Set-up the pin as an TCC0 PWM output on D7
//PORT->Group[g_APinDescription[7].ulPort].PMUX[g_APinDescription[7].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Enable Normal PWM (NPWM) mode
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC0->PER.reg = 19999; // Set the timer to output a 50Hz
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[3].reg = 1500; // Set CC3 duty-cycle to 1500us pusle width
while (TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
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 timer TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
// Enable the port multiplexer on port pin PA20
PORT->Group[PORTA].PINCFG[20].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) on port pin PA20
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; // Disable interrupts on external interrupt 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_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
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
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 | // Overflow on precaler clock, (rather than the GCLK)
TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16, 16MHz/16 = 1MHz
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
}
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
{
// 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
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
}
}