This is a follow up to my previous question.
I have Timer2 in CTC mode, with TOP=OCR2A set to 150 (with a prescaler of 8, giving a 75us timing interval).
The datasheet states that setting COM2B1 to 1 in TCCR2A will "clear OC2B on compare match" (= Arduino pin D3) when you are in CTC mode.
I would like to use this functionality to get a "pwm" signal with a period of 75us, with duty cycle determined by OCR2B. I realize that this is a very convoluted way of achieving this, but eventually I want to be continually be changing OCR2B to get a (rapidly) varying duty cycle.
In the compare channel A interrupt I would like to turn D3 high, relying on the "clear on compare match" feature of channel B to turn it low a small interval later.
I have the following minimal(-ish) example:
#define OCIEA 1
#define CA21 1
void setup() {
// PD3 as output
DDRD |= (1 << PD3);
// Pause the timers so they sync up
GTCCR = (1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC); // Halt all timers
// Reset timer
TCNT2 = 0; // set timer2 to 0 (8-bit timer)
// Enable CTC mode
TCCR2A = (1 << WGM21); // Timer 2 in CTC (TOP = OCR2A)
// Clear output pins on compare match
TCCR2A |= (1 << COM2B1); // Clear OC2B on match
// Set prescaler
TCCR2B = (1 << CA21); // set timer 2 divisor to 8
// Set output compare
OCR2A = 150; // Determines TOP for Timer2
// Determines the pulse width
OCR2B = 75; // Timer 2 output B (controls OC2B = PD3 = D3)
// Enable interrupts for timer 2
TIMSK2 = (1 << OCIEA);
// Set the timers going again
GTCCR = 0; // release all timers
}
ISR(TIMER2_COMPA_vect)
{
TCCR2A &= ~((1 << COM2B1) | (1 << COM2B0)); // Disable clear OC2B on match
DDRD |= (1 << PD3); // Enable output
PORTD |= (1 << PD3); // Output high
TCCR2A |= (1 << COM2B1); // Enable clear OC2B on match
}
void loop() {
}
Ideally the ISR would just consist of PORTD |= (1 << PD3);
but this does not give my desired 50% duty cycle, but apparently it's impossible to write to the pin with COM2B1 enabled, so I tried various combinations of disabling it and reconnecting the pin but all I am getting is a very, very short pulse. It looks like as soon as COM2B1 is re-enabled, the pin goes low, even though a compare match on channel B should not have happened yet.
Setting COM2B0 instead of COM2B1 works as expected: the pin toggles state (but this is not what I want).
Why does this not work the way I expect it to, and is there a way to get my desired behavior? Also, if this is not possible, what on earth is the purpose of the clear on compare match feature if you can't turn the pin on at all when using it?
I have discovered I can sort of get the behavior I want by setting COM2B0 instead of COM2B1 (toggle on match rather than clear on match) and forcing a compare match in the interrupt (toggling the pin off) so that later the "real" toggle on match will turn it off. However, this is not ideal (as if the real and forced match overlap, I would lose track of the pin's state), and I'm still wondering what the purpose of the clear on match functionality is if it just forces the pin low.
ISR(TIMER2_COMPB_vect)
to set the pin to low, but this isn't ideal.