Timer Interrupts re-visited

After I wrote an article on timer interrupts in the Arduino/AVR chips, I received some questions from people on how exactly to know what prescaler to use in setting timers and from some people who had problems reading the bitwise operators that are often being used, I will give some explanation here, but for a background on timers, I suggest you read my previous article on that subject:
Suppose you want your AVR to give an interrupt every second, then what is it you need to do?
Let’s first start with an AVR at 1MHz:
As 1MHz will create a timer interrupt every 1/1000.000 of a second, we would need to count 1.000.000 cycles to let a second pass. As the biggest timer is 16 bits, that can count to 65,535, that isn’t enough to count to 1,000,000. Therefore we have to divide the CPU frequency with a prescaler. But which one?
We can best chose that one as follows: If you look at all the prescalers (1 to 1024) you get the following table:
1,000,000 /1 = 1,000,000
1,000,000 / 8 = 125,000
1,000,000 / 64 = 15,625
1,000,000 / 256 = 3,906.25
1,000,000 / 1024 = 976.5625

We can already rule out 1 and 8 as prescaler because the resulting numbers are bigger than 16 bits (65,535).
We can also rule out 256 and 1024 because they result in a fraction and we cannot measure those and thus we would get an inaccuracy. Thus we are left with 64 as a prescaler.

So what do we need to do to set up that prescaler. The datasheet ( “Timer/Counter1 Control Register B” (TCCR1B) Table 15-5) teaches us that we have to set bits CS10 and CS11 in the TCCR1B register.
cs-bits TCCR1B

There are various ways to set a bit in a register, but using a left shift is a very popular one:
We will do it as follows:

TCCR1B |= 1« CS11 | 1 «CS10

Ok so what does that mean? Well CS11 and CS10 are defined in io.h and are respectively on bit 1 and 0 in the timer register. CS11 and CS10 are therefore defined as “1” resp “0”. So if we substitute those values we will find:

1<<CS11| 1<<CS10;
1«1 | 1«0;
0b00000010 | 0b00000001;

or to explain: a 0 left shift of 1 will give 0b00000001 and a 1 left shift of 1 will give 0b00000010.
If you then bitwise OR those values that looks like:
__________ OR

if you then do a bitwise OR of that number with the timer register, that gives
__________ OR

The difference between  ‘|=’ and  ‘|’ is that the one is a compound OR and the other just an OR. With the compound OR we do not just do an OR, but then also  assign that value, in this case to TCR1B. Ofcourse we could have done TCCR1B == 0b00000011;, that would have set the bits too, but it would set all the other bits to 0 as well and that is not the intention.
So now we have to wait 15,625 cycles to know that one second has passed. How do we do that?
Well you could use a software counter in the interrupt routine that just counts to 15,625. That will work, but we can also use the hardware counter of Timer1. We do that by putting the timer in ‘Clear Timer on Compare Match’ mode or CTC mode. In this mode the Timer will compare itself with a set value and trigger an interrupt when it reaches that value.

We store that value in the OCR1A register. That is a 16 bits register
In this case we can just do:
We set that 1 less than the value we found, because microcontrollers start counting at 0 and not 1
A bitwise OR is not necessary because the entire register is reserved for the compare value.
We also will have to set the CTC mode. To set the proper bits for that we consult table 15-4, that describes the Waveform generation mode.
Obviously we need to choose Mode 4 because we want to use the value in OCR1A. The tabel shows we have to set the WGM12 bit

We do that with the following statement:
TCCR1B |= 1<<WGM12
as WGM12 is defined as ‘3’
What we do is: TCCR1B |=1<<3
This is 0b00001000
OR-ed withTCCR1B gives:


We are not done yet. We have now set up the timer to generate 1 sec interrupts, but we need to start the timer.
we do that with the TIMSK1 register. The datasheet tells us that we have to set the ‘Output Compare Match Interrupt Enable bit for timer 1 (OCIE1A).
We do that as follows:
as OCIE1A is defined as ‘1’, you can again do the math:
TIMSK1 |= 0b00000010 i.e. bit 1 will be set

So the entire code in the setup now will be:

cli(); //Disable global interrupts
TCCR1B |= ((1<< CS11) | (1<< CS10));
OCR1A = 15624; //Count 15625 cycles for 1 second interrupt
TCCR1B |= 1<<WGM12; //Put Timer/Counter1 in CTC mode
TIMSK1 |= 1<<OCIE1A; //enable timer compare interrupt
sei(); //Enable global interrupts

Two statements I have not discussed yet, the cli() (clear/disable global interrupts) and sei() (set/enable global interrupts). We use those statements because we are setting up the interrupt, so just to make sure that that isn’t interrupte by a still active interrupt, we temporarily stop all interrupts and at the end enable them again..

OK, we now know how to set it up for 1 MHz… but most arduinos do 16 MHz.
Well, that is basically the same:
16,000,000 / 1 = 16,000,000
16,000,000 / 8 = 2,000,000
16,000,000 / 64 = 25,000
16,000,000 / 256 = 62,500
16,000,000 / 1024 = 15,625
Here we could actually choose 2 prescalers: 256 or 1024.
table 15-5 teaches us that involves setting bits CS12 for 256 or CS12 and CS10 for 1024.
That would be done with the statements:

TCCR1B |= 1<< OCR1A = 62499; //Count 62499 cycles for 1 second interrupt
TCCR1B |= 1<< OCR1A = 15624; //Count 15624 cycles for 1 second interrupt

There are other ways of setting timers such as pre-loading, but for that better read my earlier article on timer interrupts
Just a word of warning: Another way of changing the frequency is done by setting fuses. The Atmega328 for example is often used with a 16 MHz crystal, but it can also be used with its internal 8MHz oscillator, additionally it already can be set to have its internal clock divided by 8, causing the CPU to go at 1 MHz and not 8 or 16.

