PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit Interrupts



scrawl
29.10.2009, 19:07
Hallo,

Ich hab es endlich hingekriegt meinen Asuro über einen Interrupt anhalten zu lassen, wenn er kollidiert (ein Schalter gedrückt wird). Der Code sieht so aus:


#include "asuro.h"
#include "funktionen.h"

int main(void)

{
// Initialisierung
Init();
sei();
StartSwitch();

// hier kommt der Hauptteil des Programms
StatusLED(RED);
MotorSpeed(120, 120);

while(1);
return 0;

}
Das funktioniert auch. Obwohl er schon in der Endlosschleife ist, reagiert er trotzdem noch auf ein Hindernis. ABER, das geht nur bei Endlosschleifen! Wenn ich den MotorSpeed durch Beschleunige(120, 140, 5); ersetze, geht es nicht mehr. Meine Beschleunige-Funktion sieht so aus:

void Beschleunige(int start, int end, int zeit)
{
int momentan;
int durchlaeufe = end - start;
float zeitschritt = (float) zeit / (float) durchlaeufe * 1000;

for(momentan=start; momentan<end; momentan++)
{
Geschwindigkeit(momentan, momentan);
Warte(zeitschritt);
}
}
(Warte ist bei mir nichts anderes als Msleep)

Eigentlich tut er also dabei die meiste Zeit "Sleep". Nur das komische ist, wenn ich das Sleep im Hauptteil einfüge, funktioniert es
:-s

#include "asuro.h"
#include "funktionen.h"

int main(void)

{
// Initialisierung
Init();
sei();
StartSwitch();

// hier kommt der Hauptteil des Programms
StatusLED(RED);
Warte(90000);

while(1);
return 0;

}

Ich will doch nur, dass der Asuro auch beim beschleunigen Hindernisse erkennt :( Kann mir bitte jemand helfen?

Hubert.G
29.10.2009, 19:43
Wenn dein Programm einmal in der Endlosschleife ist, was soll es dann machen?
Da kann in deinem Hauptteil stehen was will.
Wo ist deine Interrupt-Funktion?

scrawl
29.10.2009, 20:11
Wenn dein Programm einmal in der Endlosschleife ist, was soll es dann machen?
Auf Interrupts reagieren! Oder hab ich da etwas falsch verstanden?


Wo ist deine Interrupt-Funktion?
Die steht auch in der Funktionen.c


ISR(INT1_vect)
{
StopSwitch();
MotorSpeed(0, 0);
}

Hubert.G
29.10.2009, 20:21
Wohin springt das Programm wenn der Interrupt abgearbeitet ist?
Wieder in die Endlosschleife und macht genau das was es vorher gemacht hat.
Was steht in der Endlosschleife?
Es ist sehr schwer aus Programmfragmente einen Fehler herauszulesen.

scrawl
29.10.2009, 20:32
Wohin springt das Programm wenn der Interrupt abgearbeitet ist?
Wieder in die Endlosschleife und macht genau das was es vorher gemacht hat.
Du hast recht! Nach dem Abarbeiten des Interrupts, was so schnell geht, dass man es nicht merkt, fährt er direkt weiter. Ich probiere das gleich mal aus, was passiert, wenn ich in den Interrupt noch eine kleine Wartezeit einbaue.


Es ist sehr schwer aus Programmfragmente einen Fehler herauszulesen.
Sorry, ich dachte, ich geb dir lieber ein paar Ausschnitte, da das ganze etwas umfangreicher ist.

radbruch
29.10.2009, 20:36
Hallo

Mir fällt es echt schwer aus den hingeworfenen Brocken den Zusammenhang des Programms zu erkennen. Wenn du schon so eigenwilige Wege gehst, solltest du mehr Infos rauslassen.

Da ich die Switch()-Funktionen nur aus meiner 2.3er Version der Lib kenne (aber noch nie verwendet habe) ist der Einstieg etwas zäh. Für mich scheint der Fehler in der fehlenden Kommunikation der ISR mit dem Hauptprogamm zu entstehen. Beim ersten Beispiel wird die MotorSpeed() einmal gesetzt und in der ISR nach dem Tastendruck wieder gelöscht. Beim Beschleunigen wird aber immer erneut MotorSpeed() gesetzt, auch wenn die ISR aktiv war. Wie in der orginalen LIB benötigt man ein in der ISR gesetztes Flag (heißt dort switched) das in der Beschleunigungsschleife abgefragt wird und nach der ISR das Beschleunigen abbricht:


for(momentan=start; momentan<end; momentan++)
{
Geschwindigkeit(momentan, momentan);
Warte(zeitschritt);
if(switched) break;
}


Gruß

mic

Valen
29.10.2009, 20:55
Ein kleinigkeit, und warscheinlich nicht die ursache von dein problehm. Asuro könnte aber die speicherplatz falsch interpretieren:

Zeitschrit ist einer float, aber Msleep (Warte weiss ich nich) muss einer integer wert bekommen. Typ-umwandlung geht nicht automatisch meines wissen.

scrawl
29.10.2009, 21:18
Ein kleinigkeit, und warscheinlich nicht die ursache von dein problehm. Asuro könnte aber die speicherplatz falsch interpretieren:

Zeitschrit ist einer float, aber Msleep (Warte weiss ich nich) muss einer integer wert bekommen. Typ-umwandlung geht nicht automatisch meines wissen.
Warte erwartet auch einen Integer wert. Aber anscheinend geht das wohl automatisch, denn die Beschleunige-Funktion funktioniert. Er reagiert dann halt nur nicht auf Hindernisse.

Jedenfalls gehts jetzt. Der Fehler war, dass die Beschleunigen Funktion nach dem Interrupt nicht unterbrochen wird. Danke an alle für die schnellen Antworten O:)

