Timer Interruptions
28/02/2021 | Views: 6785 | Arduino | by: ELECTRONOOBS      


The Arduino has two types of interruptions. Hardware interruptions which we have already seen in the previous episode with the pin change interruptions and then we have timer interruptions and that’s what we will talk about today. This post is about the Arduino timers, how many we have, how we could use them and how to define an interruption based on these timers in compare match mode. Working directly with timers and registers, will make your code a lot faster and fluent and you could create your own interruptions without using pre made Arduino functions such as delay, analogWrite, millis and so on. So let's start.





Part 1 - What is a Timer


As an Arduino programmer, for sure you have used timers and interrupts without even knowing. That’s because all the low-level hardware stuff is hidden by the Arduino functions which are already premade. A lot of Arduino functions uses timers, for example the time functions: delay, millis, micros and delayMicroseconds. They all use the Arduino timers in the background. Other functions such as the PWM analogWrite also uses timers. The same for the tone and the noTone functions and even the Servo library. So, what is this timer?

A timer is a piece of hardware builtin the Arduino controller and depending on the model, it could have different amount of timers. For example, the Arduino UNO has 3 timers, Timer0, Tmer1 and Timer2. This is like a clock, and can be used to measure time events. The timer can be programmed by some special registers so is like programming a clock.

ATmega238 timer interrupt tutorial




Part 2 - The Prescalar


Imagine our timer as a counter that counts pulses. This timer is fed by the system clock, which for our Arduino UNO could be the 16Mhz crystal clock or depending on the bootloader could also be an 8MHz internal oscillator. Between our timer counter and the system clock, we have another piece of hardware which is called prescalar. This prescalar divides the number of pulses from the system clock by the number you define which could be 8, 64, 256 and so on, and later we will see how to define this prescalar division number.

So for now imagine the main system clock is the 16Mhz crystal that will create a 16Mhz square signal. That’s 62.5ns for each pulse, right? You feed that to the prescalar which lets say is set to 8. So, the output signal from the prescalar will be 8 time slower so 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 and we will see examples in a moment.

ATmega238 timer interrupt prescalar


Part 3 - Why we use timers?


So what can we do with these timers. Well, 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.


ATmega238 timer interrupt bits


Part 4 - Timer ISR Modes


Each timer can generate one or more interrupts. One type of interrupt is the compare match. 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. Another interrupt type is for 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. Finally, we have the input capture interrupt, which for the Arduino UNO, could 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.


Arduino timer ISR modes compare match



Part 5 - Setting the Prescalar


First register we need to change is the prescalar. To control the timers we have two main registers, the TCCR A and the TCCR B, each with the number of the timer. So for timer 1 we have TCCR 1A and TCCR 1B. TCCRA 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.

Now, we need to change the TCCRB register. Here, all we care are the first 3 bits which are used to define the prescaar value. As you can see in the table below, using the CS10 CS11 and CS12 bits, we can disable the prescalar 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.

Arduino timer TCCR1B register


TCCRB Register

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
          CS12 CS11 CS10

Example code

void setup() {  
  TCCR1A = 0;			//Reset entire TCCR1A register
  TCCR1B = 0;			//Reset entire TCCR1B register
  TCCR1A |= B00000100;		//Set CS12 to 1 so we get Prescalar = 256
  TCNT1 = 0;			//Reset Timer 1 value to 0
}

void loop() { 
  //your code here...
}

Why we use this prescalar then? Well imagine you want to make an LED blink each half second. So you want to trigger the timer interruption each 500ms, right? The system clock is 16Mhz so each pulse is 62ns. In order to count up to 500ms, which are 5.000.000 ns we would need to count up to 800.000 so we can’t use the 16 bit register for example, because it could only count up to 65536. But if we use a prescalar of 256, we would have a pulse each 16us. So now, to count up top 500ms we would need 500.000 divided by 16 equal to 31.250 pulses, and now we could use the 16 bits register.

To set timer 1 to have a 256 prescalar, according to the table above, we need to set the CS10, CS11 and VS12 to be 1, 0, 0. The timer value is stored in the TCNT register which in case of timer 1 is made out of two registers because it is a 16 bits one. So if you want to reset the timer value you should equal the TCNT register to 0 and taht's what we've amde in the example code above.




Part 5 - Setting the Interrupt (Compare Match)

