Hi, ich versuche eine größere main.c in viele kleine wiederverwendbare Module (.c+.h) aufzuteilen. Wie macht man das mit den shared Interrupts? Beim stm32 gibt es den EXTI4_15, der ist für 12 gpio Interrupts zuständig. Die Module können ja nicht alle den selben Interrupt definieren.
In jedem Modul eine inline Funktion die von einer zentralen stelle wo die eigentliche isr implementiert ist aufgerufen werden.
Jedes modul registriert bei der initialisierung seine eigene isr beim isr-modul als callback (funktionspointer) Kommt der interrupt, wird die isr des isr moduls aufgerufen, diese feuert die registrierten callbacks. Auf diese art ist es sogar zur laufzeit veränderlich..
Anfänger schrieb: > Die Module können ja nicht alle den selben Interrupt definieren. Wieso nicht - die ISR muss halt als erstes prüfen, welcher Pin den Interrupt ausgelöst hat. Natürlich gibt es diese ISR nur einmal, da gibt es viele Möglichkeiten, z.B. ein eigenes Modul. Bei Systemen wie PCI werden shared Interrupts bei der Installation verkettet, jeder muss prüfen "bin ich das", wenn ja wird was gemacht, und dann an die nächste ISR in der Kette übergeben. Aber so ein Aufwand ist für ein paar GPIOs überflüssig. Georg
dunno.. schrieb: > Jedes modul registriert bei der initialisierung seine eigene isr beim > isr-modul als callback (funktionspointer) > > Kommt der interrupt, wird die isr des isr moduls aufgerufen, diese > feuert die registrierten callbacks. Eine saubere Lösung läuft am Ende darauf hinaus. Leider ist der Overhead (Laufzeit, extra RAM-Bedarf) nicht unbeträchtlich. Und vor allem: man kriegt ihn auch dann, wenn man den trivialen Fall hat, daß es für alle genutzten Interrupts jeweils nur eine ISR gibt.
Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der ISR-Hauptfunktionen bekannt machen, ohne das die ISR-Hauptfunktionen auf die Existenz aller ISR-Unterfunktionen besteht?
Pascal C. schrieb: > Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der > ISR-Hauptfunktionen bekannt machen, ohne das die ISR-Hauptfunktionen auf > die > Existenz aller ISR-Unterfunktionen besteht? In Assembler geht sowas problemlos und ohne allzu großen Overhead (das Asm-Äquivalent von inline-Funktionen ist mit diesem Mechanismus leider auch nicht möglich) Der Trick ist: sorge dafür, das der Aufruf zunächst erstmal als nop (oder besser: als Folge von nops für die größmögliche call-Instruktion) assembliert wird. Der aufzurufende Code muss dann die nops durch den tatsächlich zum Aufruf nötigen Code ersetzen. Ist kein aufzurufender Code vorhanden, bleibt's eben bei den nops... Mit ein wenig Makro-Zucker kann man sowas in Asm sogar sehr gut lesbar schreiben... Angesichts der Tatsache, dass C auch nix anderes als ein aufgedonnerter Makro-Assembler ist, sollte es also auch in C irgendwie machbar sein. Aber wahrscheinlich nur mit den C-typischen unwahrscheinlich abstrusen syntaktischen Verrenkungen, die vielleicht dafür sorgen, dass es am Ende wie gewünscht funktioniert, aber natürlich C-typisch auch dafür, dass absolut niemand (außer dem Verfasser natürlich) beim ersten Lesen des Codes erkennen kann, was da passiert...
Dass dieser Beitrag vom c-hater kam, war ja klar. Dann habe ich nochmal hoch gescrollt und sah, dass ich Recht hatte. Auf jeden Fall merkt man mal wieder sehr deutlich, dass der c-hater die Sprache hasst, obwohl (oder weil) er sie nicht wirklich kennt.
Pascal C. schrieb: > Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der > ISR-Hauptfunktionen bekannt machen Warum ausgerechnet dann? In den Sourcefiles kann man das, egal welche Sprache, mit einem IFDEF-Konstrukt erledigen, oder beim Power On wird eine ISR-Tabelle aufgebaut, da gibt es auch viele Möglichkeiten, z.B. Verketten wie ich oben schon beschrieben habe. Der Linker bietet da die wenigsten Möglichkeiten. Bei heutigen IDEs hat man eh wenig Einfluss auf den Linker, und auf Anhieb fällt mir auch nicht ein wie man den dazu bringt statt einer nicht vorhandenen ISR eine NOP-ISR einzubinden. Georg
Anfänger schrieb: > Wie macht man das mit den shared Interrupts? Am besten: GARNICHT! Versuche doch bitte, deine Funktionalität wirklich in verschiedene Ebenen aufzuteilen. Also eine Quelle für den/die Lowlevel-Funktionalität der Serielle(n), eine andere Quelle für den I2C, eine Quelle für die Lowlevel-Funktionen des Displays und eine andere (GDI) für die High-Level Funktionalität des Displays (Text, Linien usw. zeichnen) und so weiter. Also das Trennen des Gefummels in den Peripherie-Cores und der Behandlung und Pufferung der I/O-Ströme von den höheren Schichten, also den Algorithmen usw. Offenbar willst du mit irgendwelchen Zustandsänderungen an diversen Pins des Controllers einen zuständigen Handler starten, der dann irgendwas macht, aber du hast viele verschiedene Handler für viele _nicht zusammenhängende_ Dinge, die von vielen verschiedenen Pins angestoßen werden sollen. Sowas mit Tabellen von Callback-Funktionen tun zu wollen, halte ich für einen viel zu absturzfreudigen halsbrecherischen Zirkus. Deshalb schlag ich dir vor, es mit einem Event-System zu tun. Das geht dann etwa so: - ein einziger Interrupt-Handler ist für die Interrupts aus deinen diversen Pins zuständig. Er macht nur eines: er findet heraus, welcher Pin das war und dann packt er einen Event (z.B. ein unsigned long) mit der Information "Porthandler" und "Pinnummer" und "h->l" oder "l->h" in eine Event-Queue. Das war's für ihn. - so ein Event ist also ne ausreichend große Zahl, die einen Identcode und bei Bedarf auch noch ein paar Bits für Zusatz-Informationen enthalten kann. Beispiel: Bits 0..9 sind für den Identcode (Maus=1, Tastatur=2, Systemuhr=3, Touchscreen=4, Porthandler=5 usw.), die restlichen Bits 10..31 sind dann für quasi Parameter verfügbar. Sagen wir mal so:
1 | #define idPorthandler 5
|
2 | event = (pinnummer<<11) | (richtung<<10) | idPorthandler; |
- die Event-Queue ist schlichtweg ein üblicher Ringpuffer, wo man mit einem Handler bool AddEvent(unsigned long aEvent); so einen Event hineinbekommt und mit zwei anderen Handlern bool EventAval(void) unsigned long GetEvent(void) die Events wieder herausbekommt. - in deiner Grundschleife fragst du einfach nur die Eventqueue ab und wenn du dort was findest, dann verteilst du das der Reihe nach an alle Pinhandler. Ein jeder davon guckt, ob das "sein" Pin war und wenn, dann tut er halt was er soll, wenn nicht, dann macht er nix. Auf die Weise hast du alles voneinander getrennt - und das ohne Callback's und ohne Pointerlisten. W.S.
W.S. schrieb: > Anfänger schrieb: >> Wie macht man das mit den shared Interrupts? > > Am besten: GARNICHT! [...] > Sowas mit Tabellen von Callback-Funktionen tun zu wollen, halte ich für > einen viel zu absturzfreudigen halsbrecherischen Zirkus. Deshalb schlag > ich dir vor, es mit einem Event-System zu tun. Sehe ich auch so. Allerdings muss man sich so ein Event-System nicht unbedingt selbst bauen, sowas gibt es schon fertig, nennt sich RTOS. Je nach RTOS gibt es verschiedene Methoden um die Events aus dem Interrupthandler in die eigentlichen Handlingfunktionen zu bekommen. Z.B. threads die blockieren, bis sie eine Message gesendet bekommen. Ich verwende als RTOS gerne Chibios, dort gibt es ein fertiges Event-System für sowas: http://www.chibios.org/dokuwiki/doku.php?id=chibios:book:kernel_events
Pascal C. schrieb: > Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der > ISR-Hauptfunktionen bekannt machen, ohne das die ISR-Hauptfunktionen auf > die > Existenz aller ISR-Unterfunktionen besteht? Mit weak könnte was gehen. Wenn Du es geschickt anstellst (und auch LTO verwendest) werden womöglich alle leeren default-Implementierungen wegoptimiert und die vorhandenen strong implementierungen werden an Ort und Stelle geinlined.
Bernd K. schrieb: > Pascal C. schrieb: >> Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der >> ISR-Hauptfunktionen bekannt machen, ohne das die ISR-Hauptfunktionen auf >> die >> Existenz aller ISR-Unterfunktionen besteht? > > Mit weak könnte was gehen. Wenn Du es geschickt anstellst (und auch LTO > verwendest) werden womöglich alle leeren default-Implementierungen > wegoptimiert und die vorhandenen strong implementierungen werden an Ort > und Stelle geinlined. Genau so. Der Ansatz ist genau der gleiche wie mit der Vektortabelle auch. Mit LTO sollte der Overhead bei exakt null liegen im Gegensatz zu der oben skizzierten Lösung mit zur Laufzeit eingehängt en Funktionspointer. Matthias
Pascal C. schrieb: > Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der > ISR-Hauptfunktionen bekannt machen, ohne das die ISR-Hauptfunktionen auf > die > Existenz aller ISR-Unterfunktionen besteht? Ja, das geht, über linker-sections, in denen Funktionspointer liegen. Ist nur relativ viel Aufwand. Da ist eine linkListe einfacher, in die sich jeder eintragen kann.
Bernd K. schrieb: > Pascal C. schrieb: >> Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der >> ISR-Hauptfunktionen bekannt machen, ohne das die ISR-Hauptfunktionen auf >> die >> Existenz aller ISR-Unterfunktionen besteht? > > Mit weak könnte was gehen. Wenn Du es geschickt anstellst (und auch LTO > verwendest) werden womöglich alle leeren default-Implementierungen > wegoptimiert und die vorhandenen strong implementierungen werden an Ort > und Stelle geinlined. Schön. Nur löst das nicht das Problem des TO. Denn auf diese Weise kannst du jede Default-Implementierung (weak symbol) nur genau einmal überschreiben. Sobald zwei Module sich in den selben Interrupt einklinken wollen, knallt es beim Linken.
Axel S. schrieb: > Bernd K. schrieb: >> Pascal C. schrieb: >>> Kann man die 'ISR-Unterfunktionen' nicht irgend wie zur Linkzeit der >>> ISR-Hauptfunktionen bekannt machen, ohne das die ISR-Hauptfunktionen auf >>> die >>> Existenz aller ISR-Unterfunktionen besteht? >> >> Mit weak könnte was gehen. Wenn Du es geschickt anstellst (und auch LTO >> verwendest) werden womöglich alle leeren default-Implementierungen >> wegoptimiert und die vorhandenen strong implementierungen werden an Ort >> und Stelle geinlined. > > Schön. Nur löst das nicht das Problem des TO. Denn auf diese Weise > kannst du jede Default-Implementierung (weak symbol) nur genau /einmal/ > überschreiben. Sobald zwei Module sich in den selben Interrupt > einklinken wollen, knallt es beim Linken. Ich hab das jetzt so interpretiert als ob der TO den Vektor EXTI4_15 der ja für 12 externe Interrupts zuständig ist gerne so implementieren würde als wären es zwölf separate Vektoren. Und das ließe sich mit weak-Funktionen und einer Verteilerfunktion erreichen. Mehrere Funktionen pro Pin gehen dann nicht mehr so einfach. Evtl. könnte man das mit C++ constexpr zur Compilerzeit lösen. Matthias
Axel S. schrieb: > chön. Nur löst das nicht das Problem des TO. Denn auf diese Weise > kannst du jede Default-Implementierung (weak symbol) nur genau einmal Nein, ich hab mir das so gedacht:
1 | #define WEAK __attribute((weak))
|
2 | |
3 | |
4 | /**
|
5 | * define empty weak default handlers,
|
6 | * they will be optimized away if not
|
7 | * overridden by application provided
|
8 | * hook functions.
|
9 | */
|
10 | WEAK void hook_porta_bit0() {} |
11 | WEAK void hook_porta_bit1() {} |
12 | WEAK void hook_porta_bit2() {} |
13 | WEAK void hook_porta_bit3() {} |
14 | |
15 | |
16 | |
17 | /**
|
18 | * Interrupt für Beispielhardware
|
19 | * mit Phantasie-Registern, es geht
|
20 | * ums Prinzip.
|
21 | */
|
22 | void PORTA_IRQHandler() { |
23 | u32 flags = PORTA->IRQFLAGS; |
24 | |
25 | if (flags & (1UL << 0)) |
26 | hook_porta_bit0(); |
27 | |
28 | if (flags & (1UL << 1)) |
29 | hook_porta_bit1(); |
30 | |
31 | if (flags & (1UL << 2)) |
32 | hook_porta_bit2(); |
33 | |
34 | if (flags & (1UL << 3)) |
35 | hook_porta_bit3(); |
36 | |
37 | // [...]
|
38 | |
39 | // clear all flags that were set
|
40 | PORTA->IRQFLAGS = flags; |
41 | }
|
Und in irgendeiner anderen C-Datei dann:
1 | /**
|
2 | * Implement the IRQ-Hook for Port A bit 2
|
3 | * to detect broken deflector energy relay
|
4 | */
|
5 | void hook_porta_bit2() { |
6 | warp_drive_set_speed(WARP_0); |
7 | shields_up(); |
8 | red_alert(); |
9 | }
|
10 | |
11 | int main() { |
12 | make_it_so(); |
13 | }
|
Sollte dazu führen daß alles verschwindet bis auf das dritte if und obige Hook-Funktion wird wahrscheinlich auch komplett in den IRQHandler geinlined.
:
Bearbeitet durch User
Bernd K. schrieb: > Axel S. schrieb: >> chön. Nur löst das nicht das Problem des TO. Denn auf diese Weise >> kannst du jede Default-Implementierung (weak symbol) nur genau einmal > > Nein, ich hab mir das so gedacht: Wie gesagt: das deckt nur einen Spezialfall ab; wenn zwar nur ein Vektor für N verschiedene Interruptquellen (du nutzt hier Bits in einem Register als Marker für die Quelle) benutzt wird und man höchstens eine ISR pro Quelle registrieren will. Dann geht das natürlich. Aber im allgemeinen Fall will man diese Einschränkung nicht. Sondern jedes Modul soll sich in jeden Interrupt einklinken können. Im Zweifel auch mehrere in den gleichen Interrupt. Das ist durchaus sinnvoll, wenn das z.B. ein Timekeeper-Interrupt ist. Aber auch in anderen Fällen, z.B. für einen UART, wo Empfangs- und Sende-Events über den gleichen Vektor signalisiert werden. Denn warum sollte man den Sendepuffer nicht als separates Modul realisieren?
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.