Hubert.G
29.10.2009, 21:49
In den Interrupt sollst du keine Wartezeit setzen, dort gehört ein Flag gesetzt.
Solange du im Interrupt bist kann kein zweiter aufgerufen werden, er wartet bis zum Ende des ersten und wird erst dann aktiv.
Ich nehme aber einmal an das deine Programmstruktur nicht für eine Unterbrechung durch Interrupt geeignet ist.

scrawl
30.10.2009, 14:59
In den Interrupt sollst du keine Wartezeit setzen, dort gehört ein Flag gesetzt.
Ok, aber wie? Ich krieg es einfach nicht hin, globale Variablen zu definieren, die ich sowohl in der main.c als auch in der funktionen.c benutzen kann.

Hubert.G
30.10.2009, 18:45
Da gab es gerade einen Thread darüber: www.roboternetz.de/phpBB2/viewtopic.php?p=467705#467705

scrawl
30.10.2009, 21:29
Irgendwas mache ich immer noch falsch!

funktionen.c: In function ‘Beschleunige’:
funktionen.c:51: error: ‘abgebrochen’ undeclared (first use in this function)...

main.c


#include "asuro.h"
extern int abgebrochen;
#include "funktionen.h"

int main(void)

{
// Initialisierung
Init();
sei();
StartSwitch();

// hier kommt der Hauptteil des Programms
StatusLED(RED);
while (1)
{
/*
** Nach einem erfolgreichen Beschleunigungsvorgang soll so lange weitergefahren
** werden, wie kein Hindernis kommt. Ansonsten wird an den Anfang der Schleife
** zurückgesprungen und wieder von 0 beschleunigt.
*/
if (Beschleunige(120, 150, 3))
{
while (abgebrochen == 0);
abgebrochen = 0;
}
}

return 0;

}

funktionen.c (Auszug)


#include "asuro.h"
int Beschleunige(int start, int end, int zeit)
{
StatusLED(GREEN);
int momentan;
int durchlaeufe = end - start;
float zeitschritt = (float) zeit / (float) durchlaeufe * 1000;

for(momentan=start; momentan<end; momentan++)
{
// wenn die Geschwindigkeit von einem Interrupt verändert wurde, wird der Vorgang unterbrochen
if (abgebrochen == 1)
{
abgebrochen = 0;
return 0;
}
Geschwindigkeit(momentan, momentan);
Warte(zeitschritt);
}
return 1;
}

funktionen.h


int Beschleunige(int, int, int);

Hubert.G
30.10.2009, 21:49
Das extern int abgebrochen; gehört in die funktionen.h
In dem *.c in dem main ist, musst du dann noch int abgebrochen definieren.

Dein Stoppen und wieder beschleunigen sollte eher so aussehen

if(abgebrochen)
abgebrochen =0;
Beschleunige(120, 150, 3);
}


In deiner ISR setzt du abgebrochen auf 1.

Damit wird dein Beschleunigen nur einmal durchlaufen, oder wieder nach einer ISR.

scrawl
30.10.2009, 22:10
Achso. Gut, jetzt kompiliert er schonmal durch. Nach dem ersten Hindernis will er aber nicht mehr weiterfahren, er kommt anscheinend aus der while-if-Schleife nicht mehr raus. Meine Vermutung war, dass durch Compiler-Optimierung die Schleife in ein while(1); "umgewandelt" wird, da das abgebrochen ja normalerweise währenddessen nicht verändert werden kann, also habe ich es mit int volatile abgebrochen probiert, aber das Ergebnis ist das gleiche. Ich verzweifel hier langsam ](*,)

