3
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ I'm not really clear exactly what you're trying to achieve here. Do you want a single shot pulse after an external trigger? Or a train of pulses after an external trigger? Or something else again? How often do you want this/these pulse/s? \$\endgroup\$ – DiBosco Jan 19 '18 at 8:53
1
\$\begingroup\$

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

\$\endgroup\$
0
\$\begingroup\$
ISR(TIMER1_COMPA_vect) {
    delayMicroseconds(8);
    OC1A = 0; // <---------- what do I do here?
}

having the delays in the isr wouldn't be too good of coding.

Multiple ways of doing it. and here is one:

1) set up the time base, don't start the timer yet;

2) set up the compare for the desired output, interrupt enabled. OC1A/B pin set/cleared depending on your needs;

3) set up the interrupt to watch your input pulse.

4) in the input pulse isr, start the timer;

5) in the output compare isr, clear/set the OC1A/B pin, disable the output compare interrupt and reset / disable the timer.

edit: it should be noted that the above approach assumes trivial amount of isr overhead. for short durations or slow processors, that may not be the case.

\$\endgroup\$
  • 1
    \$\begingroup\$ My question is about how one clears OC1A/B at all. I know using a delay in an interrupt is a bad idea, and I'll be changing that later, but I need some way to actually clear the output bits. \$\endgroup\$ – AJMansfield Feb 20 '17 at 20:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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