Electronic – arduino – How to manually clear OC1A and OC1B

arduinointerruptspwmtimer

On the arduino uno (= ATmega328/P), how can I manually clear the timer 1 output compare signal?

The purpose of the code is to generate some output pulses after a precise interval from the input pulse. There are two output channels that need to be driven, potentially with different delays. The pulse length isn't critical, just needs to be at least 8┬Ás long, but the timing for the rising edge needs to be accurate.

Currently my code looks like this:

#define MAINS_PERIOD 16667 // in microseconds

ISR(TIMER1_CAPT_vect) {
    GTCCR = _BV(TSM);
    TCNT1 = TCNT1 - ICR1 - MAINS_PERIOD + 1 + 300;
    GTCCR = 0;
}
ISR(TIMER1_COMPA_vect) {
    delayMicroseconds(8);
    OC1A = 0; // <---------- what do I do here?
}
ISR(TIMER1_COMPB_vect) {
    delayMicroseconds(8);
    OC1B = 0; // <---------- what do I do here?
}

void setup() {
    TCCR1A = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1); // set output on compare match, normal mode, prescaler /8
    TCCR1B = _BV(CS11) | _BV(ICNC1) | _BV(ICES1); // input capture noise cancel, positive edge
    TIMSK1 = _BV(ICIE1) | _BV(OCIEA1), | _BV(OCIEB1); // generate interrupts for input capture, A/B compare match

    pinMode(8, INPUT);  //set data direction
    pinMode(9, OUTPUT); 
    pinMode(10, OUTPUT);
}

I could potentially reconfigure the timer's output mode to clear on match and then force a match, but it seems like there ought to be a more straightforward way to do it:

ISR(TIMER1_COMPA_vect) {
    delayMicroseconds(8);

    TCCR1A &=~ _BV(COM1A0);
    TCCR1C = _BV(FOC1A);
    TCCR1A |= _BV(COM1A0);
}

Just clearing the output pin register bit (PORTB &=~ _BV(PORTB1);) won't work in this case because the port register is ignored whenever an alternate function (like the timer output) is enabled on the pin.

Alternately, if there is a way to get the timer outputs to clear on overflow (some kind of undocumented 16-bit PWM mode?) that would be even better, but I doubt there is a way to do that.

Best Answer

It is possible to configure the timer to to toggle OC1A with

TCCR1A |= (1 << COM1A0);

Within the interrupt routine use the control register to trigger an additional compare event. This does NOT have side effects like changing the timer value.

TCCR1C |= (1 << FOC1A);

To change immediatly set the type to clear or set mode and trigger a change

TCCR1A = (1 << COM1A0);
TCCR1C |= (1 << FOC1A);

see https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf p.137