Electronic – arduino – Handling timer overflow and compare interrupts in ATMega328 (Arduino)

arduinoavrinterrupts

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);
}

unsigned long iterations = 0;

#define MAX_ITERATIONS (1000000)

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?

Update: Could the problem be in digitalWrite() usage in interrupts?
Update2: Changing digitalWrite to direct PORTB bit manipulation produces single flash probably on initial interrupt and subsequent very dim LED so it is not the only problem.

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

Best Answer

I am not fully sure as to why, but using fast pwm mode instead of normal mode seems to fix this problem.

Use the same code, but replacing

TCCR2A = 0x00;

with

TCCR2A = _BV(WGM20)|_BV(WGM21);

I would also ask why you seem to be clearing asynchronous mode both upon entering and leaving pwm mode, but it does not seem to affect anything.