Go Down

Topic: realtime clock, microseconds, etc. (Read 10 times) previous topic - next topic

mellis

dcb: thanks for doing these calculations.  One other thing that would be useful to check is the running total of the difference between the micros delta and 500,000 (i.e. how much does the change in micros differ from the expected change).  The running total of micros deltas just says that the values are always increasing, but not necessarily that they do so by the right amount each time.  Also, I'm wondering what the minimum increment (i.e. best resolution) you can get from the micros() function is.  Is it on the order of 1 us, or more like 10?  100?

Also, I'm worried that if the values returned from micros() don't overflow on a regular data size boundary (e.g. 2^16 or 2^32), they will be difficult to work with.  What if keep a running count of the micros() inside the timer0 overflow as we do with millis now?  Or can we somehow truncate to, say, an unsigned int so that the overflow works out properly?

mikalhart

Quote
Also, I'm wondering what the minimum increment (i.e. best resolution) you can get from the micros() function is.


Even ignoring the amount of time the micros() function call itself takes, the resolution would be capped at 4us (8us on the 8MHz processor) for the simple reason that a single tick of TCNT0 takes 64 * 1/16 =4us.

Mikal

dcb

#27
Nov 11, 2008, 02:58 pm Last Edit: Nov 11, 2008, 02:59 pm by dcb Reason: 1
"how much does the change in micros differ from the expected change)"

The last column was the sum of deltas, and the first column is the millis() from timer2, after 168 seconds they are still spot on to the first 6 digits.  The sum of deltas has not wandered off and is tracking at the millisecond level accurately, which implies that it is also accurate.

"what the minimum increment"
best case is 4us @ 16mhz, but need to test.


"if the values returned from micros() don't overflow on a regular data size boundary (e.g. 2^16 or 2^32), they will be difficult to work with."

Understandable concern.  I think there should be a companion elapsedMicros(ulong start, ulong end) function in any event, to detect the overflow and do the right thing so it may be ok if the return value has a more arbitrary overflow point as long as it is predictable.


"What if keep a running count of the micros() inside the timer0 overflow as we do with millis now?"

I'm struggling to see the implementation there.

But I think this problem can be handled nicely by keeping track of the number of calls to timer0ovf and not touching when we update millis.  Then Dons function a few posts earlier will work, and it is 5 times faster than the one in the test harness by my measure, and will overflow every 45 days or so.

so this is what wiring.c might look like.

Code: [Select]

volatile unsigned long timer0_tics = 0;
volatile unsigned long timer0_clock_cycles = 0;
volatile unsigned long timer0_millis = 0;

SIGNAL(TIMER0_OVF_vect)
{
     timer0_tics++;
     // timer 0 prescale factor is 64 and the timer overflows at 256
     timer0_clock_cycles += 64UL * 256UL;
     while (timer0_clock_cycles > clockCyclesPerMicrosecond() * 1000UL) {
           timer0_clock_cycles -= clockCyclesPerMicrosecond() * 1000UL;
           timer0_millis++;
     }
}


dcb

and here the updated test program that uses the timer0_tics functionality added to wiring.c.

16mhz notes:
micros() is not rolling over at 268 seconds,
it takes about 3 microseconds to call and return from this version of micros()
no drift noticed after 268 seconds.

Code: [Select]


extern volatile unsigned long timer0_clock_cycles;
extern volatile unsigned long timer0_millis;
extern volatile unsigned long timer0_tics;

unsigned long micros()
{
 unsigned long m, t;
 uint8_t oldSREG = SREG;
 cli();
 t = TCNT0;
 if ((TIFR0 & _BV(TOV0)) && (t == 0))
   t = 256;
 m = timer0_tics;
 SREG = oldSREG;
 return ((m << 8) + t) * 4;
}




volatile unsigned long timer2_clock_cycles = 0;
volatile unsigned long timer2_millis = 0;
ISR(TIMER2_OVF_vect){
// timer 2 prescale factor is 64 and the timer overflows at 256
 timer2_clock_cycles += 64UL * 256UL;
 while (timer2_clock_cycles > clockCyclesPerMicrosecond() * 1000UL) {
   timer2_clock_cycles -= clockCyclesPerMicrosecond() * 1000UL;
   timer2_millis++;
 }
}

unsigned long millis2(){
       unsigned long m;
     uint8_t oldSREG = SREG;
     cli();
     m = timer2_millis;
     SREG = oldSREG;
     return m;
}

void init2(){
     // on the ATmega168, timer 0 is also used for fast hardware pwm
     // (using phase-correct PWM would mean that timer 0 overflowed half as often
     // resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(__AVR_ATmega168__)
       TCCR2A=1<<WGM20|1<<WGM21;
//      sbi(TCCR2A, WGM21);
//      sbi(TCCR2A, WGM20);
#endif  
     // set timer 0 prescale factor to 64
#if defined(__AVR_ATmega168__)
     TCCR2B=1<<CS22;
//      sbi(TCCR2B, CS21);
//      sbi(TCCR2B, CS20);
#else
     TCCR2=1<<CS21|1<<CS20
#endif
     // enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
     TIMSK2|=1<<TOIE2;
#else
     TIMSK|=1<<TOIE2;
#endif

}


void setup(){
 init2();
 Serial.begin(9600);
}

unsigned long nextMillis=500;
unsigned long microsSum;
unsigned long lastMicros;

void loop(){
// performanceTest();
accuracyTest();
}

void performanceTest(){ //15564   //3036

 unsigned long micros0a;
 unsigned long micros0=micros();
 for(int x = 0; x < 1000; x++)
   micros0a=micros();
 Serial.println(micros0a-micros0,DEC);
}

void accuracyTest(){
 while(millis2()<nextMillis);
 unsigned long micros0;
 unsigned long millis0;
 unsigned long millisb;
 micros0=micros();
 millis0=millis();
 millisb=millis2();
 microsSum=microsSum + (micros0-lastMicros);
 lastMicros = micros0;
 Serial.print(millisb);
 Serial.print(" ");
 Serial.print(millis0);
 Serial.print(" ");
 Serial.print(micros0);
 Serial.print(" ");
 Serial.print(microsSum);
 Serial.println(" ");
 
 nextMillis+=500;
}



dcb

8mhz initial notes

micros function returns 1/2 the expected value
performance is 1/2 of 16mhz (~6microseconds per call)
no drift noticed after short test run.

Go Up