Timer 2 Interrupts (when OCR2A is set to TOP) in Fast PWM mode

Hello, I'm confused about Timer 2, in Fast PWM mode (WGM2:0 == 3, so it counts to 0xFF). In what order do the overflow and OCR2A compare interrupts take place, and when is OCR2A updated from the buffer, when OCR2A is at MAX (==0xFF)?

I am using an ATmega328p to drive a number of LEDs. I want it to multiplex over all the LEDs. To do so, I am storing a brightness value for each LED (1 LED per pin), in an array. I am using OC2A (pin 11), driven by the OCR2A compare match, to turn on the LED for a length of time determined by the brightness value.

During the TIMER2_COMPA_vect interrupt, I turn off the current LED and advance to the next LED (note that LED_OFF == HIGH):

ISR(TIMER2_COMPA_vect) {
  digitalWriteFast(this_led_pin, LED_OFF);
  current_led++;
  if (current_led == ARRAY_SIZE(led_pins)) current_led = 0;
  OCR2A = led_brightness[current_led]; // DOUBLE BUFFERED, SET ON BOTTOM!
}

During the overflow interrupt, I turn on the LED. The interrupt sets OC2A to HIGH, so the LED stays on until OCR2A matches TCNT2A:

ISR(TIMER2_OVF_vect) {
  this_led_pin=led_pins[current_led];
  // Only turn on when OCR2A is not 0 because of the short-pulse behavior with FastPWM 
  if (OCR2A != 0) { digitalWriteFast(this_led_pin, LED_ON); }
}

Everything works as expected up to and including an OCR2A value of 0xFE. But when OCR2A is set to 0xFF, it's as if the next brightness value (let's say it's 0x01), is not copied from the buffer into OCR2A during the overflow interrupt. It seems to me that in this case, TIMER2_OVF_vect takes place before TIMER2_COMPA_vect, but I'm not sure.

Does it? Is that expected?

The datasheet only says that, "Setting the OCR2A to MAX will result in a constantly high or low output..." but it doesn't talk about the Interrupts.

Thanks.

It seems to me that in this case, TIMER2_OVF_vect takes place before TIMER2_COMPA_vect, but I'm not sure. Does it? Is that expected?

Not expected. This is the interrupt priority table and it would appear that the COMP vectors are higher priority than the OVF vectors.

Below is a list of interrupts, in priority order, for the Atmega328: 1 Reset 2 External Interrupt Request 0 (pin D2) (INT0_vect) 3 External Interrupt Request 1 (pin D3) (INT1_vect) 4 Pin Change Interrupt Request 0 (pins D8 to D13) (PCINT0_vect) 5 Pin Change Interrupt Request 1 (pins A0 to A5) (PCINT1_vect) 6 Pin Change Interrupt Request 2 (pins D0 to D7) (PCINT2_vect) 7 Watchdog Time-out Interrupt (WDT_vect) 8 Timer/Counter2 Compare Match A (TIMER2_COMPA_vect) 9 Timer/Counter2 Compare Match B (TIMER2_COMPB_vect) 10 Timer/Counter2 Overflow (TIMER2_OVF_vect) 11 Timer/Counter1 Capture Event (TIMER1_CAPT_vect) 12 Timer/Counter1 Compare Match A (TIMER1_COMPA_vect) 13 Timer/Counter1 Compare Match B (TIMER1_COMPB_vect) 14 Timer/Counter1 Overflow (TIMER1_OVF_vect) 15 Timer/Counter0 Compare Match A (TIMER0_COMPA_vect) 16 Timer/Counter0 Compare Match B (TIMER0_COMPB_vect) 17 Timer/Counter0 Overflow (TIMER0_OVF_vect) 18 SPI Serial Transfer Complete (SPI_STC_vect) 19 USART Rx Complete (USART_RX_vect) 20 USART, Data Register Empty (USART_UDRE_vect) 21 USART, Tx Complete (USART_TX_vect) 22 ADC Conversion Complete (ADC_vect) 23 EEPROM Ready (EE_READY_vect) 24 Analog Comparator (ANALOG_COMP_vect) 25 2-wire Serial Interface (I2C) (TWI_vect) 26 Store Program Memory Ready (SPM_READY_vect)

I don't think your issue is with overall priority, more likely the sequence in which they are triggered.

The data sheet says

The Timer/Counter Overflow Flag (TOV2) is set each time the counter reaches TOP. If the interrupt is enabled, the interrupt handler routine can be used for updating the compare value.

The data sheet also says

The PWM waveform is generated by setting (or clearing) the OC2x Register at the compare match between OCR2x and TCNT2, and clearing (or setting) the OC2x Register at the timer clock cycle the counter is cleared (changes from TOP to BOTTOM).

Possibly the OVF vector is being called first at TOP if the COMP vector at 0xFF is being set HIGH or LOW at the change from TOP to BOTTOM( or at BOTTOM).

cattledog: Not expected. This is the interrupt priority table and it would appear that the COMP vectors are higher priority than the OVF vectors.

I don't think your issue is with overall priority, more likely the sequence in which they are triggered.

The data sheet also says ... (removed for brevity)

Possibly the OVF vector is being called first at TOP if the COMP vector at 0xFF is being set HIGH or LOW at the change from TOP to BOTTOM( or at BOTTOM).

