Saturday, 17 December 2016

Timer Interrupts and clocks - Part 2 - CTC Mode

 There are a few ways to do interrupts.
The last post was a basic intro on Timer Interrupts
in Arduino. It used the TimerOne Library
 
Another common way uses "Clear Timer on Compare Match" or CTC Mode.
The Uno has three timers called timer0, timer1, and timer2.  
Each of the timers has a counter that increases with each tick of the timer's clock. 
 
These interrupts are a good way of clocking events in Arduinos. 

CTC interrupts are triggered when the counter reaches a specific value.
This value is stored in the "Compare Match Register".
On reaching this value, the counter resets to zero.
You can set this specific counter value and you can also set the speed of the timer.
This allows you to control the frequency of the timer interrupts.
 
So the two important things to set when making your clock are:
1. speed of the timer
2. value of the counter reset

Speed.
The fastest speed of the Arduino clock is 16Mhz.
We can divide this speed , thus slowing things down using a "prescaler".
The formula for working out the speed is:
 
(timer speed (Hz)) = (Arduino clock speed (16MHz)) / prescaler
 
 
Counter Reset
Timer0 and timer2 are 8 bit timers. Thus they can store a max counter value of 255. 
Timer1 is a 16 bit timer. Thus it can store a max counter value of 65535.  
 
// --------------------------------------------------
 
Timer setup code is mostly done in the setup(){} function in an Arduino sketch.
We need to do things like turning on the CTC mode, setting the prescaler
 
Below is a example of some code:
 
//*********************
 
void setup(){

cli();//stop interrupts

//set timer0 interrupt at 2kHz
  TCCR0A = 0;// set entire TCCR0A register to 0
  TCCR0B = 0;// same for TCCR0B
  TCNT0  = 0;//initialize counter value to 0
 // set compare match register for 2khz increments
  OCR0A = 124;// = (16*10^6) / (2000*64) - 1 (must be <256)
// OCRnA/B is the output compare register 
 // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  // Set CS01 and CS00 bits for 64 prescaler
  TCCR0B |= (1 << CS01) | (1 << CS00);   
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);
sei();//allow interrupts

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

