PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Genaue Zeitmessung mit einem AVR



OnkelTobi
11.03.2007, 03:14
Servus,

ich habe mir eine kleine Rundenzeiten-Messanlage gebaut...

Wie kann ich die Messung möglichst genau bekommen?
Ich habe ein Abweichung die ich mir aktuelle nicht erklären kann.

Zunächst kurz wie ich es bisher gelöst habe:

Hardware:
http://farm1.static.flickr.com/169/417047402_d04b280117.jpg
Tiny26 mit externem 11,0592Mhz Quarz
HD44780 Display
Infrarot- bzw. Laserlichtschranke


Software:
Timer1 mit Prescaler 512
Vergleichsregister so eingestellt, dass alle 10ms ein Interrupt ausgelöst wird.
Bei Interrupt wird Zählregister rückgesetzt und die Hunderstel addiert.

Time0 sorgt alle 200ms dafür, dass das angeschlossende LCD aktualisiert wird.

Hier mal der Code des Zählers:


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

#include "global_time.h"

//Variable für Zeitmessung
time_struct globalTime;
volatile uint8_t milliSeconds = 0;
volatile uint8_t secondFlag = 0;


ISR(TIMER1_CMPA_vect)
{
TCNT1 = 0x00;
if (100 == ++milliSeconds)
{
secondFlag = 1;
milliSeconds = 0;
}
}

void globalTimeRefresh()
{
if (secondFlag)
{
if (60 == ++globalTime.sec)
{
globalTime.min++;
globalTime.sec = 0;
}
secondFlag = 0;
}
globalTime.hSec = milliSeconds;
}

void globalTimeInitialize(void)
{
/*
Timer1:
Interrupt genau jede Millisekunde
Prescaler 512
Vergleichsregister = F_CPU / 512 / 100
Zähler zurücksetzen, wenn OCR1A erreicht ist

*/
TCCR1B = (1<<CS13)|(0<<CS12)|(1<<CS11)|(0<<CS10);
OCR1A = F_CPU / 512 / 100 + 1;

TIMSK |= (1<<OCIE1A); //Interrupt
}


Problem:
Aktuell geht die Zeitmessung 0,3% zu langsam (ca 4s auf 23Min).
Die Genauigkeit des Quarzes ist mit ppm angegeben, also nicht ursächlich für die Abweichung.

0.3% sind bei 11Mhz immerhin 30.000 Takte pro Sekunde - wobei gehen die "verloren"?

Bei der Interruptbehandlung der anderen Interrupts?
Oder durch das manuelle Rücksetzen des Zählers? (TCNT1 = 0);

Es gibt keinerlei atomaren Code, es können also keine Interrupts unterdrückt sein.

Geht vielleicht mein Handy falsch? ;)

MfG
Tobias

SprinterSB
11.03.2007, 10:39
Lass das das TCNT=1 weg. In dem Mode, den du eingestellt hast, erledigt das die Hardware. Wenn du es nochmal zurüchsetzt, geht deine Uhr zu langsam.

OnkelTobi
12.03.2007, 16:10
Leider nicht.

der TIMER1_CMPA_vect-Interrupt wird korrekt ausgelöst, der Timer1 zählt aber bis 0xFF weiter.

Der Tiny26 hat irgendwie keinen Mode um den Zähler nach erreichen von OCR1A automatisch zurückzusetzen. Der Tiny44 hats... mal schauen.

SprinterSB
12.03.2007, 16:16
Du betreibst den Timer im "normal" Mode. Schau mal ins Datenblatt und verwende den CTC-Mode. Da fehlt wohl noch ein Bit im TCCRnB. CTCx oder WGMx.


Bit 7 CTC1: Clear Timer/Counter on Compare Match

When the CTC1 control bit is set (one), Timer/Counter1 is reset to $00 in the CPU clock cycle after a compare match with OCR1C Register value. If the control bit is cleared, Timer/Counter1 continues counting and is unaffected by a compare match.

Du schreibst also den Wert nicht nach OCR1A, sondern nach OCR1C und setzt OCR1A=0.

Ausserdem hast du einen schlechten OCR-Wert. Der Wert muss in 8 Bit passen und die Division muss ausgehen, ansonsten hasst du Ungenauigkeit wegen der Rundungsfehler (Timer geht schneller als gewünscht). Zudem muss da eine -1 hin und keine +1.

OnkelTobi
12.03.2007, 16:38
Kurz nachdem ich das oben gepostet habe, habe ich das CTC1-Flag auch bemerkt. :)

Dazu habe ich noch den Interupt auf TOIE1 geändert und es klappt.

Die Genauigkeit muss ich aber noch bestimmen.

/edit
Beim setzen von OCR1C subtrahiere ich noch eine 1:
OCR1C = F_CPU / 512 / 100 - 1;

Und schon zeigt die Uhr die gewünschte Genauigkeit.
Nach 21Minuten ist keine Abweichung in Hundertstel zu bestimmen.

Danke.