So sieht meine main.c jetzt aus:


#include "asuro.h"
#include "funktionen.h"

// Standardmäßig auf 1, da am Anfang von 0 beschleunigt werden soll
volatile int abgebrochen = 1;

int main(void)

{
// Initialisierung
Init();
sei();
StartSwitch();

// hier kommt der Hauptteil des Programms
StatusLED(RED);
while (1)
{
/*
** Bei einer Kollision soll wieder von 0 beschleunigt werden und nicht
** beim vorherigen Beschleunigungsvorgang weiter gemacht werden
*/
if (abgebrochen)
{
abgebrochen = 0;
Beschleunige(120, 150, 3);
}
}

return 0;

}

radbruch
31.10.2009, 08:38
Hallo

Wenn du die Variable mit 0 initialisierst und mit dem Beschleunigen startest wird der Compiler auf keinen Fall optimieren:

...
volatile int abgebrochen = 0;
...
Beschleunige(120, 150, 3);
while (1)
{
...


Fehlt hier nicht noch ein Start_Switch())

if (abgebrochen)
{
abgebrochen = 0;
Beschleunige(120, 150, 3);
}

Gruß

mic

Hubert.G
31.10.2009, 08:47
Was machst du denn in deiner ISR, fährt er kurz zurück?
Kann man mit Beschleunigen weitermachen oder braucht man noch eine Startsequenz dazu.

scrawl
31.10.2009, 11:02
@radbruch: Hab ich alles eingebaut - immer noch das gleiche Ergebnis...


Was machst du denn in deiner ISR, fährt er kurz zurück?
Kann man mit Beschleunigen weitermachen oder braucht man noch eine Startsequenz dazu.
In der ISR wird angehalten und dann 2s gewartet:


ISR(INT1_vect)
{
abgebrochen=1;
StopSwitch();
HalteAn();
Warte(2000);
}

Ich bau gleich mal ein bisschen Debug-Ausgabe ein (via LEDs, hehe)

scrawl
31.10.2009, 11:46
Habe das Programm nochmal etwas vereinfacht und mit den LEDs rausgefunden: Er kommt aus dem Interrupt gar nicht mehr raus, und zwar liegt es da an dem Warte(2000); . Im normalen Programm funktioniert die Warte funktion aber, und wenn ich das Warte in der ISR durch Sleep ersetze, bleibt er auch hängen. Wenn ich Warte bzw. Sleep da ganz rausnehme, klappt es, allerdings hält er dann bei einem Hindernis nicht mehr an bzw. fährt direkt weiter.

So sieht das ganze aus:


ISR(INT1_vect)
{
StatusLED(RED);
abgebrochen=1;
StopSwitch();
HalteAn();
Warte(2000);
StatusLED(YELLOW);
}

Die Status LED wird nicht mehr Yellow. Wenn ich das Warte rausnehme aber schon.

Hier noch die Warte Funktion


void Warte(int msek)
{
int i;
for (i=msek; i>0; i--)
{
Sleep(85);
}
}

Hubert.G
31.10.2009, 12:02
In der ISR sollst du auch nicht warten.
Ist warten überhaupt sinnvoll?
Wäre es nicht besser wenn er zurück fährt oder dreht?
Sonst in der ISR ein Flag für warten, eine eigene Funktion für Warten und dort dann das Flag für Beschleunigen setzen.

scrawl
31.10.2009, 12:22
In der ISR sollst du auch nicht warten.
Ist warten überhaupt sinnvoll?
Wäre es nicht besser wenn er zurück fährt oder dreht?

Das ganze war ja auch nur zu Testzwecken. Außerdem impliziert das zurückfahren ja auch wieder ein Warten, nämlich so lange wie er zurück fahren soll:


void FahreZurueck(int speed, int msek)
{
MotorDir(RWD, RWD);
Geschwindigkeit(speed, speed);
Warte(msek);
MotorDir(FWD, FWD);
HalteAn();
}

ISR(INT1_vect)
{
StatusLED(RED);
abgebrochen=1;
StopSwitch();
HalteAn();
FahreZurueck(120, 1000);
StatusLED(YELLOW);
}

So geht es nämlich auch nicht, er kommt aus dem "Warte" nicht mehr raus und fährt nur noch zurück.



