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.