Arduino Timers and Interrupts
Finally getting some time (Happy Easter!) to work on a long overdue hobby…Microcontrollers!
In Arduino programming most of your code happens in the void loop() {} function. Every loop may take some unknown time (as long as it takes) and depending on what actions you perform, it could vary; but there are scenarios where you want to perform an action in exact time intervals (e.g. read a sensor precisely every 500ms and write the value to a serial port that is waiting for data). In these cases you should not rely on the loop{}. There is a need for a more accurate mechanism. Interrupts exist for this exact reason. You will implement your logic inside an ISR(){} function instead.
Interrupt
Interrupt is a (hardware) mechanism by which an I/O or instruction can suspend the normal execution of the processor and get itself serviced like it has higher priority. For example, a processor doing a normal execution can be interrupted by some sensor to execute a particular process that is present in ISR (Interrupt Service Routine) function. After executing the ISR{} function in your code, processor can again resume the normal execution.
An example of interrupts is touch screen mobile phones which have the highest priority to the “Touch” sense.
Interrupts can occur at any point in time but they become inefficient if devices frequently interrupt the cpu.
Timer
Timer is a kind of Interrupt.
A timer is a piece of hardware built in the Arduino controller and depending on the model, it could have different number of timers. For example, the Arduino UNO has 3 timers, Timer0, Timer1 and Timer2.
Timer is like a clock, and can be used to measure time events. The timer can be programmed by some special registers (cpu memory) so is like programming a clock.
Timer can also be see as a counter that counts pulses. The timer is fed by the system clock, which for Arduino UNO is16Mhz crystal clock or depending on the bootloader could also be an 8MHz internal oscillator.
The clock speed of computers is usually measured in megahertz (MHz) or gigahertz (GHz). One megahertz equals one million ticks per second, and one gigahertz equals one billion ticks per second. You can use clock speed as a rough measurement of how fast a computer is.
16 Mhz = 16,000,000 ticks per sec so one tick or count is 1/16,000,000 of a sec = 6.25e-8 second = 62.5 nano second
In another words, timer is like a simple clock which can measure time interval of an event. Every microcontroller has a clock (oscillator); in Arduino Uno it is 16Mhz. This clock is responsible for speed. Higher the clock frequency, higher will be the processing speed. A timer uses counter which counts at certain speed depending upon the clock frequency. In Arduino Uno it takes 1/16,000,000 seconds or 62.5 nano seconds to make a single count. Meaning Arduino moves from one instruction to another every 62 nano second.
Prescaler
Between our timer counter and the system clock, we have another piece of hardware which is called prescaler.
Prescaler divides the number of pulses from the system clock by the number you define which could be 8, 64, 256, etc.
A prescaler is used to setup the clock speed of the timer.
Our main system clock is the 16Mhz crystal that will create a 16Mhz square signal. That’s 62.5ns for each pulse. You feed that to the prescaler which lets say is set to 8. So, the output signal from the prescaler will be 8 time slower so 8 x 62.5 = 500ns. So each 8 pulses from the system clock, our timer will increase by 1. You can configure the prescaler for each timer using the related register.
Timer Interrupt Modes
Timer 0 and 2 are 8 bits meaning it could count from 0 to 255. Timer 1 on the other hand, is 16 bits so it could count from 0 to 65546. Once a timer reaches its maximum value, it will go back to 0 or start decreasing depending on the selected mode. So basically it would create a zig zag curve. The timer starts increasing its value from 0. When we reach 255 in case of the 8 bits register, we go back to 0 and increase once again. This will create a triangle shape curve and we could use this to create our interruptions. Each timer can generate one or more interrupts.
Interrupt types:
-
Compare match: We use this interrupt in our example below. We can write a value in a different register and when the timer value is equal to the compare value, it will trigger the interrupt. For example, we set our compare match register to 100, each time timer 0 reaches 100, it will create an interruption. When the Output Compare Match Interrupt occurs then the interrupt service ISR (TIMERx_COMPy_vect) is called.
-
Overflow: In this case an interruption is triggered each time the timer overflows, meaning it passes from its maximum value back to 0, which in case of an 8-bit timer will be each time it reaches 255. Whenever the timer reaches to its maximum value say for example (16 Bit-65535) the Timer Overflow Interrupt occurs.
-
Input capture interrupt: For Arduino UNO this can be implemented on timer 1. In this case the timer could store its value in a different register, each time an external event happens on one of the Arduino pins. When the timer Input Capture Interrupt occurs, the interrupt service ISR (TIMERx_CAPT_vect) is called.
Registers
This is not an exhaustive list of Arduino Uno registers. See the ATmega328 microcontroller’s data sheet for more info here.
Timer/Counter Control Registers TCCRnA and TCCRnB
This register holds the main control bits of the timer and used to control the prescalers of timer. It also allows to control the mode of timer using the WGM bits.
Timer 1 will have TCCR1A/B, timber 2 will have TCCR2A/B, etc.
TCCRA
This register is for controlling the PWM mode so for timer 1 we can control the OC1A which is related to the PWM signal on pins 9 and 10.
For our interrupt examples we don’t need this register but we do need to set all its bits to 0 since Arduino by default has them set to 1. So, setting all these to 0 will disable the PWM signal on pin 9 and 10.
Frame format:
TCCRB
On TCCRB register we care about first 3 bits to define prescaler.
Frame Format:
The CS12, CS11, CS10 bits in TCCR1B sets the prescaler value. A prescaler is used to setup the clock speed of the timer. Arduino Uno has prescalers of 1, 8, 64, 256, 1024.
we can disable the prescaler or set it to 1, divided by 8, 64, 256, 1024 or even use an external clock source. For the timers 0 and 2 we have to use the TCCR0B and TCCR2B and bits CS00, CS01, cS02 ands bits CS20, CS21 and CS22.
TCCR1A = 0; //Reset entire TCCR1A register
TCCR1B = 0; //Reset entire TCCR1B register
TCCR1A |= B00000100; //Set CS12 to 1 so we get Prescaler = 256
TCNT1 = 0; //Reset Timer 1 value to 0
Timer/Counter Register TCNTn
This Register is used to control the counter value and to set a preloader value.
Formula for preloader value for required time in second:
TCNTn = 65535 – (16x1010xTime in sec / Prescaler Value)
To calculate preloader value for timer1 for time of 2 Sec:
TCNT1 = 65535 – (16x1010x2 / 1024) = 34285
Output Compare Register (OCRnA/B)
when the Output Compare Match Interrupt occurs then the interrupt service ISR (TIMERx_COMPy_vect) is called and also OCFxy flag bit will be set in TIFRx register. This ISR is enabled by setting enable bit in OCIExy present in TIMSKx register. Where TIMSKx is Timer Interrupt Mask Register.
TIMSKn Register
To activate the compare match interruption we set the TIMSK register. As you can see, according to the lines in the datasheet below, setting the bits 1 and 2, we can enable time compare interrupt on the value defined on registers OCRA and OCRB.
So these OCR registers will tell when to make the interruption. For example, if we set OCR1A to be 2000, when timer 1 reaches 2000 it will create an interruption. Knowing this, let’s make our example that will blink an LED each 500ms.
TIMSK1
Blink Example
Imagine you want to make an LED blink every half second. You should trigger the timer interruption every 500ms.
The system clock is 16 Mhz (16,000,000 pulse) so each pulse is 62.5 ns. In order to count up to 500ms, which is 500,000,000 ns we would need to count up to 8,000,000 pulse (500,000,000/62).
8,000,000 pulse = 500 ms
we can’t use the 16 bit register, because that could only count up to 65,536. But if we use a prescaler of 256 we would have a pulse each 256 x 62.5ns = 16000ns
To count up to 500ms=500,000,000ns then we need 500,000,000/16,000ns = 31,250 pulse. Now we could use the 16 bit register.
To set timer 1 to have a 256 prescaler, according to the table above, we need to set the CS10, CS11 and CS12 to be 1, 0, 0.
The following code blinks the builtin LED every 500ms:
int led = LED_BUILTIN;
bool LED_STATE = true;
void setup() {
pinMode(led, OUTPUT);
Serial.begin(9600);
cli(); //stop interrupts for till we make the settings
/*
reset the control register to make sure we start with everything
disabled.
*/
TCCR1A = 0; // Reset entire TCCR1A to 0
TCCR1B = 0; // Reset entire TCCR1B to 0
/*
set the prescaler to 256 by changing the CS10 CS11 and CS12 bits. in
this case CS12=1,CS10=0,CS11=0
*/
TCCR1B |= B00000100; //Set CS12 to 1 so we set prescaler to 256
/*
enable compare match mode on register A
Set OCIE1A bit to 1 so we enable compare match A
*/
TIMSK1 |= B00000010;
/*
Set the value of register A to 31250, that is the number of pulses that
counts to 500ms
*/
OCR1A = 31250; //set compare register A to this value
sei(); //Enable back the interrupts
}
void loop() {
// put your main code here
}
//With the settings above, this IRS will trigger each 500ms.
ISR(TIMER1_COMPA_vect){
TCNT1 = 0; //reset timer for next interrupt
LED_STATE = !LED_STATE;
digitalWrite(led,LED_STATE);
}
References
-
https://circuitdigest.com/microcontroller-projects/arduino-timer-tutorial
-
https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
-
https://electronoobs.com/eng_arduino_tut140.php
-
https://community.robotshop.com/forum/t/arduino-101-timers-and-interrupts/13072