Handling timer overflow and compare interrupts in ATMega328

I'm trying to simulate PMW mode on multiple pins in software by controlling a Timer2.

I'm using maximum prescaler value to get approximately 60 pulses per second when timer counts to its maximum value. Value of OCR2A is used to trigger the actual moment within full timer cycle.
Overflow interrupt is used to set the LED (PIN13) on and compare interrupt is used to reset LED off.

When I run it, I see that LED blinks but very dimly and its brightness is not affected by CUT_OFF value. If I disable LED off in compare interrupt and just toggle LED in overflow I see it blinks at a reasonable rate which match the final report printed to serial port (number of interrupts in 5 sec). Final report also shows that both interrupts are being called correct number of times.

Here's the code that I use:

    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    #define LED_PORT (13)
    
    #define CUT_OFF (100)
    
    volatile unsigned long time1 = 0;
    volatile unsigned long time2 = 0;
    
    ISR(TIMER2_OVF_vect) {
      PORTB |= 0x20;
      time1++;
    }
    
    ISR(TIMER2_COMPA_vect) {
      PORTB &= ~0x20;
      time2+=2;
    }
    
    void startTimer() {
      cli();
      // enable timer interrupt overflow + reg a
      TIMSK2 = _BV(OCIE2A) | _BV(TOIE2);
      // counter
      TCNT2  = 0x00;
      // cut off value
      OCR2A  = CUT_OFF;
      // mode - normal + prescaler 1024
      TCCR2A = 0x00;
      TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);
      // async mode off
      ASSR  &= ~(_BV(AS2) | _BV(EXCLK));
      sei();
    }
    
    void stopTimer() {
      cli();
      // disable interrupts
      TIMSK2 = 0x00;
      // mode (disconnect clock source)
      TCCR2A = 0x00;
      TCCR2B = 0x00;
      // async mode off
      ASSR  &= ~((1<<AS2) | (1<<EXCLK));
      sei();
    }
    
    void blinkLed(uint8_t times) {
      for(uint8_t i=0;i<times;i++) {
        digitalWrite(LED_PORT, HIGH);
        delay(500);
        digitalWrite(LED_PORT, LOW);
        delay(500);
      }
    }
    
    void setup() {
      pinMode(LED_PORT, OUTPUT); 
      Serial.begin(9600);
    }
    
    void loop() {
      blinkLed(3);
      startTimer();
      delay(5000);
      stopTimer();
      blinkLed(2);
    
      Serial.println("Iterations finished!");
      Serial.print("Timer1=");
      Serial.println(time1);
      Serial.print("Timer2=");
      Serial.println(time2);
    
      while(1);  
    }

Is there something wrong with my understanding of when interrupts are called:

  • COM2A - when we reach CUT_OFF value
  • OVF when counter reaches 255 and flips to 0

or there's a problem with setting up timer/or handling interrupts?

I tried with both digitalWrite() and direct PORTB manipulation to control the LED. Behaviour is a bit different, with digitalWrite() LED is dim all the time and with setting pin it flashes once and then turns dim. It looks as if compare interrupt is triggered immediately after every overflow and turns off LED.

P.S. blinking LED is just a test so using built in PWM is not an option.

I think that this will help you:

I looked on the tutorial, but it explixitly said:

The timers can also generate interrupts on overflow and/or match against either output compare register, but that's beyond the scope of this article.

which makes mostly unrelated. They don't go into details of Normal operation mode and how it generates interrupts.

And you've read through section 17 of the data sheet?

InvalidApple:
And you've read through section 17 of the data sheet?

That's what I started from actually. It looked pretty straightforward from the first glance, but apparently there are some nuances that I missed. I gonna try to look at http://www.atmel.com/dyn/resources/prod_documents/doc2505.pdf when I get on the train for more hints.

When using the arduino software you cannot rely on the control registers to have their default values as indicated in the datasheet! Make sure every bit is set to what you need it to be.

I read the AVR Timer app notes and still can't find the clue. Any advices are muchly appreciated.
I wish I have an oscilloscope at hand to check signals.

I usually find that I need to read the sections of the data sheet a few times before I understand it.

This isn't what you are doing, but it may be a good starting point. The order you do things sometimes make a big difference: I noticed that you initiate the timer interrupt before setting it up properly (TIMSK2).

This is working code that uses timer 2, and interrupts every 10 microseconds. Try starting with this and then modifying it to what you want:

void SetupTimerISR() {
  // Disable interrupts while setting registers
  cli();

  // Reset control registers
  TCCR2A = 0;
  TCCR2B = 0;

  // Clear Timer on Compare Match (CTC) Mode
  TCCR2A |= (1 << WGM21);

  // Prescaler x1
  TCCR2B |= (1 << CS20);

  // Interrupt every 160/16e6 = 10 usec
  OCR2A = 159;

  // Use system clock for Timer/Counter2
  ASSR &= ~(1 << AS2);

  // Reset Timer/Counter2 Interrupt Mask Register
  TIMSK2 = 0;

  // Enable Output Compare Match A Interrupt
  TIMSK2 |= (1 << OCIE2A);

  // Enable interrupts once registers have been update
  sei();
}

// This ISR is run when Timer/Counter2 reaches OCR2A
ISR(TIMER2_COMPA_vect)
{
  int_count++;
}

Every 10 microseconds, a global variable increments.