Go Down

### Topic: Another way to do periodic timing (Timer2) instead of millis() (Read 2903 times)previous topic - next topic

#### mk3

##### Jan 31, 2011, 12:09 am
hi all,

This is not a project, but rather a bit of working code you might find useful.  I am building data acquisition modules and needed only a relatively slow data rate.  I have been bothered by serial data getting lost and decided to look for a more efficient way to trigger my 1 time per second reporting.  I could be wrong, but I just had the idea that the millis() function would be some overhead I could dump.

Timer2 has a phase-correct PWM mode in which the timer counts UP and then DOWN before overflowing so it slows things down a bit - just what I wanted!  It also turned out to be not so difficult to get the timing super-close to true 1second intervals with the following formula

1024 prescaler
252 count to interrupt
2 Timer2 counts UP then DOWN before triggering overflow
Count 31 overflows to get ~1 second
(1024*252*2*31)/16000000 = .999936 seconds - less than .01% error

This is so close to the Arduino millis() function that I decided to test it and I think just by chance this substitute was closer to real time than millis() over a 20 minute period... but both are close enough for my purposes.

Here is the sample code - it puts out the Binary values of the registers upon reboot and then it sends out both time values one time per second so one can see how they compare.  From what I have read, Timer2 is used in some of the PWM (analog output) but this does not matter for my projects which are only data acquisition.  Here's the code.
Code: [Select]
`//=========================================================================!/*  filename Timer2_999_94ms  //1024 prescaler  //252 count to interrupt  //2 Timer2 counts UP then DOWN before triggering overflow  //Count 31 overflows to get ~1 second  //(1024*252*2*31)/16000000 = .999936 seconds    Reference of other Arduino items that use Timer2   Tone   PWM   ? *///macros for setting bits to make setting bits more clear#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))//Count to this number of interrupts, then DO the action# define howManyInterrupts  31  #define bps 9600/*to allow resetting the millis() timer to 0 This is only an example as this code is intended as a possible substitute for millis() in some specific cases */extern volatile unsigned long timer0_millis;int int_counter = 0;unsigned long time_ms=903;  //Can start at 0, but starting at 903 matches millis()#define increment 1000  //The timer inc is 999.92 but 1000 is close enough#define ledPin 13       // hard code the pin number for the LEDboolean timetoprint = false;void setup() {                  // initialize the digital pin as an output.  // Pin 13 has an LED connected on most Arduino boards:  pinMode(ledPin, OUTPUT);     Serial.begin(bps);  setupTimer2();  //Prints out the setup for your curiosity  Serial.println();  Serial.print("TCCR2A  B");  Serial.println(TCCR2A,BIN);  Serial.print("TCCR2B  B");  Serial.println(TCCR2B,BIN);  Serial.print("TIMSK2  B");  Serial.println(TIMSK2,BIN);  Serial.print("OCR1A  B");  Serial.println(OCR1A,BIN);  Serial.print("OCR2A  B");  Serial.println(OCR2A,BIN);  Serial.print("ASSR  B");  Serial.println(ASSR,BIN);  Serial.println();  Serial.println("millis(),Timer2 millis");}void loop() {  if (timetoprint){    timetoprint=false;    Serial.print(millis());    Serial.print(',');    Serial.println (time_ms);    time_ms=time_ms+increment;  }  else{    if (Serial.available()>0){      unsigned char in = Serial.read();      switch(in)  //just an example of reading some serial commands      {      case 'r': //reset the time to 0 ( for data acquisition it's nice to reset time)        time_ms=0;         timer0_millis=0;        break;      default:        Serial.println("not recognized");      }//switch    }   }}/*TCCR2A  B00000001 TCCR2B  B00001111 TIMSK2  B00000001 OCR1A   B00000000 OCR2A   B11111100 ASSR    B00000000 */void setupTimer2 (){  //set up timer2    TIMSK2 = 0 ;  //We do NOT Generate interrupts   cbi(ASSR, AS2);   //// use clock, not T2 pin .. probably defaulted anyway  /*When the value of AS2 is changed, the contents of TCNT2, OCR2A,   OCR2B, TCCR2A and TCCR2B might be corrupted. :8271C-AVR-08/10 p165*/  //Clear the Timer Registers - now we know what we have  TCCR2B = 0;    TCCR2A = 0;  TCNT2 = 0;  /*TCCR2A contains the compare match mode and part of the wave generation   mode bits   */  sbi(TCCR2A, WGM20);   //WGM20 is bit 1  /*TCCR2B contains the prescaler and the remainer of the wave generation   mode bits.  Results in Waveform generation mode 5 on Table 17-8 of the   datasheet.  However, we are not actually generating a waveform.   */  sbi(TCCR2B, WGM22);   //WGM22 is bit 1  //Timer2 Settings:  Timer Prescaler /1024  sbi(TCCR2B,CS22); //set this bit  sbi(TCCR2B,CS21); //set this bit  sbi(TCCR2B,CS20); //set this bit  //Timer2 Overflow Interrupt Enable  /*   Bit 0 - TOIE2: Timer/Counter2 Overflow Interrupt Enable   When the TOIE2 bit is written to one and the I-bit in the Status Register   is set (one), the Timer/Counter2 Overflow interrupt is enabled. The   corresponding interrupt is executed if an overflow in Timer/Counter2   occurs, i.e., when the TOV2 bit is set in the Timer/Counter2 Interrupt   Flag Register - TIFR2.   */  OCR2A = 252;       //252 results in a 1000ms period. 63 results in 250ms      sbi(TIMSK2,TOIE2); //enable the timer to raise overflow interrupts}ISR(TIMER2_OVF_vect){  /*This interrupt fires once every    32.64 milliseconds   The counter has to count UP and then it counts back down due to the   way the registers are set.   16MHz / (1024*(255*2)) = 30.63725 interrupts per second ==>(1012 ms period)   16MHz / (1024*(252*2)) = 31.00198413 interrupts per second    */  int_counter += 1;  if (int_counter == howManyInterrupts)  {    int_counter = 0;    timetoprint=true;  }};`

I tested this on a Nano and a Mega
of course I got some help to start this from other posts on this forum and others so I am sorry if I have repeated something
obvious.  The code isn't cut/paste so I couldn't figure out if anyone else should be cited... if so just let me know.

#### UnaClocker

#1
##### Jan 31, 2011, 09:58 am
Great information.. But where did you get the 16000000 that you divide by?
Brian from Tacoma, WA
Arduino evangelist - since Dec, 2010.

#### xlopez

#2
##### Jan 31, 2011, 10:53 am
16 MHz clock --> 16000000

#### UnaClocker

#3
##### Jan 31, 2011, 06:00 pm
Ahhh.. Ok, thanks.
Brian from Tacoma, WA
Arduino evangelist - since Dec, 2010.

Go Up

Please enter a valid email to subscribe