During an upload the bootloader normally activates the board's LEDs. However, some M0 boards turn on the LEDs by sourcing current to them, others by sinking current from them. Sinking current from an LED inverts the logic required to turn it on. This might explain why your board's LED is on all the time.
I upload the bootloader but the flash verifying fails anyway with this error.
Timestamp: 2021-08-26 11:27:34.238
Severity: ERROR
ComponentId: 20000
StatusCode: 0
Verifying Flash...Failed! address=0x0000 expected=0xfc actual=0xff
It could have something to do with the bootloader protection fuse that's used to prevent the bootloader from being overwritten. I see from your fuses that the BOOTPROT is set to 16k. I'll have a look at the fuses on one of my old M0 based custom boards.
SOLUTION: set the BOOTPROT to 0, program the bootloader and then re-set it to 16K.
Now the led is fading.
Loaded the blink sketch. is working fine thanks
Glad to hear that it's now working.
Sorry for the slight off topic, but the watchdog seems not enabled by default and so i cant manage it by sw. right?
The fuses should normally switch the WDT off. If it's switched on your board will keep continuously resetting and rebooting.
If you require the WDT, it can be programmed separately with software.
Sorry for annoying again @MartinL, but if i want to do some economy with timers, is possible to use TC3 for delay generation and also for generate the output pulse?
I think about to keep it in free run and when it reaches the delay time to count, it sets the output pin high and in its ISR load a new value to count that will be the actual+pulsewidth. When it reaches again the counter value it sets the output low.
In this way i do not use TCC0. What do you think? Could you suggest a code to implement?
Thanks
@ekjk Yes, it's possible by reversing the timer's output polarity, then setting the match compare for the delay time and period for the pulse width. This will cause the timer to output a 0 for the delay and a 1 for the pulse.
However, the TCC timers are much better suited to this role than the TC timers, since they offer driver control, which would pull the output back to 0, once the oneshot cycle has finished.
I'm not saying it can't be done with the TC timers, but it would require CPU intervention in an ISR, to hold the output at 0 at the end of the pulse.
There is the possibility to do that as you said with TCC instead of TC timer? Or the TCC timer shall be engaged from a TC timer as already done?
Using the TCC0 timer would free up TC3. If you'd prefer to give TC3 a try, it would free up TCC0, but at the expense of calling TC3's ISR for each and every pulse.
Anyway the timer shall call its ISR at the compare match to re-enable the interrupt event on D6 (as in the code requested to you before). So in that isr the cpu can pull down the output.
@MartinL Could you please help me to wrap that type of code (again)?
thanks
Here's an example that retriggers the TCC0 timer every second. The start of the timer's retrigger cycle is indicated on pin PA20 (D6) with a GPIO driven pulse. The timer output is on port pin PA21 (D7).
The polarity of the TCC0 is reversed so that initially, at the start of the cycle, the timer outputs a LOW that determines the delay. Upon a compare match (CC3) the timer outputs a HIGH generating the pulse. The pulse continues for the remainder duration of the timer cycle determined by the timer's period (PER). Once the oneshot pulse is complete the driver control pulls the output line low.
Here's the example code, it output a narrow pulse on D6 with the timer generating a 5ms delay, followed by a 15ms pulse:
// Generate timer TCC0 oneshot 5ms delay (low) followed by a 15ms pulse (high) on output D7 (PA21)
// Start of timer cycle indicated by a pulse on output D6 (PA20)
void setup()
{
PORT->Group[PORTA].DIRSET.reg = PORT_PA20; // Set D6 as a TCC0 retrigger indicator 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 | // 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[PORTA].PINCFG[21].bit.PMUXEN = 1;
// Set-up the pin as an TCC0 PWM output on D7
PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;
TCC0->WAVE.reg = TCC_WAVE_POL3 | // Reverse the polarity on channel 3
TCC_WAVE_WAVEGEN_NPWM; // Set the TCC0 timer counter to normal PWM mode (NPWM)
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC0->PER.reg = 19999; // Set the timer delay plus pulse to 20ms
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[3].reg = 5000; // Set CC3 duty-cycle to 5ms pusle width
while (TCC0->SYNCBUSY.bit.CC3); // 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 timer TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop()
{
PORT->Group[PORTA].OUTSET.reg = PORT_PA20; // Generate pulse on D6 to indicate the start of the TCC0 retrigger
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TCC0
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchronization
PORT->Group[PORTA].OUTCLR.reg = PORT_PA20;
delay(1000); // Wait 1 second
}
@ekjk Looking into things a bit further, implementing a similar scheme for the TC timer is more problematic, since it lacks the TCC's driver control functionality.
This means that with the TC timer output inverted, (it doesn't have the TCC's polarity option), it's not possible to drive the timer output low at the end of the timer cycle.
HI @MartinL i have tried your sketch of post 33 but is not working. No output pulses are generated. I tried to comment out the detach and attach of input D6 but this change nothing.
Any suggestions?
thanks
Hi @ekjk
My apologies, looking back at my post (#33), I didn't explain it very well.
In this example code, both D6 and D7 are outputs. (This sketch doesn't include any pulse width and period code and just shows how TCC0 can be used to combine both delay and pulse generation).
D6 generates a narrow pulse. This acts as a marker that indicates the start of the TCC0 timer cycle and can be used to trigger an oscilloscope.
D7 is the combined TCC0 delay and pulse output that occurs after D6, in this case it will generate a 5ms low for the delay, followed by a 15ms high.
In your pulse width and period sketch, the TC3 and TCC0 code can be removed and replaced with this TCC0 example code instead. TCC0 being software retriggered in the TC4_Handler() ISR function.
Hi @MartinL ,
2 problems.
- i have tried the sketch to disable and enable interrupt on D6 (post 26 and 29). I have integrated it in my working code but now is not working anymore.
Here the code updated
// Setup timer TC4/TC5 to trigger on input event and set time delay for TCC0 output pulse, (TCC1 test output)
// 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 PA20 (D6)(Input) and PA21(D7)(Pulse output)
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[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 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_MFRQ | // Set TC3 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 200us on channel 3
while (TCC0->SYNCBUSY.bit.CC3); //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_NRE7; //1 // 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
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_MC3; // Enable match compare channel 3 (MC3) interrupts
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
{
period = isrPeriod;
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)
{
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
EIC->CONFIG[0].bit.SENSE4 = 0; // Switch off interrupts on D6
/////////////////////////////////////////////////////////////////////////////////////////////
// Calculate the output pulse delay here...
TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TC3, pulse automatically output on TCC0 after delay
//TC3->COUNT16.CTRLA.bit.ENABLE = 1;
TC3->COUNT16.CC[0].reg = 100; // Set the delay in us
while (TC3->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization
}
}
void TCC0_Handler() // Interrupt Service Routine (ISR) for timer TCC0
{
if (TCC0->INTFLAG.bit.MC3) // Check for match counter 3 (MC3) interrupt
{
TCC0->INTFLAG.bit.MC3 = 1; // Clear the interrupt flag
EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE4_HIGH; // Set event detecting a HIGH level
}
}
- i have merged the code you wrote to add a second delay timer and pulse generator (on D8). I think in your last reply you mention the last code you have posted but i meant the code you posted in post 31.
Here the code merged with the original one working that is now not working anymore.
// Setup timer TC4/TC5 to trigger on input event and set time delay for TCC0 output pulse, (TCC1 test output)
// Input: PA20 (D6), Pulse Output1: PA21 (D7) Pulse Output2: PA06 (D8)
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 PA20 (D6)(Input) and PA21(D7)(Pulse output) PA06 (D8) (second Pulse outptu)
PORT->Group[PORTA].PINCFG[20].bit.PMUXEN = 1;
PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
PORT->Group[PORTA].PINCFG[6].bit.PMUXEN = 1;
// Set-up the pin multiplexers
PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_A;
PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;
PORT->Group[PORTA].PMUX[6 >> 1].reg |= PORT_PMUX_PMUXE_E; // Configure PA06 as a TCC1 timer output on channel 0
// 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->USER.reg = EVSYS_USER_CHANNEL(3) | // Attach the event user (receiver) to channel 2 (n + 1)
EVSYS_USER_USER(EVSYS_ID_USER_TCC1_EV_0); // Set the event user (receiver) as timer TCC1, 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
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_TCC2_MCX_0) | // Set event generator (sender) as TCC2 match compare on channel 2
EVSYS_CHANNEL_CHANNEL(2); // Attach the generator (sender) to channel 2
TCC2->EVCTRL.reg = TCC_EVCTRL_MCEO0; // Output event on match compare on channel 0
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 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_MFRQ | // Set TC3 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) /////////////////////////////////////////////////////////////////////////////////
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_MC3; // Enable match compare channel 3 (MC3) interrupts
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.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
// Timer TCC2 (Delay Timer) /////////////////////////////////////////////////////////////////////////////////
TCC2->WAVE.reg = TCC_WAVE_WAVEGEN_NFRQ; // Set the TCC2 timer counter to normal frequency mode (NFRQ)
while (TCC2->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC2->PER.reg = 0xFFFF; // Set period to the timer's maximum value (2^16)
while (TCC2->SYNCBUSY.bit.PER); // Wait for synchronization
TCC2->CC[0].reg = 100; // Set the delay to 1ms
while (TCC2->SYNCBUSY.bit.CC0); // Wait for synchronization
TCC2->CTRLBSET.reg = TC_CTRLBSET_ONESHOT; // Enable oneshot operation
while (TCC2->SYNCBUSY.bit.CTRLB); // Wait for synchronization
TCC2->CTRLA.reg = TCC_CTRLA_PRESCSYNC_PRESC | // Reload timer on the next prescaler clock
TCC_CTRLA_PRESCALER_DIV16; // Set prescaler to 16, 16MHz/16 = 1MHz
TCC2->CTRLA.bit.ENABLE = 1; // Enable TCC2
while (TCC2->SYNCBUSY.bit.ENABLE); // Wait for synchronization
// Timer TCC1 (Pulse Output) /////////////////////////////////////////////////////////////////////////////////
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 = 200; // Set pulse width to 1.5ms on channel 0
while (TCC1->SYNCBUSY.bit.CC0); // Wait for synchronization
TCC1->PER.reg = 0xFFFFFF; // Set period to the timer's maximum value (2^24)
while (TCC1->SYNCBUSY.bit.PER); // Wait for synchronization
TCC1->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT; // Enable oneshot operation
while (TCC1->SYNCBUSY.bit.CTRLB); // Wait for synchronization
TCC1->DRVCTRL.reg |= TCC_DRVCTRL_NRE0; // Set the non-recoverable state output to 0 when inactive
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
}
void loop()
{
if (periodComplete) // Check if the period is complete
{
period = isrPeriod;
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)
{
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
/////////////////////////////////////////////////////////////////////////////////////////////
// Calculate the output pulse delay here...
TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TC3, pulse automatically output on TCC0 after delay
//TC3->COUNT16.CTRLA.bit.ENABLE = 1;
TC3->COUNT16.CC[0].reg = 100; // Set the delay in us
while (TC3->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization
TCC2->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TCC2, pulse automatically output on TCC0 after delay
while (TCC2->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
}
Thanks
Hi @ekjk
I've merged the TCC0 and TCC1 code that frees up timers TCC2 and TC3.
Here's the code with the (TC4) input on D6, (TCC0) output on D7 and (TCC1) output on D8:
// Setup timer TC4/TC5 to trigger on input event and set time delay and pulse time for TCC0 output pulse, (TCC1 test output)
// Input: PA20 (D6), Pulse Output1: PA21 (D7) Pulse Output2: PA06 (D8)
volatile boolean periodComplete;
volatile uint32_t isrPeriod;
uint32_t period;
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 TC4 and TCC5
GCLK_CLKCTRL_GEN_GCLK4 |
GCLK_CLKCTRL_ID_TC4_TC5;
// Port Multiplexing //////////////////////////////////////////////////////////////////////////////////
// Enable the port multiplexer on PA20 (D6)(Input) and PA21(D7)(Pulse output) PA06 (D8) (second Pulse outptu)
PORT->Group[PORTA].PINCFG[20].bit.PMUXEN = 1;
PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
PORT->Group[PORTA].PINCFG[6].bit.PMUXEN = 1;
// Set-up the pin multiplexers
PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_A;
PORT->Group[PORTA].PMUX[21 >> 1].reg |= PORT_PMUX_PMUXO_F;
PORT->Group[PORTA].PMUX[6 >> 1].reg |= PORT_PMUX_PMUXE_E; // Configure PA06 as a TCC1 timer output on channel 0
// 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->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
// 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 TCC0 (Delay and Pulse Output) //////////////////////////////////////////////////////////////////////
//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_MC3; // Enable match compare channel 3 (MC3) interrupts
TCC0->WAVE.reg = TCC_WAVE_POL3 | // Reverse the polarity on channel 3
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.CC3); // Wait for synchronization
TCC0->PER.reg = 800; // Set period to delay + pulse width: 800us - 200us = 600us pulse width
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
// Timer TCC1 (Delay and Pulse Output) ///////////////////////////////////////////////////////////////////////
TCC1->WAVE.reg = TCC_WAVE_POL0 | // Reverse the polarity on channel 0
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 = 400; // Set pulse width to 400us on channel 0
while (TCC1->SYNCBUSY.bit.CC0); // Wait for synchronization
TCC1->PER.reg = 800; // Set period to delay + pulse width: 800us - 400us = 400us pulse width
while (TCC1->SYNCBUSY.bit.PER); // Wait for synchronization
TCC1->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT; // Enable oneshot operation
while (TCC1->SYNCBUSY.bit.CTRLB); // Wait for synchronization
TCC1->DRVCTRL.reg |= TCC_DRVCTRL_NRE0; // Set the non-recoverable state output to 0 when inactive
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
}
void loop()
{
if (periodComplete) // Check if the period is complete
{
noInterrupts(); // Turn off interrupts
period = isrPeriod; // Copy the ISR period to the loop() period variable
interrupts(); // Turn on interrupts
SerialUSB.print(F("P us: ")); // Display the new results
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
/////////////////////////////////////////////////////////////////////////////////////////////
//
// Calculate the output pulse delay here...
//
/////////////////////////////////////////////////////////////////////////////////////////////
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TCC0, timer to output delay and pulse
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchronization
TCC1->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger ONESHOT operation on TCC1, timer to output delay and pulse
while (TCC1->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
}
Here's the result on my scope. My Arduino Due input signal D6 (yellow) showing the rising edge of a 1.5ms pulse every 50Hz (20ms) at the end of the last period. The TCC0 output D7 (light blue) with a 200us delay followed by a 600us pulse. The TCC1 output D8 (purple) with a 400us delay followed by a 400us pulse.
Very nice job @MartinL , thanks a lot.
Remains only the issue about detach and attach the D6 input event. I will try to merge the code of it in your last sketch and let you now.
thanks