www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik avr while-Schleife und Interrupt


Autor: kleriker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich hab ein mir nicht so ganz nachvollziehbares Problem.
Und zwar wollte ich in einer while schleife ein Flag abfragen, das Flag 
wird in einer Interrupt-Routine gesetzt.

Das ganze funktioniert beim debuggen allerdings nur wenn ich das 
Programm anhalte. Wenn ich das Programm durchlaufen lasse hänge ich 
"Stunden" an dieser Stelle. Halt bis ich das Programm wieder anhalte, 
dann ist das Flag auf einmal gesetzt und das Programm läuft weiter.

Die Optimierung ist ausgeschaltet.

Hat jmd eine Idee woran das liegen kann?

Grüße

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja, vermutlich an volatile

Autor: kleriker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daran liegt es nicht.
Ich habe ja die Optimierung ausgeschaltet. Habe es sicherheitshalber 
auch nochmal mit volatile versucht. Ergab aber das selbe Ergebnis.

Zudem wird ja alles richtig ausgeführt wenn ich per Hand eine Pause 
mache.

Autor: EinGast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Fehler liegt ganz klar in Zeile 42

Autor: Alex F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohne Code kann man nur raten...

Autor: kleriker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t Flag=0;

int main (void)
{
  TCCR0 |= (0<<CS02) | (0<<CS01) | (1<<CS00);  // start timer
  TIMSK |= (1<<TOIE0);  // Interrupt enable

  sei();  

  while(Flag==0);

  while(1) 
  {
     asm("nop");
  }
  return 0;
}


ISR(TIMER0_OVF_vect)
{
  Flag=1;
} 

Also hier mal ein stark vereinfachtes Prog dazu.
Der Interrupt wird ausgeführt aber die while(1) erreiche ich erst 
nachdem ich den degugger angehalten habe.