Hmmm... interesting idea, but the datasheet says, :-)

The 8-bit comparator continuously compares TCNT2 with the Output Compare Register (OCR2A and OCR2B). Whenever TCNT2 equals OCR2A or OCR2B, the comparator signals a match. A match will set the Output Compare Flag (OCF2A or OCF2B) at the next timer clock cycle. If the corresponding interrupt is enabled, the Output Compare Flag generates an Output Compare interrupt. ...

and

The Timer/Counter Overflow Flag (TOV2) is set each time the counter reaches TOP. If the interrupt is enabled, the interrupt handler routine can be used for updating the compare value.

So it seems as if TOV2 happens at TOP. OCF2A happens at the next timer clock cycle. The TOV2 takes place first, then, followed by OCF2A.

I think this explains it. If the value is anything less than TOP, the OCF2A flag triggers the COMP interrupt, then later the TOV2 interrupt takes place as expected.

Thanks a lot for your response, you got me on the right track. The devil is always in the details.

(BTW, for completeness: the datasheet quote you posted about the OC2x Register is simply talking about the output. It is the result of the interrupts... it doesn't affect the interrupt behavior.)

So it seems as if TOV2 happens at TOP. OCF2A happens at the next timer clock cycle. The TOV2 takes place first, then, followed by OCF2A.

I think you got it.

OK, I did a lot of experimentation with Timer2. I connected some pushbuttons to an Arduino Duemilanove (not Due btw), then connected the Duemilanove to an ATmega328p running on the internal 8MHz clock. I used the pushbuttons to activate a hand-controlled "clock" on one of the Arduino's output pins (the clock runs at about 2Hz-20Hz). Then, I configured the ATmega328p to be FastPWM mode, with an externally controlled asynchronous clock signal that came from the hand controlled clock from the Arduino. This is what I discovered:

  • When the OCR2A is set to 0xFF, the Compare A (TIMER2_COMPA_vect) interrupt takes place on TCNT2's transition from 0xFF to 0. The value of TCNT2, as shown from inside the compare interrupt vector routine, is 0.
  • The overflow interrupt (TIMER2_OVF_vect) takes place when TCNT2 transitions from 0 to 1. The value of TCNT2 during the overflow interrupt is 1.

So the comparison interrupt occurs first, as expected.

Inside the COMPA interrupt, I am changing the value of OCR2A. Its value during the first TIMER2 cycle is 0xFF. Then I change it to 0x04 for the next cycle. Then I change it to 0x08. Then back to the beginning, and so on to infinity (or the battery runs out).

So after the first 0xFF cycle of TIMER2, on the transition of TCNT2 from 0x00 to 0xFF, I'm expecting the next COMPA interrupt to take place when it matches 0x04. On the following TCNT2 cycle I'm expecting the COMPA interrupt to take place at 0x08. However, I'm receiving interrupts at: 0xFF, 0xFF, and 0x04. Then repeat, 0xFF, 0xFF, 0x04, etc. I think this is what's happening:

  • The first 0xFF interrupt TIMER2 OCR2A comparison interrupt takes place at the proper time. Then in the TIMER2_COMPA_vect routine, I try to load OCR2A with 0x04. However, it's double buffered. So OCR2A will not get loaded. It remains 0xFF.
  • In the next timer cycle, we have 0x04 waiting in the wings and OCR2A is 0xFF so our OCR2A match is at 0xFF. At the end, we load 0x08 into the buffer, and 0x04 is copied into OCR2A.
  • In the next timer cycle, we finally count the 0x04. During COMPA interrupt, we load 0xFF into the buffer, and we load 0x08 into OCR2A.
  • Now we have a lot of timer clock cycles until the overflow interrupt, so at this point the 0xFF which is in the buffer gets loaded into OCR2A.

Where I'm fuzzy is under what circumstances the OCR2A buffer gets loaded into OCR2A. It seems to me that it takes a couple timer clock cycles to load the buffer into OCR2A, not simply for example the TCNT2 overflow taking place. I would expect that the TCNT2 overflow may be the place where the load happens, except in the third of the TIMER2 cycles that I discuss above, 0xFF is loaded into OCR2A as requested, sometime after the COMPA interrupt. If it was the TCNT2 overflow that loaded the buffer into the OCR2A, I would expect that the 0x08 would be the thing that got loaded.

Furthermore, I discovered that I can set OCR2A to 0xFC in the first iteration (instead of 0xFF) and, during the COMPA overflow, the next value 0x04 gets loaded properly into OCR2A. But if I try to load 0xFD, 0xFE, or 0xFF into OCR2A, it gets missed.

So the moral of the story is: be careful with your expectations when mucking with OCR2A during a FastPMW or Phase Correct PWM TIMER mode.

I should probably just use Normal mode. In the documents they say not to use it to generate waveforms because it will "occupy too much CPU time" but I don't understand that- it's just a flip of an output port bit. It's true, that the PWM modes give you the bit flippage for free but in the case of what I'm trying to do- manage OCR2A during the COMPA overflow routine- I don't think it matters.

Still, I'm going to leave my code to use Fast PWM and just ensure that my maximum value never goes above 0xFC. I don't think the designers envisioned that the ATmega328 was going to be doing something like a combination of PWM across multiple pins, with each pin having their own PWM value.