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"); }
Die beiden ISR haben im Hauptprogramm nichts zu suchen und globale Variablen auch nicht. Schreib z.B.:
1 | volatile double sec=0; |
2 | volatile unsigned char run = 0; // 0=stop, 1=run |
3 | volatile unsigned int ctr=0; // counter latch |
4 | |
5 | int main(void) |
6 | {
|
7 | char t [8]; // Text |
8 | //-----Initialisierung----------------------------------------
|
9 | DDRC = 0xFF; // Port C sind Ausgänge |
10 | DDRA = 0x00; // Port A sind Eingänge |
11 | uart1_write("Start\r"); |
12 | _delay_ms(999); |
13 | TIMSK1 |= (1<<ICIE1)|(1<<OCIE1A); // solltest du eigentlich erst machen, nachdem der Timer initialisiert ist |
14 | OCR1A = F_CPU/256; |
15 | TCCR1B|=(1<<WGM12)|(1<<CS12); |
16 | sei(); // Globale Interrupts ein |
17 | //-----Endlosschleife-----------------------------------------
|
18 | while(1) |
19 | {
|
20 | //Start counter
|
21 | if (ctr) |
22 | {
|
23 | dtostrf (sec, 8,2,t); |
24 | uart1_write_str(t); |
25 | uart1_write("Pulse\r"); |
26 | }
|
27 | }
|
28 | }
|
29 | ISR (TIMER0_COMPA_vect) |
30 | {
|
31 | |
32 | if (run) |
33 | {
|
34 | run = 0; ctr = 1; sec = 100; |
35 | }
|
36 | }
|
37 | ISR (TIMER1_CAPT_vect) |
38 | {
|
39 | if (run) |
40 | {
|
41 | ctr=ICR1; sec = ctr/(F_CPU/256.0);run = 0; |
42 | }
|
43 | else
|
44 | {
|
45 | TCNT1 =0; run=1; |
46 | }
|
47 | }
|
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. .
Autsch. Jetzt seh ichs erst
1 | 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 ....
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.