Autor: nils (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Du bist dir schon im klaren was du da machst?

  while(Flag==0);

  while(1)
  {
     asm("nop");
  }
  return 0;


Wenn Flag == 0, tu nix, anosnten auch nix
dann geh in eine endlosscheife

Autor: kleriker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja ich weiß, dass das oben keinen zweck erfüllt. Ist ja auch nur eine 
Vereinfachung um mein Problem zu zeigen.
In dem kompletten Quelltext steht natürlich noch einiges mehr.

Nichts desdo trotz müsste ja die while(1) schleife ohne Probleme 
erreicht werden. Wird sie aber nicht!

Ich denke mal fast es liegt am AVR Studio?!

Autor: Raik C. (raik_c)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kleriker schrieb:
> Ich denke mal fast es liegt am AVR Studio?!

nein es liegt an deiner ersten while schleife , die ist endlos

raik

Autor: g457 (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
> TCCR0 |= (0<<CS02) | (0<<CS01) | (1<<CS00);  // start timer

Bis Du Dir bewusst, dass Du hier möglicherweise [1] einen externen(!) 
Takt an T0 (m8: PD4) brauchst, damit der Timer0 (dessen Flanken) zählen 
kann? Ohne Takt kein Timer0-Overflow und damit wird..

while(Flag==0);

..zur Endlosschleife.

HTH

[1] beispielhaft definitiv m8 - ist das bei allen AVRs (mit Timer) so?

Autor: kleriker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay stimmt ich hab vergessen den Controller mit anzugeben. Ist ein 
atmega16.

@  g457:
Die Zeile stimmt so. Bei dem Controller wird damit einfach nur der Timer 
gestartet. Der Interrupt löst auch aus, womit der einwand von  Raik C. 
auch nich so ganz richtig ist. Da die Schleife nach dem Interrupt 
verlassen werden muss.

Nochmal zu meinem Problem: Das läuft so auch bis zur zweiten Schleife , 
aber halt erst NACHDEM ich es manuell angehalten habe.

Autor: g457 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Die Zeile stimmt so. Bei dem Controller wird damit einfach nur der Timer
> gestartet.

Stimmt. Ich mach mal einen Verbesserungsvorschlag was die Lesbarkeit 
betrifft..

> TCCR0 |= (1<<CS00);  // start timer

..und putz meine Brille. Lass den Code mal auf echter Hardware laufen, 
ggf. nur(!) um LED-Pintoggeln erweitert.

Autor: EinGast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, ich habe den Code, den du da oben gegeben hast, mal durch den 
Simulator geschickt, und er funktioniert genau so, wie er soll. Die 
while(1)-Schleife wird recht schnell erreicht, auch ohne dass ich 
irgendetwas tue.

Daher schließe ich daraus, dass in deinem echten Programm ein anderer 
Fehler (vielleicht ein = statt == ?) mit reinspielt.
Daher wäre der Code, mit dem du das wirklich ausführst interessant.

Autor: kleriker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Stimmt. Ich mach mal einen Verbesserungsvorschlag was die Lesbarkeit
> betrifft..

Naja ich schreib die unbenutzten immer mit da kann man schneller was 
ändern.

> Daher schließe ich daraus, dass in deinem echten Programm ein anderer
> Fehler (vielleicht ein = statt == ?) mit reinspielt.
> Daher wäre der Code, mit dem du das wirklich ausführst interessant.

Das kurze hatte so auch nicht bei mir funktioniert (copy & paste).

Hab jetzt mal alles neu installiert und schon lief es. Ich versteh´s 
zwar nicht aber immerhin es geht nun.

Ich danke für die Unterstützung.

Autor: Dirk M. (dmd)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich muss diesen alten Beitrag nochmal rauskramen, weil ich wohl ein 
ähnliches Problem habe.
Ich benutze einen ATmega8-16PU mit 3,6864 MHz und Atmel Studio 6 (SP2).

Hier der Problem-Code:
ISR(TIMER1_OVF_vect)
{
//  PORTC |= 1<<PC0;
  TimeOut=1;
//  _delay_ms(100);
//  PORTC &= ~(1<<PC0);

}

void Messschieber_suchen(void)
{
  TCCR1B = 1<<CS11 | 1<<CS10;      // 16-Bit Timer: Vorteiler 64
  TCNT1 = 7937;                    // Setze Zählregister(16 Bit Timer1) - Überlauf ca. jede Sekunde
  TIMSK = 1<<TOIE1;
  sei();
  
  while(!TimeOut)
  {
    if ( (PINB & (1<<MS1_Takt)) == 0 )
    {
      Messschieber_gefunden = 1;
    }
    _delay_us(1);       // so geht es ...
//    PORTC |= 1<<PC1;  // und so auch ...
  }
}

Wie man an den Kommentaren erkennen kann, möchte ich eine 
Messschieberanzeige auf ein LCD übertragen. Nach dem Start des 
Controllers frage ich die Taktleitung ca. 1 Sekunde ab. Wenn in dieser 
Zeit ein Pegelwechsel auftrat, ist ein Messschieber angeschlossen - so 
die Idee...
Ich habe nun schon mehrere Abfragen-Versionen durchgespielt, u.a. die 
if-Bedingung als while-Bedingung usw.
Der Controller bleibt immer in der while-Schleife hängen, bis ich sie 
durch irgendetwas "bremse" - z.B. durch _delay_us(1) oder durch setzen 
irgendeines Ausgangs.
Auch wenn es so funktioniert, würde ich schon gerne wissen was da falsch 
läuft?

-Dirk-

Autor: Georg G. (df2au)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wie und wo ist Timeout definiert?

Autor: Dirk M. (dmd)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
TimeOut habe ich als globale Variable vor 'main' definiert.
uint8_t TimeOut = 0;

Dirk

Autor: Georg G. (df2au)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dirk M. schrieb:
> uint8_t TimeOut = 0;

mach mal

volatile uint8_t TimeOut = 0;

Dann weiß der Compiler, dass die Variable im Interrupt verändert wird 
und er sie nicht in einem Register halten darf.

Autor: Dirk M. (dmd)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin aber auch besch...
Habe mir gerade eines meiner letzten Programme angeguckt. Dort habe ich 
auch alle Variablen, welche im Interrupt verändert werden als 'volatile' 
deklariert (ist allerdings schon fast ein Jahr her).

Nun geht's auch ohne "Bremse".

Vielen Dank für die schnelle und kompetente Hilfe.



Nur in­te­r­es­se­hal­ber:
Was aber passiert wenn ich 'TimeOut' nicht als volatile deklariere und 
stattdessen ein 'delay' in die while-Schleife einfüge? Dann geht's ja 
auch.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn du ein delay einfügst, veränderst du das Programm.
delay macht Warteschleifen, dazu muss es Zähler benutzen.
Diese Zähler brauchen beim Abarbeiten Platz in der CPU, sprich Register.
Damit hat der Compiler keine Möglichkeit mehr, den Wert von TimeOut 
während des kompletten Durchlaufs durch die Hauptschleife in einem 
Register vorzuhalten. Sprich, es muss beim nächsten Durchlauf durch die 
Hauptschleife neu geladen werden. Und damit hast du dann zumindest beim 
ersten Zugriff auf TimeOut den jeweils aktuellen Wert wieder da.

volatile teilt dem Compiler mit: Der Wert der Variablen kann sich auf 
Wegen ändern, die du in deiner Analyse nicht entdecken wirst - 
unterlasse daher alle Optimierungen auf dieser Variablen.

FAQ: Was hat es mit volatile auf sich

: Bearbeitet durch Moderator
Autor: Dirk M. (dmd)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sehr schöne Erklärung, die verstehe sogar ich ;-)

Meine ersten Programme habe ich immer in ASM geschrieben, dort hatte man 
diese Probleme nicht. Da aber auch meine "Mini-Programme" immer größer 
wurden, habe ich mich für C entschieden, weil ich manchmal den Überblick 
verloren habe. Bin halt nur ein Gelegenheitsprogrammierer.

Also, nochmals danke für die Hilfe und bis bald...
(Bin ja erst am Anfang meines Projekts)

-Dirk-

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dirk M. schrieb:
> Sehr schöne Erklärung, die verstehe sogar ich ;-)
>
> Meine ersten Programme habe ich immer in ASM geschrieben, dort hatte man
> diese Probleme nicht.

