Forum: Mikrocontroller und Digitale Elektronik Atmel 328P: Zugriff auf Variablen in Interrupt-Routine


von Jan (Gast)


Lesenswert?

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.

von 16 bit ought to be enough for everyone (Gast)


Lesenswert?

Jan schrieb:
> dt_Drehzahl = TCNT0 + 256*DrehzahlTimerUeberlaeufe;


Pack da ein "l" hinter das 256.

von Jan (Gast)


Lesenswert?

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.

von H.Joachim S. (crazyhorse)


Lesenswert?

Dann schau dir das listing an.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Wolfgang (Gast)


Lesenswert?

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;

von Oliver S. (oliverso)


Lesenswert?

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

von Beo Bachta (Gast)


Lesenswert?

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?

von Bernd B. (bbrand)


Lesenswert?

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

von neuer PIC Freund (Gast)


Angehängte Dateien:

Lesenswert?

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.

von STK500-Besitzer (Gast)


Lesenswert?

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.

von Harald (Gast)


Lesenswert?

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.

von M. K. (sylaina)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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).

von m.n. (Gast)


Lesenswert?

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.

von Harald (Gast)


Lesenswert?

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.

von neuer PIC Freund (Gast)


Lesenswert?

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
}

von m.n. (Gast)


Lesenswert?

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
}

von Oliver S. (oliverso)


Lesenswert?

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

von m.n. (Gast)


Lesenswert?

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.

von Helmut L. (helmi1)


Lesenswert?

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

von Carl D. (jcw2)


Lesenswert?

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".

von Oliver S. (oliverso)


Lesenswert?

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

von Marc V. (Firma: Vescomp) (logarithmus)


Angehängte Dateien:

Lesenswert?

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
Noch kein Account? Hier anmelden.