6
\$\begingroup\$

Trying to wrap my head around these overflow timers for the ATTinyx5 (45 in this particular case). I seemed to have a great handle on it with the ATMega328 but I cannot get it to work the way I want on the tiny chips.

I'm trying to achieve a 1 second pulse (for the purposes of testing, LED on for 1s then off for 1s). I know there are many ways to achieve this, but the simplest seem to be by utilizing the overflow ISR. Here's what I've got so far:

//Who can ever remember what the prescaler combinations are?
//These are for Timer0
#define PRESCALE0_1 _BV(CS00)
#define PRESCALE0_8 _BV(CS01)
#define PRESCALE0_64 (_BV(CS00) | _BV(CS01))
#define PRESCALE0_256 _BV(CS02)
#define PRESCALE0_1024 (_BV(CS02) | _BV(CS00))

void setup()
{
  DDRB |= _BV(PINB0); // Set Pin 5 (PB0) as output;
  PORTB &= ~_BV(PINB0); //Set it to low, just to be safe

  cli();
  TIMSK |= _BV(TOIE0);
  TCCR0B |= PRESCALE0_64;
  sei();

}

uint8_t _interval = 2;
uint16_t _pulseCount = 0;
uint16_t _pulseMax = 15625; // = 1s / 0.000064s
bool state = false;

ISR(TIM0_OVF_vect) 
{ 
  _pulseCount++;
  if(_pulseCount >= _pulseMax)
  {
    _pulseCount = 0;

    if(!state)
    {
      PORTB |= _BV(PINB0);
      state = true;
    }
    else
    {
      PORTB &= ~_BV(PINB0);
      state = false;
    }
  }
} 

As you can see, I'm doing little more than enabling the Timer0 overflow ISR and setting the prescaler to 8. Now, I'm running the chip at 1Mhz by setting the /8 clock fuse.

Given that 1 / (F_CPU / 64) = 0.000064 or 64 uS that should mean that 1/0.000064 = 15625 overflows per second. So, the general plan was to count overflows in a uint16_t variable util it reached 15625 and then do what I needed to do at 1 second. Sort of an overflow for the overflow.

But instead, I get a pulse every couple of minutes!. To get the pulse I need I have to set _pulseMax to more like 128 which should be 0.000064s * 128 = ~8ms.

I'm sure whatever I'm doing is terribly stupid, but I can't see it. What am I doing wrong here?

\$\endgroup\$
3
\$\begingroup\$

I don't use AVRs (I use PICs, but the basic peripherals are much the same), but I imagine the timer register is 8-bit, and the basic setting is to trigger an interrupt on an overflow.
So unless I've missed something (very possible, been up all night :-)) I think you are out by a factor of 256, since you don't appear to have allowed for this.

If your clock is 1MHz, and the prescaler is 64, then the overflow should occur every:

1 / (1e6 / 64 / 256) = 16.3ms.

If you multiply this by the 128, you get ~2 seconds.

\$\endgroup\$
  • \$\begingroup\$ Well spotted, Timer0 is 8-bit on an AVR. \$\endgroup\$ – PeterJ Jun 30 '13 at 4:35
  • \$\begingroup\$ See... I told you it was something stupid. Thanks! \$\endgroup\$ – Adam Haile Jun 30 '13 at 11:00
-1
\$\begingroup\$

You can get more flexibility by using CTC mode. With that, you can count from 0 to some 8 bit number and then start back at 0 again. You get a compare match interrupt instead of an overflow. You would do something like this:

void setup() {
  // blah blah blah
  TCCR0A = _BV(WGM01); // mode 2 - CTC
  TIMSK = _BV(OCIE0A); // OCR0A interrupt only.
  OCR0A = 49; // don't forget to subtract 1 - the counter is 0-49 inclusive.
}

ISR(TIMER0_COMPA_vect) {
  // whatever
}

This will give you an interrupt every 50 timer clocks.

This mechanism is very helpful for getting, say, 10 Hz interrupts from a system clock of 1 MHz. You set the prescaler to divide by 1024, but that means that to get 10 Hz you need to count to 97 21/32. To do that, you set OCR0A to 97 for 21 cycles, then 96 for 11 cycles. You'll get an interrupt nominally every 10 Hz, though some periods will be 1.024 ms longer than others.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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