ATtiny85 timer tutorial: generating time delay using Interrupts

Timers are popular peripherals in microcontrollers. These timers are responsible for generating accurate time delays using Microcontroller. Also Timers can be used to work as a counters, PWM generation, Capture external signals and so on. Hence these timers are quite important to understand. This tutorial explains the timer in ATtiny85 microcontroller and how to configure and use them.

Clock source selection:

This is the first question that comes to mind when speaking of timers. What clock source does ATtiny85 timers use ? The The timer peripheral can be clocked with internal or external clock source. As per the ATtiny85 datasheet will use its internal RC Oscillator of 8MHz frequency when being shipped. For tutorial purposes we are using ATtiny85 development board from Digistump. In this ATtiny85 is configured to operate at internally generated 16.5MHz clock and we are using this clock source to drive the timer.

Timer0:

There are two timer peripherals in ATtiny85 Timer0 and Timer1. In this tutorial we are going to use Timer0 to explain its working. Timer0 is a 8 bit timer which means it can count from 0 to 255. This can be configured to operate as a counter as well. The timer functionality can be further extend using the compare match peripherals which enables the timer to count events, PWM generation and so on.

TCCROA – Timer/Counter Control Register A

Configuration of Timer0 register can be done using two registers TCCR0A and TCCR0B

 

TCCR0A register helps user to configure the Timer to operate in normal mode. This is the mode in which the timer will start the count from 0 and reaches a maximum of 255 which is the highest limit for 8 bit register. In normal operation mode the compare match features and its pin will be disconnected. The below table shows the types of modes that can configured by writing the bits COM0A1, COM0A0, COM0B0, COM0B1.

We are going to write 0 to COM0A1, COM0B1, COM0A0, COM0B0 bits to choose the normal port operation here. Next we need to configure the bits WGM02, WGM01, WGM00. The below table shows the values for these bits and respective mode selections.

We will configure these bits to operate in Normal mode since we want our timer to count from 0 to TOP value which is 0xFF or 255. Alternatively you have options to configure timer to generate PWM signal and Clear timer on Compare ( will be explained different article ).

TCCR0B:

This register is also used to configure the operation of Timer0. Remember the bit WGM02 from the previous table. Note that WGM02 bit is located in TCCR0B register not TCCR0A register. The CS02, CS01, CS00 bits in this register is used to prescale the clock pulse of our timer. Prescaling is used to divide the incoming clock pulse to feed our timer peripheral. This feature will be extremely useful when our microcontroller runs at high speed and we need our timer to run in relatively low speed which will make delay generation more to speed which humans can relate to.

Our microcontroller runs at 16.5MHz which is relatively high speed. The time period of this frequency clock pulse

T = 1 / F = 1/ 16500000

T = 606us

This time period is quite less to create delays that can be perceived by humans. So we need to reduce the clock frequency that is fed to Timer0 peripheral. This can be achieved by using prescaling the input clock frequency.

Setting the bits CS00, CS01, CS02 bits to 101 will divide the input clock frequency that is 16.5MHz by 1024. Therefore Timer0 will operate at a clock frequency of 16.11Khz. Now the time period of clock pulse will be

T = 1/ 16.11Khz

= 62us

The time period has been reduced now, this will enable us to generate time delays.

TCNT0 register:

This is a 8 bit data register at which the timer value gets stored. Being a 8 bit register this register can hold values from 0 to 255 after it overflows and start incrementing from 0 again. Upon completion of this count in TCNT0 register timer sets the Overflow flag ( explained below ) as an indication of this event. This overflow can also trigger an interrupt if user has activated this feature.

Time delay calculation:

The input clock pulse to Timer0 is prescaled from 16.5MHz to 16.11KHz. This is the speed at which our timer operates now. The time delay generated by Timer0 can be calculated by calculating the time taken for one increment in TCNT0 register and then multiply it to the maximum increments which is 255.

Time taken per increment = 1/16.11KHz = 62us
Time taken for 0 to 255 count in TCNT0 register = 62us x 255 = 15.81ms

This implies it takes 15.81ms for Timer0 to increment from 0 to 255 and set the overflow flag or trigger interrupt. Now if we need a delay of 1 sec using Timer then

Number of times timer has to run for 1 sec delay = 1 / 15.81ms = 63.2

Rounding this off, we can conclude our timer need to repeat its overflow operation for 63 times to get a 1 sec time delay. We will use this count to repeat the timer operation in our code to obtain 1 sec time delay.

It’s worth noting that generating time delay using timers can achieve only certain level of accuracy. As per the above calculation repeating the timer loop 63 times will fetch us

