Gibt es eine Möglichkeit (Routine, C Befehle, Syntaxe....) einen
Funktionsablauf nach Rückkehr aus einer ISR sofort zu beenden?
Konkret: Ich sende in einer Funktion (nicht main) in einer while
schleife jede Sekunde ein kleines Datenpaket per UART raus. Nun soll
dieses Senden aber sofort beendet werden, wenn ein bestimmtes Byte
empfangen wird. Daüfr habe ich natürlich eine UART ISR angelegt die
aufgerufen wird sobald Daten empfangen werden. (Das funktioniert auch
alles). In dieser ISR prüfe ich dann ob dieses empfangene Byte einen
bestimmten Wert hat und dann will ich das Senden einstellen.
Möglichkeiten die mir einfallen aber unpraktisch sind:
1. in der Sende While schleife nach jedem!!! Sendebefehle (UCA0TXBUF
usw..) eine andere Funktion aufrufen und überprüfen ob letztes
empfangenes Byte eben dem Abbruchkriterium entsprich. Wenn ja dann mit
break schleife verlassen. Diese Möglichkeit finde ich doof, weil mich
dieser unnötige ständige Funktionsaufruf stört.
2. Die SendeFunktion derart veränder das zb. vorher immer geprüft wird
ob a==1. In der ISR bei wahrer Abbruchbedingung einfach a=1 setzen und
die Sendefunktion erneut aufrufen (oder zb. mit Übergabewert). Dann in
der Sendefunktion entsprechend handeln. Diese Variante wird
wahrschenlich funktionieren ABER ich bekome Bauchschmerzen, weil ich
damit den ganzen Stack voll lege. Denn dann befindet sich die ISR und
die Sendefunktion auf dem Stack (bzw. die Sprungadressen). Wenn ich dan
irgendwann die Sendefunktion beende, wird aufeinmal wieder die ISR zu
Ende geführt bzw. es erfolgt ein Senden (weil quasi die Daten ja noch
nicht alle rausgeschickt wurden... es wurde ja abgebrochen...) Das ist
also auch doof.
Ich suche nach einer Möglichkeit wie ich allgemein gesagt, einer
Funktion nach Wiedereinritt aus einer anderen mitteile dass sie nun
beendet werden soll. Da die ISR aber eben nicht durch Software sondern
Hardware aufgerufen wird kann ich nicht einfach sows machen:
Sendefunktion (void)
{
//sende daten..
...
if(Empfangs_ISR()==0) break;
}
Auch kann ich der ISR ja keinen Rückgabewert erteilen oder?? (Sie ist ja
immer void). Ist ja auch logisch, denn wenn das Programm aus der ISR
wiederkommt, kann es mit einem Rückgabewert nichts anfangen. (es gibt ja
kein if(ISR()==0).. oder a=ISR()
Erledigen vielleicht CallBack Funktionen sowas? Hört sich zumindestens
so an.
Wäre über ein par Gedanken sehr dankbar...
Martin schrieb:
> 1. in der Sende While schleife nach jedem!!! Sendebefehle (UCA0TXBUF> usw..) eine andere Funktion aufrufen und überprüfen ob letztes> empfangenes Byte eben dem Abbruchkriterium entsprich. Wenn ja dann mit> break schleife verlassen. Diese Möglichkeit finde ich doof, weil mich> dieser unnötige ständige Funktionsaufruf stört.
Warum sollten Aufrufe stören?
Die CPU führt ständig Aufrufe aus, von Einschalten der VCC bis zum
Abschalten.
Das ist ihr Job.
Das ist also der ganz normale Weg:
Du sendest ein Byte und dann rufst Du ne Funktion auf, die Dir sagt, ob
Du das nächste senden sollst.
Peter
hmm ist das nen µC?
falls ja hat der doch bestimmt nen interrupt wenn ein byte fertig
gesendet wurde? damit wird dann eine subroutine aufgerufen die prüft ob
noch weitere bytes gesendet werden sollen oder ob der ganze string "weg"
ist. da kann man doch einfach ein flag einbauen, daß er abbricht und den
rest vom string verwirft wenn dieses flag gesetzt ist...
Oh danke schonmal
@Ben
Das ist eine gute Idee, mit dem TXD Interrupt hab ich bis jetzt nicht
gearbeitet. Wers mal versuchen. Verstehe ich sas richtig? Sobald das
Byte das Shiftregister verlassen hat, wird dann bei richtiger
Initialisrung das TXD Flag gesetzt und ISR ausgeührt? Also analog zu dem
von mir verwendetem RXD Flag....
@Peter
Ja klar ist es der Job des uC, aber ich will ja nicht das er anfängt zu
schwitzen und dann noch von seinen eigentlichen Aufgaben abkommt. Wenn
ichs aber recht überlege, kann ich mir eine kleine Überprüfung bei 9600
Baud wohl erlauben...
@MeinerEiner
Meinst du mit Flag einfach ne globale Variable?
Danke für eure Antworten. Ihr habt mir sehr gute Lösungsansätze gegeben
ich muß gestehen ich hab mit rs-232 noch nicht allzu viel sinnvolles
gemacht. ich wollte nur wissen ob mein marke-eigenbau-testboard das
kann. ich fand diesen interrupt die einfachere lösung weil mir der AVR
damit von ganz alleine sagt wann er mit der übertragung von einem byte
fertig ist und ich ihn nicht dauernd fragen brauch. wenn du jetzt weiter
senden willst schreibst du das nächste byte in das entsprechende
register und startest den transfer wieder. der nächste interrupt kommt
wieder wenn das weg ist usw usw usw. wenn du aber keinen neuen transfer
startest kommt auch kein neuer interrupt.
du brauchst dir also nur ein flag im ram oder so setzen und das in der
interruptroutine abfragen. ist es gesetzt verlässt du die routine
einfach und das senden ist beendet weil keine neuen interrupts kommen.
wenn du dann wieder was senden willst rufst du ja irgendeine funktion
auf, die die startposition usw. setzt. die löscht dann auch das flag und
startet den transfer des ersten bytes, der rest läuft von ganz alleine.
@Ben
Ja versteh schon prinzipiell. Ich soll also wenn ich weiter senden
möchte in der TXD-ISR die Sendefunktion aufrufen. Damit würde ich doch
aber die ISR nie richtig beenden sondern immer wieder über den
Funktionsaufruf verlassen...
Martin schrieb:
> @Ben>> Ja versteh schon prinzipiell. Ich soll also wenn ich weiter senden> möchte in der TXD-ISR die Sendefunktion aufrufen. Damit würde ich doch> aber die ISR nie richtig beenden sondern immer wieder über den> Funktionsaufruf verlassen...
Äh nein.
Die Funktion kommt zurück zur ISR und die ISR steigt ganz normal aus.
Also das versteh ich nicht. Wo wird deiner Ausführung nach, nun die
Sendefunktion aufgerufen?
a) In der ISR
b) garnicht, sie wird nur unterbrochen während ISR nach Senden eines
Bytes aufgerufen wird
Du meinst also das die ISR sozusagen akiv ein weiteres Senden
verhindert,(indem Flag überprüft wird) jedoch nicht aktiv selbst zum
Senden führt. Denn das macht die Sendefunktion alleine.
wofür steht bei dir ISR genau?
durch den interrupt startet sich die funktion solange immer wieder
selber bis sie keinen weiteren transfer startet. damit wird auch kein
neuer interrupt ausgelöst und die funktion nicht länger neu gestartet.
bei meinem test war das so gelöst, daß die zu übertragenden zeichen in
einem puffer im RAM standen und deren anzahl bekannt war, zum starten
wurde einfach der positionszeiger auf anfang gesetzt und der transfer
des ersten zeichens gestartet. das wars, den rest macht der AVR von
selber. du kannst dich also um andere dinge kümmern ohne den transfer
weiter kontrollieren zu müssen. auch eine abfrage ob fertig ist
jederzeit machbar, da wird einfach der positionszeiger mit der anzahl
der zu übertragenden zeichen verglichen. sind beide identisch ist die
übertragung beendet.
die interruptroutine wurde durch den transfer-beendet-interrupt
aufgerufen, hat kontrolliert ob noch weitere zeichen zu senden sind,
wenn ja wurde der positionszeiger inkrementiert, das nächste zeichen
geladen und ein neuer transfer gestartet, wenn nicht wurde die routine
einfach ohne weiteres wieder verlassen.
oder was willst du wissen?
Na endlich verstehen wir uns. ;)
ISR = Interrupt Service Routine
Fast genau den selben Ablauf habe ich schon implementiert. Nur eben
nicht in der ISR sondern in einer eigenen Funktion. Ich arbeite mit dem
MSP da geht der beschriebene Ablauf ebenso einfach.
Wie die Routine aufgerufen wird ist klar. Nämlich durch einen Hardware
Interrupt. Und sie staret sich immer wieder selbst. Die Daten werden
also aus der ISR herraus gesendet und dann startet wieder die ISR.
Das werde ich zwar etwas anders machen, aber das Problem mit dem
Sendeabbruch lässt sich so lösen, denke ich.
Es bleibt aber immer noch der Fakt das in deinem Fall, die ISR nicht
ordentlich beendet wird. Denn bevor sie zu Ende ist, wird sie erneut
aufgerufen.... Das heißt es wird der Stack zugemöhlt.
quatsch.
die sog. "ISR" wird natürlich ordentlich beendet. wenn der transfer des
nächsten bytes angestubst wurde holt man alle verwendeten datenregister
und statusregister vom stack und am ende gibts nen schicken iret.
Was is hier quatsch. Wenn du mal etwas genauer beschreiben würdest,
würde wir auch nicht immer an einander vorbeireden.
Wenn du innerhalb der ISR erneut die ISR aufrufen lässt (durch den
Interrupt), dann läuft die Funktion nicht "normal" durch bis ans Ende,
sondern wird früher beendet. Erst wenn diese richtig beendet wird,
nämlich dann wenn kein neuer Interrupt kommt wird der Stack leer
geräumt. Wenn der Sendebefehl der letzte in der ISR ist dann wird
natürlich nicht viel passieren außer etliche mal ein "schließen" der
ISR.
Und was ist ein iret
iret = interrupt return
die routine kann sich gar nicht so einfach selbst starten, sie startet
sich indirekt über den interrupt wieder wenn der nächste transfer
beendet ist. bis dahin ist sie aber bereits laaaaaaaaange wieder
beendet, da der uart deutlich langsamer ist als die cpu.
Ja das macht Sinn.
Letzte Frage: Wie startest du den Sendevorgang, wenn noch garkeine Daten
empfangen wurden. Das erste Zeichen muss also von außerhalb gesendet
werden damit der Zyklus ersteinmal beginnt...?
das erste zeichen muß das hauptprogramm bzw. eine subroutine senden,
sonst kann natürlich kein interrupt ausgelöst werden. aber mit diesem
einen zeichen senden ist der gesamte sendevorgang für das hauptprogramm
abgeschlossen, es braucht sich danach um nichts weiter zu kümmern.