Go Down

Topic: timer interrupts problem (Read 1 time) previous topic - next topic

leolfs

I am total newbie with both arduino and C. Am trying to get accurate timing for both clock display and later stepper motor driven hands. Will eventually synchronise with mains supply for long term accuracy. This code seems like a good idea but loses a few seconds per hour. If it's obvious then I am too dumb to see it. Is there some interaction with serial.print interrupts? Are there any other interrupts happenning. Using mega168

//#include <avr/io.h>
//#include <avr/interrupt.h>

int ledPin = 13;                // LED connected to digital pin 13
byte test=TCCR2B;
long testB;
volatile int var1=0;
int var2=0;
int seconds=0;
int minutes=0;
//----------------------------------------------------------------------------------
void setup()                    // run once, when the sketch starts
{
 
 // set up timer 1 control registers here
 TIMSK1 = B00000010;           //Timer/counter Interrupt Mask Register intrpt on match A
 TCCR1A = 0;                   // normal operation no external pins connected
 TCCR1B = B00000010;            // divide by 256 With 16MHz clock gives timer input rate of 62500Hz
 TCCR1C = 0;

 Serial.begin(28800);
 pinMode(ledPin, OUTPUT);      // sets the digital pin as output
 Serial.println("Hello");
  OCR1A=625;
  TCNT1=0;
}
//----------------------------------------------------------------------------------
void loop()                     // run over and over again
{
 digitalWrite(ledPin, HIGH);   // sets the LED on     all this to let me know something happening
 delay(500);                  // wait
 digitalWrite(ledPin, LOW);    // sets the LED off
 delay(500);                  // wait

if(var1 > 99)
 {
   var1=0;
   seconds += 1;
   if(seconds>59)
   {
     seconds=0;
     minutes +=1;
     if(minutes>59)
     {minutes=0;
     }
   }
   Serial.print (minutes);
   Serial.print (" : ");
   Serial.println (seconds);
 
 }
}

//-----------------------------------------------------------------------------------
ISR (TIMER1_COMPA_vect)         // add 625 (0x271) to comparator for 10mS interrupts

{
 
OCR1A += 625;                   //add 10mSec worth of timer clock to comparator
 var1+=1;                       //count 10mSec intervals
}

Also have seen  #include <avr/io.h> and #include <avr/interrupt.h> in other sketches but seems to work without them. What's happening?
Any enlightenment appreciated

bens

Hello.

The first thing that's worth noting is that the accuracy of external resonators is typically between 0.2 and 0.5%, meaning that you should pretty much expect to lose anywhere from 7 to 20 seconds per hour.

The second thing I see that's strange is your choice to use a timer1 compare match interrupt rather than a timer1 overflow interrupt.  You could make your code cleaner and easier to follow if you ran timer1 in CTC mode with a TOP value set to 625 (or maybe 624?).  This means that TCNT1 will run from 0 to 624, at which point it will overflow back to 0 and trigger a timer1 overflow interrupt.

The presence of other interrupts might make any instantaneous time measurement you have off by a few microseconds, but unless they are long enough to cause you to miss one of your timer1 interrupts (i.e. longer than 20 ms), you will not lose counts because of them.

- Ben

tigrezno

you can try with this lib: http://www.arduino.cc/playground/Main/MsTimer2

leolfs

OK got it working. Sorry to burn anybody's valuable brain cells.
Big lesson
1. Raw beginner and wild over optimism= guaranteed stuffup and confusion
2. Make sure NO other possibly obscuring bits of code get in the way of goal!
3 Turns out including the delays were modifying the effect of my interrupt.
  They were incorrectly set  anyway and so only seemed to work.
Q. How does one find out what other intrpts are in use that might interfere?
Q. Why dont the include i/o and interrupt headers need to be added? Are they
   included in arduino 11?
Thanks Ben, for pointing out different way of using the compare. Just never thought of it.
Also tigrezno for MsTimer2 Easy to use but is there much code overhead?
How do you see assembly output from compiler to see what it does?

Anyway this seems to work so far:

//#include <avr/io.h>             // seems not to be needed ??
//#include <avr/interrupt.h>   // seems not to be needed ??

int ledPin = 13;                // LED connected to digital pin 13
volatile int var1=0;
int seconds=0;
int minutes=0;
boolean value;
//----------------------------------------------------------------------------------
void setup()                    // run once, when the sketch starts
{
 
 // set up timer 1 control registers here
 TIMSK1 = B00000010;           //Timer/counter Interrupt Mask Register intrpt on match A
 TCCR1A = 0;                   // normal operation no external pins connected
 TCCR1B = B00000100;            // divide by 256 With 16MHz clock gives timer input rate of 62500Hz
 TCCR1C = 0;
 OCR1A =0xFFF0;
 value=LOW;

 Serial.begin(28800);
 pinMode(ledPin, OUTPUT);      // sets the digital pin as output
 Serial.println("Hello");
  OCR1A=625;
  TCNT1=0;
}
//----------------------------------------------------------------------------------
void loop()                     // run over and over again
{
if(var1 > 99)
 {
   var1=0;
   seconds += 1;
   value =!value;
     digitalWrite(ledPin, value);    // sets the LED on and off
   if(seconds>59)
   {
     seconds=0;
     value =!value
     minutes +=1;
     if(minutes>59)
     {minutes=0;
     }
   }
   Serial.print (minutes);
   Serial.print (" : ");
   Serial.println (seconds);
 }
 
}