A practical example
In my earlier article on Timer Interrupts, I gave a practicle example of a flashing LED, still using “digitalWrite()” for that.
As we are using a lot of registers for the timer, why not go into using Registers for the LED function as well:
There are three registers for each pin that that are important for us: Data Direction Register (DDR), Port register (PORT) and Pin register (PIN). Each of these will be suffixed with a letter corresponding to which set of pins we are working with. If the LED is connected to pin D13, that means it is connected to Port B bit 5 so we need to work with DDRB, PORTB, and if we was using inputs, PINB.

//Setup the I/O for the LED
DDRB |= (1<<5); //Set PortB Pin5 as an output
PORTB |= (1<<5); //Set PortB Pin5 high to turn on LED

Handling the interrupt
We have now set up everything: an interrupt will be generated every second, but we still have to tel the Arduino what to do with the interrupt. We do that in an Interrupt Service Routine (ISR). The rest of the code is halted and this routine is run. It is best to keep interrupt routines as short as possible: not a problem in this case as we just need to toggle the LED:

ISR(TIMER1_COMPA_vect) //Interrupt Service Routine
PORTB ^= (1<<5); //Use xor to toggle the LED

The name of the interrupt routine is not random: The datasheet defines the interrupt source for Timer/Counter1 Compare A match as “TIMER1 COMPA”. We use that name while replacing spaces with underscores and adding a lower case “vect” at the end. This is how the compiler knows which ISR belongs to different interrupt sources. We toggle the LED with the XOR operator and a bitmask. The bitmask ensures that only bit 5 will be changed. The XOR is a function that renders ‘0’  for bits that are the same and ‘1’ for bits that are different.
That goes as follows:
suppose at a certain moment Port B = 0b00100000 (LED on)

__________ XOR
and at the next interrupt:

__________ XOR

The entire code will thus be:

void setup(){
TCCR1B |= 1«CS11 | 1«CS10;
OCR1A=15624;// count 15625 cycles for 1 second
TCCR1B |= 1<<WGM12;//Put timer Counter in CTC mode
TIMSK1 |= 1<<OCIE1A;//Enable timer compare interrupt
sei();// Enable Global interrupt
DDRB |= (1<<5); //Set PortB Pin5 as an output
PORTB |= (1<<5); //Set PortB Pin5 high to turn on LED
void loop(){}
ISR(TIMER1_COMPA_vect) //Interrupt Service Routine
PORTB ^= (1<<5); //Use xor to toggle the LED

As website can easily screw up computer codes, here is a screenshot of what it should look like:
Some handy website are here:
Fuse calculator
Bitwise calculator
Timer calculator


8 thoughts on “Timer Interrupts re-visited

  1. Just a heads up: Websites are notoriously bad at displaying computercodes that carry ‘fishooks’ (the greater than and smaller than characters). This article contains a number of those and the code got screwed up quite some times. I did my best to make sure it is all correct, but as everytime I open up the article for editing, something might get screwed up again.
    Therefore on places that seemed to be specifically susceptible to repeated corruption, I used the “«” symbol if it called for ‘<<'
    That also means that if you copy and paste those codes, you need to correct that in your IDE

    1. Thank you for your kind words. The entire thought behind the arduino is that also non-technical people can make great things. Goodluck!!

  2. Just a general comment: the greater than and smaller than signs always create havoc on wordpress as it interprets them as html tags, even when they are in between the ‘pre -/pre html tags.
    Therefore, check the code you see her before using. I have tried to correct it as much as possible but every time I correct something, the problem appears somewhere else

  3. I just found this post a few days ago and have started working with timers on an ATTiny45. I have the basic code running on the chip. Thank you for posting this. The issue I have is the ATTiny45 has 8bit registers so my OCR0A can only be up to 255. What is the best way to time 1 second on this chip? (I am using CKDIV8 so the F_CPU is 1000000) Thanks!

      1. Yes, but due to the 8bit chip, I cannot use the 64 prescaler you use here. If I use a prescalerof 1024, it is not accurate as you stated. This will cause a drift over time and I need it to be accurate. How can I get an accurate timing on this chip?

      2. We may have a disconnect here as I didnt specifically suggest any particular prescaler.
        Now I cannot guarantee that you will be able to get a timing of exactly 1 second, but let me explain how you can get very close. Whether that is close enough for you is up to you to decide.
        I will give you the calculations as it is easy to make a mistake here so you can check what I did (and if I am correct)
        You are using 1Mhz. Suppose you use a 1024 precaler. that gives you 1000.000/1024=976.5625 Hz
        Counting to 255 at this speed thus takes 0,26112 sec (255/976.5625)

        As such 0.25 secs takes 0.25*255/0.26112 increments =244.14 (ofcourse I immediately could have used the frequency here)
        Thus set the timer compare register to 244. That will generate an interrupt every 0.25 secs (well almost, it is 0,249855974). Then put a counter in your interrupt that counts to 4. (that gives you 0,999423898 sec)

        Now suppose you do not use a prescaler without prescaler counting to 255 would take 255/1000000=0,000255sec

        Thus in 0.000250 secs the counter will be at 250,
        So if you use 250 in your timer compare register you get an interrupt every 0.00025secs
        So if you then put a counter in your interrupt routine that counts to 4000, in theory you have 1 sec
        The only drawback ofcourse is that an interrupt every 0.00025 secs might be a lot of interruption. Whether your program can take that I do not know

        Edit: my bad. ofcourse you have to put one count less in the compare register as the ‘0’is already 1 count

        You may find this program helpful with various AVR calculations

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s