Moin zusammen, in folgendem (hier stark gekürztem) Code habe ich Probleme die Variable dt_Drehzahl in der Main-Routine auszulesen. ============================== volatile unsigned long dt_Drehzahl;//Delta-Zeit für Drehzahlberechnung volatile unsigned int DrehzahlTimerUeberlaeufe; ISR(ANALOG_COMP_vect) { TCCR0B = 0; dt_Drehzahl = TCNT0 + 256*DrehzahlTimerUeberlaeufe; TCNT0 = 0; DrehzahlTimerUeberlaeufe=0; TCCR0B = (1<<CS02); //256 } ISR(TIMER0_OVF_vect) { DrehzahlTimerUeberlaeufe++; } int main(void) { TCCR0A = 0; TCCR0B = (1<<CS02); //256 // TCCR0B = (1<<CS02) | (1<<CS00); //1024 TIMSK0 |= (1<<TOIE0); sei(); { ... //Zugriff auf dt_Drehzahl unsigned long tmp=0; cli(); tmp=dt_Drehzahl; sei(); //Ausgabe von tmp auf serielle Schnittstelle } } ============================== dt_Drehzahl wird nur beim Aufruf des Komparators berechnet. Allerdings springt der Wert unerwartet hin- und her. Ist die prinzipielle Verwendung von "volatile" hier korrekt, so dass es eigentlich funktionieren müsste? Die Interrupts habe ich auch beim Zwischenspeichern des Wertes in tmp deaktiviert.
16 bit ought to be enough for everyone schrieb: > Jan schrieb: >> dt_Drehzahl = TCNT0 + 256*DrehzahlTimerUeberlaeufe; > > Pack da ein "l" hinter das 256. Das ändert leider nichts. Ich habe irgendwie die Vermutung, dass der Compiler Code-Optimierungen macht, die dazu führen, dass ich die Werte in "main" nicht immer korrekt erhalte.
Jan schrieb: > Das ändert leider nichts. Ich habe irgendwie die Vermutung, dass der > Compiler Code-Optimierungen macht, die dazu führen, dass ich die Werte > in "main" nicht immer korrekt erhalte. in der ISR Flag setzen. In der main rechnen. Nicht 256*DrehzahlTimerUeberlaeufe benutzen, sondern Left Shift. P.S. Du weißt, daß bei 256*int ohne Typecasting nur max. 255 Ueberlaeufe möglich sind?
:
Bearbeitet durch User
Marc V. schrieb: > Du weißt, daß bei 256*int ohne Typecasting nur max. 255 Ueberlaeufe > möglich sind? Genau deshalb kam von 16 bit ought to be enough for everyone schon der Vorschlag mit:
1 | dt_Drehzahl = TCNT0 + 256L*DrehzahlTimerUeberlaeufe; |
Jan schrieb: > dt_Drehzahl wird nur beim Aufruf des Komparators berechnet. Allerdings > springt der Wert unerwartet hin- und her. Was meinst du mit „springt hin und her“? Die „Drehzahl“ ist ja nichts anderes als der Zeitunterschied zwischen zwei Komparator-Interrupts. Bist du sicher, daß es da keine Schwankungen gibt? Jan schrieb: > Ist die prinzipielle > Verwendung von "volatile" hier korrekt, so dass es eigentlich > funktionieren müsste? Die Interrupts habe ich auch beim > Zwischenspeichern des Wertes in tmp deaktiviert. volatile ist korrekt. Was passieren kann, ist, daß der Timer überläuft, während die Komparator-ISR-Bearbeitung schon begonnen hat. Dann wird der Overflow nicht gezählt. Oliver
Jan schrieb: > ISR(ANALOG_COMP_vect) > { > TCCR0B = 0; > dt_Drehzahl = TCNT0 + 256*DrehzahlTimerUeberlaeufe; > TCNT0 = 0; > DrehzahlTimerUeberlaeufe=0; > TCCR0B = (1<<CS02); //256 > } Wir kennen die genauen Verhältnisse ja nicht, aber bei entsprechend hohen Drehzahlen .... Ich behaupte mal dass diese ISR es nicht schafft in "angemessener" Zeit ihre Aufgabe zu erledigen wenn entsprechend viele Interrupts pro Zeiteinheit auftreten. Auch macht es überhaupt keinen Sinn für jeden einzelnen Impuls der eintrifft die Drehzahl zu berechnen. Welche Interrupt-Rate erwartest du denn an dieser Stelle?
Ich meine mich dunkel daran zu erinnern, dass ich auch schon mal Probleme hatte, wenn IO-Makros direkt in Rechnungen verwendet wurden. Versuche doch mal bitte folgendes:
1 | ISR(ANALOG_COMP_vect) |
2 | {
|
3 | TCCR0B = 0; |
4 | dt_Drehzahl = TCNT0; |
5 | TCNT0 = 0; |
6 | dt_Drehzahl += 256*DrehzahlTimerUeberlaeufe; |
7 | DrehzahlTimerUeberlaeufe=0; |
8 | TCCR0B = (1<<CS02); //256 |
9 | }
|
Gruß, Bernd
Oder einfach den compiler explorer nutzen. Dort sieht man sehr schön, dass ein "L" eine Auswirkung hat, und "256*" ohne Multiplikation auskommt. Vielleicht als Anregung.
Jan schrieb: > ISR(TIMER0_OVF_vect) > { > DrehzahlTimerUeberlaeufe++; > } könnte man doch auch durch
1 | ISR(TIMER0_OVF_vect) |
2 | {
|
3 | DrehzahlTimerUeberlaeufe += 256; |
4 | }
|
ersetzen. Dann erspart man sich die Multiplikation.
Jan schrieb: > Allerdings springt der Wert unerwartet hin- und her. Beobachte mal, ob der Wert um einen kompletten Timerüberlauf hin- und herspringt. Das Problem ergibt sich, wenn sich der Komparatorinterrupt zum Zeitpunkt eines Überlaufes ergibt. Dann ist nämlich nicht garantiert, dass der Überlauf noch mitgezählt wird. Ich kenne das Problem noch vom C167, da konnte man das Problem umschiffen, indem man zwei Timer samt Überlaufzähler startete, die exakt um 0x8000 versetzt liefen. Dann konnte man zum Event schauen, welcher Timer sich weit genug vom Überlauf entfernt bewegte und entsprechend sicher auslesen.
Marc V. schrieb: > Nicht 256*DrehzahlTimerUeberlaeufe benutzen, sondern Left Shift. Warum? Jeder halbwegs vernünftige, 15-20 Jahre alte, Compiler erkennt in der 256 die zweier-Potenz und macht den Shift selbstständig.
M. K. schrieb: > Warum? Jeder halbwegs vernünftige, 15-20 Jahre alte, Compiler erkennt in > der 256 die zweier-Potenz und macht den Shift selbstständig. Nein, Compiler ist ein bißchen schlauer als du, er lädt einfach den HiReg mit LoReg und löscht den LoReg - das sind nur 2 Befehle. Ich schrieb das deswegen , weil bei Left Shift ev. Überlauf leichter zu erkennen ist als bei Multiplikation (für Programmierer, natürlich).
Harald schrieb: > Ich kenne das > Problem noch vom C167, da konnte man das Problem umschiffen, indem man > zwei Timer samt Überlaufzähler startete, die exakt um 0x8000 versetzt > liefen. Dann konnte man zum Event schauen, welcher Timer sich weit genug > vom Überlauf entfernt bewegte und entsprechend sicher auslesen. Das ist aber unnötiger Aufwand. Anhand des Zählerstandes kann man sehen, ob ein Überlauf gleichzeitig erfolgt ist.
m.n. schrieb: > Harald schrieb: >> Ich kenne das >> Problem noch vom C167, da konnte man das Problem umschiffen, indem man >> zwei Timer samt Überlaufzähler startete, die exakt um 0x8000 versetzt >> liefen. Dann konnte man zum Event schauen, welcher Timer sich weit genug >> vom Überlauf entfernt bewegte und entsprechend sicher auslesen. > > Das ist aber unnötiger Aufwand. Anhand des Zählerstandes kann man sehen, > ob ein Überlauf gleichzeitig erfolgt ist. Wenn der Comparator-Int quasi gleichzeitig erfolgt, könnte man RATEN, ob es kritisch war. WISSEN, ob der Überlauf noch mitgezählt wurde, kann man aber nicht. Deswegen frage ich ja, ob der Wert ggf. um einen Überlauf springt. Das ist dann der Beweis für das Problem. Das ist aber auch alles, was ich dazu sagen werde. Das Problem gab es hier schon zigmal. Jedesmal ist es auch so, dass es jemand besser weiß, das Problem dadurch aber nicht gelöst wird.
Wenn im Analog-ISR Prolog ein Zählerüberlauf stattfindet, bevor der Zähler gestoppt wird, könnte man das doch auch erfassen. TOV0 wird hier zum 9-ten Bit.
1 | ISR(ANALOG_COMP_vect) |
2 | {
|
3 | TCCR0B = 0; |
4 | dt_Drehzahl = TCNT0 + 256UL*DrehzahlTimerUeberlaeufe; |
5 | if (TIFR0 & (1 << TOV0)) |
6 | {
|
7 | dt_Drehzahl += 256UL; |
8 | TIFR0 = (1 << TOV0); |
9 | }
|
10 | TCNT0 = 0; |
11 | DrehzahlTimerUeberlaeufe=0; |
12 | TCCR0B = (1<<CS02); //256 |
13 | }
|
Harald schrieb: > Wenn der Comparator-Int quasi gleichzeitig erfolgt, könnte man RATEN, ob > es kritisch war. Da ist nichts zu raten: Ein unverarbeiteter Überlauf ist daran zu erkennen, daß das betreffende Flag gesetzt ist und der Timer einen kleinen Zählerstand aufweist. Beispiel:
1 | // Routinen zur Erfassung der Eingangsimpulse
|
2 | // Ueberlaeufe von T1
|
3 | ISR(TIM1_OVF_vect) |
4 | {
|
5 | ueberlauf++; // Ueberlaeufe von T1 ergeben obere 16bit der Messzeit |
6 | }
|
7 | |
8 | // CAPT-Ereignisse an AIN1
|
9 | ISR(TIM1_CAPT_vect) // Eingangsimpulse mit genauem Zeitpunkt erfassen |
10 | {
|
11 | static unsigned long count; // Impulse per Interrupt |
12 | count++; |
13 | if(mess_status == AUSLESEN) { // Ergebnisse synchron zum Eingangsimpuls auslesen+ablegen |
14 | end_ereignis = count; // Anzahl der Impulse lesen |
15 | zeit_low = ICR1; // capture-reg lesen: untere 16bit |
16 | zeit_high = ueberlauf; // dazu die oberen 16bit |
17 | if((TIFR1 & BIT(TOV1)) && |
18 | (zeit_low < T1_MAX/2)) // evtl. Ueberlauf T1 noch offen? |
19 | zeit_high++; // nur, wenn capture-int + overflow-int gleichzeitig ! |
20 | mess_status = AUSWERTEN; // Daten fertig fuer Auswertung |
21 | }
|
22 | }
|
m.n. schrieb: > Da ist nichts zu raten: Ein unverarbeiteter Überlauf ist daran zu > erkennen, daß das betreffende Flag gesetzt ist und der Timer einen > kleinen Zählerstand aufweist. Der Zählerstand ist doch egal. Das gesetzte Flag zeigt an, daß (mindestens) ein unverarbeiteter Überlauf vorliegt. Das ist schließlich dessen Funktion. Oliver
Oliver S. schrieb: > Der Zählerstand ist doch egal. Das gesetzte Flag zeigt an, daß > (mindestens) ein unverarbeiteter Überlauf vorliegt. Das ist schließlich > dessen Funktion. Eben nicht. Wenn der Zähler kurz vor einem Überlauf stand und OVF in der ISR als gesetzt gelesen wird, darf der Überlauf nicht dem letzten Intervall zugerechnet werden, da der Überlauf nach dem Capture-Signal auftrat.
Da der Atmega ein 8 Bit Contoller ist und die Variable 32 Bit betraegt muss man hier einen Atomaren Zugriff machen. Sonst funkt dir die Interruptroutine dazwischen und schreibt dir die Variabel teileweise um. Dafuer gibt es atomic.h
Oliver S. schrieb: > m.n. schrieb: >> Da ist nichts zu raten: Ein unverarbeiteter Überlauf ist daran zu >> erkennen, daß das betreffende Flag gesetzt ist und der Timer einen >> kleinen Zählerstand aufweist. > > Der Zählerstand ist doch egal. Das gesetzte Flag zeigt an, daß > (mindestens) ein unverarbeiteter Überlauf vorliegt. Das ist schließlich > dessen Funktion. > > Oliver Und wo sagt das Flag, ob Ovf vor oder nach Capture passierte? Z.B. in dem man die Wahrscheinlichkeit daß "vor" und Capture-Wert nahe Maximalwert als sehr gering ansieht. Also nix mit "egal".
m.n. schrieb: > Eben nicht. > Wenn der Zähler kurz vor einem Überlauf stand und OVF in der ISR als > gesetzt gelesen wird, darf der Überlauf nicht dem letzten Intervall > zugerechnet werden, da der Überlauf nach dem Capture-Signal auftrat. Für Capture bist du hier im falschen Thread... Hier gibt es einen Compare-Interrupt, in dessen ISR der Timer zunächst angehalten, und dann ausgelesen wird. Und da kann es dann nur sein, dass ein ungezählter Overflow zwischen Compare-Interrupt und auslesen der Timerregisters auftritt. Das ist dann eindeutig am gesetzten Flag erkennbar. Oliver
Es würde mich interessieren wer den obigen Beitrag positiv bewertet hat und den unteren negativ und warum... Eine Begründung (mit usernamen) wird wahrscheinlich nicht folgen, deswegen liebe Grüsse an die zwei anonymen Feiglinge.
:
Bearbeitet durch User
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.