Search code examples
cavratmegaavr-studio7

Relative Output Compare Registers values change behavior


I use Atmega328p Timer0 and its two OCRs:

void timer0_ini(void)
{
    TCCR0A = 0;// set entire TCCR0A register to 0
    TCCR0B = 0;// same for TCCR0B
    TCNT0  = 0;//initialize counter value to 0

    OCR0B = 125; // OCR0B is less than OCR0A, so it works
    OCR0A = 250;

    TCCR0A = (0<<COM0A1)|(0<<COM0A0)|(0<<COM0B1)|(1<<COM0B0)|(1<<WGM01)|(0<<WGM00);

    TCCR0B  |=  (0<<FOC0A)|(0<<FOC0B)|(0<<WGM02)|(1<<CS02)|(1<<CS00);
    TIMSK0 = (1 << OCIE0A)|(1 << OCIE0B);
}

ISR (TIMER0_COMPB_vect)
{
    PORTB ^= (1 << PORTB2);
}

ISR (TIMER0_COMPA_vect)
{
    PORTB ^= (1 << PORTB1);
}

It works only when OCR0B < OCR0A. I can't understand why it makes sense.


Solution

  • Using 0, 1, 0 for WGM02, WGM01, WGM00, you enabled CTC (Clear Timer on Compare match mode) — TCNT0 register will be cleared after it reaches OCR0A value so that OCR0B value will never be reached if it is higher than OCR0A.

    Updated to answer the questions in comment:

    1. Yes, it is possible to disable clearing of TCNT0. Just set WGM0x bits to 0. See table Waveform Generation Mode Bit Description at end of TCCR0A description.

    2. No, a top value for the TCNT0 register is OCR0A or fixed 0xFF, see the same table.

    It is not clear from your question what do you want to do. Perhaps, one can place the larger value into OCR0A and the smaller into OCR0B and set a flag that says which interrupt should change which port bit.

    If you want to make two independent frequencies, set timer in free-running mode (all WGM bits equal 0) and try

    ISR (TIMER0_COMPB_vect)
    {
        PORTB ^= (1 << PORTB2);
        OCR0A += 125;
    }
    
    ISR (TIMER0_COMPA_vect)
    {
        PORTB ^= (1 << PORTB1);
        OCR0B += 250;
    }