Hallo euch,
versuche gerade eine Periodenmessungsprogramm zu schreiben und hab mir
gedacht eine reine Zeitmessung würde auch gehen. Dafür hab ich in einem
Buch einen Programmvorschlag gefunden. Jedoch funktioniert dieser leider
nicht. Es wird immer auf eine static declaration of (TIMER0_COMPA_vect)
follows non-static declaration hingewiesen. Leider versteh ich noch
nicht was ich machen soll.
Hier mal mein Programmchen:
nt main(void)
{
//-----Initialisierung----------------------------------------
DDRC = 0xFF; // Port C sind Ausgänge
DDRA = 0x00; // Port A sind Eingänge
volatile double sec=0;
volatile unsigned char run = 0; // 0=stop, 1=run
volatile unsigned int ctr=0; // counter latch
char t [8]; // Text
ISR (TIMER0_COMPA_vect)
{
if (run)
{
run = 0; ctr = 1; sec = 100;
}
}
ISR (TIMER1_CAPT_vect)
{
if (run)
{
ctr=ICR1; sec = ctr/(F_CPU/256.0);run = 0;
}
else
{
TCNT1 =0; run=1;
}
}
uart1_write("Start\r");
_delay_ms(999);
TIMSK1 |= (1<<ICIE1)|(1<<OCIE1A);
OCR1A = F_CPU/256;
TCCR1B|=(1<<WGM12)|(1<<CS12);
sei(); // Globale Interrupts ein
//-----Endlosschleife-----------------------------------------
while(1)
{
//Start counter
if (ctr)
{
dtostrf (sec, 8,2,t);
uart1_write_str(t);
uart1_write("Pulse\r");
}
Und die eine ISR ist sowieso überflüssig. Timer 0 wird im Programm ja
gar nicht benutzt.
Ist das das Programm, welches genau so im Buch abgedruckt war? Wenn ja,
dann kauf dir zusätzlich ein ordentilches C-Buch und arbeite das erst
mal zur Hälfte durch, damit du verstehst, wo dein jetziger Buchautor
Defizite in der Sprache C hat.
.
TIMSK1|=(1<<ICIE1)|(1<<OCIE1A);// solltest du eigentlich erst machen, nachdem der Timer initialisiert ist
2
3
....
4
5
ISR(TIMER0_COMPA_vect)
6
{
das wird in die Hose gehen. So wie das jetzt ist, ist das ein
Dauerreset. Wenn du den Output Compare Interrupt A für den Timer 1 frei
gibst, dann musst du auch die richtige ISR dafür schreiben.
Nämlich eine für den Timer 1.
Das hier
ISR (TIMER0_COMPA_vect)
wäre die ISR für den Timer 0, wie unschwer am Namen erkennbar ist. (und
wenn dein µC am Timer 0 das gar nicht kann, dann ist das auch die
Erklärung für deine Fehlermeldung)
Super danke,
hab die Timer Initialisierung raus gegeben und schon funktionierts.
Beim Timer hab ich mich verschrieben das ist natürlich der Timer1.
Was mir noch aufgefallen ist das der Timer Relativ ungenau ist.
Denn je höher die Frequenz wird desto mehr schwankt der Ausgegebene
Wert.
Kann man hier noch was verbessern?
Thomas Reiterer schrieb:> Was mir noch aufgefallen ist das der Timer Relativ ungenau ist.
Der ist so genau, wie der Takt ist, mit dem du den µC versorgst, bzw.
was du dann mit dem Prescaler weiter machst.
Benutzt du den interenen Taktgenerator, dann hast du Schwankungen.
Benutzt du aber einen Quarz, dann kannst du problemlos auf ein paar
Quarzschwingungen genau messen. Genau aus dem Grund ist der Input
Capture Mode erfunden worden.
Allerdings sollte man dann auch mal darüber nachdenken, wie man die
Prescaler vom Timer einsetzt. Der einzige UNterschied zwischen dem
Minutenzeiger einer Uhr und dem Sekundenzeiger ist ein 'Prescaler' von
60 auf dem Minutenzeiger. Mit einem Minutenzeiger einer Uhr kann man
ganz wunderbar Zeiten von 30 Minuten aufwärts stoppen. Aber um 48
Sekunden zu stoppen, tut man sich schwer. Da ist es dann besser, den
kleineren Prescaler zu nehmen, sprich den Sekundenzeiger, und mit dem
die Zeit zu stoppen.
Ja ich hab schon einen Externen Quarz mit 10MHz angeschlossen. Also
sollte das schon genau sein. Ja vielleicht ist auch mein Frequenz
Generator etwas ungenauer...
Werd noch ein paar Sachen versuchen...
Thomas Reiterer schrieb:> Ja ich hab schon einen Externen Quarz mit 10MHz angeschlossen.
Ja ber: ist der auch aktiv?
> Also> sollte das schon genau sein. Ja vielleicht ist auch mein Frequenz> Generator etwas ungenauer...> Werd noch ein paar Sachen versuchen...
Ich wiederhol mich nur ungern: Prescaler!
Im übrigen ist der Ansatz mit dem 0-setzen von TCNT1 nicht wirklich
optimal. Beim Input Capture lässt man den Timer einfach durchlaufen und
rechnet mit Differenzen der Timer-Zählerstände, wobei das, solange das
Zählergebnis nicht größer als 65535 wird, sogar durch die unsigned
Rechnerei recht problemlos ist, weil man den Overflow des Timers
überhaupt nicht berücksichtigen muss. Einfach
ctr = ende - start;
und fertig.
Hab gesehen das ich da noch Aufholbedarf hab.
So die Fuses für den Quarz hab ich so gesetzt:
SUT_CKSEL -> EXTOSC_0MHZ4_0MHZ9_1KCK_0MS
und am Anfang meines Programmes definiere ich den F_CPU auf 10000000.
Den Prescaler versteh ich soweit dieser teilt die Taktfrequenz.
Und nun soll ich den Zähler starten und mit der ersten Flanke den
Startwert speichern und mit der zweiten Flanke den Endwert und den
Startwert vom Endwert abziehen und schon hab ich die Periodendauer.
Thomas Reiterer schrieb:> Hab gesehen das ich da noch Aufholbedarf hab.> So die Fuses für den Quarz hab ich so gesetzt:> SUT_CKSEL -> EXTOSC_0MHZ4_0MHZ9_1KCK_0MS> und am Anfang meines Programmes definiere ich den F_CPU auf 10000000.
Na was jetzt?
Läuft dein µC mit 10Mhz oder tut er das nicht?
Das lässt sich recht einfach mit dem µC selbst feststellen und dann
weißt du es genau. Denn ein _delay_ms liefert nur dann einigermassen
genau 1 Sekunde, wenn bei einem F_CPU Wert von 10Mio der µC dann auch
tatsächlich mit 10Mhz getaktet wird. Einfach mittels _delay_ms eine LED
1 Sekunde ein / 1 Sekunde aus. Stimmt die Zeit, dann läuft der auch mit
10Mhz. Denn die Alternative von 1Mhz würde bedeuten, dass die LED dann
nicht 1 Sekunde an ist, sondern 10 Sekunden. Und das sieht man auch mit
freiem Auge.
Ob der Quarz mit exakt 10Mhz läuf oder doch mit 9.9999 kann man so nicht
feststellen, aber das macht nichts. Zwischen den Alternative 10Mhz, 1
Mhz oder auch 8Mhz kann man so immer noch gut genug unterscheiden.
>> Den Prescaler versteh ich soweit dieser teilt die Taktfrequenz.> Und nun soll ich den Zähler starten und mit der ersten Flanke den> Startwert speichern und mit der zweiten Flanke den Endwert und den> Startwert vom Endwert abziehen und schon hab ich die Periodendauer.
Ganz genau.
Ja, man kann auch mit einer ganz gewöhnlichen Uhr mit Sekundenzeiger
Zeiten stoppen, indem man sich einfach die Zeigerstellung merkt, wann
der Rennläufer losfährt und mit der Zeigerstellung vergleicht wann er
durchs Ziel fährt. Man muss nicht unbedingt eine Uhr haben, die 2
Knöpfchen hat um eine eigene Stoppuhr zu starten und zu stoppen. Und im
Falle eines Timers mit Input Capture ist das paradoxerweise sogar
genauer, weil das 'Festhalten der Zeigerstellung' eine Hardware
verzögerungsfrei macht und man nicht darauf angewiesen ist, die (etwas)
variable Verzögerung bis es zum Aufruf der ISR kommt, ins Kalkül ziehen
zu müssen.
So das mit dem Quarz hab ich überprüft das funktioniert genau mit einer
Sekunde LED ein-aus.
Für die Zeitstoppen mit Anfangswert merken und Endwert merken da hab ich
noch nicht die Durchsicht wie ich das schreibe.
Thomas Reiterer schrieb:> So das mit dem Quarz hab ich überprüft das funktioniert genau mit einer> Sekunde LED ein-aus.>> Für die Zeitstoppen mit Anfangswert merken und Endwert merken da hab ich> noch nicht die Durchsicht wie ich das schreibe.
Prinzipskizze:
uint16_t startWert;
uint16_t endWert;
...
uint16_t Differenz;
ISR( ... Capture .... )
{
endWert = ISR1;
Differenz = endWert - startWert;
startWert = endWert;
}
machs nicht komplizierter als es ist. Was du dann genau als startWert
bzw. endWert hernimmst, hängt davon ab, was du eigentlich messen willst.
Die Pulslänge oder die Periodenlänge oder ....