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
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.
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.
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
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?!
kleriker schrieb: > Ich denke mal fast es liegt am AVR Studio?! nein es liegt an deiner ersten while schleife , die ist endlos raik
> 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?
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.
> 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.
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.
> 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.
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-
TimeOut habe ich als globale Variable vor 'main' definiert. uint8_t TimeOut = 0; Dirk
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.
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 interessehalber: 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.
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
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-
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
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?
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.