Now we have the prescalar and the timer defined so the timer will go from 0 to 65 536 and then back to 0. But we haven’t specified when to make the interruption. 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

BIT Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
OCIE1B OCIE1A TOIE1

500ms Blink Example code

I want to use timer 1. So first, in the code below we reset the values of TCCR1A and 1B to 0 to make sure everything is clear. Then let’s set the prescalar to 256 as decided before, so for that in the Arduino code we set TCCR1B equal to 00000100. There are multiple ways to set this as we have learned in previous episodes. If you want to set ones, we use an OR operation. If you want to se zeros, we use an AND operation. Now the prescalar is defined to 256, let’s enable the compare match mode for OCR1A register. For that, remember we need to set the OCIE1A bit to be a 1 and that’s from the TIMSK1 register. So we equal that to OR and this byte 0000 0010. We could also make this line here, it would do the same.

/* Example code with timer intyerrutp that will create an interruption each 
 *  500ms using timer1 and prescalar of 256.
Calculations (for 500ms): 
  System clock 16 Mhz and Prescalar 256;
  Timer 1 speed = 16Mhz/256 = 62.5 Khz    
  Pulse time = 1/62.5 Khz =  16us  
  Count up to = 500ms / 16us = 31250 (so this is the value the OCR register should have)*/  
bool LED_STATE = true;

void setup() {
  pinMode(13, OUTPUT);        //Set the pin to be OUTPUT
  cli();                      //stop interrupts for till we make the settings
  /*1. First we reset the control register to amke sure we start with everything disabled.*/
  TCCR1A = 0;                 // Reset entire TCCR1A to 0 
  TCCR1B = 0;                 // Reset entire TCCR1B to 0
 
  /*2. We set the prescalar to the desired value by changing the CS10 CS12 and CS12 bits. */  
  TCCR1B |= B00000100;        //Set CS12 to 1 so we get prescalar 256  
  
  /*3. We enable compare match mode on register A*/
  TIMSK1 |= B00000010;        //Set OCIE1A to 1 so we enable compare match A 
  
  /*4. Set the value of register A to 31250*/
  OCR1A = 31250;             //Finally we set compare register A to this value  
  sei();                     //Enable back the interrupts
}

void loop() {
  // put your main code here, to run repeatedly:
}

//With the settings above, this IRS will trigger each 500ms.
ISR(TIMER1_COMPA_vect){
  TCNT1  = 0;                  //First, set the timer back to 0 so it resets for next interrupt
  LED_STATE = !LED_STATE;      //Invert LED state
  digitalWrite(13,LED_STATE);  //Write new state to the LED on pin D5
}

So we set OCR1A register to that value and now we will have an interruption each 500ms. Each time the interrupt is triggered, we go to the related ISR vector. Since we have 3 timers we have 6 ISR vectors, two for each timer and they have these names:

TIMER1_COMPA_vect
TIMER1_COMPB_vect
TIMER2_COMPA_vect
TIMER2_COMPB_vect
TIMER3_COMPA_vect
TIMER4_COMPB_vect

We use timer 1 and compare register A so we need to use the ISR TIMER1_COMPA_vect. So below the void loop, as you can see in the code above, I define this interruption routine. Inside this interruption all I have to do is to invert the state of my LED and create a digital write. But first we reset the timer value otherwise it will continue to count up to its maximum value. So, each 500ms, this code will run and invert the LED state and that creates a blink of my LED connected to pin D5 for example. As you can see, I upload this code and I have the LED blinking each 500ms.




Part 6 - Two ISR at once

You can combine these interruptions with multiple timers at the same time. For example the code below is set to make an interruption each 100ms on timer 1 and each 8.12ms on timer 2. Each interruption we invert the pin state of D5 and D6. I upload this code and I will have a pulse each 100ms on pin 6 and each 8.12ms on pin 5.

bool LED_STATE1 = true;
bool LED_STATE2 = true;

