Forum: Mikrocontroller und Digitale Elektronik STM32 Interrupt programmatisch auslösen


von Framlin (wolfgang_e34)


Lesenswert?

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.

von Jim M. (turboj)


Lesenswert?

> 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;

von Steffen R. (steffen_rose)


Lesenswert?

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. ;-)

von TrollJäger (Gast)


Lesenswert?

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???

von W.S. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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).

von Framlin (wolfgang_e34)


Lesenswert?

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?

von Harry L. (mysth)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

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?

von Harry L. (mysth)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

ah ... ok ... danke

von Peter D. (peda)


Lesenswert?

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.

von honk (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Nils (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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

von Johnny B. (johnnyb)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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;

von Grütze (Gast)


Lesenswert?

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.

von Nils (Gast)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

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?

von Johnny B. (johnnyb)


Lesenswert?

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.

von Thomas E. (picalic)


Lesenswert?

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 ;))

von Steffen R. (steffen_rose)


Lesenswert?

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.

von Johannes S. (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

Events in einer Queue bestehen aber nicht auf Funktionszeigern, sondern 
aus Daten, die das Ereignis beschreiben bzw. die abgearbeitet werden 
sollen.

von Mr. Big (Gast)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

@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.

von Framlin (wolfgang_e34)


Lesenswert?

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 ...

von W.S. (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von Thomas E. (picalic)


Lesenswert?

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...

von Framlin (wolfgang_e34)


Lesenswert?

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 ;-)

von (prx) A. K. (prx)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

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.

von Johannes S. (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Johannes S. schrieb:
> Das abarbeiten einer dynamischen Liste in der ISR

Das wäre eher ein Szenario für ein System ohne RTOS.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.
von Framlin (wolfgang_e34)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

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 ...

von Thomas E. (picalic)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Framlin (wolfgang_e34)


Lesenswert?

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!

von Thomas E. (picalic)


Lesenswert?

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
Noch kein Account? Hier anmelden.