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 ?
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
Here I wanted to use Timer 1 which is having 3 channels (T1_CH0; T1_CH1; T1_CH2).
I initialised 2 channels channel 0 and 1.
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 .
Set Channel 1 with same parameters as channel 0 (waveform mode, rc compare ) then set the input as TIOA0 instead of master clock.
Then simply print the counter value. (check pg. 861 Clock chaining diagram from datasheet SAM3X / SAM3A Series )
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
}
The TC timer is simply counting the number of input pulses. So the time elapsed depends on how fast you're clocking the input and if that input's regular or not. If there's no input, the counter will stop counting.
The match frequency mode just resets the TC timer back to 0 when it's COUNT register matches the value in the CC0 register.
Taking the 1919 value in the example with a regular 40kHz PWM input gives you:
Time elapsed = (1 / 40000) * (1919 + 1) = 48ms
Add 1 to the 1919 to take into account that we're counting from 0.
If you want to stop the timer in your code:
REG_TC4_CTRLBSET = TC_CTRLBCLR_CMD_STOP;
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);
...and to restart...
REG_TC4_CTRLBSET = TC_CTRLBCLR_CMD_RETRIGGER;
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);
I hope someone can help me.
I am using a MKR1010 board with a SAMD21 and I am trying to change the frequency for the attachInterrupt function to use the external interrupt. I have a receiver on a pin and the external interrupt function is called via attachInterrupt(pin, ISR, CHANGE) to find a valid signal. I think that I need to change the frequency of the EIC (External Interrupt Controller). The SAMD21 uses 48MHz and I would need 16Mhz for the external interrupts. How can I change the frequency? I have no experience in changing the registers
What's the minimum pulse width and minimum/maximum period of the signal you're receiving?
Is the period (frequency) regular, or are the pulses generated at random or irregular intervals?
The reason I ask, is that if your requirement isn't too stringent then you could just use the attachInterrupt() function and measure the time between the edges using micros(). This is also good if the pulses occur at irregular intervals.
If however you require greater timing resolution then using interrupts connected to an internal timer via the SAMD21's event system is the way to go.
thank you for your fast reply
I am using the NewRemoteSwitch Library for 433Mhz signals. I have used that library with the Arduino Uno and YUN and it works great. I do not know exactly the signal width and frequency. The only thing I know is that the attachInterrupt function for external interrupts works with the Uno and YUN like a charm. Both of them are based on 16MHz oscillator. I tried to slow down the interrupt function of the MKR1010 with a fixed timer5 library and called the attachInterrupt function inside the timed based interrupt function. It worked but not reliable enough. It seems that this was the evidence that a frequency change could solve the problem. What do you think about that?
Protocol. (Copied from Wieltje, Klik aan Klik uit protocol - Forum - Circuits Online,
but with slightly different timings, as measured on my device.)
_ _
'0': | || |_____ (T,T,T,5T)
_ _
'1': | |_____| | (T,5T,T,T)
_ _
dim: | || | (T,T,T,T)
T = short period of ~260µs. However, this code tries
to figure out the correct period
A full frame looks like this:
start pulse: 1T high, 10.44T low
26 bit: Address
1 bit: group bit
1 bit: on/off/[dim]
4 bit: unit
[4 bit: dim level. Only present of [dim] is chosen]
Does the attachInterrupt interrupt service routine (ISR) funcion in Uno/Yun library happen to use the micros() function?
The reason I ask is because in the past I've found that the micros() function doesn't work in the ISR on the Arduino Zero. This might explain way it works on the Uno/Yun and not on the Zero.
It may or may not solve the problem, but you could try replacing micros() with micros2() function.
The micros2() function uses the timer counter TC3 to calculate micros() in a similar way to the AVR Arduinos such as the Uno, Mega, etc...
I've successfully used it in attachInterrupt() interrupt service routines for my flight control firmware.
Here's the code, (together with test output to the console):
// Micros2 function
volatile unsigned long timer2Counter;
void setup() {
Serial.begin(115200); // Set up the serial port for test purposes
// Set up the generic clock (GCLK4) used to clock timers
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) | // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK4
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
GCLK_GENCTRL_ID(4); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Feed GCLK4 to TCC2 (and TC3)
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC2 (and TC3)
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC2_TC3; // Feed GCLK4 to TCC2 (and TC3)
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
TC3->COUNT8.PER.reg = 0xFF; // Set period register to 255
while (TC3->COUNT8.STATUS.bit.SYNCBUSY); // Wait for synchronization
TC3->COUNT8.INTENSET.reg = /*TC_INTENSET_MC1 | TC_INTENSET_MC0 |*/ TC_INTENSET_OVF; // Enable TC3 interrupts
//NVIC_DisableIRQ(TC3_IRQn);
//NVIC_ClearPendingIRQ(TC3_IRQn);
NVIC_SetPriority(TC3_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest)
NVIC_EnableIRQ(TC3_IRQn); // Connect TC3 to Nested Vector Interrupt Controller (NVIC)
// Set the TC3 timer to tick at 2MHz, or in other words a period of 0.5us - timer overflows every 128us
// timer counts up to (up to 255 in 128us)
TC3->COUNT8.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV8 | // Set prescaler to 8, 16MHz/8 = 2MHz
TC_CTRLA_PRESCSYNC_PRESC | // Set the reset/reload to trigger on prescaler clock
TC_CTRLA_MODE_COUNT8; // Set the counter to 8-bit mode
TC3->COUNT8.CTRLA.bit.ENABLE = 1; // Enable TC3
while (TC3->COUNT8.STATUS.bit.SYNCBUSY); // Wait for synchronization
TC3->COUNT8.READREQ.reg = TC_READREQ_RCONT | // Enable a continuous read request
TC_READREQ_ADDR(0x10); // Offset of the 8 bit COUNT register
while (TC3->COUNT8.STATUS.bit.SYNCBUSY); // Wait for (read) synchronization
}
void loop() {
Serial.println(micros2()); // Testing the micros2() function
delay(1000); // Wait 1 second
}
// Micros2 is used to measure the receiver pulsewidths down to 1us accuracy
uint32_t micros2()
{
uint32_t m;
uint8_t t;
noInterrupts(); // Disable interrupts
m = timer2Counter; // Get the number of overflows
t = TC3->COUNT8.COUNT.reg; // Get the current TC3 count value
if (TC3->COUNT8.INTFLAG.bit.OVF && (t < 255)) // Check if the timer has just overflowed (and we've missed it)
{
m++; // Then in this case increment the overflow counter
}
interrupts(); // Enable interrupts
return ((m << 8) + t) / 2; // Return the number of microseconds that have occured since the timer started
}
// This ISR is called every 128us
void TC3_Handler() // ISR TC3 overflow callback function
{
if (TC3->COUNT8.INTFLAG.bit.OVF)
{
timer2Counter++; // Increment the overflow counter
}
TC3->COUNT8.INTFLAG.reg = TC_INTFLAG_OVF; // Rest the overflow interrupt flag
}
Hello MartinL,
A couple of months ago you graciously assisted with generating a variable PWM frequency on pins that use TCC0 (Pin 3) and TCC1 (Pin 8). Using the same MCU, I am attempting to read the duty cycle of a 45hz PWM signal and running into a few issues.
First, the CPU is already a little taxed (SPI, calculating PID, sending serial data, etc.) and is producing erratic readings. Second, it does not appear that the examples that use TCCx will work since they will require feeding a different clock frequency to TCC0 and TCC1.
The basic pulseIn function worked but it added a 13ms delay to the loop processing that I am trying to avoid.
I saw that you were able to free the CPU by using DMAC in post #102. Is it possible to use DMAC for TC3 example you posted?
Requirements: Read the duty cycle of a 45hz, not interested in frequency, utilize DMAC, utilize TCx.