void setup() {
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  cli();                      //stop interrupts for till we make the settings 
  //Timer 1 (interrupt each 100ms)
  TCCR1A = 0;                 // Reset entire TCCR1A to 0 
  TCCR1B = 0;                 // Reset entire TCCR1B to 0
  TCCR1B |= B00000100;        //Set CS12 to 1 so we get prescalar 256  
  TIMSK1 |= B00000010;        //Set OCIE1A to 1 so we enable compare match A
  OCR1A = 6250;              //Finally we set compare register A to this value 

  //Timer2 (interrupt each 8.128 ms)
  // interrupt time = 1/(16Mhz/1024) * 127 =  8.128ms;
  TCCR2A = 0;                 // Reset entire TCCR1A to 0 
  TCCR2B = 0;                 // Reset entire TCCR1B to 0
  TCCR2B |= B00000111;        //Set CS20, CS21 and CS22 to 1 so we get prescalar 1024  
  TIMSK2 |= B00000100;        //Set OCIE1B to 1 so we enable compare match B
  OCR2B = 127;                //Finally we set compare register B to this value 
  sei();                      //Enable back the interrupts
}

void loop() {
  // put your main code here, to run repeatedly:
}


//With the settings above, this IRS will trigger each 8.128ms.
ISR(TIMER2_COMPB_vect){                               
  LED_STATE1 = !LED_STATE1;    //Invert LED state
  digitalWrite(5,LED_STATE1);  //Write new state to the LED on pin D5
}

//With the settings above, this IRS will trigger each 100ms.
ISR(TIMER1_COMPA_vect){
  TCNT1  = 0;                  //First, set the timer back to 0 so it resets for next interrupt
  LED_STATE2 = !LED_STATE2;    //Invert LED state
  digitalWrite(6,LED_STATE2);  //Write new state to the LED on pin D5
}


Part 7 - PWM with Timer ISR

Using this same code but without resetting the OCR register when we reach the value, we can create PWM signals. This is the same process the analogWrite is using. But in this case we could use any other pin, not just the PWM pins of the Arduino. For example, in this code, we equal OCRA and OCRB to the analogRead from two potentiometers. The pulse width could go from 1000 to 2000 but you could set it to whatever you want in a range from 0 to 65 thousands in case of timer 1.

So now, when each of the OCRA or OCRB is reached, we invert the pulse. By changing the OCR value, we change the pulse width. The frequency is the same, but the width is changing. If I upload this code we can create some PWM signals on pin 2 and 3 and we control the frequency, and the pulse width and best of all the precision. As you know, analogWrite has a precision of 8 bits so values from 0 to 255. In this case, you can control the pulse with more precisely, with values from 0 to 65 thousand. That’s for example how the servo library is using these timers.

bool A_STATE = true;
bool B_STATE = true;

void setup() {
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  cli();                      //stop interrupts for till we make the settings 
  //Timer 1 (interrupt each 50ms)
  TCCR1A = 0;                 // Reset entire TCCR1A to 0 
  TCCR1B = 0;                 // Reset entire TCCR1B to 0
  TCCR1B |= B00000100;        //Set CS12 to 1 so we get prescalar 256  
  TIMSK1 |= B00000110;        //Set OCIE1A and OCIE1B to 1 -> compare match A and B
  sei();                      //Enable back the interrupts                    
}

void loop() {
  OCR1A = map(analogRead(A0),0,1024,1000,2000);
  OCR1B = map(analogRead(A0),0,1024,1000,2000);
}



ISR(TIMER1_COMPA_vect){
  A_STATE = !A_STATE;       //Invert LED state
  digitalWrite(2,A_STATE);  //Write new state to the LED on pin D5
}

ISR(TIMER1_COMPB_vect){
  B_STATE = !B_STATE;       //Invert LED state
  digitalWrite(3,B_STATE);  //Write new state to the LED on pin D5
}





Part 7 - See The Video

So guys, now you should know how to sue the timer interruptions in CTC mode or overflow. In future episodes we will take a look at different modes such as the input capture. Please see all examples on this post for more details and check the datasheet of the ATmega chip. Stay tuned for more Arduino 101 videos soon. I hope that you have learned something new. If so, maybe give a like the video below and consider subscribing. If my videos help you, consider supporting my work on my PATREON or a donation on my PayPal. Thanks again and see you later guys.





28/02/2021 | Views: 6785 | Arduino | by: ELECTRONOOBS      












Last tutorials

Homemade Balanced BMS circuit - Current Limi
Starting with STM32 and the Arduino IDE
Analog Video Transmitter Circuit
Arduino Homemade DLP printer - PART 1 (electronics)
Arduino ST7735 LCD .bmp picture from SD card

ADVERTISERS



ADVERTISERS

PCBWAY PCB service

PCBWAY PCB service





Curso Arduino Online nivel bajo