6
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ Have you read Atmel's app note on AVR timers? atmel.com/dyn/resources/prod_documents/doc2505.pdf \$\endgroup\$ Aug 22, 2011 at 21:49
  • \$\begingroup\$ @JobyTaffey I've read aforementioned note, but still can't find the explanation to the behaviour I see. Both interrupts are called, interrupt period doesn't depend on compare output value, but timing between interrupts is incorrect. \$\endgroup\$
    – aliher
    Aug 23, 2011 at 21:05

1 Answer 1

2
+150
\$\begingroup\$

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.

\$\endgroup\$
3
  • \$\begingroup\$ PWM mode description explicitly state usage of 2 interrupts to track moments when next period starts and when output value should be flipped. But it switches OCRxn to double buffered mode and you can only change it at the end of cycle. What I wanted to do is catch few irregular moments within one period by reloading compare register and resetting handler after each cycle. So it rules out PWM mode completely for me. Looks like I'll fall back to compare interrupt and register reloading but forget about overflow interrupt and just load TOP value once all intermediate steps are filtered. \$\endgroup\$
    – aliher
    Aug 31, 2011 at 13:09
  • \$\begingroup\$ @Brog, no one took the chance to make some rep for explaining why that fixes it, so as the first one to answer, here is some rep. \$\endgroup\$
    – Kortuk
    Oct 1, 2011 at 11:44
  • \$\begingroup\$ @Brog, I was clearing async mode on entering pwm just to make sure it is set to something sensible, because it is not easy to figure out in which state arduino libs can leave them. In the end looked on servo libs and they seem to manually reload register multiple times within a cycle ignoring the overflow interrupt as I planned to do. Looks like they didn't manage to work with overflow as well. \$\endgroup\$
    – aliher
    Oct 3, 2011 at 17:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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