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?
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:
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:
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:> 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.
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
staticunsignedlongcount;// 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 !
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.