Servus, uC: STM32F411RE (Nucleo 64 board) Sprache: C++ IDE: Eclipse ich möchte gerne einen Interrupt programmatisch auslösen. D.h. ich möchte nicht auf ein Ereignis an einem Pin warten, oder einen Timer oder so, sondern ich würde das gern via Quellcode auslösen. Genauer gesagt möchte ich verschiedene Interrupts auf verschiedenen Kanälen in Abhängigkeit von über Sensoren (I2C) eingelesenen Daten auslösen. Die Sensor-Werte "polle" ich aus einer Timer-Interrupt ISR heraus, das funktioniert. Aus dieser ISR heraus würde ich dann gerne weitere Interrupts (auf verschiedenen Kanälen mit unterschiedlichen Prioritäten) auslösen. Geht so was? Ich hab schon ne Weile in den reference manuals gestöbert, aber nichts gefunden.
> sondern ich würde das gern via Quellcode auslösen.
1 | NVIC_SetPendingIRQ(Peripherial_IRQn); |
"Peripherial" steht für die auslösende Hardware bzw. die IRQ Nummer. Geht nicht für Faults, PendSV oder SysTick - weil die nicht durch den NVIC durch gehen. Wolfgang E. schrieb: > Aus dieser ISR heraus würde ich dann gerne weitere Interrupts (auf > verschiedenen Kanälen mit unterschiedlichen Prioritäten) auslösen. Schau Dir mal den PendSV an. Trigger ist:
1 | SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; |
Mir ist unklar, was das soll. Aber ja: HAL_NVIC_SetPendingIRQ(CAN1_TX_IRQn); würde z.B. den CAN Interrupt per Software auslösen, falls du die HAL nutzt. Ansonsten ist es das ISPR[] Register im NVIC. Erläuterung sollte im Core handbuch zu finden sein. Edit: Jim war schneller. ;-)
Steffen R. schrieb: > Mir ist unklar, was das soll. Mir auch. Dir ist schon klar, dass du den Interrupt im Programm ganz normal als Funktion aufrufen kannst???
Wolfgang E. schrieb: > Geht so was? Sender Eriwan: Im Prinzip ja, aber... Das Aber ist: WOZU??? Wenn du schon pollst, warum willst du dann noch Interrupts daraus erzeugen? Sowas ist ne kranke Denke, laß das lieber bleiben. Wenn du irgend ein Polling vom Rest der Welt abtrennen willst, dann benutze einfach eine firmwareinterne Ereignis-Warteschlange. Dein Timer Interrupt stellt irgendwas fest und packt deswegen eine Botschaft (salopp 'event') in die Warteschlange. Außerhalb des Timer Interrupts guckt sich die Grundschleife in main die Warteschlange an und verwurstet alle events, die dort drinstehen. W.S.
Einen Interrupt benutzt man, um asynchron zum laufenden Kontext auf ein Ereignis zu reagieren. Will man aber aus einem laufenden Kontext eine Funktion aufrufen, dann ruft man sie ganz einfach auf (Call). Interrupts sind ganz normale Funktionen (neudeutsch Methoden). Steffen R. schrieb: > Mir ist unklar, was das soll. Mir auch. Z.B. einen UART-Interrupt direkt aufzurufen macht wenig Sinn. Der Handler wird dann ganz verdutzt feststellen, daß nichts zu tun ist (empfangen oder gesendet wurde).
Danke für die vielen schnellen Antworten! Ok, da alle die gleiche Frage stellen, hier mal mein (vermutlich völlig unsinniger) Gedankengang. Was ich im Grunde im Hinterkopf hab, ist asynchrone event-getriebene Programmierung. Man soll ja aus einem ISR heraus nur sehr kurzen Code ausführen. Ich hab mich also gefragt, was man tun kann, damit man nur irgend nen Wert irgendwo setzt und dann sozusagen den Kontext wechselt und an anderer Stelle weiter macht. Also in etwa war es so etwas, wie W.S. geschrieben hat: "Wenn du irgend ein Polling vom Rest der Welt abtrennen willst, dann benutze einfach eine firmwareinterne Ereignis-Warteschlange." D.h. konkret gesprochen, bau ich mir besser ne event-queue in der function-pointer liegen, die ich dann aufrufe? Wie ist das denn mit dem Speicher? Ich ab ja gelernt, dass es in uCs keine MMU gibt. Bekommt man da mit ner Menge Event/Function-Pointern keine Probleme?
Wolfgang E. schrieb: > Wie ist das denn mit dem Speicher? Ich ab ja gelernt, dass es in uCs > keine MMU gibt. Bekommt man da mit ner Menge Event/Function-Pointern > keine Probleme? hat nichts mit MMU zu tun. Function-Pointer sind auch nur Pointer.
Peter D. schrieb: > Interrupts sind ganz normale Funktionen (neudeutsch Methoden). Bei Arm ja. Ich schätze der TO ist andere Mikrocontroller gewohnt, wo man die Interruptroutinen nicht einfach wie jede andere Funktion aufrufen kann.
Harry L. schrieb: > hat nichts mit MMU zu tun. > Function-Pointer sind auch nur Pointer. Und wo liegt der Code für die Funktionen, auf die der Pointer zeigt?
Wolfgang E. schrieb: > Harry L. schrieb: >> hat nichts mit MMU zu tun. >> Function-Pointer sind auch nur Pointer. > > Und wo liegt der Code für die Funktionen, auf die der Pointer zeigt? Das sind ganz normale Funktionen. Die liegen im Code-Bereich.
Stefanus F. schrieb: > Ich schätze der TO ist andere Mikrocontroller gewohnt, wo > man die Interruptroutinen nicht einfach wie jede andere Funktion > aufrufen kann. Welche sollen das sein? Beim AVR und 8051 kann man jedenfalls RETI auch problemlos im Main-Kontext ausführen.
Peter D. schrieb: > Einen Interrupt benutzt man, um asynchron zum laufenden Kontext auf ein > Ereignis zu reagieren. > Will man aber aus einem laufenden Kontext eine Funktion aufrufen, dann > ruft man sie ganz einfach auf (Call). > Interrupts sind ganz normale Funktionen (neudeutsch Methoden). Das ist so nicht richtig. Gerade die SVC-Exception ist ja dafür gemacht, eben SYNCRON aus einem laufenden Kontext eine höher privilegierte Betriebssystem-Funktion aufzurufen.
Man kann bei den Cortex M auch gut mit mehrstufigen Interrupts arbeiten. Die Hardware löst die erste Stufe aus, in der die zeitkritischen Aktionen mit hoher Priorität abgearbeitet werden. In diesem Handler wird zudem ein zweiter Handler für den Rest der nötigen Aktivitäten in eine Warteschlange gestellt, die vom PendSV-Handler mit niedrigster Priorität abgearbeitet wird. Sobald alle höher priorisierten Handler fertig sind, greift dieser PendSV-Handler.
Peter D. schrieb: >> man die Interruptroutinen nicht einfach wie jede andere Funktion >> aufrufen kann. > > Welche sollen das sein? So ungefähr alle anderen, ausser den Cortexen und den vor dir genannten, einschliesslich der klassischen ARMs. Der Aufrufkontext eines Interrupt-Handlers unterscheidet sich oft fundamental von einer normalen Funktion. Beispielsweise wird im Rahmen von Interrupts oft ein Statusregister gesichert. RETI ist dann eine völlig andere Aktion als RET.
Da sich hier einige gefragt haben wozu das gut sein soll. Es gibt da einige Anwendungen für. Beispiel im RTOS Kontext: Du möchtest aus mehreren Threads sehr kurze SPI Transaktinen auführen. Die Transaktionen sind nur wenige Byte und passen komplett in einen Hardware FIFO. D.h. es sind am Ende nur ein paar Writes in die Peripherie Register. Damit die Threads sich nicht in die Quere kommen kann man das über einen Mutex absichern. So lernt man es an der UNI. Alternativ kann man aber auch die Routine als ISR implementieren, die an einen ungenutzten Interrupt klemmen und den Interrupt dann von Hand auslösen. Das ist wesentlich schneller aber auch thread-sicher, da Thread Wechsel während des ISRs nicht passieren. Zudem lässt sich solch ein "Software"-Interrupt auch problemlos aus einem anderen Interrupt-Handler auslösen (Priorisierung sei Dank). Mit der Absicherung über den Mutex geht das nicht. Du bekommst zudem auch die Garantie, das die Transaktion ohne Unterbrechnung durchläuft (musst Du halt die Priorität deines Software-Interrupts hoch setzen). Das ist für manche Hard Realtime Anwendungen wichtig.
Peter D. schrieb: > Stefanus F. schrieb: >> Ich schätze der TO ist andere Mikrocontroller gewohnt, wo >> man die Interruptroutinen nicht einfach wie jede andere Funktion >> aufrufen kann. > Welche sollen das sein? Zum Beispiel der x86 vor deiner Nase*. Der sichert bei Aufruf einer Interruptroutine die CPU Flags auf den Stack und holt sie bei IRET wieder zurück. Wenn du dort eine Interruptroutine einfach wie eine normale Funktion aufrufst, bekommst du durch den Rücksprung eine kaputten Stack. (https://www.i8086.de/asm/8086-88-asm-iret.html) *) Zugegeben: das ist kein Mikrocontroller
Wenn das so eine gescheite Idee wäre, dann würden die erhältlichen RTOS das auch so nutzen; ist mir jedoch nicht bekannt. Programmiere Deine Software lieber wie üblich (am besten unter Verwendung von FreeRTOS, embOS, ChibiOS, etc.), das ist wartbarer, und mache die zeitkritischen oder langen Datentransfers mittels DMA. Gerade die STM32 haben eine sehr ausgeklügelte DMA-Funktionalität.
Nils schrieb: > Damit die Threads sich nicht in die Quere kommen kann man das über einen > Mutex absichern. So lernt man es an der UNI. In solchen Fällen muss man oft eine Thread-Operation gegen eine Interrupt-Aktion absichern, da beide auf die gleichen Ressourcen zugreifen. Mit einer Mutex ist das nicht möglich. Mit Interrupt-Prioritäten schon. Statt dafür einen Dummy-Interrupt zu verwenden, kann man ebenso gut den kritischen Code direkt auf der Ebene des entsprechenden Interrupts laufen lassen. Die Cortex M besitzen speziell dafür das BASEPRI_MAX MSR für eine sichere Kontrolle der Interrupt-Priorität: unsigned saved = BASEPRI; BASEPRI_MAX = ..; ...kritischer Code... BASEPRI = saved;
Nimmt der ISR Änderungen am State der Peripherie vor? Das könnte dann möglicherweise in die Grütze gehen wenn es kein "echter" IRQ ist.
Johnny B. schrieb: > Wenn das so eine gescheite Idee wäre, dann würden die erhältlichen RTOS > das auch so nutzen; ist mir jedoch nicht bekannt. Die RTOS wissen aber auch nicht, welche Interrupts wirklich benutzt werden und welche zweckentfremdet werden können. Der Tip mit BASEPRI ist sehr gut. Habe ich auf der Reihe gehabt aber selbst noch nie eingesetzt.
Johnny B. schrieb: > Wenn das so eine gescheite Idee wäre, dann würden die erhältlichen RTOS > das auch so nutzen; ist mir jedoch nicht bekannt. > Programmiere Deine Software lieber wie üblich (am besten unter > Verwendung von FreeRTOS, embOS, ChibiOS, etc.), das ist wartbarer, und > mache die zeitkritischen oder langen Datentransfers mittels DMA. Gerade > die STM32 haben eine sehr ausgeklügelte DMA-Funktionalität. Na ja, mir gehts mehr ums Lernen und Experimentieren und weniger um Wartbarkeit. Wie machen RTOSse das denn? Wie implementieren die denn auf nem M4 Threads und Tasks und Nebenläufigkeit und dergl? Und welches RTOS kann ich ggf. nahtlos mit C++ nutzen?
Wolfgang E. schrieb: > Wie machen RTOSse das denn? Wie implementieren die denn auf nem M4 > Threads und Tasks und Nebenläufigkeit und dergl? Also z.B. FreeRTOS benutzt für seinen Scheduler nur einen einzigen Timerinterrupt und leitet alles davon ab. Prioritäten, Datenaustausch zwischen den Threads etc. geschieht alles Softwaremässig. Haben sie wahrscheinlich so gemacht, dass FreeRTOS möglichst Plattformunabhängig ist. Interessant wäre, wie "Mbed OS" implementiert ist, da es von ARM selbst kommt. Aber ich kenne es nicht.
Nils schrieb: > Der Tip mit BASEPRI ist sehr gut. Und ich dachte schon, ich wäre der einzige, der das so macht! (nein, nicht wirklich ;))
TrollJäger schrieb: > Steffen R. schrieb: >> Mir ist unklar, was das soll. > > Mir auch. Dir ist schon klar, dass du den Interrupt im Programm ganz > normal als Funktion aufrufen kannst??? Er wollte auch an den Prioritäten spielen. Er kann damit den Interrupt so auslösen, dass er erst nach Verlassen der aktuellen Aktivität ausgeführt wird. Außerdem bekommst du Reentranceprobleme, sollte dieser Interrupt auch durch die normale Quelle ausgelöst werden können. Insofern ist es eben nicht das gleiche.
Wolfgang E. schrieb: > Und welches RTOS kann ich ggf. nahtlos mit C++ nutzen? Johnny B. schrieb: > Interessant wäre, wie "Mbed OS" implementiert ist, da es von ARM selbst > kommt. Aber ich kenne es nicht. ich benutze mbed, nur mit dem rtos darin habe ich noch nicht viel gemacht. Es ist das RTX5 von Keil. Doku dazu ist hier: https://os.mbed.com/docs/v5.9/reference/rtos.html Das Nucleo F411RE wird auch direkt unterstützt, ich habe mal ein rtos Beispiel Projekt dafür generiert und es kann direkt in Eclipse/GNU ARM Eclipse importiert werden. Ist allerdings ein 14 MB zip das ich nicht gleich hier anhängen möchte, bei Interesse kann ich das zuschicken. Irgendwelche mbed Installationen sind dafür nicht nötig.
Wolfgang E. schrieb: > D.h. konkret gesprochen, bau ich mir besser ne event-queue in der > function-pointer liegen, die ich dann aufrufe? Oh nein, du bist ja noch jungfräulicher als ich angenommen habe. Also: Guck in die Lernbetty oder in das Mini-Projekt, das ich mal für die STM32F103C8T6 (aka "BluePill") geschrieben habe. Ich glaub, das hieß "STM32F103C8T6.ZIP". Dort findest du zum Nachlesen, wie das gedacht ist. Die Grundsätze sind dir aber hoffentlich bekannt, oder? Also: 1. Ein Interrupt ist eine Einrichtung, die dazu gedacht ist, daß der µC auf externe Ereignisse reagieren kann, die er eben NICHT auf seinem normalen Plan hat. Die kommen unvorhersehbar, weil nicht vom Programm erzeugt, sondern von der Außenwelt veranlaßt. 2. Wenn man schon pollt, also regelmäßig irgendwas abfragt, dann braucht man dazu keine Interrupts, um das Ergebnis der Abfrage zu verwerten. 3. Events, oder besser Botschaften, sind reine Software-Angelegenheiten. Es gibt eine Warteschlange, die normalerweise als Ringpuffer dargestellt ist. In diese Warteschlange kann man Events eintragen lassen (Add_Event(event);) und es gibt ne Abfrage (bool Event_Available();) und man kann Events abholen (long oder int oder sonstwas Get_Event();). 4. So eine Event kann durch irgendwas dargestellt werden, bei kleinen Systemen reicht ein Byte oft aus, sonst nimmt man ein int oder ein long oder so umfänglich wie man das haben will. Im obigen STM32-Projekt hab ich das Ganze noch etwas ausgebaut, da gibt es auch "delayed_events", was ne ganz nette Sache ist für Timeouts und verzögerte Aktionen. Dazu braucht man aber eine Systemuhr (die man bei jedem Cortex heutzutage ohnehin dabei hat). Mal kann da in eine Liste von solchen verzögerten Events einen Event zusammen mit einer Verzögerungszeit eintragen. Die Systemuhr guckt dann in sinnvollem Rhythmus nach, ob einer davon abgelaufen ist und wenn ja, packt sie ihn um: von der Liste wo er drin stand in die gewöhnliche Eventliste. Ach, lies selbst nach, wenn's dich interessiert. W.S.
Events in einer Queue bestehen aber nicht auf Funktionszeigern, sondern aus Daten, die das Ereignis beschreiben bzw. die abgearbeitet werden sollen.
Johnny B. schrieb: > Wolfgang E. schrieb: >> Wie machen RTOSse das denn? Wie implementieren die denn auf nem M4 >> Threads und Tasks und Nebenläufigkeit und dergl? > > Also z.B. FreeRTOS benutzt für seinen Scheduler nur einen einzigen > Timerinterrupt und leitet alles davon ab. Prioritäten, Datenaustausch > zwischen den Threads etc. geschieht alles Softwaremässig. > Haben sie wahrscheinlich so gemacht, dass FreeRTOS möglichst > Plattformunabhängig ist. Nö, FreeRTOS nutzt im Scheduler auch noch den SVC und den PendSV.
Hm, was spricht denn dagegen, dass man in einem „Event“ zusätzlich zu den Daten auch einen „callback“ hinterlegt? Letztlich müssen die Daten ja zu Konsequenzen führen. D.h. es muss Adressaten geben, die dann anhand der Daten irgendwelche Aktionen auslösen. Ich denke da an so ne Art publish-subscribe Szenario. Daten von verschiedenen Sensoren (egal ob gepollt oder via interrupt angeliefert), führen dazu, dass diejenigen subscriber oder listener, die sich dafür interessieren, benachrichtigt werden. Ich kann jetzt erst mal nicht erkennen, warum man das nicht in Form von „Events“ in ne Queue oder einen Ringpuffer legen sollte, wo sie dann von einem dispatcher via callback an die subscriber ausgeliefert werden. Im Grunde kann ich die Daten auch irgendwo anders ablegen, wenn der Subscriber weiß, wo er nachschauen muss. In dem Fall würde ein event.callback(void) ausreichen.
Mr. Big schrieb: > Nö, FreeRTOS nutzt im Scheduler auch noch den SVC und den PendSV. Hätte mich auch gewundert. Der PendSV wurde eigens für die effiziente Implementierung eines RTOS mit schachtelbaren Interrupt-Handlern in die Cortexe eingebaut. Mein obiges Beispiel damit ist nur eine leidlich ähnliche Nutzung in einem System ohne RTOS.
In einer sauberen Architektur sollte es dem Sender einer Nachricht (bzw Event in der Queue) weitgehend egal sein, wer das wie und wann bearbeitet. Irgendwo wird einen Job geben, der die Quelle nach und nach abarbeitet. Er wird die Einträge identifizieren und dann die dazu nötigen Funktionen aufrufen. Etwa so:
1 | while (1) |
2 | {
|
3 | Event* event = queue->get(); |
4 | if (event!=null) |
5 | {
|
6 | switch (event->type) |
7 | {
|
8 | case KEYDOWN: process_keydown_event(event); break; |
9 | case HARDFAULT: process_hardware_fault(event); break; |
10 | case DATA_RECEIVED: process_received_data(event); break; |
11 | }
|
12 | event->status=PROCESSED; |
13 | queue->remove(event); |
14 | }
|
15 | delay_ms(10); |
16 | }
|
Bitte vergebt mir eventuelle Syntaxfehler, ich programmiere seit Wochen nur in Java.
@Stefanus F. Ne, diese Art von „Eventloop“ gefällt mir nicht ;-) Das ist mir zu „prozedural“. Das passt nicht so richtig in meine publish/subscribe-Vorstellung. Abgesehen davon mag ich switches, die bestimmte Aspekte hardcodieren nicht so sehr. Ich versuche solche Unterscheidungen, wenn möglich, über andere Mechanismen, z.B. Klassenhierarchien, Templates oder Overloading zu vorzunehmen.
A. K. schrieb: > Hätte mich auch gewundert. Der PendSV wurde eigens für die effiziente > Implementierung eines RTOS mit schachtelbaren Interrupt-Handlern in die > Cortexe eingebaut. Mein obiges Beispiel damit ist nur eine leidlich > ähnliche Nutzung in einem System ohne RTOS. Ok, dann lag ich mit meinem ursprünglichen Ansatz evtl. doch nicht ganz so daneben. Grad hab ich gelesen: „Using this characteristic, we can schedule the PendSV exception handler to be executed after all other interrupt processing tasks are done, by making sure that the PendSV has the lowest exception priority level. This is very useful for a context-switching operation, which is a key operation in various OS designs.“ Klingt eigentlich ziemlich vielversprechend ...
Wolfgang E. schrieb: > Ne, diese Art von „Eventloop“ gefällt mir nicht ;-) > Das ist mir zu „prozedural“. Das passt nicht so richtig in meine > publish/subscribe-Vorstellung. > > Abgesehen davon mag ich switches, die bestimmte Aspekte hardcodieren > nicht so sehr. Ich versuche solche Unterscheidungen, wenn möglich, über > andere Mechanismen, z.B. Klassenhierarchien, Templates oder Overloading > zu vorzunehmen. Das sind alles deine persönlichen Vorstellungen und Vorlieben, die eventuell auf die Verhältnisse am PC zutreffen mögen - aber nur, solange du auf irgendwelchen Klassenbibliotheken herumprogrammierst. Die nehmen dir nämlich das eigentliche Geschehen fast vollständig ab, so daß du davon nix zu sehen bekommst. Wenn du jemals ein Programm mit grafischer Oberfläche direkt auf dem API des Betriebssystems programmiert hättest, dann wärest du mit der Eventloop und dem while DispatchEvent(GetEvent()); in nähere Berührung gekommen und wüßtest das deshalb. Nun, auf dem µC hast du aber kein Betriebssystem, was dir all diese Dinge quasi unter der Haube erledigt - hier mußt du diese Dinge eben SELBER erledigen. Deshalb ist es schnurz, ob du das magst oder nicht, du muß dich halt an die mainloop gewöhnen und auch daran, daß du selber hardcodierte Funktionen vorhalten mußt, die sich um das Erledigen dessen kümmern, was mit den Events signalisiert wird. Man kann das sinnvollerweise so gestalten, daß der betreffende Event als Referenz übergeben wird und von der Stelle in der Firmware, die ihn exklusiv behandelt, anschließend gelöscht wird.
1 | if (Event_avail()) event = Get_Event(); |
2 | if (event) |
3 | { switch (event & evGruppenMaske) |
4 | { case idTast: MenuEventHandler(&event); |
5 | BackgroundEventHandler(&event); |
6 | KarlheinzEventHandler(&event); |
7 | break; |
8 | case idHard: /* Platz für Handleraufruf */ break; |
9 | case idMaus: /* Platz für Handleraufruf */ break; |
10 | usw. |
11 | case id...sonstwas... |
12 | }; |
13 | if(event) |
14 | { UnhandledEventHandler(&event); |
15 | event = 0; |
16 | }; |
17 | }; |
So ungefähr. W.S.
Man könnte auf µC eine Liste von Event-Handlern (Funktionszeiger) verwalten, die innerhalb der Loop nacheinander aufgerufen werden. Irgendein Event Handler fühlt sich (hoffentlich) für das Event zuständig und markiert es als "erledigt". Ich würde das aber aus Performancegründen auf µC nicht tun, lieber mit einem switch/case unnötige Funktionsaufrufe sparen.
Servus, hmm - also wenn ich das richtig verstehe, ist doch der PendSV auch nichts anderes, als ein "normaler" Interrupt, nur eben, daß er vom Hersteller für diesen Zweck vorgesehen ist. Den Mechanismus zur Task-Umschaltung könnte man also auch mit jedem anderen Interrupt machen. Nur hat man da ja immer noch das Problem, daß man einen Event-Queue braucht, wenn man von verschiedenen, beliebigen Programmteilen irgendwas anderes antriggern will. Und diesen Event-Queue von beliebigen Quellen (incl. aus Interrupts heraus) zu befüllen, ist auch nicht ohne - mir ist da jedenfalls nocht nichts effektiveres eingefallen, als an der kritischen Stelle (Behandlung des FIFO-Zeigers) die Interrupts zu sperren. Die Uni-gelernten Informatiker schlagen da wahrscheinlich die Hände über dem Kopf zusammen und schreiben hier gleich, wie man es richtig macht...
W.S. schrieb: > Das sind alles deine persönlichen Vorstellungen und Vorlieben, die > eventuell auf die Verhältnisse am PC zutreffen mögen - aber nur, solange > du auf irgendwelchen Klassenbibliotheken herumprogrammierst. > Wenn du jemals ein Programm mit grafischer Oberfläche direkt auf dem API > des Betriebssystems programmiert hättest, dann wärest du mit der > Eventloop und dem > while DispatchEvent(GetEvent()); > in nähere Berührung gekommen und wüßtest das deshalb. Sorry, aber das ist nicht zutreffend. Das ist nicht nur meine persönliche Meinung, es gibt Bücher darüber. Welche Probleme z.B. "switches" verursachen können, hab ich bereits vor 15 Jahren einem Buch über "Antipattern" entnommen und darin nur bestätigt gefunden, was ich damals nach mehreren Jahren Entwicklung in verschiedenen Bereichen (auch ohne irgendwelche Klassenbibliotheken, die vor 25 Jahren noch nicht sooo verbreitet waren), selbst an Erfahrungen gesammelt habe. Ich hab auch heutzutage durchaus konkrete Vergleichsmöglichkeiten, bezüglich der langfristigen Wartbarkeit und Erweiterbarkeit von Anwendungen im "Enterprise-Bereich", was z.B. den Einsatz von switches vs der Verwendung anderer Mechanismen (Polymorphismus, Overloading, Injection etc. pp.) betrifft. Gerade dass ich durchaus auch schon intensiver mit ner Eventloop in Berührung gekommen bin, gibt mir eine Vorstellung davon, was ich daran mag und was nicht. Es ist mir z.B. auch bekannt und ich hab damit auch schon diverse Erfahrungen sammeln dürfen, dass viel und gerne mit State-Machines gearbeitet wird. Das bedeutet aber nicht, dass sie unbedingt immer der Weisheit letzter Schluss sind und dass ohne sie keine sinnvolle Software geschrieben werden könnte. Es gibt immer auch andere Wege. Es gibt unendlich viele Evangelisten, die lauthals und überzeugt verkünden, dass man C++ auf nem Mikrocontroller nicht verwenden kann. Es gibt aber eben auch andere Stimmen, die sagen, es kommt einfach nur darauf an, dass man es richtig macht. > Deshalb ist es schnurz, ob du das magst oder nicht, du muß dich halt an > die mainloop gewöhnen und auch daran, daß du selber hardcodierte > Funktionen vorhalten mußt, die sich um das Erledigen dessen kümmern, was > mit den Events signalisiert wird. Abgesehen davon muss ich gar nix ;) Ich hab in Beug auf "embedded C++" keinerlei kommerziellen Zwang und weder Zeit- noch Kostendruck im Nacken. Dafür sind mein "Brotjob" und meine Kunden zuständig und da ist auch tatsächlich entsprechender Pragmatismus angesagt ;) Im Zusammenhang mit den Mikrocontrollern geht es mir einzig ums Experimentieren, Spielen und Lernen und das beinhaltet ausdrücklich nicht, irgendwelche "alten" Vorstellungen, die sich bei diversen "alten Hasen" (durchaus auch mit Berechtigung) festgefressen haben, bedenkenlos zu übernehmen. Im Gegenteil, ich hab Spass daran, immer wieder mit einer Idee zu scheitern und danach eine neue Vorgehensweise auszuprobieren, an der ich dann auch wieder scheitere. Und das bedeutet eben ausdrücklich auch, dass ich keinerlei Drang verspüre, von vornherein irgendwelche ausgetretenen Pfade zu beschreiten. Gerade dass ich in Beug auf Mikrocontroller so "jungfräulich" bin, wie Du geschrieben hast, macht es besonders einfach, unbelastet naiv und spielerisch an die Sache ranzugehen und konventionelle Weisheiten beiseite zu lassen. Darin liegt gerade der Reiz und der Spaß. Trotzdem hab ich natürlich ständig Fragen, wie ich eine (eventuell völlig unsinnige) Idee konkret umsetzen kann und die richte ich dann an dieses großartige Forum. Und ich bin wirklich sehr dankbar, dass ich da auch immer nützliche und inspirierende Antworten bekomme, auch wenn ein paar davon eher kontrovers sind ;-)
Thomas E. schrieb: > hmm - also wenn ich das richtig verstehe, ist doch der PendSV auch > nichts anderes, als ein "normaler" Interrupt, nur eben, daß er vom > Hersteller für diesen Zweck vorgesehen ist. Den Mechanismus zur > Task-Umschaltung könnte man also auch mit jedem anderen Interrupt > machen. Korrekt. Nur benötigt man dann eben einen freien Interrupt, was für 3rd party RTOS Kernels etwas ungünstig ist. > mir ist da jedenfalls nocht nichts effektiveres eingefallen, als an > der kritischen Stelle (Behandlung des FIFO-Zeigers) die Interrupts > zu sperren. Das hängt ein wenig vom Prozessor ab. Die Cortexe besitzen load/store-exclusive Operationen, mit denen sich Listen auch ohne Interrupt-Sperre verketten lassen. Nicht zu steinalte x86er haben cmpxchg8b/cmpxchg16b Operationen auf 2 Maschinenwörter, die ihr Dasein der Manipulation verketteter Listen zu verdanken haben.
Stefanus F. schrieb: > Man könnte auf µC eine Liste von Event-Handlern (Funktionszeiger) > verwalten, die innerhalb der Loop nacheinander aufgerufen werden. > Irgendein Event Handler fühlt sich (hoffentlich) für das Event zuständig > und markiert es als "erledigt". > Das, was Du beschreibst, ähnelt dem DOM-Event-Modell, wo ein Event durch den ganzen NodeTree "wandert", bis sich jemand zuständig fühlt und es entfernt. Aber das muss man ja nicht machen. > Ich würde das aber aus Performancegründen auf µC nicht tun, lieber mit > einem switch/case unnötige Funktionsaufrufe sparen. Ich versteh immer noch nicht, warum in der Eventloop nicht einfach gleich die Handler (bzw. deren callbacks) liegen können sollen, die sich zuständig fühlen. Ich hab z.B. mein Objekt, das Sensordaten einliest. Dieses Objekt könnte doch eine Funktion bereitstellen, womit sich interessierte "Beobachter" registrieren können. Das SensordatenSammelObjekt kann die Interessenten in einer Liste ablegen. Wenn dann Sensordaten eingelesen sind, also das "Event" SensordatenSindDa eingetreten ist, könnte das SensordatenSammelObjekt einem EventDispatcher die Liste der Interessenten zusammen mit den aktuellen Sensordaten übergeben und es damit beauftragen, bei Gelegenheit die Interessenten mit den Daten zu versorgen. Ich seh da jetzt (aus meiner naiven Unwissenheit heraus) erst mal keinen Overhead und weder EventDispatcher noch SensordatenSammelObjekt müssen ein konkretes, hardcodiertes Wissen über die Interessenten haben. Sie müssen nur wissen, dass es allgemein Interessenten sind. Der EventDispatcher muss dann auch kein konkretes Wissen darüber haben, welche Arten von Events es gibt. Er nimmt einfach "abstrakte" Daten und "abstrakte" Interessenten entgegen und sorgt dafür, dass die Interessenten an ihre Daten kommen, wenn die Zeit dafür gekommen ist. Theoretisch kann das natürlich auch das SensordatenSammelObjekt selber in der ISR machen, in der es die Daten vom Sensor bekommen hat. (So hab ich das momentan implementiert und es funktioniert auch prima) Aber dann kann das, je nachdem wie viele Interessenten das sind und wie lange die für die Verarbeitung brauchen, theoretisch recht lange dauern. Dagegen könnte eben die Entkoppelung durch einen Eventmechanismus helfen, oder der Gedanke, der mich zu meiner Eingangsfrage geführt hat, dass das SensordatenSammelObjekt einen nachgelagerten, niedriger priorisierten Interrupt auslöst, dessen ISR dann die Aufgabe übernimmt, Interessenten und Daten zueinander zu bringen.
Wolfgang E. schrieb: > Dagegen könnte eben die Entkoppelung durch einen Eventmechanismus > helfen dafür gibts doch Queues, Event in der ISR in die Queue packen und in einem Thread Queue Elemente lesen und dspatchen. Deferred Interrupts, wie bei Windows NT ff. Das abarbeiten einer dynamischen Liste in der ISR verlangt ja das sich alle EventHandler brav cooperativ verhalten. Und irgendwie bist du dabei beim MessageQueue Konzept. Im µC ist die Bearbeitung von Sensordaten doch eher fest und kaum veränderbar. Die Daten an einen z.B. MQTT Broker zu schicken und dann die Subscriber extern zu haben finde ich sinnvoller.
Ein RTOS erlaubt üblicherweise Interrupt-Handlern, bestimmte RTOS-APIs zu nutzen, etwa um Threads zu steuern / entblocken. Mit Interrupts assoziierte Aktivitäten, die man nicht direkt im Handler erledigt, kann man u.U. in entsprechende Threads auslagern, ohne über Queues gehen zu müssen. Je nachdem, um was es konkret geht. In manchem RTOS mit Interrupt Levels gibt es allerdings eine Schwelle für Interrupts. Nur die niedriger priorisierten können dann RTOS-APIs nutzen.
Johannes S. schrieb: > Das abarbeiten einer dynamischen Liste in der ISR Das wäre eher ein Szenario für ein System ohne RTOS.
Achtung Offtopic! Wolfgang E. schrieb: > Interrupt programmatisch auslösen Nur, weil mir das im Auge wehtut: https://de.wiktionary.org/wiki/programmatisch Du meinstest vermutlich aber "per Software" oder "programmtechnisch". Jetzt aber weiter im Text... ;-)
Beitrag #5547797 wurde von einem Moderator gelöscht.
Johannes S. schrieb: >> dafür gibts doch Queues, Event in der ISR in die Queue packen und in > einem Thread Queue Elemente lesen und dspatchen. Deferred Interrupts, > wie bei Windows NT ff. ja klar. Aber Threads gibt's auf meinem bare-metal-controller halt nicht. Ich könnte jetzt natürlich freeRTOS oder dergl. benutzen, das will ich momentan aber nicht ;) > Und irgendwie bist du dabei beim MessageQueue Konzept. Im µC ist die > Bearbeitung von Sensordaten doch eher fest und kaum veränderbar. Die > Daten an einen z.B. MQTT Broker zu schicken und dann die Subscriber > extern zu haben finde ich sinnvoller. Ja klar, das hab ich auch schon in nem anderen Projekt so gemacht. Der MQTT-Broker läuft dabei auf nem Raspberry PI, die Daten werden vom µC via BLE übertragen und auf dem PI verarbeitet. Dort läuft auch noch ein (NodeJS-)WebServer, der über WLAN Webseiten ausliefert, die via WebSockets geupdated werden. Das ist mehr oder weniger trivial und funktioniert auch problemlos. Jetzt soll die Verarbeitung der Sensordaten aber auf einem autonomen System passieren. D.h. ich will ein Modellauto (ein 3D gedrucktes OpenTruggy) durch die Gegend fahren lassen, das seine eigene Fortbewegung und Position mittels mehrerer Sensoren (accel, magnet, gyro, GPS, lidar ...) misst und dabei einen geplanten Weg in ner FeedbackLoop mit dem tatsächlichen Kurs abgleicht. Dabei soll es mehrere ControlObjekte geben, die für unterschiedliche Aspekte (Richtung, Geschwindigkeit, Wegplanung etc. zuständig sind und unabhängig voneinander und parallel zueinander arbeiten. (Das ist zumindest die Idee ;) ) Ich will also keine Daten aus dem Fahrzeug zu nem externen System schicken, das dann Berechnungen anstellt und Anweisungen an das Fahrzeug zurückschickt. Das soll alles im Fahrzeug selber passieren. Es soll aber eben auch nicht so sein, wie in den "klassischen" Robo-Bastel-Kits, dass es eine harcodierte Funktion gibt, die dafür sorgt, dass der Robo mittels Lichtsensordaten einem schwarzen Strich auf dem Boden folgt, oder ne vorprogrammierte Lenkbewegung macht, wenn der Entfernungssensor irgendwas Bedroliches detektiert. Hab ich schon gemacht, war auch spaßig. Das waren meine ersten Schritte in der µC - Welt mit Arduinos/Adafruit Feathers und den passenden Libraries, jetzt will ich einen Schritt weiter gehen. Ich hab schon darüber nachgedacht, ob ich zwei µC verwenden soll, einen für die Datenerfassung und Fahrzeugsteuerung und einen für die Datenverarbeitung und Wegeplanung, die sich dann über nen CAN-Bus unterhalten, aber erst mal will ich es mit einem µC versuchen.
Lothar M. schrieb: > Achtung Offtopic! > > Wolfgang E. schrieb: >> Interrupt programmatisch auslösen > Nur, weil mir das im Auge wehtut: > https://de.wiktionary.org/wiki/programmatisch > Du meinstest vermutlich aber "per Software" oder "programmtechnisch". > Jetzt aber weiter im Text... ;-) Im Gegenteil, wo denkst Du hin. Ich will den programmatischsten Interrupt ever auslösen ... ;) Stimmt, Recht hast du ...
A. K. schrieb: > Die Cortexe besitzen > load/store-exclusive Operationen Ok, wenn ich das Keil-Manual dazu befrage, stoße ich auf Sätze wie: >code using the __ldrex and __strex intrinsics can have unexpected behavior. >Where LDREX and STREX instructions are needed, ARM recommends using embedded >assembly. Das würde ich gerne vermeiden. Da scheint mir sowas
1 | __disable_irq(); // critical section! |
2 | if((index = NextFree) >= 0) |
3 | NextFree = Queue[index].link; |
4 | __enable_irq(); |
evtl. dann doch etwas pragmatischer und übersichtlicher zu sein.
Thomas E. schrieb: > evtl. dann doch etwas pragmatischer und übersichtlicher zu sein. Nur sind dann alle Interrupts weg, nicht nur die bis zur betroffenen Priorität. Mit ldrex/strex sollte man wissen, was man tut. Es lohnt sich, einen Blick in den erzeugten Code zu werfen - oder man packt das in eine eigene Funktion und verhindert inlining.
Wolfgang E. schrieb: > Trotzdem hab ich natürlich ständig Fragen, wie ich eine (eventuell > völlig unsinnige) Idee konkret umsetzen kann und die richte ich dann an > dieses großartige Forum. Ja, das ist mir inzwischen auch sauer aufgestoßen: Du postest ziemlich unsinnige Ideen und erwartest dann vom Rest der Welt, sich ernsthaft damit auseinanderzusetzen. Und wenn man dir sagt, wo es langgeht und warum es eben dort aus welchen sachlichen Gründen so lang geht, dann gibst du unwillige Antworten, wo du behauptest, daß du bereits alles ja soviel besser wüßtest und daß es bereits Bücher darüber gäbe, welche Vorlieben DU persönlich hast. Ich nenne das schlichtweg Lernresistenz - und das ist vornehmlich dein Problem, nicht meines. Es ist aber denn doch ein Problem, das sowohl mich als auch alle anderen betrifft, die dir helfen wollen: Du stiehlst uns unsere Zeit damit. W.S.
W.S. schrieb: > Ja, das ist mir inzwischen auch sauer aufgestoßen: Du postest ziemlich > unsinnige Ideen und erwartest dann vom Rest der Welt, sich ernsthaft > damit auseinanderzusetzen. > Und wenn man dir sagt, wo es langgeht und warum es eben dort aus welchen > sachlichen Gründen so lang geht, dann gibst du unwillige Antworten, ist ja wirklich bewundernswert, dass Du weißt, was die allein seeligmachende Wahrheit ist und wo es lang geht. Ich hab da nen einfachen Hinweis. Verschwende einfach nicht Deine Zeit damit, dass Du unsinnige Fragen von lernresistenten Trollen beantwortest. So weit ich weiß, steht es nicht unter Strafe, die Postings von kognitiv minderbemittelten Nichtsnutzen einfach zu ignorieren. Du solltest Deine kostbare Zeit nur denen widmen, die sich ernsthaft bemühen, Deine Weisheit als solche zu erkennen und zu befolgen, ohne Dich mit ihren eigenen wirren Gedanken zu belästigen. Nur wenn alle dem Pfad der 1000fach bewährten Wahrheit folgen, kann wirklicher Fortschritt erreicht werden. Ich denke mal, es sollte ein Leichtes für Dich sein, meinen Nutzernamen in Zukunft einfach auf Deine (interne) schwarze Liste zu setzen. Dann hat sich das Problem erledigt und Du musst Dich nicht mehr aufregen. Und das gilt selbstverständlich auch für alle anderen Nutzer dieses Forums, für die Du spricht und denen es so geht wie Dir, dass sie sich um ihre Zeit betrogen fühlen. Allen anderen möchte ich ausdrücklich 1000 mal für ihre hilfreichen Antworten und die vielen inspirierenden Hinweise danken!
A. K. schrieb: > Nur sind dann alle Interrupts weg Richtig, aber der o.a. Beispielcode erzeugt bei mir im realen Projekt auch nur 9 Instruktionen zwischen CPSID I und CPSIE I, was bei z.B. 48 MHz Takt im schlimmsten Fall die Interrupt-Latenz um den Bruchteil einer Mikrosekunde (schätze mal so maximal ca. 0.2µs) erhöht. Eher Sorge hatte ich, ob nicht irgendwelche Cache- oder umsortierten Buszugriffe für Ärger sorgen könnten und deshalb evtl. noch irgendwelche Memory-Barrieren eingebaut werden müssten - scheint aber wohl nicht so zu sein. A. K. schrieb: > Mit ldrex/strex sollte man wissen, was man tut. Tja, das weiss ich eben nicht.
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.