Hallo! Beim Atmel ATmega* ist für das Interrupt Timing-Verhalten folgendes spezifiziert: "When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pending interrupts." "When the AVR exits from an interrupt, it will always return to the main program and execute one more instruction before any pending interrupt is served." Damit ist es leicht möglich, das Äquivalent einer "wait condition" zu implementieren, die folgenden Zeilen entsprechen einem "wait()": __enable_interrupt(); __sleep(); __disable_interrupt(); Dabei übernimmt das globale Interrupt-Flag die Rolle des Mutex, und das Auslösen eines Interrupts entspricht einem "wakeup()". Den entsprechende Code für den Cortex-M4 ist: __enable_irq(); __WFI(); __disable_irq(); Allerdings habe ich für den Cortex-M4 keine vergleichbare Interrupt Timing-Spezifikation gefunden. Wie kann man also beim Cortex-M4 garantieren, dass *) ein Interrupt, der nach __enable_irq(), aber vor __WFI() ausgelöst wird, erst dann bearbeitet wird, wenn sich der Prozessor bereits im "sleep mode" befindet? *) ein Interrupt, der nach __WFI(), aber vor __disable_irq() ausgelöst wird, erst dann bearbeitet wird, wenn das Hauptprogramm Interrupts wieder erlaubt? Oder gibt es am Cortex-M4 ganz andere Mechanismen, um Hauptprogramm und Interrupts zuverlässig zu synchronisieren? Die exklusiven load/store Befehle helfen hier jedenfalls nicht, weil sie den Prozessor nicht in den "sleep mode" versetzen. Danke & schöne Grüße, Markus
was willst du denn überhaupt genau machen, was versteht du unter "Hauptprogramm und Interrupts zuverlässig zu synchronisieren?"
Peter II schrieb: > was willst du denn überhaupt genau machen Zeitkritische Operationen (z.B. das Empfangen von Daten über eine Schnittstelle) sollen im Interrupt Handler bearbeitet werden, länger dauernde Operationen (z.B. das Bearbeiten der empfangenen Daten) im Hauptprogramm, etwa im Sinne eines FIFO-Buffers. > was versteht du unter "Hauptprogramm und Interrupts zuverlässig zu synchronisieren?" Um beim obigen Beispiel zu bleiben: immer nachdem neue Daten empfangen wurden, soll das Hauptprogramm die Gelegenheit bekommen, diese zu bearbeiten, also aus dem "sleep mode" aufgeweckt werden. Beim ATmega* funktioniert das aufgrund des speziellen Interrupt-Timings zu 100%, aber ohne dieses spezielle Verhalten wäre es möglich (wenn auch sehr unwahrscheinlich), dass neue Daten empfangen wurden, ohne dass das Hauptprogramm aufgeweckt wird. Die empfangenen Daten würden also nicht oder verspätet bearbeitet, das Programm wäre daher unzuverlässig. Schöne Grüße, Markus
wenn du aber beim Atmel: > __enable_interrupt(); > __sleep(); > __disable_interrupt(); machst und dann die Daten bearbeitest, dann kannst du es auch gleich in der ISR machen. Dann wenn die interupts disabled sind passiert sonst eh nichts weiter.
Peter II schrieb: > wenn du aber beim Atmel: > >> __enable_interrupt(); >> __sleep(); >> __disable_interrupt(); > > machst und dann die Daten bearbeitest, dann kannst du es auch gleich in > der ISR machen. Dann wenn die interupts disabled sind passiert sonst eh > nichts weiter. Ja, aber ich möchte die Bearbeitung aus dem Interrupt-Handler heraushalten, weil die möglicherweise länger dauert und dann Daten vom Interface verloren gehen könnten. Mittels DMA kann man das Problem am Cortex-M4 in manchen Situationen sicher entschärfen, aber nicht vollständig lösen. Abgesehen von dieser konkreten Aufgabe interessiert es mich grundsätzlich, wie das am Cortex-M4 gelöst ist. Das ist ja doch eine wesentlich neuere Architektur, ich kann mir nicht vorstellen, dass so etwas einfach nicht geht. Schöne Grüße, Markus
Markus Grabner schrieb: > Ja, aber ich möchte die Bearbeitung aus dem Interrupt-Handler > heraushalten, weil die möglicherweise länger dauert und dann Daten vom > Interface verloren gehen könnten. die sperrst doch die interupts - damit passiert nichts weiter! Es ist damit kein unteschied zur bearbeiten in der ISR.
Markus Grabner schrieb: > __enable_irq(); > __WFI(); > __disable_irq(); Es funktioniert genau anders herum: __disable_irq(); // Interrupts sperren ...; // testen of jetzt noch Interrupts bearbeitet werden, warten __WFI(); // alle Interrupts bearbeitet -> Sleep __enable_irq(); // neuer Interrupt -> aufwachen -> bearbeiten
Peter II schrieb: > Markus Grabner schrieb: >> Ja, aber ich möchte die Bearbeitung aus dem Interrupt-Handler >> heraushalten, weil die möglicherweise länger dauert und dann Daten vom >> Interface verloren gehen könnten. > > die sperrst doch die interupts - damit passiert nichts weiter! Es ist > damit kein unteschied zur bearbeiten in der ISR. Es gibt zwei Situationen, wo es einen Unterschied macht: *) Ein Interrupt wird zwischen __enable_irq() und __WFI() ausgelöst und vollständig bearbeitet, bevor das Hauptprogramm auf weitere Interrupts warten kann. *) Ein Interrupt wird zwischen __WFI() und __disable_irq() ausgelöst und vollständig bearbeitet, bevor das Hauptprogramm weitere Interrupts sperren kann. In beiden Fällen wird das folgende __WFI() warten, obwohl soeben ein Interrupt ausgeführt wurde, d.h. die empfangenen Daten würden nicht bearbeitet werden. Am ATmega* sind durch das spezielle Interrupt-Timing diese beiden Situationen ausgeschlossen, wie macht das der Cortex-M4? Schöne Grüße, Markus
Es mag sein, das ich Dein Problem nicht verstehe. Die nächst-naive Antwort wäre, doch einfach die WFI instruction nicht auszuführen, falls soeben ein Interrupt aufgetreten in der Interrupt-Routine behandelt wurde. Dann behandelst Du das Ergebnis im Hauptprogramm und führst dann erst WIF aus. Ich muss aber auch sagen, das das Thema im Reference Manual und User Guide ein wenig lakonisch behandelt wird.
Markus Grabner schrieb: > In beiden Fällen wird das folgende __WFI() warten, obwohl soeben ein > Interrupt ausgeführt wurde, d.h. die empfangenen Daten würden nicht > bearbeitet werden. Am ATmega* sind durch das spezielle Interrupt-Timing > diese beiden Situationen ausgeschlossen, wie macht das der Cortex-M4? wenn es zeitkritisch ist, dann spricht doch dageben es in der ISR zu machen. Damit ist das Problem doch gelöst. Was ist wenn deine Main gar nicht in Sleep steht weil sie etwas anderes macht. Dann werden deine Daten auch später verarbeitet.
Lothar schrieb: > Markus Grabner schrieb: >> __enable_irq(); >> __WFI(); >> __disable_irq(); > > Es funktioniert genau anders herum: > > __disable_irq(); // Interrupts sperren > ...; // testen of jetzt noch Interrupts bearbeitet werden, > warten > __WFI(); // alle Interrupts bearbeitet -> Sleep > __enable_irq(); // neuer Interrupt -> aufwachen -> bearbeiten Was meinst Du mit "testen of jetzt noch Interrupts bearbeitet werden"? Im Cortex-M4 User Guide steht: "On completion of an exception handler, if there is a pending exception that meets the requirements for exception entry, the stack pop is skipped and control transfers to the new exception handler." Da kommt also das Hauptprogramm dazwischen gar nicht an die Reihe. Zur WFI-Anweisung steht im User Guide: "WFI is a hint instruction that suspends execution until one of the following events occurs: *) a non-masked interrupt occurs and is taken *) an interrupt masked by PRIMASK becomes pending..." D.h. sobald bei gesperrten Interrupts das Programm nach __WFI() weitergeht, weiß man, dass ein Interrupt angefordert, aber noch nicht bearbeitet wurde. Was passiert denn, wenn man jetzt im Hauptprogramm unmittelbar hintereinander __enable_irq() __disable_irq() aufruft? Ist dann sichergestellt, dass alle noch "offenen" Interrupt-Anforderungen bearbeitet wurden? Schöne Grüße, Markus
Markus Grabner schrieb: >> Markus Grabner schrieb: >>> __enable_irq(); >>> __WFI(); >>> __disable_irq(); >> >> Es funktioniert genau anders herum: >> >> __disable_irq(); // Interrupts sperren >> ...; // testen of jetzt noch Interrupts bearbeitet werden, >> warten >> __WFI(); // alle Interrupts bearbeitet -> Sleep >> __enable_irq(); // neuer Interrupt -> aufwachen -> bearbeiten > > Was meinst Du mit "testen of jetzt noch Interrupts bearbeitet werden"? > Im Cortex-M4 User Guide steht: > > "On completion of an exception handler, if there is a pending exception > that meets the requirements for exception entry, the stack pop is > skipped and control transfers to the new exception handler." > > Da kommt also das Hauptprogramm dazwischen gar nicht an die Reihe. __disable_irq() = CPSID I bewirkt das kein Interrupt Handler mehr gestartet werden kann. Sobald also der aktuelle Handler beendet ist, kommt das Hauptprogramm, und damit WFI. Das ist eigentlich kein Problem, weil bei Pending Interrupts WFI nicht in den Sleep gehen sollte. Es soll aber Hersteller geben, bei denen der M4 zuerst in den Sleep geht und dann unmittelbar wieder aufwacht. Das könnte dann ein Timing-Problem geben. > > Zur WFI-Anweisung steht im User Guide: > "WFI is a hint instruction that suspends execution until one of the > following events occurs: > *) a non-masked interrupt occurs and is taken > *) an interrupt masked by PRIMASK becomes pending..." > > D.h. sobald bei gesperrten Interrupts das Programm nach __WFI() > weitergeht, weiß man, dass ein Interrupt angefordert, aber noch nicht > bearbeitet wurde. Was passiert denn, wenn man jetzt im Hauptprogramm > unmittelbar hintereinander > > __enable_irq() > __disable_irq() > > aufruft? Ist dann sichergestellt, dass alle noch "offenen" > Interrupt-Anforderungen bearbeitet wurden? Wie oben beschrieben, nein. __disable_irq() klemmt praktisch den NVIC vom Core ab und Pending Interrupts werden nicht mehr verarbeitet. Es macht aber ohnehin keinen Sinn, im Hauptprogramm __disable_irq() zu verwenden, ausser zur Konfiguration und für Atomic Blocks. https://sites.google.com/site/iprinceps/Home/embedded-system-and-operating-systems/hardware-and-firmware/nested-vectored-interrupt-controller-of-arm-cortex-m3 > > Schöne Grüße, > Markus
Hmm schrieb: > Es mag sein, das ich Dein Problem nicht verstehe. > > Die nächst-naive Antwort wäre, doch einfach die WFI instruction nicht > auszuführen, falls soeben ein Interrupt aufgetreten in der > Interrupt-Routine behandelt wurde. Dann behandelst Du das Ergebnis im > Hauptprogramm und führst dann erst WIF aus. Das macht man ohnehin, aber irgendwann kommt der Zeitpunkt, wo alle Interrupts bearbeitet wurden, und dann müssen neue Interrupts wieder zugelassen werden und das Haputprogramm in den "sleep mode" gehen. Und genau das ist das Problem, das Zulassen von Interrupts und Betreten des "sleep mode" müssen eine "atomare" Operation sein, da darf dazwischen nichts passieren (insbesondere kein Interrupt, weil der sonst verloren geht). Das Beispiel war etwas abgekürzt, der vollständige Ablauf im Pseudo-Code sieht ca. so aus: while(true) { // Endlosschleife für Hauptprogramm lock(); while(!dataAvailable()) { unlock(); wait(); lock(); } getData(); // zeitkritisch, weil Interrupts gesperrt unlock(); processData(); // nicht zeitkritisch, weil Interrupts zugelassen } lock()/unlock() beziehen sich aufs Sperren/Zulassen von Interrupts. Während die Verfügbarkeit von Daten geprüft wird bzw. die Daten abgeholt werden, müssen Interrupts gesperrt sein, damit die von Hauptpgrogramm und Interrupts gemeinsam benutze Datenstruktur konsistent bleibt. Während die letzten Daten bearbeitet werden, können schon neue empfangen werden. Die Bedingung dataAvailable() muss nach jedem Interrupt erneut geprüft werden, denn es könnte ja sein, dass ein anderer Interrupt bearbeitet wurde, der nichts mit dem Empfangen von Daten zu tun hat. Hier sind zum Vergleich ein paar Links mit einem (high-level) Beispiel in Qt (die "Consumer Class" entspricht unserem Hauptprogramm): http://qt-project.org/doc/qt-4.8/threads-waitconditions.html http://qt-project.org/doc/qt-4.8/qwaitcondition.html#wait Und hier wird eindrucksvoll gezeigt, wie mühsam es ist, ein vergleichbares Verhalten zu erzielen, wenn die entsprechenden Funktionen nicht bereits low-level verfügbar sind: http://www.cs.wustl.edu/~schmidt/win32-cv-1.html > Ich muss aber auch sagen, das das Thema im Reference Manual und User > Guide ein wenig lakonisch behandelt wird. Ja, leider :-( Schöne Grüße, Markus
Das Problem ist aber nur deshalb eines, weil du krampfhaft versuchst, Interruptroutinen in den Ablauf des Hauptprogramms hinein zu synchronisieren. Der Sinn eines Interrupts und der dazugehörigen Interrupt-Routine ist es aber nunmal, asynchron abzulaufen. Dein ganzes Konzept ist iergendwie - seltsam (ich will ja höfich bleiben...) Wenn irgend etwas im Ablauf des Hauptprogramms passieren soll, dann schreib das einfach da rein, und fertig. Oliver
Peter II schrieb: > Markus Grabner schrieb: >> In beiden Fällen wird das folgende __WFI() warten, obwohl soeben ein >> Interrupt ausgeführt wurde, d.h. die empfangenen Daten würden nicht >> bearbeitet werden. Am ATmega* sind durch das spezielle Interrupt-Timing >> diese beiden Situationen ausgeschlossen, wie macht das der Cortex-M4? > > wenn es zeitkritisch ist, dann spricht doch dageben es in der ISR zu > machen. Damit ist das Problem doch gelöst. > > Was ist wenn deine Main gar nicht in Sleep steht weil sie etwas anderes > macht. Dann werden deine Daten auch später verarbeitet. Mit "später" meine ich, dass z.B. jede Minute eine Nachricht in der Länge von 16 Bytes übertragen wird, wobei die Übertragung eines Bytes, sagen wir mal, 10µs dauert. Wenn jetzt ausgerechnet der letzte Interrupt nicht zum Aufwecken des Hauptprogramms führt, würde die erste Nachricht erst (fast) eine Minute später, nämlich mit dem ersten Byte der zweiten Nachricht, bearbeitet werden. Statt "nicht zeitkritisch" hätte ich "weniger zeitkritisch" sagen sollen. Eine Verzögerung von ein paar Millisekunden ist in vielen Fällen kein Problem, aber eine beliebig lange Verzögerung ist wohl kaum akzeptabel. Schöne Grüße, Markus
Oliver S. schrieb: > Das Problem ist aber nur deshalb eines, weil du krampfhaft versuchst, > Interruptroutinen in den Ablauf des Hauptprogramms hinein zu > synchronisieren. Der Sinn eines Interrupts und der dazugehörigen > Interrupt-Routine ist es aber nunmal, asynchron abzulaufen. Dein ganzes > Konzept ist iergendwie - seltsam (ich will ja höfich bleiben...) Das sehe ich nicht so. Die Informatik beschäftigt sich seit mindestens einem halben Jahrhundert damit, wie man Prozesse parallel und asynchron ablaufen lassen kann, und ebenso lange damit, wie man, wo es nötig ist, die Prozesse wieder synchronisieren kann. Der ATmega* kann das, warum sollte es der Cortex-M4 nicht können? > Wenn irgend etwas im Ablauf des Hauptprogramms passieren soll, dann > schreib das einfach da rein, und fertig. Geht eben nicht, wenn, wie Du oben sagst, Interrupts verwendet werden, um zeitkritische Aktionen asynchron zu bearbeiten. Schöne Grüße, Markus
Tut mir leid, aber ich verstehe das Problem immer noch nicht so recht. Ich denke Du machst gedanklich einige Voraussetzungen die ich/wir nicht machen. Wozu ein unlock zwischen getData () und processData ()? Das processing ist einfach ein integraler Vorgang der die Daten holt und verarbeitet. Ggf. mit einem Ringbuffer. Das löst das Problem mit: "...Interrupts gemeinsam benutze Datenstruktur konsistent bleibt." Das Processing ist ja nur dann überhaupt zeitkritisch wenn die Ereignisse in kürzen oder nahezu gleichen Abständen auftreten wie Du brauchts um die Daten zu verarbeiten. [c] while(true) { while (dataAvailable()) getNprocessData(); // nicht zeitkritisch, weil Interrupts zugelassen lock (); if (!dataAvailable()) { wait(); unlock (); } [\c] Jedenfalls ist in der Schleife, meiner unmaßgeblichen Ansicht nach [c] while(!dataAvailable()) { unlock(); wait(); lock(); } [\c] ein logischer Widerspruch. Nach dem Aufwecken sind nämlich garantiert Daten vorhanden.
Oliver S. schrieb: > Das Problem ist aber nur deshalb eines, weil du krampfhaft > versuchst, > Interruptroutinen ind den Ablauf des Hauptprogramms hinein zu > synchronisieren. Nein, das versucht er nicht. Es kann durchaus den Fall geben, daß die Mainloop nichts zu tun hat, solange kein Interrupt neue Daten zur Verfügung stellt und man für diese Zeit Strom sparen will. Daran ist nichts ungewöhnliches oder verwerfliches. Jeder MC sollte das also supporten können. Da Interrupts aber asynchron zum Programm erfolgen, kann just direkt vor dem Sleep ein Interrupt eintreffen und genau dann muß man verhindern, daß er vor dem Sleep abgearbeitet wird. Sonst wacht der MC erst beim nächsten Interrupt auf. Angenommen der Interrupt kommt alle Stunde dann wacht man ab und zu eine Stunde zu spät auf. Die Daten sind nicht verloren, der Interrupthandler hat sie ja in eine FIFO gesteckt, aber sie werden verspätet bearbeitet. Und wenn der Absender erst auf die Antwort wartet, bevor er das nächste Paket sendet, hat man einen klassischen Deadlock.
Markus Grabner schrieb: > Der ATmega* kann das, Sei mir nicht böse, aber das ein AVR nach der ISR ins Hauptprogramm zurückkehrt, und dann dort einen Befehl abarbeitet, hat nichts mit Synchronisation zu tun. Natürlich muss Synchronisation sein, dafür gibt es aber doch nun ausreichend programmtechnische Mittel, von einfachen Flags bis hin zu den ausgefeilten Mechanismen der Echtzeitbetriebssysteme. Oliver
Oliver S. schrieb: > Natürlich muss Synchronisation sein, dafür gibt es aber doch nun > ausreichend programmtechnische Mittel, von einfachen Flags bis hin zu > den ausgefeilten Mechanismen der Echtzeitbetriebssysteme. Das Flag ist völlig nutzlos. Dann haut der Interrupt eben zwischen Flag testen und Sleep. Der Deadlock ist genau der gleiche. Du brauchst einen atomaren Mechanismus, der entweder nach einen Interrupt das Sleep nicht ausführt oder vor dem Sleep keinen pending Interrupt.
Hmm schrieb: > Tut mir leid, aber ich verstehe das Problem immer noch nicht so recht. > Ich denke Du machst gedanklich einige Voraussetzungen die ich/wir nicht > machen. > > Wozu ein unlock zwischen getData () und processData ()? Das processing > ist einfach ein integraler Vorgang der die Daten holt und verarbeitet. > Ggf. mit einem Ringbuffer. Das löst das Problem mit: "...Interrupts > gemeinsam benutze Datenstruktur konsistent bleibt." Wenn die Bearbeitung schnell geht, kann man sich das extra unlock()/lock() sparen. Wenn aber die Bearbeitung wesentlich länger dauert als das Abholen der Daten aus dem Ringbuffer, ist es sinnvoll, die Daten zuerst abzuholen und dann, sobald der Ringbuffer nicht mehr benötigt wird, Interrupts wieder zuzulassen. > Das Processing ist ja nur dann überhaupt zeitkritisch wenn die > Ereignisse in kürzen oder nahezu gleichen Abständen auftreten wie Du > brauchts um die Daten zu verarbeiten. Genau, und damit das Programm insgesamt korrekt ist, muss es für jede beliebige zeitliche Abfolge der Ereignisse ein korrektes Verhalten zeigen. Das Gemeine an diesen Dingen ist ja, dass sich Fehler bei der Synchronisierung praktisch nicht reproduzieren lassen und deshalb sehr schwer zu beheben sind. Man sollte also eine Lösung suchen, deren Korrektheit beweisbar ist (ein etwas idealistisches Ziel :-), aber zumindest für kurze Synchronisationsblöcke durchaus erreichbar). > [c] > while(true) { > while (dataAvailable()) > getNprocessData(); // nicht zeitkritisch, weil Interrupts > zugelassen > lock (); > if (!dataAvailable()) { > wait(); > unlock (); > } > [\c] Das geht nicht, weil beim Zugriff auf die Datenstruktur (erstes dataAvailable() und getNprocessData()) Interrupts gesperrt sein müssen, damit die Daten eben konsistent sind. Ein Ringbuffer, der zum Datenaustausch zwischen asynchronen Prozessen geeignet ist, tut intern ja auch nichts anderes, als "gleichzeitiges" Lesen und Schreiben zu verhindern. Außerdem erzeugt die Sequenz lock(),wait() zumindest bei einem Betriebssystem definitiv ein dead lock, wobei der Cortex-M4 hier etwas nachsichtiger ist und offenbar auch bei einem "pending interrupt" das Warten beendet. > Jedenfalls ist in der Schleife, meiner unmaßgeblichen Ansicht nach > > [c] > while(!dataAvailable()) { > unlock(); > wait(); > lock(); > } > [\c] > > ein logischer Widerspruch. Nach dem Aufwecken sind nämlich garantiert > Daten vorhanden. Nur dann, wenn es nur eine einzige Interrupt-Quelle im System gibt, nämlich die, die die Daten vom Interface entgegennimmt. Wenn noch andere Dinge interrupt-gesteuert ablaufen, wird bei jedem Interrupt das Hauptprogramm aufgeweckt, aber nur wenn es der "passende" Interrupt war, sind tatsächlich Daten vorhanden, also muss man das jedes mal überprüfen. Schöne Grüße, Markus
>> Das Processing ist ja nur dann überhaupt zeitkritisch wenn die >> Ereignisse in kürzen oder nahezu gleichen Abständen auftreten wie Du >> brauchts um die Daten zu verarbeiten. >Genau, und damit das Programm insgesamt korrekt ist, muss es für jede >beliebige zeitliche Abfolge der Ereignisse ein korrektes Verhalten >zeigen. Lass uns das mal zuerst klären, bitte: "Jede beliebige zeitliche Abfolge" ist sicherlich nicht realisierbar (vermutlich meinst Du das nicht wortwörtlich). Aber doch eine Reihe von "spezifizierten" Abfolgen. Das würde dann heissen, das die Spezifikation hier in der Diskussion noch fehlt (sofern Du Dich auf eine Diskussion darüber einlassen willst). >Wenn die Bearbeitung schnell geht, kann man sich das extra >unlock()/lock() sparen. Wenn aber die Bearbeitung wesentlich länger >dauert als das Abholen der Daten aus dem Ringbuffer, ist es sinnvoll, >die Daten zuerst abzuholen und dann, sobald der Ringbuffer nicht mehr >benötigt wird, Interrupts wieder zuzulassen. Ggf. brauchst Du die Interrupts garnicht sperren. Die Frage ist ja, a) ob es überhaupt möglich ist das Daten in kürzeren Abständen kommen als Du für die Verarbeitung brauchst b) ob es irgendeinen längeren Zeitraum gibt in der die Summe der Zeitabstände der Interrupts kleiner ist als für die Verarbeitung der Daten insgesamt gebraucht wird Falls Du b mit nein beantworten muss, dann geht das sowieso so nicht. Dann musst Du die Verarbeitung verkürzen. >Ein Ringbuffer, der zum Datenaustausch zwischen asynchronen Prozessen >geeignet ist, tut intern >ja auch nichts anderes, als "gleichzeitiges" Lesen und Schreiben zu >verhindern. Dieses "tut ... auch nichts anderes" suggeriert als wenn die Lösungen gleichwertig sind. Das sind sie aber dann nicht wenn man sich mit dem Ringbuffer die ganze Frage überhaupt erspart. >Nur dann, wenn es nur eine einzige Interrupt-Quelle im System gibt, Da hast Du natürlich recht. Falls Du das schon geschrieben hast, dann habe ich es übersehen; falls nicht, dann habe ich es nicht übersehen. ;-)
Ich fürchte, vielen in dieser Diskussion ist gar nicht klar, worum es wirklich geht. PeDa hat es richtig erkannt und es steht auch hier: http://www.mikrocontroller.net/articles/Interrupt#Interrupts_und_Low_Power_Modes_.28Sleep.29 Das Problem ist folgendes: Angenommen ihr wollt das System zyklisch per Timer wecken (z.B. 1x pro s). Ihr müßt also sicherstellen, daß der Timer-Interrupt ein schlafendes System unter allen Umständen weckt. Nun gibt es folgenden Fall (mir selbst so schon untergekommen): - Es ist kurz vor Timer-Aufweck-Zeit - Jetzt kommt ein anderer Interrupt (bei mir war's der ADC) und weckt das System - In main() merkt Ihr: ich wurde geweckt, aber es war nicht der Timer, also wieder schlafen gehen - Und nun kommt der Timer-Interrupt ganz genau zwischen InteruptEnable und Sleep
1 | __enable_interrupt(); |
2 | __sleep(); |
Was würde ohne die um einen Befehl verzögerte Interruptfreigabe passieren? Die Timer-ISR wird bearbeitet, da ja der Interrupt erlaubt ist. Danach gehen wir ins Sleep und werden nicht mehr vom Timer geweckt! => Timing-Fehler Wie gesagt, genau dieser Fehler ist mir mal untergekommen und er war reichlich schwierig zu finden.
Markus Grabner schrieb: > while(true) { // Endlosschleife für Hauptprogramm > lock(); > while(!dataAvailable()) { > unlock(); > wait(); > lock(); > } > getData(); // zeitkritisch, weil Interrupts gesperrt > unlock(); > processData(); // nicht zeitkritisch, weil Interrupts zugelassen > } while(true) { // Endlosschleife für Hauptprogramm while(!dataAvailable()) { lock(); wait(); unlock(); } lock(); getData(); // zeitkritisch, weil Interrupts gesperrt unlock(); processData(); // nicht zeitkritisch, weil Interrupts zugelassen }
Hmm schrieb: >>> Das Processing ist ja nur dann überhaupt zeitkritisch wenn die >>> Ereignisse in kürzen oder nahezu gleichen Abständen auftreten wie Du >>> brauchts um die Daten zu verarbeiten. >>Genau, und damit das Programm insgesamt korrekt ist, muss es für jede >>beliebige zeitliche Abfolge der Ereignisse ein korrektes Verhalten >>zeigen. > > Lass uns das mal zuerst klären, bitte: "Jede beliebige zeitliche > Abfolge" ist sicherlich nicht realisierbar (vermutlich meinst Du das > nicht wortwörtlich). Aber doch eine Reihe von "spezifizierten" Abfolgen. > Das würde dann heissen, das die Spezifikation hier in der Diskussion > noch fehlt (sofern Du Dich auf eine Diskussion darüber einlassen > willst). Lieber nicht :-), aber ich sollte noch ergänzen, dass "korrektes Verhalten" auch eine wohldefinierte Fehlermeldung einschließt, wenn das System z.B. erkennt, dass es zu langsam ist, um die empfangenen Daten zu verarbeiten ("buffer overflow" o.ä.). >>Wenn die Bearbeitung schnell geht, kann man sich das extra >>unlock()/lock() sparen. Wenn aber die Bearbeitung wesentlich länger >>dauert als das Abholen der Daten aus dem Ringbuffer, ist es sinnvoll, >>die Daten zuerst abzuholen und dann, sobald der Ringbuffer nicht mehr >>benötigt wird, Interrupts wieder zuzulassen. > > Ggf. brauchst Du die Interrupts garnicht sperren. Die Frage ist ja, > a) ob es überhaupt möglich ist das Daten in kürzeren Abständen kommen > als Du für die Verarbeitung brauchst Ich möchte mich jedenfalls nicht darauf verlassen, dass das nie der Fall sein wird. Dann wird das System irgendwann erweitert, und auf einmal kommen sporadisch Fehler. > b) ob es irgendeinen längeren Zeitraum gibt in der die Summe der > Zeitabstände der Interrupts kleiner ist als für die Verarbeitung der > Daten insgesamt gebraucht wird > > Falls Du b mit nein beantworten muss, dann geht das sowieso so nicht. > Dann musst Du die Verarbeitung verkürzen. Ja, das sollte das System aber selbst bemerken (siehe oben). >>Ein Ringbuffer, der zum Datenaustausch zwischen asynchronen Prozessen >geeignet > ist, tut intern >>ja auch nichts anderes, als "gleichzeitiges" Lesen und Schreiben zu >>verhindern. > > Dieses "tut ... auch nichts anderes" suggeriert als wenn die Lösungen > gleichwertig sind. Das sind sie aber dann nicht wenn man sich mit dem > Ringbuffer die ganze Frage überhaupt erspart. In dem konkret skizzierten Beispiel würde ein Ringbuffer das Problem lösen (bzw. in seine interne Implementierung verlagern), eine allgemeine "wait condition" kann aber auch für andere Zwecke eingesetzt werden. > >>Nur dann, wenn es nur eine einzige Interrupt-Quelle im System gibt, > > Da hast Du natürlich recht. Falls Du das schon geschrieben hast, dann > habe ich es übersehen; falls nicht, dann habe ich es nicht übersehen. > ;-) Ja, hier: Beitrag "Re: wait condition mit ATmega* vs. Cortex-M4" Nebenbei bemerkt bin ich beeindruckt, dass mein erstes Posting hier, noch dazu zu einem nicht ganz trivialen Thema, eine so intensive Diskussion auslöst :-) Schöne Grüße, Markus
Oliver S. schrieb: > Markus Grabner schrieb: >> Der ATmega* kann das, > > Sei mir nicht böse, aber das ein AVR nach der ISR ins Hauptprogramm > zurückkehrt, und dann dort einen Befehl abarbeitet, hat nichts mit > Synchronisation zu tun. Ob alle Interrupt Handler hintereinander abgearbeitet werden oder nicht, spielt keine Rolle, aber die Zusicherung, dass zumindest ein Befehl vom Hauptprogramm bearbeitet wird, stellt sicher, dass das Hauptprogramm nach Beendigung eines Interrupt Handlers weitere Interrupts sperren kann, ohne dass dazwischen ein Interrupt ausgeführt worden ist. Bei einer "wait condition" sind "unlock() + Wartezustand beginnen" sowie "Wartezustand beenden + lock()" atomare Operationen, und genau das kann der AVR auch. Schöne Grüße, Markus
Bronco schrieb: > Ich fürchte, vielen in dieser Diskussion ist gar nicht klar, worum es > wirklich geht. PeDa hat es richtig erkannt und es steht auch hier: > http://www.mikrocontroller.net/articles/Interrupt#Interrupts_und_Low_Power_Modes_.28Sleep.29 > > Das Problem ist folgendes: > Angenommen ihr wollt das System zyklisch per Timer wecken (z.B. 1x pro > s). Ihr müßt also sicherstellen, daß der Timer-Interrupt ein schlafendes > System unter allen Umständen weckt. > Nun gibt es folgenden Fall (mir selbst so schon untergekommen): > - Es ist kurz vor Timer-Aufweck-Zeit > - Jetzt kommt ein anderer Interrupt (bei mir war's der ADC) und weckt > das System > - In main() merkt Ihr: ich wurde geweckt, aber es war nicht der Timer, > also wieder schlafen gehen > - Und nun kommt der Timer-Interrupt ganz genau zwischen InteruptEnable > und Sleep > >
1 | > __enable_interrupt(); |
2 | > __sleep(); |
3 | >
|
> > Was würde ohne die um einen Befehl verzögerte Interruptfreigabe > passieren? > Die Timer-ISR wird bearbeitet, da ja der Interrupt erlaubt ist. > Danach gehen wir ins Sleep und werden nicht mehr vom Timer geweckt! > => Timing-Fehler > > Wie gesagt, genau dieser Fehler ist mir mal untergekommen und er war > reichlich schwierig zu finden. Danke für das anschauliche Beispiel! Die große Preisfrage: wie macht das der Cortex-M4??? Schöne Grüße, Markus
@ Markus: Wenn man eine neue, andere Architektur verwendet, sollte man sich schon im Klaren sein, dass wenn man architekturspezifische Eigenheiten benutzt, dass man Techniken, die auf einer Architektur gingen, nicht so ohne weiters auf einer anderen funktionieren. Generell scheint es mir halt äusserst fragwürdig mit WFI sich auf einen bestimmten Interrupt zu synchronisieren, wenn es mehre verschiedene ISRs im System gibt. Saubere Lösungen dafür bieten realtime syteme (z.B. FreeRTOS). Praktisch jedes vernünftige RTOS hat dafür Funktionen zur Synchronisation mit anderen Tasks zur Verfügung. Nur in der Idle-Task ist dann eine endless loop in der das WFI ausgeführt werden kann.
Fritz schrieb: > Generell scheint es mir halt äusserst fragwürdig mit WFI sich auf einen > bestimmten Interrupt zu synchronisieren Nein, das will man nicht. Man will nur schlafen, wenn es mal nichts zu tun gibt. Und eben zuverlässig mit dem nächsten Interrupt auch wieder aufwachen, der aber rein zufällig direkt vor dem Sleep pending werden könnte. Es geht wirklich nur um die Verhinderung einer Race-Condition und deren Folgen. Wenn man nicht 100%-ig sicher mit dem nächsten Interrupt aus einem Sleep aufwachen kann, dann kann man das Sleep in die Tonne treten, die Entwickler haben schlichtweg gepennt.
Beim AVR gibt es übrigens eine weitere Möglichkeit, das Sleep interruptsicher zu machen. Man setzt erst das Sleep-Enable Bit, dann die Interruptfreigabe und dann das Sleep. Und jeder Interrupthandler löscht das Sleep-Enable. Wird nun vor dem Sleep ein Interrupt ausgeführt, wandelt er das folgende Sleep quasi in ein NOP um. Vielleicht gibt es ja so ein Bit auch beim ARM, dann kann man das Sleep doch noch verwenden.
Peter Dannegger schrieb: > Man setzt erst das Sleep-Enable Bit, dann die Interruptfreigabe und dann > das Sleep. > Und jeder Interrupthandler löscht das Sleep-Enable. wozu braucht man da noch die Interruptfreigabe? (am einfang einmal ist klar)
Peter Dannegger schrieb: > dann kann man das Sleep in die Tonne treten, die > Entwickler haben schlichtweg gepennt. Vorher sollte man aber dann doch mal die komplette Doku des Prozessors lesen, um zu verstehen, was der so alles bietet. Nur weil es einen zweckentfremdeten Spezialtrick, der auf einem AVR zufällig funktioniert, auf einem ARM nicht gibt, heisst das ja noch lange nicht, das man dieses Problem da gar nicht gelöst bekommt. Oliver
Fritz schrieb: > @ Markus: > Wenn man eine neue, andere Architektur verwendet, sollte man sich schon > im Klaren sein, dass wenn man architekturspezifische Eigenheiten > benutzt, dass man Techniken, die auf einer Architektur gingen, nicht so > ohne weiters auf einer anderen funktionieren. Schon klar, und wenn es ganz anders geht, ist es auch recht. Aber dass es ein Synchronisierungs-Feature auf einer neueren und insgesamt wohl fortgeschritteneren Plattform überhaupt nicht mehr geben soll, kann ich mir nicht vorstellen. > Saubere Lösungen dafür bieten realtime syteme (z.B. FreeRTOS). Praktisch > jedes vernünftige RTOS hat dafür Funktionen zur Synchronisation mit > anderen Tasks zur Verfügung. Nur in der Idle-Task ist dann eine endless > loop in der das WFI ausgeführt werden kann. Das habe ich auch schon überlegt, aber die zusätzliche Komplexität durch ein RTOS würde ich gerne vermeiden, vor allem wenn die Synchronisierung so einfach geht (bzw. ginge) wie beim ATmega*. Wenn ich mal etwas mehr Zeit habe, kann ich in den FreeRTOS Quellcode schauen, wie das dort gemacht wird, vielleicht lässt sich daraus was ableiten... Schöne Grüße, Markus
Markus Grabner schrieb: > Das habe ich auch schon überlegt, aber die zusätzliche Komplexität durch > ein RTOS würde ich gerne vermeiden, vor allem wenn die Synchronisierung > so einfach geht (bzw. ginge) wie beim ATmega*. Natürlich hat ein RTOS eine gewisse Komplexität, aber du brauchst nur einen Bruchteil davon zu verwenden. In Bezug auf RAM- und Flashgrösse ist das für einen Cortex M4 ein Klacks. Es mag ja bei einfachen Applikationen so mit dem ATmega gut funktionieren, aber wenn die Applikation mit der Zeit koplexer wird, wirst du mit dieser Art von Synchronisation nicht glücklich werden. Markus Grabner schrieb: > Wenn ich mal etwas mehr Zeit habe, kann ich in den FreeRTOS Quellcode > schauen, wie das dort gemacht wird, vielleicht lässt sich daraus was > ableiten... Du musst dich von deinem "Hauptprogramm <--> Interrupts" Vorstellungen lösen. In einem RTOS hast du halt vrschiedene Tasks (in sich abgeschlssene Programmteile) die bestimmte Aufgaben übernehmen und über die definierten Funtionen mit anderen Tasks oder ISR kommunizieren. Ein Interrupt kommuniziert dann mit einer Task1 Z.B. UART-ISR. Ein anderer kommuniziert aber mit Tsk2 Z. B. ADC-ISR. Tasks kann man Prioritäten geben, sodass eine Aufgabe Vorrang hat. Hauptprogramm gibt es eigentlich nicht, wenn das System nichts zu tun hat, läuft eben die Idle-Task mit Z. B. nur sleep in einer loop. Kommt dann ein Interrupt, kann man in der ISR die zugehörige Task anstossen um weiterzumachen. Timeouts werden ähnlich behandelt.
Peter II schrieb: > Peter Dannegger schrieb: >> Man setzt erst das Sleep-Enable Bit, dann die Interruptfreigabe und dann >> das Sleep. >> Und jeder Interrupthandler löscht das Sleep-Enable. > > wozu braucht man da noch die Interruptfreigabe? (am einfang einmal ist > klar) Jedes mal, wenn auf die gemeinsame Datenstruktur zugegriffen wird, müssen Interrupts vorher gesperrt (und daher nachher wieder freigegeben) werden. Hier habe ich eine kompakte Abhandlung zu dem Thema gefunden: http://people.cs.umass.edu/~emery/classes/cmpsci377/current/notes/lecture_8_synch.pdf Schöne Grüße, Markus
Markus Grabner schrieb: > Jedes mal, wenn auf die gemeinsame Datenstruktur zugegriffen wird, > müssen Interrupts vorher gesperrt (und daher nachher wieder freigegeben) > werden. schon klar, aber das ist hier ja nicht der fall.
Markus Grabner schrieb: > Jedes mal, wenn auf die gemeinsame Datenstruktur zugegriffen wird, > müssen Interrupts vorher gesperrt (und daher nachher wieder freigegeben) > werden. Aber nicht beim Cortex-M dafür gibt es ja wie schon erwähnt: LDREXW/STREXW und DMB (Data Memory Barrier)
Oliver S. schrieb: > Peter Dannegger schrieb: >> dann kann man das Sleep in die Tonne treten, die >> Entwickler haben schlichtweg gepennt. > > Vorher sollte man aber dann doch mal die komplette Doku des Prozessors > lesen, um zu verstehen, was der so alles bietet. Ja, RTFM ist oft hilfreich, aber wer tut das schon, darum habe ich halt einfach mal hier gefragt :-) > Nur weil es einen zweckentfremdeten Spezialtrick, der auf einem AVR > zufällig funktioniert, auf einem ARM nicht gibt, heisst das ja noch > lange nicht, das man dieses Problem da gar nicht gelöst bekommt. Also, ich unterstelle den Atmel-Entwicklern einfach mal so, dass sie im Vollbesitz ihrer geistigen Kräfte die um einen Taktzyklus verzögerte Interruptfreigabe mit voller Absicht genau für den hier diskutierten Zweck implementiert haben, sodass damit weder zweckentfremdet noch zufällig das Synchronisierungsproblem gelöst werden kann. Diese Theorie wird unterstützt durch das folgende Code-Beispiel aus dem Manual: _SEI(); /* set global interrupt enable */ _SLEEP(); /* enter sleep, waiting for interrupt */ /* note: will enter sleep before any pending interrupt(s) */ Beim Cortex-M4 geht es offenbar anders, aber wie? Schöne Grüße, Markus
Peter II schrieb: > Markus Grabner schrieb: >> Jedes mal, wenn auf die gemeinsame Datenstruktur zugegriffen wird, >> müssen Interrupts vorher gesperrt (und daher nachher wieder freigegeben) >> werden. > > schon klar, aber das ist hier ja nicht der fall. Wenn dataAvailable() feststellt, dass keine Daten vorhanden sind, und unmittelbar danach (noch vor dem sleep()) ein Interrupt kommt, geht das Programm trotzdem schlafen, obwohl Daten verfügbar sind. Schöne Grüße, Markus
Lothar schrieb: > Markus Grabner schrieb: >> Jedes mal, wenn auf die gemeinsame Datenstruktur zugegriffen wird, >> müssen Interrupts vorher gesperrt (und daher nachher wieder freigegeben) >> werden. > > Aber nicht beim Cortex-M dafür gibt es ja wie schon erwähnt: > > LDREXW/STREXW und DMB (Data Memory Barrier) Ja, wenn man den Zugriff auf die Datenstruktur so formulieren kann, dass nur eine einzige Speicheradresse betroffen ist, kann man so die Konsistenz der Daten sicherstellen (aus dem Manual: "The result of executing a Store-Exclusive instruction to an address that is different from that used in the preceding Load-Exclusive instruction is unpredictable"). Bei einer Queue hat man typischerweise neben den eigentlichen Daten noch einen Pointer/Index auf die aktuelle Schreib- bzw. Lese-Position, das ist also nicht ganz offensichtlich, wie das mir LDREX/STREX geht. Das sichere Aufwecken des Prozessors aus dem "sleep mode" ist ein davon unabhängiges Problem. Schöne Grüße, Markus
Fritz schrieb: > Markus Grabner schrieb: >> Das habe ich auch schon überlegt, aber die zusätzliche Komplexität durch >> ein RTOS würde ich gerne vermeiden, vor allem wenn die Synchronisierung >> so einfach geht (bzw. ginge) wie beim ATmega*. > > Natürlich hat ein RTOS eine gewisse Komplexität, aber du brauchst nur > einen Bruchteil davon zu verwenden. In Bezug auf RAM- und Flashgrösse > ist das für einen Cortex M4 ein Klacks. > Es mag ja bei einfachen Applikationen so mit dem ATmega gut > funktionieren, aber wenn die Applikation mit der Zeit koplexer wird, > wirst du mit dieser Art von Synchronisation nicht glücklich werden. > > Markus Grabner schrieb: >> Wenn ich mal etwas mehr Zeit habe, kann ich in den FreeRTOS Quellcode >> schauen, wie das dort gemacht wird, vielleicht lässt sich daraus was >> ableiten... > > Du musst dich von deinem "Hauptprogramm <--> Interrupts" Vorstellungen > lösen. In einem RTOS hast du halt vrschiedene Tasks (in sich > abgeschlssene Programmteile) die bestimmte Aufgaben übernehmen und über > die definierten Funtionen mit anderen Tasks oder ISR kommunizieren. Ein > Interrupt kommuniziert dann mit einer Task1 Z.B. UART-ISR. Ein anderer > kommuniziert aber mit Tsk2 Z. B. ADC-ISR. Tasks kann man Prioritäten > geben, sodass eine Aufgabe Vorrang hat. Hauptprogramm gibt es eigentlich > nicht, wenn das System nichts zu tun hat, läuft eben die Idle-Task mit > Z. B. nur sleep in einer loop. Kommt dann ein Interrupt, kann man in der > ISR die zugehörige Task anstossen um weiterzumachen. Timeouts werden > ähnlich behandelt. Hört sich interessant an und ist sicher einen Versuch wert. Wobei eine einzelne "main loop", die gleichzeitig auf mehrere Ereignisse warten kann, und dann sequentiell die entsprechenden Aktionen anstößt, den Vorteil hat, dass die dazugehörigen Programmteile untereinander implizit synchronisiert sind. So funktionieren z.B. das select() unter Unix/Linux, bzw. darauf aufbauend auch das signal/slot Konzept in Qt. Wenn es keinen zwingenden Grund gibt, Tasks parallel laufen zu lassen, ist eine sequentielle Bearbeitung meistens einfacher. Schöne Grüße, Markus
Markus Grabner schrieb: > Bei einer Queue hat man typischerweise neben den > eigentlichen Daten noch einen Pointer/Index auf die aktuelle Schreib- > bzw. Lese-Position, das ist also nicht ganz offensichtlich, wie das mir > LDREX/STREX geht. Es reicht doch, die Pointer-Adresse als Mutex zu behandeln. Den Datenbereich kann man per MPU schützen. Alternativ dazu könnte man auch das zugehörige Area-Bit der MPU als Mutex nehmen.
Oliver S. schrieb: > Peter Dannegger schrieb: >> dann kann man das Sleep in die Tonne treten, die >> Entwickler haben schlichtweg gepennt. > > Vorher sollte man aber dann doch mal die komplette Doku des Prozessors > lesen, um zu verstehen, was der so alles bietet. > > Nur weil es einen zweckentfremdeten Spezialtrick, der auf einem AVR > zufällig funktioniert, auf einem ARM nicht gibt, heisst das ja noch > lange nicht, das man dieses Problem da gar nicht gelöst bekommt. Das Problem ist schon lange bekannt, schließlich hat schon der gute alte 8051 eine um einen Befehl verzögerte Interruptfreigabe. Also muß der ARM etwas vergleichbares besitzen.
Markus Grabner schrieb: > Ja, RTFM ist oft hilfreich, aber wer tut das schon, darum habe ich halt > einfach mal hier gefragt :-) Wenn man sich schon in solche Niederungen eines Prozessors begibt, dann ist alles andere ausser RTFM völlig sinnlos. Ohne vollständiges Verständnis der Funktion des Interruptsystems geht es halt nicht. Oliver
Oliver S. schrieb: > Vorher sollte man aber dann doch mal die komplette Doku des Prozessors > lesen, um zu verstehen, was der so alles bietet. Das ist beim ARM leider nicht trivial, da es viele verschiedene Dokumente sind, die oft auch für verschiedene ARMs zusammen gewürfelt sind, d.h. nicht alles darin muß auf den speziellen ARM auch zutreffen. Beim AVR hat man nur ein Datenblatt, wo alles drinsteht. Warum umständlich, wenn es auch einfach geht. Vielleicht hat schon jemand das Dokument und die Stelle gefunden, ob es eine Möglichkeit gibt, daß das Wait 100% deterministisch funktioniert oder ob man es in die Tonne treten muß. Wie es beim AVR geht, ist nebensächlich, es muß einfach nur überhaupt gehen. Ein Wait muß garantiert immer mit dem 1. Interrupt beendet werden und nicht erst mit dem 2. oder 3. Man kann sich nicht damit herausreden, daß es nur selten passiert und man dafür ja einen Watchdog aufsetzen kann. Wenn man erstmal mit dem Schludern anfängt, gibt es kein Halten mehr.
Peter Dannegger schrieb: > Ein Wait muß garantiert immer mit dem 1. Interrupt beendet werden und > nicht erst mit dem 2. oder 3. > Man kann sich nicht damit herausreden, daß es nur selten passiert und > man dafür ja einen Watchdog aufsetzen kann. > Wenn man erstmal mit dem Schludern anfängt, gibt es kein Halten mehr. Warum machst du nicht eine Schleife im Hauptprogramm die nur WFI ausführt. Den verschiedenen Interrupts gibst du halt entsprechende Prioritäten und machst alles in den ISRs. P:S: Was man unter Schludern versteht, ist halt Ansichtsache. Für mich zählt die Methode WFI Interrupt_diablen Interrupt_enablen .. egal ob beim ATmega oder ARM eher zum Schludern. Softwaretechnisch sauber ist halt eine Synchronisation wie in einem RTOS!
10 Sekunden Googlen findet das hier: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHBFEIB.html Vielleicht hilft das ja weiter... Oliver
Oliver S. schrieb: > http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHBFEIB.html Ah, sehr gut, die haben sich ja doch was dabei gedacht :-) Schöne Grüße, Markus
Oliver S. schrieb: > http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHBFEIB.html Das beschreibt leider das genaue Gegenteil: "If it is necessary to ensure a pended interrupt is recognized before subsequent operations" Benötigt wird aber "after subsequent operations".
Markus Grabner schrieb: > Oder gibt es am Cortex-M4 ganz andere Mechanismen, um Hauptprogramm und > Interrupts zuverlässig zu synchronisieren? Ich habe inzwischen auch im ARM-Forum gefragt: http://forums.arm.com/index.php?/topic/16975-cortex-m4-guaranteed-wakeup-from-wfi Aus den Beiträgen hier und dort ergibt sich als Lösung die folgende Code-Sequenz: __WFI(); __enable_irq(); __ISB(); __disable_irq(); Nach allem, was ich bisher darüber erfahren habe, bin ich zuversichtlich, dass das funktioniert, also werde ich es mal ausprobieren... Danke für die Hilfe & schöne Grüße, Markus
Peter Dannegger schrieb: > Oliver S. schrieb: >> > http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHBFEIB.html > > Das beschreibt leider das genaue Gegenteil: > "If it is necessary to ensure a pended interrupt is recognized before > subsequent operations" > > Benötigt wird aber "after subsequent operations". Doch, das passt schon: *) Zuerst sind Interrupts gesperrt, ein "pending interrupt" weckt den Prozessor aber trotzdem auf, ohne dass gleich der Interrupt Handler aufgerufen wird. *) Wenn dann Interrupts wieder zugelassen werden, hat der Prozessor die Gelegenheit, den Handler auszuführen. *) Durch die Synchronisation mit __ISB() ist garantiert, dass der Prozessor diese Gelegenheit auch wahrnimmt. *) Wenn die Interrupts danach sofort wieder gesperrt werden, können die im Interrupt Handler empfangenen Daten entgegengenommen werden. Die Lösung ist ganz anders als beim ATmega*, erfüllt aber den gleichen Zweck. Schöne Grüße, Markus
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.