Hallo ertsmal an alle hier ;)
also ich habe gerade mit Mikrocontrollern angefangen also verzeiht mir
wenn die Frage etwas "doof" ist.
Ich bin gerade dabei mich mit externen Interrupts zu befassen jedoch
habe ich ein komisches Problem und zwar habe ich zwei LEDs, eine davon
blinkt durchgehend die andere soll beim auslösen des Interrupts einmal
blinken.
Soweit so gut jedoch bleibt das Programm am Ende der Interruptroutine
hängen.
Das komische daran ist das dies nur passiert wenn ein "mdelay" im code
des Interrupts oder in der Hauptschleife enthalten ist.
Hier mal mein Code (ist in Delphi mit AVRco geschrieben):
Ohne jetzt Basic zu verstehen, aber ein PRoblem dürfte sein, dass du den
PORTB im main und im Interrupt vollständig beschreibst. Du solltest nur
das eine Bit für die LED beschreiben.
In C würd ich das so machen:
statt:
PortB := %00000010;
mDelay (200);
PortB := %00000000;
mDelay (200);
wird
PORTB |= (1<<PB1);
mDelay(..);
PORTB &= ~(1<<PB1);
und im Int nimmst du (..PB0).
Aber wie das im Basic ist...
Lass mal das
DisableInts
und
EnableInts
aus der Interrupt Routine raus. Beim AVR sind die Interrupts automatisch
während der Abarbeitung einer Interrupt Routine gesperrt und das
solltest du auch berücksichtigen. Das vorzeitige Freigeben der
Interrupts während gerade eine ISR bearbeitet wird, ist problematisch,
weil du unter Umständen in einer Endlosrekursion landest.
Und in einer ISR zu warten ist generell eine schlechte Idee (sofern das
warten länger als ein paar µs dauert).
Die einfache Zuweisung an den Port ist ebenfalls problematisch, weil du
damit auch die andere LED beeinflusst.
PortB := 0;
setzt nun mal alle Portpins auf 0 und nicht nur den einen, an dem
deine LED hängt, die blinken soll.
Ich hoffe mal du findest hier noch jemanden der besser Delphi, speziell
auf dem AVR, kann als ich. Viele werden es nicht sein.
Danke erstmal für die Antworten.
Das mit den DisableInts und EnableInts habe ich getestet jedoch ohne
Erfolg.
Wenn man die Interruptroutine so ändert funtioniert alles:
1
Interrupt Int0;
2
begin
3
DisableInts;
4
PortB := 00000001;
5
EnableInts;
6
end;
Jedoch kann ich mir nicht erlären warum ein normales delay sowas
auslöst.
PS: Es funktioniert ja auch wenn man die Delays nur in der Hauptschleife
entfernt.
MfG
>Ich hoffe mal du findest hier noch jemanden der besser Delphi, speziell>auf dem AVR, kann als ich. Viele werden es nicht sein.
Gibts irgendwo auch Fortran für AVR? SCNR;)
holger schrieb:> Fortran für AVR
Es gibt Forth. Das kann man mit einem speziellen Bootloader drauf
klatschen und dann über die Serielle arbeiten.
mfg mf
PS: Ich tu mir das nicht an.
Mir ist gerade etwas eingefallen und ich wollte mal hören ob das
wirklich der Auslöser sein könnte und zwar wird der Interrupt durch
einen Taster ausgelöst.
Diese Taster ist jedoch nicht entprellt, könnte es sein der der
Interrupt dadurch "zu oft" in sehr kurzer Zeit aufgerufen wird und das
Hängen daduch verursacht wird ?
Aber auf der anderen Seite sollten weitere Interrupts ja am Anfang der
Interroptroutine deaktivirt werden.
Naja vill. hat ja noch jemand ne Idee und falls es wirklich mit den
Delays zu tun hat dann ist das ja Programmiersprachenunabhängig denn
Delays gibt es ja nicht nur in Delphi ;)
MfG.
Kai J, schrieb:> Diese Taster ist jedoch nicht entprellt, könnte es sein der der> Interrupt dadurch "zu oft" in sehr kurzer Zeit aufgerufen wird und das> Hängen daduch verursacht wird ?
Nein.
Deine BLinksequenz wird kräftig durcheinander kommen, aber hängen wird
er nicht.
Wie kommst du überhaupt darauf, dass er hängt?
(Was ich mir allerdings vorstellen kann: das die Funktion mdelay nicht
reentrant ist. D.h. das ein Aufruf der Funktion mdelay aus dem Interrupt
heraus nicht so gut ist, wenn das Hauptprogramm ebenfalls gerade im
mdelay ist.)
> Aber auf der anderen Seite sollten weitere Interrupts ja am Anfang der> Interroptroutine deaktivirt werden.
Noch mal:
Lass das weg. Du schiesst dir nur ins eigene Knie, wenn du innerhalb
einer ISR selber die Interrupts wieder freigibst. Der AVR hat eine
spezielle Return Instruktion, die das macht und die vom Compiler ans
Ende der Interrupt Routine gesetzt wird.
> Delays zu tun hat dann ist das ja Programmiersprachenunabhängig denn> Delays gibt es ja nicht nur in Delphi ;)
Die Frage ist: wie ist die Funktionalität von mdelay implementiert? Ist
die Prozedur reentrant oder nicht.
Hi
Ich weiß nicht, wer mit Delphi BAsic verbindet... das ist PASCAL.
Leider sibd meine Erfahrungen mit PASCAL und µC nicht besonders
erfolgreich und so bin ich bei Assembler geblieben. Daher kann ich dir
hier auch nicht weiterhelfen, jedenfalls nicht mit Code....
Gruß oldmax
Unabhängig von Deinem Problem: Hast Du da oben die Prozentzeichen vor
den Ziffernkolonnen vergessen? Ich kann zwar kein Delphi, konnte aber
aus dem Kontext herauslesen, dass Binärzahlen offenbar mit einem
Prozentzeichen gekennzeichnet werden. Da oben stehen aber Dezimalzahhlen
(in C wären es Oktalzahlen). Du hast Glück: 00000001 und 00000000 haben
in beiden Zahlensystemen denselben Wert. Trotzdem würde ich diese
Unschönheit korrigieren. Wechselst Du später den Pin, hättest Du hier
einen dicken Fehler ("warum klappt das nur an PB0?").
Zu Deinem Problem: Da kann ich nur Karl Heinz zustimmen: Innerhalb von
Interrupt-Routinen Finger weg von DisableInts und EnableInts. Damit
bringst Du den µC nur durcheinander. Innerhalb von Interrupts sind
weitere Interrupts automatisch gesperrt. Du rennst daher mit beiden
Aufrufen offene Türen ein, wobei der Schuss nach hinten losgeht.
Wie auch schon Karl Heinz sagte: es könnte tatsächlich so sein, dass
mdelay nicht reentrant ist. Ich selbst tippe auch darauf. Deshalb: Nicht
machen.
Gruß,
Frank
Kai J, schrieb:> Naja vill. hat ja noch jemand ne Idee und falls es wirklich mit den> Delays zu tun hat dann ist das ja Programmiersprachenunabhängig denn> Delays gibt es ja nicht nur in Delphi ;)>> MfG.
Es handelt sich um einen Bug in diesem Compiler: mDelay wird als
Funktion in den Code eingebunden. Wenn der Interrupt während eines
mDelay passiert, dann wird mDelay noch einmal mit neuen Werten
aufgerufen. mDelay selbst sichert keine Register, verändert aber so
einige davon, und die Interruptroutine "vergisst", r20/r21
(_ACCDLO,_ACCDHI) wiederherzustellen. Damit kommt der Delay in der
Hauptroutine kräftig durcheinander. Warte ein gehöriges Weilchen, und es
fängt wieder an zu blinken. Ist wenigstens Deine ProcClock richtig
eingestellt?
Lasse DisableInts und EnableInts auf jeden Fall weg! Dadurch werden
nämlich Interrupts freigegeben, bevor der Interrupt den Stack aufräumen
kann. Soooviel Stack hast Du in einem AVR nicht. Das Sperren und
Freigeben der Interrupts geschieht automatisch.
BTW: Delphi ist ein Pascal-Dialekt von der Firma Borland. Es ist
eleganter - und wie Du siehst weniger verwirrend -, wenn Du hier
schlicht von Pascal redest.
viele grüße
ralph
Frank M. schrieb:>> Unabhängig von Deinem Problem: Hast Du da oben die Prozentzeichen vor> den Ziffernkolonnen vergessen? Ich kann zwar kein Delphi, konnte aber> aus dem Kontext herauslesen, dass Binärzahlen offenbar mit einem> Prozentzeichen gekennzeichnet werden.
Ja stimmt, hab ich wohl beim ganzen rumprobieren übersehen, macht aber
leider keinen Unterschied.
@ Ralph
du hast den nagel aufn Kopf getroffen ;). Es ist wirklich so das wenn
man einige zeit wartet das dann die Hauptschleife wieder normal
funktioniert.
Jetzt bräuchte ich nur noch ein Tipp oder eine Lösung wie ichs trotzdem
zum laufen bekomme.
PS: Von den DisableInts und EnableInts hab ich mich verabschiedet.
MfG.
Kai J, schrieb:> Jetzt bräuchte ich nur noch ein Tipp oder eine Lösung wie ichs trotzdem> zum laufen bekomme.
Indem du eine Grundregel beherzigst:
In einer Interrupt Funktion wird nicht gewartet!
Überhaupt solltest du dir Funktionen wie mDelay wieder abschminken. Sie
sind für Einsteiger verführerisch, sind aber der falsche Ansatz. Sie
führen dazu, dass Programme lahm und unhandlich werden. Insbesondere
wenn sie scheinbar mehrere Dinge gleichzeitig machen sollen, wie bei dir
eben mehrere LED in unterschiedlichen Zeiten blinken zu lassen. Das bei
dir die 2.te LED von einem Taster angestossen wird, ist dazu erst mal
unerheblich.
Was du brauchst:
Du brauchst einen Timer als Zeitbasis. Das ist eine der Grundtechniken
in allen Programmen, die irgendetwas mit Zeitsteuerung zu tun haben.
Selbst dann, wenn es nur so etwas scheinbar banales wie das Blinken von
2 LEDs ist. An diesen Timer koppelst du eine weitere Interrupt Funktion,
die zb beim Timerüberlauf aufgerufen wird. Damit hast du deine
Zeitbasis, es ist der regelmässige Aufruf eben dieser Funktion. Das muss
dir genügen.
Man kann ja zb die Anzahl der Aufrufe mitzählen und nur wenn dieser
Zähler bestimmte Werte hat, eine Aktion (bei dir das Umschalten der LED)
ausführen.
Hi
Pascal ist voll mit Stolpersteinen für einen Anfänger und ich möchte
behaupten, Assembler ist da doch eher zu empfehlen. Vermutlich ist der
Ansatz: "ich kann Delphi, also auch AVRco...."
Delphi ist objektorientiert und ereignisgesteuert... also, da ist ein µC
doch weit weg von. Na ja, eine Ereignissteuerung mit Timer oder einem
anderen Interrupt, das geht bestenfalls noch. Aber auch hier ist die
Regel, der Interrupt erledigt nur das Notwendigste. Nimm dir deine
Timer- ISR mal vor:
1
Interrupt Int0;
2
begin
3
DisableInts;
4
PortB := 00000001;
5
mDelay (150);
6
PortB := 00000000; //HIER "HÄNGT" ER SICH AUF
7
EnableInts;
8
end;
Das mit Dis- und Enable ist bereits besprochen. Delais sind auch
ausreichend behandelt, was bleibt ist die Portzuweisung.. reicht da
nicht einfach nur das setzen eines Bits, welches von der Hauptschleife
abgefragt wird.. sozusagen ein Flag.
1
Interrupt Int0;
2
begin
3
Flag:=true;
4
end;
Nun kann in der Programmschleife dieses Flag behandelt werden:
1
...
2
If Flag then
3
Begin
4
If LED true then LED :=false else LED:=True;
5
Flag:=False;
6
end;
7
Set_Out(LED);
8
...
Wobei hier eine weitere Procedure hinzugekommen ist, die die Ausgabe
erledigt. Den Timer würde ich so initialisieren, das er im mSek.-Takt
aufgerufen wird. Dann kannst du Bspw. durch Zählen der mSek. die
gewünschte Zeit einstellen....
etwa so:
1
Interrupt Int0;
2
begin
3
Inc(mSek)
4
if mSek=1000 then
5
begin
6
mSek:=0;
7
Flag:=true; { hier fügst du nun das gewünschte Zeitereignis ein}
8
Inc(Sek);
9
If Sek=60 then
10
begin
11
Inc(Min);
12
.....
13
end;
14
end;
15
end;
Wie du siehst, ist es nun einfach, eine LED blinken zu lassen oder
andere Zeitabhängige Programmaufgaben aufzurufen. Zur Not mußt du halt
die Zähler etwas feiner einstellen.
Gruß oldmax