Klar. Weil genau das, was du geschrieben hast, auch auf der Maschine 
gelaufen ist.
Dafür musstest du dich mit Dingen wie Registerbelegung rumschlagen. Und 
wehe du hast dabei mal einen Fehler gemacht.
Hier hast du eine Hochsprache (na, ja). Da kümmert sich der Compiler um 
die Registerbelegung und versucht (mehr oder weniger aggresiv), die 
beteiligten Variablen so in den Registern zu verteilen, so dass er 
möglichst selten Datentransfers von und zum Speicher machen muss. Damit 
er das möglichst gut kann, macht er eine Datenflussanalyse, d.h. er 
versucht rauszufinden, an welchen Stellen sich Variablenwerte überhaupt 
ändern können. Augrund dieser Erkentnisse kann der dann die Lade bzw. 
Speicheroperationen im Assemblerprogramm verschieben. Manchmal sogar 
soweit, dass sie komplett rausfallen.

Das kannst du im Prinzip im Assemblerprogramm auch tun. Nur wird das 
sehr schnell sehr aufwändig und vor allen Dingen kann es sein, dass sich 
bei Programmerweiterungen alles umdreht, wenn die vorhandenen Register 
prinzipiell nicht reichen. Was gestern noch eine optimale 
Registerbelegung war, kann heute schon ganz anders aussehen.
Dafür hast du allerdings im Assemblerprogramm den Vorteil, dass du dich 
nicht an Konventionen, wie zb eine per Konvention vorgegebene 
Standard-Registerbelegung halten musst, in der bestimmte Register ben 
ausschliesslich für bestimmte Zwecke benutzt werden und daher dem 
Compiler nicht zur freien Benutzung zur Verfügung stehen. Bei kleineren 
Programmen ist das ein unbestreitbarer Vorteil in der Laufzeit gegenüber 
einer Hochsprache. Sobald aber eine kritische Masse überschritten ist, 
relativiert sich dieser Vorteil ganz schnell.

: Bearbeitet durch Moderator
Autor: Dirk M. (dmd)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohja, ich denke nur an die 1000 Zettel, die ich neben der Tastatur 
liegen hatte um alle Register im Blick zu behalten.
Mal nebenbei und auch nur wenn du noch Lust und Zeit hast...
Wenn ich schreibe _delay_us(1); kann das doch eigentlich nicht 
funktionieren. Der Controller wird mit 3.6864 MHz betrieben d.h. ca.0,27 
Mikrosekunden pro Takt. Er hätte ja nicht mal 4 Takte um die Funktion 
aufzurufen, abzuarbeiten und wieder zurückzuspringen.
Oder macht der Compiler aus der Funktion nur ein paar nop-Anweisungen?

Autor: Georg G. (df2au)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Quelltext der Delay Routinen ist dabei. Der Compiler ist deutlich 
schlauer, als man denkt. Natürlich gibt es Rundungsfehler... aber es 
geht erstaunlich gut.

Autor: Dirk M. (dmd)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok danke, das werde ich mir am Wochenende mal ansehen. Nun muss ich aber 
erstmal mit meinen Messschiebern weiterkommen, damit meine Fräse wieder 
einsatzbereit ist.

-Dirk-

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.