Actual time delay = 63 x 15.81 ms = 996.03 ms . This is close to 1000ms which is our 1 second delay. We can generate even more accurate time delay using Compare match in ATtiny85 which is explained here.

Using manual monitoring of Overflow flag:

There are two methods we can achieve time delay in ATtiny85. First method is to manually check the Overflow flag after triggering the timer and act when overflow occurs.

TIFR register:

This register holds the bits which indicates the status of Timer0 peripheral. All the bits in this registers are flag bits which will go high when a certain external event occurs. We are interested in the bit TOV0 bit which is the Timer0 overflow flag. This bit will be normally read zero but when the count in TCNT0 register reaches its maximum value that is 255 by default, this bit will be set high by hardware. The programmer needs to read this bit  and understand that Timer has reached its maximum value. Upon reading this programmer has to write 1 to this bit to reset this to low state or it will be reset by hardware if enabled interrupt is executed.

In this method the program needs to check the overflow flag and wait for it to go high. It means the code have to be idle in the meantime or there is a good chance the code will miss reading the timer status because upon overflow Timer TCNT0 register will start over again from 0 to 255. This is the disadvantage of using this method.

Steps to configure Timer and use Overflow flag to generate delay:

  1. Select the normal mode of operation for timer0
  2. Prescale the internal clock by a factor of 1024
  3. Wait until the TCNT0 reaches the maximum value and flag is raised.
  4. Clear the TOV0 flag by writing one to it.
  5. Repeat from step 3 for 63 times to achieve a time delay of 1 second.
  6. Toggle the LED connected to PB1 pin.

Sample code:

The below code configures the timer to prescale the input clock by a factor of 1024 and uses overflow flag to read the timer status and repeats the operation to generate time delay and toggle LED connected to its PB1 pin.

#include<avr/io.h>
#define F_CPU 16500000UL
#include<util/delay.h>


void timer_config()
{
 DDRB =0b00000010; // set PB1 as output
 TCCR0A=0x00;             //Normal mode
 TCCR0B=0x00;
 TCCR0B |= (1<<CS00)|(1<<CS02);   //prescaling with 1024
 TCNT0=0;
}

void tmdel()
{
 unsigned int i=0;
 while(i<=62)
  { 
   while((TIFR & (1 << TOV0) )==0);        //Waiting for 0-255 and flag to raise
   TIFR|=(1<<TOV0);      //Clear the flag
   i++;                          //increment by one
  }
}

int main()
{
 timer_config();
 while(1)
 {
  PORTB|=(1<<PB1);     //PortB1 high 
  tmdel();                         //Delay of 1 second
  PORTB&=~(1<<PB1);     //PORTB1 low
  tmdel();
 }
}

Using Interrupt method:

This is considered to be the elegant method since it uses the timer interrupt feature of ATtiny85 and doesn’t require code to wait until overflow flag is raised. Here when value in TCNT0 register reaches 255 TOV0 flag will be raised and simultaneously interrupt will be triggered. The program control will be then transferred to Interrupt service routine and there we can use our desired code in the sub routine. In our case we will repeat the timer operation for 63 times to get 1 second time delay.

SREG – AVR status register:

In this register we will use the 7th bit ‘I’ which is Global interrupt enable bit. This bit must be enabled in order to use any interrupt in ATtiny85 controller. Writing one to this enables this bit. Also each of the interrupts will have individual enabling registers and bits which must be used in combination with Global interrupt enable to use interrupts.

TIMSK -Timer/Counter Interrupt Mask Register

Upon enabling the Global interrupt bit I in SREG we also need to enable Timer0 associated interrupt. This bit is in a special register called as TIMSK register which is responsible for activating and deactivating interrupts associated with Timer0 and Timer1 peripheral in ATtiny85. The bit TOIE0 enables / disables Timer0 interrupts. Writing one to this bit enables the interrupt and zero disables the interrupt.

Do note that overflow flag TOV0 will be set despite using interrupts. But clearing the flag will be taken care by hardware when interrupt in enabled.

Steps to configure Timer and use Interrupt to generate delay:

  1. Select the normal mode of operation for timer0
  2. Prescale the internal clock by a factor of 1024
  3. Enable Global interrupt enable I bit in SREG register
  4. Enable TOIE0 bit in TIMSK register to enable Timer0 interrupt.
  5. Write the Interrupt service routine to repeat the counting operation 63 times to obtain a delay of 1 second.
  6. Toggle the LED connected to PB1 pin.

Sample code:

The below code configures the timer to prescale the input clock by a factor of 1024 and uses interrupts to detect overflow and repeat the operation 63 times to generate time delay and toggle LED connected to its PB1 pin.

#include<avr/io.h>
#include <avr/interrupt.h>
#include<util/delay.h>
#define F_CPU 16500000UL

int intr_count=0;
int sec=0;

ISR (TIMER0_OVF_vect)      //Interrupt vector for Timer0
{
  if (intr_count==63) //waiting for 63 because to get 1 sec delay
  {
    PORTB^=(1<<PB1); //toggling the LED
    intr_count=0; //making intr_count=0 to repeat the count
    ++sec;
  }
  else  intr_count++; //incrementing c upto 63
}

void timer_setup()
{
  DDRB |= (1<<PB1); // set PB1 as output(LED)
  TCCR0A=0x00;   //Normal mode
  TCCR0B=0x00;
  TCCR0B |= (1<<CS00)|(1<<CS02);   //prescaling with 1024
  sei(); //enabling global interrupt
  TCNT0=0;
  TIMSK|=(1<<TOIE0); //enabling timer0 interrupt
  PORTB|=(1<<PB1);
}

int main () 
{
  timer_setup();
  while(1)
  {
    
  }
}

Other ATtiny85 tutorials:


10 Comments

  1. Peter

    Hi,
    Nice article, well explained.

    You wrote
    “Time taken for 0 to 255 count in TCNT0 register = 62us x 255”

    “62us x 255” but shouldn’t there be 256 not 255 ?

    From 0 to 255, I think we have 256 periods of 62us not 255.
    It counts like this
    0>1
    1>2
    … and so on until the end …
    254>255
    255>0 <– here when zeroed the interrupt is triggered,
    but from 255 to that 0 there still is a period of 62us,
    so there should be 256 periods counted
    Am I right?

    Reply
    1. latki8

      I’d say yes. He says when the counter overflows and interrupt occurs so that would be when it reached 256 and is immediately reset to zero. In truth, it isn’t reset at all, it just rolls over to zero in the same way as all its other bits roll over to zero as it increments through each power of 2.

      Reply
  2. Michel De Meester

    Hello,

    Is it possible the calculations are wrong.

    I think that 16.5MHz gives a period of 0.0606 µs

    Quote:
    8<————————————————
    Our microcontroller runs at 16.5MHz which is relatively high speed. The time period of this frequency clock pulse

    T = 1 / F = 1/ 16500000
    T = 606us
    8<————————————————

    Reply
  3. Ernesto

    I’m having my ATtiny85 display temperature (polled from a DHT11) on an SSD1306 OLED display. I’m using the USI to write to it.
    At the moment, functionality is simple: Press a button to poll DHT11 and display the number in the OLED.

    For the OLED I’m using the USI to implement I2C. I’m wondering if I can use a timer that starts counting for 5 seconds and, if no button is pressed to poll the temperature, the attiny85 goes to sleep. Is it possible to do something like this?

    Reply
    1. Frank DonaldFrank Donald

      Hi Ernesto,
      Yes it is totally possible to do this. Let me know how this turns up, also we have published ATtiny85 I2C tutorial in our website – https://www.gadgetronicx.com/attiny85-i2c-protocol-tutorial/

      Reply
  4. Harvey

    How does the timer go from T = 606us to 62uS? I thought the prescaler divided the clock?

    Reply
    1. Frank DonaldFrank Donald

      The 606us is the time period that will be derived if we don’t prescale the clock. 62us is when we prescale the clock by 1024. Both are shown to point out the advantages of prescaling the clock when using timers. Hope this helps

      Reply
      1. MD TALHA AKHTAR

        Pre-scaled value of time period should be higher than the time period of clock signal generator…

        Here, since the clock frequency of the microcontroller is 16.5 MHz so, it should generate a time period of 60.6ns for a single clock cycle…

        F = 16.5MHz or 16,500,000
        T = 1/F = 1/16,500,000
        = 6.0606060606060606060606060606061e-8 or 6.06 * 10^(-8) = 60.6ns

        By setting a prescaler to 1024 we command the microcontroller to complete 1 cycle after every 1024 clock cycle generated by the clock generator…

        So, our time period of a cycle after being prescaled by 1024 will be
        prescaled time = 1024 * 60.6
        = 62,054.4ns or 62us (rounded value)

        Reply
  5. Stv

    This is a great write-up: it’s clear, concise and easy to understand. More please on the Attiny85! It’s oft-ignored in many blog posts and tutorials! Thanks

    Reply
    1. Frank DonaldFrank Donald

      Thank you Steve. Yes we have written tutorials about almost all the peripherals of ATtiny85, please refer to this link https://www.gadgetronicx.com/category/microcontroller/attiny85/attiny85-tutorials/ . We will add more tutorials to this.

      Reply

Leave a Comment

Your email address will not be published. Required fields are marked *