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


von kleriker (Gast)


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

von Peter (Gast)


Lesenswert?

ja, vermutlich an volatile

von kleriker (Gast)


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.

von EinGast (Gast)


Lesenswert?

Der Fehler liegt ganz klar in Zeile 42

von Alex F. (Gast)


Lesenswert?

Ohne Code kann man nur raten...

von kleriker (Gast)


Lesenswert?

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
volatile uint8_t Flag=0;
5
6
int main (void)
7
{
8
  TCCR0 |= (0<<CS02) | (0<<CS01) | (1<<CS00);  // start timer
9
  TIMSK |= (1<<TOIE0);  // Interrupt enable
10
11
  sei();  
12
13
  while(Flag==0);
14
15
  while(1) 
16
  {
17
     asm("nop");
18
  }
19
  return 0;
20
}
21
22
23
ISR(TIMER0_OVF_vect)
24
{
25
  Flag=1;
26
}

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.

von nils (Gast)


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

von kleriker (Gast)


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?!

von Raik C. (raik_c)


Lesenswert?

kleriker schrieb:
> Ich denke mal fast es liegt am AVR Studio?!

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

raik

von g457 (Gast)


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?

von kleriker (Gast)


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.

von g457 (Gast)


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.

von EinGast (Gast)


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.

von kleriker (Gast)


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.

von Dirk M. (dmd)


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:
1
ISR(TIMER1_OVF_vect)
2
{
3
//  PORTC |= 1<<PC0;
4
  TimeOut=1;
5
//  _delay_ms(100);
6
//  PORTC &= ~(1<<PC0);
7
8
}
9
10
void Messschieber_suchen(void)
11
{
12
  TCCR1B = 1<<CS11 | 1<<CS10;      // 16-Bit Timer: Vorteiler 64
13
  TCNT1 = 7937;                    // Setze Zählregister(16 Bit Timer1) - Überlauf ca. jede Sekunde
14
  TIMSK = 1<<TOIE1;
15
  sei();
16
  
17
  while(!TimeOut)
18
  {
19
    if ( (PINB & (1<<MS1_Takt)) == 0 )
20
    {
21
      Messschieber_gefunden = 1;
22
    }
23
    _delay_us(1);       // so geht es ...
24
//    PORTC |= 1<<PC1;  // und so auch ...
25
  }
26
}

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-

von Georg G. (df2au)


Lesenswert?

wie und wo ist Timeout definiert?

von Dirk M. (dmd)


Lesenswert?

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

Dirk

von Georg G. (df2au)


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.

von Dirk M. (dmd)


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.

von Karl H. (kbuchegg)


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 User
von Dirk M. (dmd)


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-

von Karl H. (kbuchegg)


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 User
von Dirk M. (dmd)


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?

von Georg G. (df2au)


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.

von Dirk M. (dmd)


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-

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.