Hallo.
Ich habe folgendes Problem:
es gibt eine simple Schleife:
1
......
2
......
3
loop:
4
sbisPINB,0x00
5
rjmploop
6
......
7
......
Die schleife wird durchgegangen bis ein Signal eintrifft.
Diese schleife muss auch sehr schnell sein, deshalb möchte ich nichts
mehr in die Schleife reinpacken.
MCU soll aber nur eine bestimmte Zeit auf das Signal warten, und wenn
es nicht ankommt, die schleife verlassen und mit ausführung des Code
fortfahren.
Hier bietet sich also an, einen Timer und Interrupt zu verwenden.
Wenn der Timer überläuft springt MCU zu der entsprechenden Routine und
kehrt genau da wieder zurück wo der Interrupt kamm.
Die schleife läuft also weiter.
Ich weiss jetzt nicht was ich in der Interrupt-Routine anstellen kann
damit nach dem die Interrupt-Routine beendet ist auch die Schleife
bendet wird, sprich rjmp loop übersprungen.
PIN am PORTB kann ich nicht setzen weil es als Eingang konfiguriert
wurde.
Ich hoffe, ich habe das Problem einigermassen verständlich
beschrieben.
Wäre dankbar für eine Lösung dieses Problems.
Eine Möglichkeit, dieses Problem zu lösen, sieht so aus:
1
......
2
......
3
clrt
4
loop:
5
tstt
6
sbisPINB,0x00
7
breqloop
8
......
9
......
Im Timer-Overflow-Interrupt-Handler mußt Du das t-Register auf einen
Wert ungleich Null setzten (z. B. 1). Dann wird die loop-Schleife
(auch) verlassen (Wirkungsweise klar?).
Die Frage, ob diese Lösung mit Deinen Geschwindigkeitsanforderungen
noch vereinbar ist, kann ich natürlich nicht beantworten.
Die Idee ist ja mal Super!
Die Befehle BRIE und BRID hab ich noch gar nicht gekannt, obwohl ich
die Datenblätter schon zichmal gelesen hab, man lernt ja nie aus.
"Und was wäre, wenn Du einen externen Interrupt nimmst?"
Du wirst lachen, Polling ist schneller als ein Interrupt (7..10 Zyklen
bis zum Eintritt).
Ich habe auch mal in einem ATTiny26 pollen müssen, weil ich nur so
schnell genug auf ein Signal reagieren konnte. Ich mußte einen CPLD
ersetzen.
Peter
Vielen Dank für Eure Antworten.
Sicher sind alle hier vorgeschlagen Lösungen wert sie zu testen um die
optimale Lösung für meine Anwendung zu finden.
Wir gesagt je länger die Schleife desto schlechter, stack würde ich
auch ungern maniplieren.
Die Lösung von Peter scheint beides zu erfüllen (kurz, keine Stack
manipulation),ich verstehe nur nicht warum da beide Befehle vorhanden
sein müssen, eins springt doch wenn Interrupt-Flag gesetzt ist, das
andere wenn nicht, oder sind das Beispiele
Ich werde aber alle Lösungen testen, der ATMega8 wird hier halt an
seine grenzen gebracht, da muss die optimale Lösung her.
Zu besseren verständnis erkläre ich einfach wozu die Schleife ist und
warum man hier ein externer Interrupt keinen Sinn macht.
Ich versuche hier so was wie ein Osilloskop zu bauen.
Das Signal das am PORTB ankommt, ist von der Trigger-Einhheit die aus
dem Eingangsignal Triggerimpulse generiert. Sie funktioniert recht
zuverlässig, aber wenn der MCU mit unterschidlicher geschwindikkeit auf
diese Impulse reagiert erhällt man kein stabieles Bild. Deshalb muss
diese Schleife möglichst kurz sein.
Wenn jedoch kein Signal am Eingang gibt, hängt MCU bei dieser Schleife,
das ist jedoch in sofern schlecht dass dann keine kommunikation mit dem
MCU möglich ist (UART-Interrupt ist hier auch problematisch weil Daten
gesendet und empfangen werden).
Es soll also nur eine bestimmte Zeit auf den Trigger-Impuls gewartet
werden. Einen externen Interupt zu verwenden macht keinen Sinn, weil
dazu bräuche man sowie so zusätzlichen externen Timer der ja eh schon
im MCU vorhanden ist, und aus sicht des Programms sich wie ein externes
Gerät verhält, läuft nebenbei und keine Rechenkapazität braucht.
Ausserdem es gibt mehrere Sampling-Frequenzen von 5kHz bis 2MHz und für
die höchsten brauch man getrennte Routinen(mindestens 3), sonst sind sie
zu langsam.
Ich bedanke mich noch mal für die Lösungsvorschläge.
@Peter: Wieder etwas dazugelernt, das wußte ich auch noch nicht.
@Martin: Ich meinte eigentlich, daß man einen der Ext-In Pins zum
Triggern nimmt. Ein externer Timer ist natürlich Unsinn, aber auch
nicht notwendig. Abgesehen davon, ist die Lösung von Peter in diesem
Fall die bessere.
Hallo,
ich suche eine Lösung für ein ähnliches Problem. Allerdings nutze ich C
als Programmiersprache.
Ich möchte über einen exdternen Interrupt (Taster) eine Schleife im
Main() beenden. Gibt es eine Möglichkeit z.B. im ISR(INT0_vect){}
anzugeben, das dann im Main() eine Schleife beendet wird? Ein einfaches
break; reicht da ja nicht aus.
Bin für jede Hilfe dankbar.
Gruss Siegfried
@ Peter Danegger
das Programmteil ist sicher ganz gut, wenn das eine Standalone Lösung
ist. Wenn noch andere Interrupts auftreten, ist das nicht mehr zu
gebrauchen. Der Threaderöffner sprach aber auch von Kommunikation und da
sind Interrupts erforderlich.
Die universellere Lösung finde ich daher mit der Manipulation des
Stacks.
Auch wenn das Thema schon alt ist, so finde ich die vorgeschlagenen
Lösungen schon beachtenswert.
mfg
Wolfram Quehl wrote:
> @ Peter Danegger>> das Programmteil ist sicher ganz gut, wenn das eine Standalone Lösung> ist. Wenn noch andere Interrupts auftreten, ist das nicht mehr zu> gebrauchen.
Warum denn nicht?
Der Interrupt wird mit RET verlassen, die Schleife wird dadurch sofort
beendet und schon kannst Du die Interupts wieder freigeben (den Timer
dann sperren oder anhalten).
Sind vielleicht 15 Zyklen unter Interrutsperre, jeder Interrupt dauert
länger.
Man könnte aber auch jedes andere Bit im SREG nehmen.
> Die universellere Lösung finde ich daher mit der Manipulation des> Stacks.
Nö.
Peter
der Timer war ja dafür da, daß die Wartezeit bis zum Ausstieg definiert
ist. Wenn vor Ablauf des Timers z.B. ein Uart Interrupt auftritt, dann
wird die Schleife vorzeitig verlassen. Das T-bit wäre sicherlich besser
geeignet, aber auch nur, wenn es nicht woanders verwendet wird.
Da ich noch Anfänger bin, muß ich mir das wohl noch mal überlegen.
mfg
Mit dem T-Flag hat es den Haken, dass es dank SREG-Sicherung nicht
ankommt. Wenn also die ISR das T-Flag verändert, merkt das Hauptprogramm
nix davon.
...
Hannes Lux wrote:
> Mit dem T-Flag hat es den Haken, dass es dank SREG-Sicherung nicht> ankommt. Wenn also die ISR das T-Flag verändert, merkt das Hauptprogramm> nix davon.
Wenn die ISR das T ändern soll, gibts nen ganzen einfachen Trick: das
SREG nicht sichern. Niemand zwingt Dich, das SREG zu sichern.
Man sichert das SREG nur, wenn die ISR irgendwo zuschlagen kann, der
Kontext also unbekannt ist.
Hier weiß aber die ISR ganz genau, in welchem Kontext sie zuschlägt und
damit darf sie auch Informationen im SREG übergeben.
Peter
Ja sicher kommt es immer auf den Kontext an, was in einem Fall
zweckmäßig ist, kann im anderen Fall unzweckmäßig sein. Ein überall
gültiges Universalrezept wird es nicht geben.
Ich bezog mich allerdings auf Wolframs Aussage, dass für diesen Zweck
das T-Flag besser geeignet sei als das I-Flag.
Wolfram: Die Nutzung des I-Flags als Timeout-Merker ist genial. Deine
Bedenken, dass andere Interrupts stören, sind unbegründet, denn alle
anderen ISRs werden ja weiterhin mit RETI beendet. Nur der
Timeout-Interrupt endet mit RET und wirft damit das Programm aus der
Warteschleife. Sorry, das ist mir beim ersten Lesen noch gar nicht
aufgefallen, erst jetzt, wo ich drüber nachdenke.
...
ja, das war mir auch nicht gleich aufgefallen, aber dann müßte das
I-Flag wieder zurückgesetzt werden, wenn die Schleife verlassen wurde.
Sonst könnten keine anderen Interrupts mehr auslösen. Wenn also hinter
Timeout: noch SEI stehen würde, dann wäre es vielleicht klarer gewesen.
mfg
Hallo,
Wozu eigentlich muss man für so etwas eine ISR benutzen? Wenn man im
Hauptprogramm nur ein Flag pollt kann man doch auch gleich das
entsprechende Ereignissflag im Interrupt Flag Register benutzen.
Dann spart man die Verzögerung durch die ISR.
Thomas
>Wozu eigentlich muss man für so etwas eine ISR benutzen? Wenn man im>Hauptprogramm nur ein Flag pollt kann man doch auch gleich das>entsprechende Ereignissflag im Interrupt Flag Register benutzen.>Dann spart man die Verzögerung durch die ISR.
Die Verzögerung durch die ISR ist Nebensache, weil sie ja nur im
Timeout-Fall wirksam wird. Wichtig ist, dass das Pin-Abtastintervall
möglichst kurz sein soll, und das beträgt hier lediglich 3 (!)
Taktzyklen. Denkt man ein wenig nach, erkennt man, dass es keinen Code
gibt, mit dem sich 3 Taktzyklen (oder gar noch weniger) wesentlich
anders erreichen ließen.
@ Thomas J
wenn das Interrupt Flag gesetzt wird, muß ein Interrupt ausgelöst werden
und dann geht es in die ISR. Ich wüßte nicht, wie man das verhindern
kann.
mfg
das habe ich jetzt verstanden.
zur prakt. Anwendung:
es wird ein Timer benötigt, der vor Aufruf des Programms initialisiert
bzw. auf einen definierten Zustand gesetzt werden muß, um eine bestimmte
Timeout Zeit zu bekommen. Zusätzlich besteht die Einschränkung, daß die
Timeout Zeit nicht länger sein kann als die Timerzeit. Mehrmaliges
Durchlaufen des Timers ist nicht möglich. Das kann bei längeren Pulsen
am Eingang zum Problem werden.
Nun habe ich als weiteren Gedankengang gehabt, daß statt brie ein
Registerbit abgefragt wird, das z.B. von der sowieso vorhandenen Uhr in
der Uhr-ISR gesetzt wird. Dieses einfache Setzen reicht jedoch nicht
aus, weil beim Aufruf des Pin-abfrageprogramms gerade das Registerbit
gesetzt worden sein kann. Dann wäre die Zeit zu kurz.
Abhilfe durch Differenzbildung: Sek. oder Überlaufzähler lesen, Zeit
addieren und in der Uhr als Abfrage zum Setzen des Bits verwenden.
Ob mir das jetzt was nutzt, weiß ich noch nicht. Schließlich soll der MC
nicht warten, sondern in der Timeout Zeit andere Aufgaben ausführen. Und
dann wären wir wieder in der Nähe der Jobflags.
Aber als Idee auf jeden Fall anerkennenswert.
mfg
Kann mir mal bitte einer erklären was schlecht daran sein soll den Stack
zu manipulieren. Sicher es ist keine Standardvorgehen, aber beim
Assembler programmieren muss man sowieso auf alles und jeden aufpassen,
da kommt es darauf auch nicht mehr an, es ist eben noch eine Stufe
Tiefer als die normalen Assembler Mnemonics, aber ich kann da nichts
schlechtes drin entdecken, klar muss man aufpassen das nacher kein Müll
im Stack landet, aber wie gesagt aufpassen muss man sowieso, und auf
die weise lernt und versteht man auch gleich was bei einem Interrupt und
einem RET wirklich im hintergrund abläuft, ich finde eigentlich die
Lösung mit dem Stack sogar sehr elegant. -wiebel
Michael Waiblinger wrote:
> aber ich kann da nichts> schlechtes drin entdecken
Du hast nen bedingten Sprung ohne Sprungziel (Label) !!!
Und wenn Du dann nach 2 Jahren mal wieder auf den Code schaust, beginnt
das große Kopfkratzen. Du änderst was am Code und wunderst Dich, warum
was ganz anderes ausgeführt wird.
Auch Assemblerprogramme darf man strukturiert und lesbar schreiben.
Außerdem brauchst Du einen zusätzlichen Call, um dahinter zu springen,
also 2 Bytes mehr Stackverbrauch.
Peter
das mit dem Stack halte ich fast für genauso gut wie Peters Code. Nur
das Bessere schlägt das Gute. Bei der Stackmanipulation wird etwas mehr
Code und Zeit verbraucht. Ist nicht erheblich, aber es bringt keinen
Vorteil gegenüber Peters Code.
Und die Nachteile im prakt. Teil sind da auch nicht besser. Entweder
extra Timer oder man ist auf bestimmte Zeiten eingegrenzt.
mfg
Peter Dannegger wrote:
> Michael Waiblinger wrote:>>> aber ich kann da nichts>> schlechtes drin entdecken>> Du hast nen bedingten Sprung ohne Sprungziel (Label) !!!
Wenn ich das Beispiel von Marco ganz oben nehme hab ich sehr wohl ein
Label.
>> Und wenn Du dann nach 2 Jahren mal wieder auf den Code schaust, beginnt> das große Kopfkratzen. Du änderst was am Code und wunderst Dich, warum> was ganz anderes ausgeführt wird.>> Auch Assemblerprogramme darf man strukturiert und lesbar schreiben.
Ich finde nicht das die Lesbarkeit darunter leidet, selbstverständlich
muss man sich darüber bewusst sein, dass man sowas verwendet hat, aber
ich finde das kein bisschen unlesbarer als viele andere Sachen in
Assembler.
>> Außerdem brauchst Du einen zusätzlichen Call, um dahinter zu springen,> also 2 Bytes mehr Stackverbrauch.
Das verstehe ich nicht, ich hole die 2 Byte raus und stopfe zwei neue
rein, klar wenn ich die einfach dazustopfe wird das nix, aber das
erfüllt ja auch dann nichtmehr den Zweck.
Ich finde wenn es funktioniert isses gut, wenn nicht nicht. Assembler
nach zwei Jahren zu verstehen ist so oder so knifflig wenn man das gut
kommentiert geht das doch.
Wenn ich einen echten Grund höre warum das schlecht ist, seh ich das ja
gerne ein, aber so klingt das ähnlich wie das "goto ist böse" Dogma,
dass heute auch keiner mehr richtig begründen kann.
Ja ja, is schon ne Weile her, ich hab den Code nicht nochmal angesehen.
Ich dachte, er popt einfach nur die Returnadresse weg und landet dann
beim Caller (wäre ja auch ne Möglichkeit).
Peter
Peter Dannegger wrote:
> Ich dachte, er popt einfach nur die Returnadresse weg und landet dann> beim Caller (wäre ja auch ne Möglichkeit).
Ahh, ok verstehe, das wäre auch sehr flott, da muss ich dann aber auch
gestehen, dass sowas dann in der tat dringend kommentierungspflichtig
ist. Find ich aber eigentlich auch wieder elegant, wenn auch tatsächlich
sehr schwer zu beherrschen. -wiebel
> Hallo,> Wozu eigentlich muss man für so etwas eine ISR benutzen? Wenn man im> Hauptprogramm nur ein Flag pollt kann man doch auch gleich das> entsprechende Ereignissflag im Interrupt Flag Register benutzen.> Dann spart man die Verzögerung durch die ISR.> Thomas
Bis zu diesem Post musste ich lesen bis einer genau meiner Meinung war,
wieso nicht:
Timer = Wert;
TF = 0;
while(!TF); // warten bis Timer ueberlaueft
ist die schnellst Variante. Warum zum Teufel soll man in die Isr
springen und dort eine Variable verändern, dann zurückt, dann erst aus
der Schleife springen - ist völliger Unsinn. Den Timer Interrupt
deaktivieren, dann wird auch die Isr nicht aufgerufen, das Timer Flag
wird aber trotzdem gesetzt.
bcf TimerFlag
loop: btfss TimerFlag
goto loop