Sonst in der ISR ein Flag für warten, eine eigene Funktion für Warten und dort dann das Flag für Beschleunigen setzen.
Das verstehe ich nicht ganz. Mit "Flag" meinst du wieder eine globale Variable, die dann in der Beschleunigen-Funktion abgefragt wird und dann wird in der Beschleunigen Funktion gewartet? Das ist doch irgendwie nicht ganz der Sinn der Sache. Ich dachte, das Behandeln der Kollision wäre Sinn der ISR?

Sorry, das ich hier so pingelig bin, aber es muss doch möglich sein, in eine ISR ein Sleep einzubauen!

Mal so nebenbei: Könnte es sein, dass mein Problem mit der Start bzw. StopSwitch zu tun hat? Was machen diese Funktionen eigentlich, das habe ich noch nicht verstanden.

Achja, hier meine vereinfachte main.c:


#include "asuro.h"
#include "funktionen.h"

volatile int abgebrochen = 0;

int main(void)

{
// Initialisierung
Init();
sei();
StartSwitch();

// hier kommt der Hauptteil des Programms
while (1)
{
StatusLED(GREEN);
Beschleunige(120, 150, 3);
}

return 0;

}

Hubert.G
31.10.2009, 14:04
Mit Flag meine ich eine globale Variable.
Wenn der Speicherplatz knapp wird verwende ich für die Flags gerne struct.
volatile struct {
unsigned char Zaehler :1; /* Zählerstand auswerten */
unsigned char Error :1; /* Wenn Error dann 0 */
unsigned char Wert_neu :1; /* neue Werte berechnen */
}flag;

Aufgerufen wird das dann z.B. mit:
if (flag.Error)

Der Sinn einer ISR ist etwas erkennen, aber nicht abzuarbeiten. Das soll nach Möglichkeit in der Endlosschleife gemacht werden.

StartSwitch aktiviert den INT1 Interrupt, sehe aber nicht wo dieser abgearbeitet wird.

Eigentlich sollte es das gleiche wie PollSwitch sein, es wird erkannt das ein Taster (Bumper) betätigt wurde, ADC wird gestartet um zu eruieren welcher es ist.

scrawl
31.10.2009, 14:43
Mit Flag meine ich eine globale Variable.
Wenn der Speicherplatz knapp wird verwende ich für die Flags gerne struct.
volatile struct {
unsigned char Zaehler :1; /* Zählerstand auswerten */
unsigned char Error :1; /* Wenn Error dann 0 */
unsigned char Wert_neu :1; /* neue Werte berechnen */
}flag;

Aufgerufen wird das dann z.B. mit:
if (flag.Error)

Der Sinn einer ISR ist etwas erkennen, aber nicht abzuarbeiten. Das soll nach Möglichkeit in der Endlosschleife gemacht werden.

Achso. Meine Idee wäre dann, das Abarbeiten in der Warte-Funktion durrchzuführen. Denn beim Beschleunigen z.B. ist er ja auch die meiste Zeit am warten, ich müsste also das Abarbeiten nur in einer Funktion machen. Müsste so gehen, oder?


StartSwitch aktiviert den INT1 Interrupt, sehe aber nicht wo dieser abgearbeitet wird.
Eigentlich sollte es das gleiche wie PollSwitch sein, es wird erkannt das ein Taster (Bumper) betätigt wurde, ADC wird gestartet um zu eruieren welcher es ist.

Wo müsste ich StartSwitch dann aufrufen? Am Ende der Interrupt-Routine? Oder nach dem Abarbeiten der Flags?

radbruch
31.10.2009, 14:56
Hallo

Die INT1-ISR ist in asuro.c direkt nach der Timer2-ISR. Der Interrupt wird über R24 (nach Spannungsteiler mit R23 und den Tastenwiderständen/C7) an PD3 ausgelöst. Es wird nur der Tastendruck erkannt, welche Taste es war muss man anschliessend selbst ermitteln.

Gruß

mic


SIGNAL (SIG_INTERRUPT1)
{
switched=1;
StopSwitch();
}

scrawl
31.10.2009, 16:03
Endlich, es funktioniert O:)

Ich habe es jetzt so gelöst:

Die Interrupt-Routine setzt das Flag motor_unterbrochen und hält ihn an. In jeder Funktion, die sich nicht sofort beendet (z.B. beschleunige) wird bei jedem Schleifendurchlauf das Flag überprüft und ggf. die Funktion beendet.

Danke an alle die mir geholfen haben O:)

