Hallo ! Ich möchte in einem Interrupt Handler (UART; ist aber letztlich egal) das empfangene Zeichen in einen Ring schreiben - RingWrite(). Der Schreibvorgang ist übrigens höchst einfach. Ein Task holt sich die Zeichen vom Ring ab, und verarbeitet diese - RingRead(). Nun muss gewährleistet sein, dass RingWrite() nicht durch RingRead() unterbrochen wird, und umgekehrt. Angenommen der Task liest den Ring; hier wird der Lesezeiger weitergestellt. Wenn zufällig an einer ungünstigen Stelle unterbrochen wird (innerhalb von zwei drei Anweisungen), kann es zu Inkonsistenzen kommen. Wie löst man solche Zugriffsprobleme richtig? Ich machte es bisher immer so, dass ich im neuralgischen Teil der Zugriffe Interrupts kurzfristig deaktivierte, sodass die Code-Sequenz garantiert unterbrechungsfrei wird. Ich könnte auch über binäre Semaphore arbeiten: Die ISR muss sich, bevor sie in den Ring schreibt einen Semaphor holen (mein OS unterstützt das auch für ISRs), und gibt diesen zurück, wenn sie fertig ist. Umgekehrt muss sich auch der Task einen Semaphor holen und zurückgeben. Dann wird es aber bald passieren, dass die ISR keinen Semaphor bekommt, da der gerade dem Task gehört. Müsste ich hier "blockierend" warten, bis der Semaphor frei wird? Irgendwie gefällt mir der Gedanke nicht, in einer ISR zu blockieren... Die Frage: Gibt es hier noch eine andere Möglichkeit, die ich übersehen habe und besser ist, als meine Vorgehensweise? Danke !
Michael W. schrieb: > Müsste ich hier "blockierend" warten, bis der > Semaphor frei wird? Irgendwie gefällt mir der Gedanke nicht, in einer > ISR zu blockieren... Das wird so schonmal gar nicht funktionieren, außer du hast einen µC mit mehreren Kernen und kannst tatsächlich parallel Code ausführen. Wenn du im Interrupt auf den Semaphor wartest hast du einen schönen Deadlock weil die ISR nie fertig wird und auch der restliche Code nicht weiter läuft und so der Semaphor niemals frei wird. Falls du bei Semaphors bleiben willst darf die ISR nicht blockieren und falls du in der ISR den Semaphor nicht kriegst musst du die ISR abbrechen. Dann musst du aber später dafür sorgen, dass die ISR erneut aufgerufen wird wenn der Semaphor wieder frei ist. Im Endeffekt läuft das aber darauf hinaus, dass du genauso gut Interrupts (oder speziell nur den UART Interrupt) deaktivierst, solange du Daten aus dem Ring ließt. So habe ich das bisher immer gemacht.
Ja, blockierend in einer ISR, das ist natürlich Unsinn. Ich habe bisher Interrupts immer einfach deaktiviert. Nun verwende ich ein sehr einfaches Betriebssystem (freeRTOS) und ich frage mich, ob ich nicht irgendwelche Features des OS verwenden kann, um solche Dinge eleganter zu machen. Mir ist im Manual die Funktion xQueueReceiveFromISR aufgefallen, diese kann in einer ISR verwendet werden um aus einer Queue zu lesen. Diese Funktion ist aber nicht blockierend...und für mein Anliegen wahrscheinlich nicht zu gebrauchen.
Michael W. schrieb: > Ich machte es bisher immer so, dass ich im neuralgischen Teil der > Zugriffe Interrupts kurzfristig deaktivierte, sodass die Code-Sequenz > garantiert unterbrechungsfrei wird. Genau so ist es auch richtig und das ist auch die einzige Möglichkeit, ISRs "wechselseitig" mit normalem Code zu synchronisieren. > Ich könnte auch über binäre Semaphore arbeiten: Die ISR muss sich, bevor > sie in den Ring schreibt einen Semaphor holen Das geht nicht. ISRs können zwar freie Semaphoren belegen, aber sie können nicht darauf warten, daß ein belegter Semaphor irgendwann mal freigegeben wird, denn das wird niemals passieren, weil die ISR exklusiv läuft. Der Code, der den Semaphor belegt hat, kommt also nie zu der Stelle, wo er ihn wieder freigeben würde. Ein klassischer deadlock ist die Folge dieser Situation. > (mein OS unterstützt das > auch für ISRs) In einer ISR auf einen belegte Semaphor warten, das kann es sicher nicht. Oder das OS isoliert dich von den wirklichen ISRs und es sind nur queued handler, mit denen du da tatsächlich zu tun hast. Die laufen als normaler Task (wenn auch typisch mit Realtime-Priorität) und können dadurch dann unterbrochen werden, wenn sie implizit oder explizit eine Wartefunktion aufrufen.
Vielleicht kannst du den Ring mithilfe von atomaren Instruktionen so implementieren, dass du gar kein Blockieren brauchst. Ansonsten darfst du halt nicht von der ISR aus schreiben, sondern musst von dieser aus einen normalen Task aufwecken, der normal unterbrechbar ist und über Mutex in den Ring schreibt.
c-hater schrieb: > In einer ISR auf einen belegte Semaphor warten, das kann es sicher > nicht. Wie gesagt: es kann "nicht blockierend" ein Item auf einer Queue empfangen. Aber das hilft mir hier nicht weiter...
Mit welchem Controller arbeitest du denn. Mit einem avr sollte man einen Buffet mit 8-bittigem Index konfliktfrei updaten können. Zuerst Daten in den BUfer schreiben Dann neuen Index ausrechnen und mit einer Operation updaten. Unter der Annahme, du machst Kein read-modify-Write sollten alle 8bit zugriffe atomar sein. Außerdem schreibt di Isr nur den in-Index und das Main schreibt nur den out-Index. Da gibt es also keine schreib- Konflikte. Bei Arm controllern geht das dann auch mit 32 bit. Grüße, adib.
Wenn in einer ISR ein Zeichen reinkommt, aber der Puffer voll ist, dann ist das Pech. Warten geht nicht. Da bleibt nur wegwerfen und "overflow" signalisieren. Um Konflikte zwischen der dazu gehörende Main-Routine und ISRs zu verhindern schaltet man Interrupts ab, oder arbeitet mit speziellen konfliktfreien Befehlssequenzen, wenn das im betreffenden Fall geht. Letzteres ist etwas Denksport. Interrupts kann man mit der Brechstange abschalten, indem man alle abschaltet. Man kann die Prioritäts-Ebenen explizit kontrollieren, was insbesondere bei Cortex-M gut funktioniert. Und man kann gezielt nur den Interrupt des betreffenden Device abschalten.
xQueueReceiveFromISR macht genau das selbe wie xQueueReceiveFrom, nämlich ein Element (hier wohl Byre) in eine Queue einstellen. Das Semaphor-Problem wird dabei durch taskENTER_CRITICAL gelöst, was in der ISR-Variante nicht notwendig ist, denn taskENTER_CRITICAL ist in der Regel "CLI", um's mal in AVR zu sagen. Das macht doch ein OS aus, daß es für solche Probleme Lösungen bereit hält. Warum bei FreeRTOS beiden Varianten das ganze Queue-Geturne machen selbst machen müssen, who knows?
A. K. schrieb: > Um Konflikte zwischen der dazu gehörende Main-Routine und ISRs zu > verhindern schaltet man Interrupts ab, oder arbeitet mit speziellen > konfliktfreien Befehlssequenzen, wenn das im betreffenden Fall geht. > Letzteres ist etwas Denksport. Würde ich auch so machen (den Denksport). Mit einem modernen GCC muß man da aber aufpassen; der spielt gelegentlich an der Reihenfolge der Speicherzugriffe herum. Herauszufinden, welche Optimierungen man abschalten muß, damit er das bleibenläßt, artet schon fast in ein Compilerbau-Studium aus.
Danke! Jedenfalls weiß ich nun, dass es OK ist die ISR zwischendurch kurz auszuschalten.
Nosnibor schrieb: > Mit einem modernen GCC muß man > da aber aufpassen Eine gute libc bringt dir atomic Definitionen mit. Die überreden dann den GCC zu passender Arbeit.
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.