3

I'm trying to understand how to fully use the COMPB vector ISR of Timer0 on the ATMega328 (standard Arduino core). I realize it will make delay() and millis() not work but that's ok by me. I've setup some test code that should output a 244Hz signal on D4 and 488Hz on D5 (note, this is running at 8MHz). But D5 (COMPB) is always what D4 (COMPA) is. Any thoughts? Code below:

#include <avr/interrupt.h>

volatile bool stateA = false;
ISR(TIMER0_COMPA_vect)
{
    if (stateA)
        PORTD |= _BV(PIND4);
    else
        PORTD &= ~(_BV(PIND4));

    stateA = !stateA;
}

volatile bool stateB = false;
ISR(TIMER0_COMPB_vect)
{
    if (stateB)
        PORTD |= _BV(PIND5);
    else
        PORTD &= ~(_BV(PIND5));

    stateB = !stateB;
}

void setup()
{
    DDRD |= _BV(PIND4) | _BV(PIND5); 

    //set timer0 interrupt at 2kHz
    TCCR0A = 0;// set entire TCCR2A register to 0
    TCCR0B = 0;// same for TCCR2B
    TCNT0 = 0;//initialize counter value to 0
    // set compare match register for 2khz increments
    OCR0A = 255;//244Hz
    OCR0B = 127;//488Hz
    // turn on CTC mode
    TCCR0A |= (1 << WGM01);
    // Set CS01 and CS00 bits for 64 prescaler
    TCCR0B |= (1 << CS01) | (1 << CS00);
    // enable timer compare interrupt
    TIMSK0 |= (1 << OCIE0B) | (1 << OCIE0A);

    interrupts();

}

Update: After thinking about it, I'm realizing that it's not that I need two separate frequencies (though I just kinda wanted to know how, so this is good stuff) but I just need two separate interrupts, only using Timer0 (1 & 2 are already in use elsewhere). They can be the same frequency, most likely. I probably could put all the functionality I need in the same TIMER0_COMPA_vect, however I was trying to use that in a library I was writing and was trying to keep the functionality separate... this may not be possible.

  • 2
    You could optimize your code a little bit by replacing if (stateA) ... with PORTD ^= _BV(PIND4); (exclusive or of this bit) and removing those volatile state variables. – jfpoilpret Apr 3 '14 at 4:12
  • I don't see the point why you would need 2 interrupts at the same frequency, you can do your stuff in the same ISR. – jfpoilpret Apr 3 '14 at 18:25
  • Because some of the stuff is in a library and I want to keep it separate from other logic – Adam Haile Apr 3 '14 at 20:40
1

I think your code did not set TIMSK0 properly.

Instead of:

TIMSK0 |= (1 << OCIE0B) | (1 << OCIE0A);

You should use:

TIMSK0 = (1 << OCIE0B) | (1 << OCIE0A);

Why? Because TIMSK0 is already initialized by Arduino libraries before your setup() is called.

If you take a look at hardware/arduino/cores/arduino/wiring.c and search for the init() function, you will see that it contains the following lines:

    // enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
    sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
    sbi(TIMSK0, TOIE0);
#else
    #error  Timer 0 overflow interrupt not set correctly
#endif

With these lines, the TOIE0 bit of TIMSK0 is set, but your setup() does not clear it.

  • This shouldn't actually interfere though. Timer 0 overflow is only for the Arduino timing functions. – Ignacio Vazquez-Abrams Apr 3 '14 at 4:14
  • That's what I thought, but I think it is still preferrable to ensure you set all Timer0 registers properly. Timer functions are a bit complex and you may be bitten by some strange behavior you did not expect, if you don't care for it. – jfpoilpret Apr 3 '14 at 4:19
  • See update above – Adam Haile Apr 3 '14 at 13:47
1

Your code doesn't do what you think it does. The timer always runs at 244Hz, and OCR0B only matches twice per period, causing it to have the same frequency but a different phase. What you need to do is set OC0B on compare match and clear it in the overflow ISR. OCR0A you can simply set to toggle on match.

TCCR0A = _BV(COM0A0) | _BV(COM0B1) | _BV(COM0B0) | _BV(WGM01);
TIMSK0 |= _BV(OCIE0A);

 ...

ISR(TIMER0_COMPA_vect)
{
  PORTB &= ~_BV(PD5);
}

Also, OC0A is D6, not D4.

  • Your code doesn't seem to match what you are saying to do... could you maybe clarify a little? – Adam Haile Apr 3 '14 at 4:12
  • CTC resets the timer when it matches OCR0A. We set COM0A[1:0] to 0b01 in order to tell OC0A to toggle. For OC0B we set it in the middle of the timer (COM0B[1:0]=0b11, OCR0B ~= OCR0A/2) period, and then clear it at the end (via the ISR). This gives it twice the frequency of OC0A. – Ignacio Vazquez-Abrams Apr 3 '14 at 4:17
  • Sorry, but some code would be really helpful, I'm still a bit lost. – Adam Haile Apr 3 '14 at 4:18
  • The code I gave integrates directly into the code you already have, simply replace the assignment to TCCR0A as well as the whole ISR. – Ignacio Vazquez-Abrams Apr 3 '14 at 4:20
  • As well as the overflow interrupt enable that I just added (since you won't have the Arduino libraries to do it for you). – Ignacio Vazquez-Abrams Apr 3 '14 at 4:26
1

Actually, after reading the ATmega328P datasheet again, I am not sure that you can use both OCR0A and OCR0B in normal timer mode.

In particular, since you use CTC mode, I think that TCNT0 will be cleared every time its value reaches OCR0A i.e. 127.

Since you need 244Hz and 488Hz (exactly twice 244Hz), I think you can operate with only one of OCR0A or OCRB0 and do the work in one ISR only:

#include <avr/interrupt.h>

volatile bool setBothPins = false;
ISR(TIMER0_COMPA_vect)
{
    if (setBothPins)
        // D4 is changed only once (244Hz)for every 2 changes of D5 (488Hz)
        PORTD ^= _BV(PIND4);
    PORTD ^= _BV(PIND5);

    setBothPins = !setBothPins;
}

void setup()
{
    DDRD |= _BV(PIND4) | _BV(PIND5); 

    //set timer0 interrupt at 2kHz
    TCCR0A = 0;// set entire TCCR2A register to 0
    TCCR0B = 0;// same for TCCR2B
    TCNT0 = 0;//initialize counter value to 0
    // set compare match register for 2khz increments
    OCR0A = 127;//488Hz
    // turn on CTC mode
    TCCR0A |= (1 << WGM01);
    // Set CS01 and CS00 bits for 64 prescaler
    TCCR0B |= (1 << CS01) | (1 << CS00);
    // enable timer compare interrupt
    TIMSK0 = (1 << OCIE0A);

    interrupts();
}
  • Just checked it on my UNO, seems to work fine (with double frequencies, as the UNO runs on 16MHz). – jfpoilpret Apr 3 '14 at 5:21
  • See update above – Adam Haile Apr 3 '14 at 13:47

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.