//-----------------------------------------------------------------------------------
ISR (TIMER1_COMPA_vect)         // add 625 (0x271) to comparator for 10mS interrupts

{
 
OCR1A += 625;                   //add 10mSec worth of timer clock
 var1+=1;

}


bens

Quote

void loop()                     // run over and over again
{
if(var1 > 99)
 {
   var1=0;

I'm glad to hear you have things working better.  I took a closer look at your code and can see a few small problems and one potentially large one.  I've quoted the big problem above.  This is the reason why things improved after you removed your delays.  Basically you are assuming that your main loop is excecuting so fast that you are evaluating this statement at least once for every new value of var1, but if you experience some kind of delay in your program var1 might be updated several times by the interrupt before you check its value.

Let's assume var is 99 when such a delay hits.  30 ms later you reach the top of loop(), notice that var is greater than 99 (it's actually 102 right now), and clear it.  By now clearing var1, you've completely lost any record of those extra two counts, and your concept of the current time is now off by 20 ms.  The solution to this is to do the following:

if (var1 > 99)
{
 var1  -= 100;
 ...
}

Usually this will cause var1 to reset to zero, but if for some reason var1 is something like 102 when you reach the top of loop(), subtracting off 100 preserves those extra two counts, giving var1 a head start on the next cycle that will keep you from losing time.  If you do this, the time-keeping ability of your program should pretty much become impervious to overhead from the Arduino framework, and you won't have to worry about delays from other interrupts.


The minor problems I see are that you aren't using your timer1 interrupt in a safe way.  For one thing, you could experience an interrupt in the middle of reading or writing to var1 (var1 is a two byte value, so reading and writing it are non-atomic operations that can be interrupted).  What this means is that you might have read the first byte of var1, then the interrupt happens and changes var1, then you read the second byte of var1.  Your resulting two-byte integer is now potentially corrupted.  Fortunately there is a simple solution to this problem in your case: make var1 an unsigned char.  This turns it into a single-byte value, so reading it and writing it become uninterruptable atomic operations.  This works for you because you never require var1 to be much higher than 100, so you can get by with it having a range of 0 - 255.  Does this make sense?

Lastly, you could experience an interrupt to the line

var1 -= 100;

because at the assembly level what you're doing is

load var1 from RAM into a register
decrement register by 100
save register to var1

If the interrupt happens after the load but before the save, you will miss that interrupt's incrementing of var1, and you will have lost 10 ms.  Fortunately, the chance of the interrupt happening in that critical place is incredibly small, so what you end up with is a very slim chance that you will lose 10 ms every loop cycle (i.e. it's not going to be a very big source of error).  The way to protect yourself against this, if you even think such a thing is worth it, is to disable interrupts for the duration of that non-atomic operation:

if (var1 > 99)
{
 cli();  // disable all interrupts
 var -= 100;
 sei();  // enable interrupts again
 ...
}

I think if you do all those things, the only source of systematic error in your timing will be the inaccuracy of the external resonator.


Quote

Q. How does one find out what other intrpts are in use that might interfere?

You could figure it out by looking at the assembly version of your code.  Check out the ISR jump table for non-zero entries.  Or you could try to trace through what the Arduino code is doing behind the scenes.  For example, before setup is called, the function init() defined in wiring.c is called.  This enables an overflow interrupt on timer0 that is used to drive millis() and delay().  I don't think the Arduino code enables any other timer interrupts, and I don't think it uses serial interrupts (but I'm not certain of this).  The timer0 overflow interrupt might be the only one.

Quote

Q. Why dont the include i/o and interrupt headers need to be added? Are they
   included in arduino 11?

These files are automatically being included by the Arduino code that frames your code.

Quote

How do you see assembly output from compiler to see what it does?

I'd be very interested to know if this is easily doable using the Arduino IDE (or even just GCC).  One really inconvenient way is to use avrdude to generate a hex file of your program.  You can then open this hex file in a text editor and decode the assembly from the hex by hand (ugh).  The only convenient way I know of is to use AVR Studio to compile your code and configure your project to output a list file.  This list file shows the assembly in a remarkably readable format.  You can even step through this list file line by line as you debug your program in the AVR Studio simulator (just switch from the standard view to the disassembler view).  I'm not sure how you would get the Arduino backbone of your program into AVR Studio, though.


- Ben

tigrezno

#5
May 24, 2008, 11:15 am Last Edit: May 24, 2008, 11:16 am by tigrezno Reason: 1
overload? just download the zip and check the code, it's small enough IMO.

mem

#6
May 24, 2008, 02:10 pm Last Edit: May 24, 2008, 02:10 pm by mem Reason: 1
Quote

Q. How do you see assembly output from compiler to see what it does?

I posted a tip for generating dump files here:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1207951658#2

Go Up