any timer/interrupt experts?

hi all -

i’m developing some r/c servo code to run on an atmega168 board, using its timer0 to generate servo pulses. (i need the other two timers for something else). i need to use a timer so i don’t miss incoming serial data. on the '168, timer0 runs in fast pwm mode. i only have an atmega8 board at the moment, so i’m testing it out on timer2.

i’ve got it sort of working, but i’m really stuck on one problem. maybe someone can see what it is?

i’m using the overflow interrupt to set the pin high, and then the compare match interrupt to turn it off again. because it’s only an 8-bit timer, i want to extend the resolution by going through multiple timer cycles if necessary. well, here’s some test code, just using an LED on pin 2:

// code for testing timer 2

// macro to set or clear bits in special function registers
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int ticks = 10;
int cycleCount = 1;

ISR(TIMER2_OVF_vect) {
  switch (cycleCount) {
  case 1:
    sbi(PORTD, 2); // turn on pin 2
    cycleCount = 2;
    break;
  case 2:
    OCR2 = ticks;       // set output compare match register
    sbi(TIMSK, OCIE2);  // enable output compare match interrupt
    cycleCount = 1;
    break;
  }
} 

ISR(TIMER2_COMP_vect) {
  cbi(PORTD, 2);     // turn off pin 2
  cbi(TIMSK, OCIE2); // turn off output compare match interrupt
}

void setup()
{
  // set timer 2 to prescaler 64  
  sbi(TCCR2,CS22);
  cbi(TCCR2,CS21);
  cbi(TCCR2,CS20);
  // set timer 2 to fast pwm mode 
  // (for compatibility with timer0 on atmega168)
  sbi(TCCR2,WGM21);
  sbi(TCCR2,WGM20);
  // set timer 2 for no output on pwm pins
  cbi(TCCR2,COM21);
  cbi(TCCR2,COM20);
  // enable timer 2 overflow interrupt
  sbi(TIMSK,TOIE2);
  
  pinMode(2, OUTPUT);
}

void loop()
{    
  if (++ticks > 253) ticks = 10;
  
  // delay and blink the led
  digitalWrite(13, HIGH);
  delay(10);
  digitalWrite(13, LOW);
  delay(10);
}

basically the idea is that the first time the overflow interrupt goes, it sets the pin high, and it will stay high until the next overflow 256 timer ticks later. at the next overflow, it sets up and enables the compare match to turn the pin off at some time before the next overflow after that. in the main loop i’m ramping the number of extra ticks it will wait (between 10 and 253). so i should be seeing a pulse going between 266 to 509 ticks, in a period of 512 ticks. i.e. from about half-on to almost full-on. however, when i run it, it just seems to be pretty much full on all the time. i don’t see any variation at all.

if i just skip over the first timer cycle, it works fine - i get a smooth ramp from 10 to 253 ticks in a period of 256, and the LED goes from nearly-off to nearly-full on. in other words, like this:

int cycleCount = 2;

ISR(TIMER2_OVF_vect) {
  switch (cycleCount) {
  case 1:
    // sbi(PORTD, 2); // turn on pin 2
    // cycleCount = 2;
    // break;
  case 2:
    sbi(PORTD, 2); // turn on pin 2
    OCR2 = ticks;       // set output compare match register
    sbi(TIMSK, OCIE2);  // enable output compare match interrupt
    // cycleCount = 1;
    break;
  }
}

i’ve tried a million different things but just can’t figure why waiting the extra cycle breaks it. even if i do nothing in the first cycle, just wait and turn it on in the second one, then it’s pretty much full off, with no variations.

i’d really appreciate any advice… thanks!

You may need to declare ticks and cycleCount as volatile. Otherwise, the compiler may make assumptions about your code that break things.

You may need to declare ticks and cycleCount as volatile. Otherwise, the compiler may make assumptions about your code that break things.

tried that already, doesn't seem to make any difference either way. but thanks...