Hubert.G
31.10.2009, 19:38
Der INT1 Interrupt wäre dann:
ISR(INT1_vect){
Hier kommt dann der ADC--Aufruf zu feststellen des Taster.

Das Abarbeiten in der Warte-Funktion sollte schon gehen.
Dann besteht dann auch die Möglichkeit einer weiteren ISR.

Sternthaler
01.11.2009, 02:38
Hallo scrawl und alle anderen natürlich auch.

@scrawl
Nur mal kurz, warum das Sleep(), Msleep() und auch dein Warte() nicht in einer Interruptfunktion funktionieren.
Im Asuro wird eine Interruptfunktion in der Regel mit ISR(XXX_vect) oder SIGNAL (... ) (SIGNAL ist die alte Version von ISR) bekannt gegeben. Dies hat zur Folge, dass eine ISR erst beendet werden muss bevor eine andere ISR aufgerufen wird. Dies hatte schon Hubert.G geschrieben.

Wenn du also in deiner ISR(INT1_vect) bist und dort Warte() aufrufst, wird der Timer-Interrupt nicht mehr aufgerufen. Deshalb wird die Variable count72kHz, oder count36kHz bei neuerer Lib, nicht mehr weitergezählt. Dann aber kann Sleep() und somit auch dein Warte() nie mehr beendet werden.
Das alles stimmt aber nur dann, wenn man ISR() nur mit dem XXX_vect als Parameter aufruft. ISR geht aber auch so: ISR(XXX_vect, ISR_NOBLOCK). Dann aber spielt die Priorität der Interrupts eine Rolle, ob z.B. der Timer2 den Int0 unterbrechen darf. Wenn ja, dann kann Sleep auch im Tasteninterrupt benutzt werden. (Gehts, oder gehts nicht? Kapitel "Interrupt Vectors in ATmega8" bringt die Lösung.)

Vor einiger Zeit hatte ich hier (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=341368#341368) mal aufgeführt, wie man Interrupts nutzt. (Zufällig entspricht mein Beispiel dort genau deinem hier bearbeiteten Problem.)
Ihr habt hier zwar genau die Lösung gefunden, aber vielleicht hilft dir das Geschreibsel ja zum Verständnis wenn es mal am Stück aufgeführt ist.

Gruß Sternthaler

Hubert.G
01.11.2009, 10:09
@Sternthaler
Ich hoffe du kennst auch die Probleme die bei verwenden dieses Parameter ISR(XXX_vect, ISR_NOBLOCK) auftreten können.
Eine Priorisierung wie bei z.B. 8051 gibt es bei den AVR nicht.

Für diejenigen die nicht so versiert bei der Verwendung von ISR sind.
Es werden notwendige Daten wie Variable im SRAM abgelegt.
Im AVR-Studio werden diese Daten nach dem Compilieren unter Data angezeigt.
Diese Daten werden am Beginn des SRAM abgelegt.

Die CPU legt ihre für den Ablauf notwendigen Daten am Ende des SRAM im sogenannten Stack ab.
Dazu gehören auch Daten bei Aufruf einer ISR.
Dazu gehören Rücksprungadresse und notwendige Register.
Nach beenden der ISR wird der Adresszeiger wieder zurückgestellt.
Die jenigen die schon mal Assembler programmiert habe kennen diese push und pop Spielchen.

Normalerweise kann keine ISR aufgerufen werden solange die laufende nicht beendet ist.
Wird nun durch den Parameter ISR_NOBLOCK die ISR-Sperre gelöscht,
das gleiche geschieht auch wenn man am Anfang der ISR-Routine ein (sei) schreibt,
werden bei jedem weiteren ISR-Aufruf die Daten neuerlich im Stack gespeichert.
Dies kann dazu führen das der Stack den vorhandene Datenbereich im SRAM erreicht und diesen dann überschreibt.
Das Ergebnis sind scheinbar unerklärliche Ablauffehler.

Also kein ISR_NOBLOCK und kein (sei) in einer ISR-Routine.

Sternthaler
05.11.2009, 18:13
Hallo Hubert.G,

ja, kenne ich. Deshalb hatte ich auch auf die Prio. hingewiesen. (Hoffnung: Lesen der Doku ;-))


Ahhh, noch ein 8051-Eingeweihter. Mein letzter war ein 80535, mit dem ich eine Sonnenverfolgungsmaschine zum fotografieren gebaut hatte. Hübsch in Assembler und mit Sheduler für viele, viel gleichzeitige Aufgaben.



Hier dann auch mal die Lösung für den AVR und die Prioritäten bei den Interrupts:
Nein, es wird nicht gehen, da der INT1 eine höhere Prio hat als der Timer. Und somit unterbricht der Timer nicht die auf den Timer wartende INT1-Verarbeitung. (Katze und Schwanz-Problem)

Gruß Sternthaler