2

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;
} ```

2
  • Fist try to access pins directly. The digitalWrite is extremely slow, and at least for the first pulse it's no digitalWrite(pin4, LOW);, so there is few microseconds jitter. Just like that. Maybe consider MCU with DMA
    – KIIV
    May 16, 2020 at 5:42
  • 2
    I'm not 100% sure, but I think the interrupt priorities are fixed in hardware. May 16, 2020 at 6:24

1 Answer 1

2

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:

  1. make the pulseLength properties (or the whole Pulse objects) volatile
  2. within writeMicro(), disable interrupts while changing pulseLength.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.