I am using the ATMega32u4 to generate multiple pulses at 2.5 kHz. I am using Timer 3, which is a 16-bit timer and using a prescaler of 1, so I have a 0.0625uS resolution. I want to generate pulses with pretty high accuracy and consistency. I have my previous post here, which gave me accuracy. But when I look at the pulse with my oscilloscope it has some jitter. Youtube Video of Jitter.
My final goal is to create a pulse on multiple different pins in this configuration. enter image description here
What I am assuming is that the reason there is jitter is that there are other interrupts going on in the background, that could be shifting when the pulse stops and starts by a few microseconds. Looking through the datasheet, I found this, which seems to show interrupt priority. enter image description here
It seems that my TIMER3 COMPA and COMPB are very low down the list of priorities, is there a way to change this priority list? Or is there something else that is causing the jitter?
I have posted my code below:
#define pin2 12
#define pin3 6
#define pin4 8
#define REFRESH 6400
int PULSEIndex;
class Pulse {
public:
int pulseLength;
void Begin(byte GPIO_PIN) {
pinMode(GPIO_PIN, OUTPUT);
}
void writeMicro(double pulse) {
pulseLength = (pulse * 16);
}
};
Pulse PULSE1;
Pulse PULSE2;
Pulse PULSE3;
Pulse PULSE4;
void setup() {
PULSE1.Begin(pin1);
PULSE2.Begin(pin2);
PULSE3.Begin(pin3);
PULSE4.Begin(pin4);
// Reset Timer 3 Control Register to 0
TCCR3A = 0;
TCCR3B = 0;
// Set CTC Mode
TCCR3B &= ~(1 << WGM33); //0
TCCR3B |= (1 << WGM32); //1
// Set prescaler to 1
TCCR3B &= ~(1 << CS32); //0
TCCR3B &= ~(1 << CS31); //0
TCCR3B |= (1 << CS30); //1
//Reset Timer 3 and set compare value
TCNT3 = 0;
// Enable Timer 3 compare interrupt A and B
TIMSK3 |= (1 << OCIE3B);
TIMSK3 |= (1 << OCIE3A);
//Enable global interrupts
sei();
//Set Refresh Rate
OCR3A = REFRESH;
OCR3B = 1000;
}
void loop() {
PULSE1.writeMicro(40);
PULSE2.writeMicro(40);
PULSE3.writeMicro(40);
PULSE4.writeMicro(40);
delay(2);
}
ISR(TIMER3_COMPA_vect) {
digitalWrite(pin1, HIGH);
OCR3B = PULSE1.pulseLength;
}
ISR(TIMER3_COMPB_vect) {
if (PULSEIndex == 1) {
digitalWrite(pin1, LOW);
digitalWrite(pin2, HIGH);
OCR3B += PULSE2.pulseLength;
}
if (PULSEIndex == 2) {
digitalWrite(pin2, LOW);
digitalWrite(pin3, HIGH);
OCR3B += PULSE3.pulseLength;
}
if (PULSEIndex == 3) {
digitalWrite(pin3, LOW);
digitalWrite(pin4, HIGH);
OCR3B += PULSE4.pulseLength;
}
if (PULSEIndex == 4) {
digitalWrite(pin4, LOW);
}
PULSEIndex++;
if (PULSEIndex > 4) PULSEIndex = 1;
} ```
1 Answer 1
What I am assuming is that the reason there is jitter is that there are other interrupts going on in the background
This seems like a reasonable assumption. Since you are not doing serial communication, the Timer 0 interrupt is likely the only one triggering in the background. You can disable it with:
TIMSK0 = 0;
but then all the Arduino timing functions will stop working. You will
have to #include <util/delay.h>
and replace delay()
with
_delay_ms()
. Note that this avr-libc function only works with
compile-time constant delays. On the other hand, it is cycle-accurate
and accepts floating point delays.
is there a way to change this priority list?
There is not, and even if this was possible, it would not be that useful. Interrupts in the Arduino are not interruptible: if a low-priority interrupt is running when a high-priority one triggers, the high-priority interrupt has to wait for the currently running one to terminate. Priorities are only relevant when the processor is about to serve an interrupt request but has to decide which one, among several pending requests, it will serve first.
Now, a small remark about this:
void loop() {
PULSE1.writeMicro(40);
PULSE2.writeMicro(40);
PULSE3.writeMicro(40);
PULSE4.writeMicro(40);
delay(2);
}
There is no point in rewriting those values over and over again. If you plan to change the code in order to make those widths non-constant, then you have to take care to:
- make the
pulseLength
properties (or the wholePulse
objects) volatile - within
writeMicro()
, disable interrupts while changingpulseLength
.
digitalWrite
is extremely slow, and at least for the first pulse it's nodigitalWrite(pin4, LOW);
, so there is few microseconds jitter. Just like that. Maybe consider MCU with DMA