ISR(TIMER0_COMPA_vect){  //change the 0 to 1 for timer1 and 2 for timer2
   //interrupt commands here
// ******************** 
 
It all looks very confusing, but if you look at any CTC code, you will see these similar
lines of code:
 // turn on CTC mode
  TCCR0A |= (1 << WGM01);
It turns on the CTC mode 
The 3 Different timers will use slightly different code.
TCCR0A |= (1 << WGM01);//for timer0
TCCR1B |= (1 << WGM12);//for timer1
TCCR2A |= (1 << WGM21);//for timer2
----------------------------------- 
TCCR#A/B - this stands for "Timer/Counter Control Register".
This register holds the main control bits of the timer.
After TCCR there is a number and a letter A or B   
-------------------- 
The next bit of code involves setting the prescaler
// Set CS01 and CS00 bits for 64 prescaler
  TCCR0B |= (1 << CS01) | (1 << CS00);  
Each of the 3 timers uses a slightly different bit of code.
 TCCR2B |= (1 << CS22);  // Set CS#2 bit for 64 prescaler for timer 2
TCCR1B |= (1 << CS11);  // Set CS#1 bit for 8 prescaler for timer 1
TCCR0B |= (1 << CS02) | (1 << CS00);  
// Set CS#2 and CS#0 bits for 1024 prescaler for timer 0
 ---------------------
Next we have the Timer/Counter Register (TCNTn).
   TCNT0  = 0;//initialize counter value to 0
This register controls the counter value and is needed to set a preloader value.
Here, the preloader value is zero. 

The formula for the preloader value, for a required time (in seconds) is:

TCNTn = 65535 – (16x1010xTime in sec / Prescaler Value)

To calculate the preloader value for timer1 for a time of 2 Secs is:

TCNT1 = 65535 – (16x1010x2 / 1024) = 34285

 
 
------------------------------------------------- 
 These commands need to be placed outside the void setup() and void loop()
functions
 // ======================================================
ISR(TIMER0_COMPA_vect){  
   //this is for timer0
   //change the 0 to 1 for timer1 and 2 for timer2 
 // you would place all your timer commands here.
}
// =============================================== 
ISR stands for Interrupt Service Routine. 
TIMER1_COMPA_vect - this is your TIMER1/compare match interrupt.
When the TIMER1 matches the OCR1A, (ie: compare match interrupt == OCR1A)
an interrupt is generated. 
If we were using timer1 the commands would looks like this:
 ISR(TIMER1_COMPA_vect){


//this is for timer1 // you would place all your timer commands here. 
}
For the record there are three types of interrupts.
1. Compare Match interrupt (TIMER1_COMPA)
   These are good if you wish to toggle a pin or read from a sensor at precise
   regular intervals 
2. Overflow interrupt (TIMER1_OVF)
   Here, the timer clocks over 65535 ticks for timer1 and 255 ticks for timer 0&2 .  
3. Input capture interrupt (TIMER1_CAPT) 
   If a logic level change occurs on a specific pin , an input capture interrupt
   is generated. These are great if you want to measure the time between pulses
   or measure the frequency of an unknown signal.
// ================================================== 
 Below is an example of code using timer interrupts.
This uses timers 0,1 & 2. It works on the Arduino Uno.
// =========================================== 
 //timer interrupts
//by Amanda Ghassaei
//June 2012


/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
*/



//this code will enable all three arduino timer interrupts.
//timer0 will interrupt at 2kHz
//timer1 will interrupt at 1Hz
//timer2 will interrupt at 8kHz

//storage variables
// these are used in the timer command section at the end of the code
boolean toggle0 = 0;
boolean toggle1 = 0;
boolean toggle2 = 0;

void setup(){
  
  //set pins as outputs
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(13, OUTPUT);

cli();//stop interrupts

//set timer0 interrupt at 2kHz
  TCCR0A = 0;// set entire TCCR2A register to 0
  TCCR0B = 0;// same for TCCR2B
  TCNT0  = 0;//initialize counter value to 0
  // set compare match register for 2khz increments
  OCR0A = 124;// = (16*10^6) / (2000*64) - 1 (must be <256)
  // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  // Set CS01 and CS00 bits for 64 prescaler
  TCCR0B |= (1 << CS01) | (1 << CS00);   
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);

//set timer1 interrupt at 1Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1hz increments
  OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12 and CS10 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

//set timer2 interrupt at 8kHz
  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0
  // set compare match register for 8khz increments
  OCR2A = 249;// = (16*10^6) / (8000*8) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS21 bit for 8 prescaler
  TCCR2B |= (1 << CS21);   
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);


sei();//allow interrupts

}//end setup

ISR(TIMER0_COMPA_vect){//timer0 interrupt 2kHz toggles pin 8
//generates pulse wave of frequency 2kHz/2 = 1kHz 
//(takes two cycles for full wave- toggle high then toggle low)
 if (toggle0){
    digitalWrite(8,HIGH);
    toggle0 = 0;
  }
  else{
    digitalWrite(8,LOW);
    toggle0 = 1;
  }
}

ISR(TIMER1_COMPA_vect){//timer1 interrupt 1Hz toggles pin 13 (LED)
//generates pulse wave of frequency 1Hz/2 = 0.5kHz 
//(takes two cycles for full wave- toggle high then toggle low)
 if (toggle1){
    digitalWrite(13,HIGH);
    toggle1 = 0;
  }
  else{
    digitalWrite(13,LOW);
    toggle1 = 1;
  }
}
  
ISR(TIMER2_COMPA_vect){//timer1 interrupt 8kHz toggles pin 9
//generates pulse wave of frequency 8kHz/2 = 4kHz 
//(takes two cycles for full wave- toggle high then toggle low)
 if (toggle2){
    digitalWrite(9,HIGH);
    toggle2 = 0;
  }
  else{
    digitalWrite(9,LOW);
    toggle2 = 1;
  }
}


void loop(){
  //do other things here
}
// ============================================================== 
Links
+ https://circuitdigest.com/microcontroller-projects/arduino-timer-tutorial
+ https://www.youtube.com/watch?v=2kr5A350H7E 
 

No comments:

Post a Comment