4

I found the following code within an example for performing very fast changes on a PWM-output. It works, however I'm wondering about some details.

TIMER2 was set up in setup() as follows:

TCCR2A = 0;               // We need no options in control register A
TCCR2B = (1 << CS21);     // Set prescaller to divide by 8
TIMSK2 = (1 << OCIE2A);   // Set timer to call ISR when TCNT2 = OCRA2
OCR2A = 32;               // sets the frequency of the generated wave
sei();                    // Enable interrupts to generate waveform!

If I understand that correctly, the timer will run until overflow and generate interrupts any time in between as defined by OCR2A.

ISR(TIMER2_COMPA_vect) {  // Called each time TCNT2 == OCR2A
  static byte index=0;    // Points to successive entries in the wavetable
  OCR1AL = wave[index++]; // Update the PWM output
  asm("NOP;NOP");         // Fine tuning
  TCNT2 = 6;              // Timing to compensate for time spent in ISR
}

TIMER2 is used to call this routine in equidistant points in time. To adapt the timespan between two calls of the ISR the author sets OCR2A and within the ISR resets TCNT2. As the timer is reset at the end of the ISR there's some time "already gone" written into the start value of the timer. However there are two things I don't understand.

  1. Why isn't CTC mode used by setting WGM21 in TCCR2A? This should call the ISR in precise intervals.

  2. If not using CTC mode the counter has to be reset by the ISR. But why isn't that done directly at the beginning of the ISR? Everytime the ISR gets some minor changes the correction value written into TCNT2 has to be changed.

Am I missing some vital detail? Or is my idea of using CTC better?

For sake of completeness, the only other things done in setup() are configurations on timer1:

pinMode(9, OUTPUT);       // Make timer's PWM pin an output
TCCR1B  = (1 << CS10);    // Set prescaler to full 16MHz
TCCR1A |= (1 << COM1A1);  // PWM pin to go low when TCNT1=OCR1A
TCCR1A |= (1 << WGM10);   // Put timer into 8-bit fast PWM mode
TCCR1B |= (1 << WGM12); 

loop() is empty.

1
  • 1
    Mhh, not using CTC in such a case doesn't make much sense to me either. Especially since the timer does not stop when the compare ISR is called. It will keep running just fine while the ISR is working (since the Timer works in hardware). I would guess that this is just a bad code
    – chrisl
    Jun 20, 2022 at 16:11

1 Answer 1

5

Why isn't CTC mode used by setting WGM21 in TCCR2A?

As we cannot read the author's mind, we cannot say for sure. That being said, to me, the most likely reason is that either the author was not aware of CTC mode, or they just didn't think about it.

You are right that CTC is the most straightforward way to get precise intervals. You are also right that the resetting the timer at the end of the ISR makes the code somewhat fragile. If, for some reason, I needed to avoid CTC in a code like this, I would

OCR2A += 32;

rather than resetting TCNT2.

1
  • 1
    I tend to think, the author did not know the CTC mode well enough. Otherwise I cannot explain the solution proposed. I was just stunned because it was published in a magazine. makezine.com/projects/advanced-arduino-sound-synthesis Not that I think this is a guarantee for quality but who knows...
    – Ariser
    Jun 20, 2022 at 16:22

Your Answer

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

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