I'm generating a wave that's roughly 2.94MHz (340ns periodicity) feeding to digital pin 2. I attached an interrupt with its rising edge to do something. In this case, to make things as simple as possible, I just made digital pin 8 high then low right away.
When viewed through a logic analyzer, I see that it works but the timing when the pin 8 changes is inconsistent to when the rising edge happens on pin 2.
You didn't say what type of Arduino you are using, so we will assume Uno, which has a 16MHz clock speed. Your interrupts are occurring every 16/2.94=5 or 6 clock cycles. There's no way your interrupt code is going to execute in less than 5 clock cycles because each individual machine instruction takes a minimum of one clock cycle. A line of C code can translate into many machine instructions, tens or even hundreds.
Maybe your interrupt code takes less than 50 cycles, in which case you need an MCU with a clock speed of at least 160MHz.
Oops, I completely missed that, having picked it up from the "New" topics. Sorry @etk2022
So the clock speed is 5x faster and interrupts will happen every ~28 clock cycles. That still might not be fast enough, I guess. There will be additional overhead cycles needed when an interrupt is called, in addition to the interrupt code itself.
As the CPU is unable to service interrupts at the 3MHz, another option is to use 3MHz signal as an input to the Due's TC timer trigger pin and output a delayed pulse on the timer's output.
The following code sets up timer TC6 (confusingly also know as: TC2 Channel 0) with the trigger input on D30 and the output on D5. The input set to trigger on the rising edge of the signal. The result is an output pulse delayed with respect to the rising edge of the input trigger:
// Set up the Arduino Due's TC6 (TC2 Channel 0) timer to trigger on the rising edge of input D30 and
// output a delayed pulse output on D5
void setup()
{
PMC->PMC_PCER1 |= PMC_PCER1_PID33; // Enable peripheral TC6 (TC2 Channel 0)
PIOC->PIO_ABSR |= PIO_ABSR_P26 | PIO_ABSR_P25; // Switch the multiplexer to peripheral B for TIOA6 (D5) and TIOB6 (D4)
PIOC->PIO_PDR |= PIO_PDR_P26 | PIO_PDR_P25; // Disable the GPIO on the corresponding pins
PIOD->PIO_ABSR |= PIO_ABSR_P9; // Switch the multiplexer to peripheral B for TCLK8 (D30)
PIOD->PIO_PDR |= PIO_PDR_P9; // Disable the GPIO on the corresponding pin
TC2->TC_CHANNEL[0].TC_CMR = TC_CMR_ACPC_CLEAR | // Clear TIOA6 on counter match with RC0
TC_CMR_ACPA_SET | // Set TIOA6 on counter match with RA0
TC_CMR_WAVE | // Enable wave mode
TC_CMR_WAVSEL_UP_RC | // Count up with automatic trigger on RC compare
TC_CMR_ENETRG | // Enable an external trigger event
//TC_CMR_EEVT_TIOB | // Set the external trigger input to TIOB6 (D4)
TC_CMR_EEVT_XC2 | // Set the external trigger input to TCLK8 (D30)
TC_CMR_ETRGEDG_RISING | // Set to trigger event on rising edge
TC_CMR_CPCSTOP | // Stop the timer at the end of the cycle (oneshot operation)
TC_CMR_TCCLKS_TIMER_CLOCK1; // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)
TC2->TC_CHANNEL[0].TC_RA = 1; // Set the trigger to output rising edge delay 1/42MHz * 1 = 24ns + propagation delay
TC2->TC_CHANNEL[0].TC_RC = 2; // Set the output pulse width 1/42 * (2 - 1) = 24ns
TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN; // Enable the timer TC6 (TC2 Channel 0)
}
void loop() {}
Set TC_RA to adjust the delay and TC_RC the pulse width.
Hi @PaulRB, as others mentioned, I'm indeed doing this on the Due. I have a question in regards to interrupts. Let's just use your example of 5 clock cycles for simplicity. If the command I want in the interrupt takes 20 clock cycles, will that entire command not complete since it's being constantly interrupted every 5 cycles or does it wait for whatever is happening inside the interrupt function?
Hi @MartinL, thanks for the example. I have tried it and it works well but the end goal is to have more code than just a pin going high and low so I was really hoping attachInterrupt would be the answer.
An interrupt on a cortex-m3 CPU takes at least 12 cycles (for 0-wait-state RAM) to enter an ISR, and another 12 cycles to exit the ISR. An 84MHz CPU is not going to handle a 3MHz interrupt rate...
will that entire command not complete since it's being constantly interrupted every 5 cycles or does it wait for whatever is happening inside the interrupt function?
Usually the interrupt isn't re-enabled until the ISR is exited. The CM3 has a tail-chaining ISR feature such that if a new interrupt occurs before the ISR exit, it will skip most of the register stacking it would normally do, which can reduce the latency in responding to the second/etc interrupt. But if you have continuous interrupts coming in faster than the overhead+ISR code time, you might never get to execute and non-interrupt code.
If I only have one interrupt in the program, does setting a higher priority provide higher consistency in the interrupt? For my project, the consistency in timing between the two events (falling edge to trigger interrupt and actions taken in the interrupt) is much more important than the latency.
If setting a higher priority can help, what commands do I need to include in the program?