Atmel 328P: Zugriff auf Variablen in Interrupt-Routine

Gast #6128738
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.
(Firma: Vescomp) #6128841
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?
#6129015
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
Gast #6129028
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?
#6129031
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
Gast #6129217
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.
(Firma: Vescomp) #6129259
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).
Gast #6129301
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.
Gast #6129327
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.
Gast #6129344
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
}
Gast #6129349
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
}
#6129500
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
Gast #6129646
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.
#6129680
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".
#6129692
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren