3

I am using the ATMega32u4 to generate a pulse at 1 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. The good news is that this is working, I can control the frequency and pulse length of the square wave. The issue is that I noticed the pulse always seems to be 1uS off.

If I set a period of 1000uS, and a pulse of 500uS, the oscilloscope shows a period of 1001uS and a pulse of 501uS.

If I set a period of 100uS, and pulse of 10uS, the oscilloscope shows a period of 101uS, and a pulse of 11uS.

enter image description here

enter image description here

If the Timer had an error percentage, then both the period and pulse should be off by the same percentage, but there are always off by the same 1uS length. I assume I am doing something wrong, so I have posted my code below.

const int pin1 = 4;

void setup() {
  //Set pin to output
  pinMode(pin1, OUTPUT);

  // Reset Timer 3 Control Register to 0
  TCCR3A = 0;

  // 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 = 1000;
  OCR3B = 100 / 0.0625;
}

void loop() {
  delay(100);
}

ISR(TIMER3_COMPA_vect) {
  PORTD &= ~_BV(pin1); //LOW
  OCR3A = 160;
}

ISR(TIMER3_COMPB_vect) {
  TCNT3 = 0;
  PORTD |= _BV(pin1); //HIGH
}
6
  • Why doing a division with a float in 100 / 0.0625 instead of simply 1600?
    – hcheung
    May 13, 2020 at 1:29
  • @hcheung I just do this to make it easy to change. I will say "1600" when I lock down a frequency I want to use. Right now I am just testing, and it still works. May 13, 2020 at 1:39
  • The thing is floating point calculation take time, your question is about timing.... Of course the compiler might optimised it for you during the compilation, but that's an assumption.
    – hcheung
    May 13, 2020 at 1:57
  • @hcheung, the floating-point operation is only in the setup, so it should only be crunching that number once and then putting it in the register. If I do change it to "1600", there is no change to the timing. May 13, 2020 at 2:25
  • oh yes, you are right.
    – hcheung
    May 13, 2020 at 2:30

1 Answer 1

4

The problem is here:

ISR(TIMER3_COMPB_vect) {
  TCNT3 = 0;
  ...

If you want to get anything that vaguely looks like accurate timings, you should never reset the timer. The MCU needs time to process an interrupt: it has to save the program counter to the stack, load the address of the interrupt vector, execute it (it's typically a jump), save all the registers it needs to the stack, and only then can it start executing the code you wrote in the ISR. From your experiment, it looks like all this process takes about 1 µs. But don't take this delay for granted: from time to time the interrupt will fire while the MCU is processing another interrupt, and this will increase the latency.

You have two options:

  1. Update the compare registers in order to program the next interrupts some time in the future. In this case, the COMPB ISR would do OCR3A = OCR3B + 160; OCR3B += 1600;. Do not worry about the overflows: they do the Right Thing.

  2. Let the timer reset itself to zero: it will then do so without loosing a single cycle. See the CTC and fast PWM modes of the timer.

For your specific use case, PWM seems like the obvious choice. Not only will the timer handle the resetting itself, it can also generate the output signal without requiring you to write any ISR. Beware that in this case the period of the signal is whatever you load in corresponding compare register plus one, and the duration of the pulse is another compare register plus one.

As a side note:

  // Reset Timer 3 Control Register to 0
  TCCR3A = 0;

I would also reset TCCR3B. The Arduino core configures both registers for it's own purposes, so it would be safer to clear them both.

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.