Hallo,
ich habe hier meinen STM32F4.
Ich würde gerne eine andere Funktion als die Defaultmäßige bei einem
Interrupt anspringen.
Als Beispiel:
Anstatt
1
SysTick_Handler();
als Interrupthandler für den SysTick zu nuzten, würde ich gerne
1
Foot();
anspringen.
Wo muss ich die Funktion eintragen, damit das geht?
Ich habe mal irgendwo gelesen, dass das auch dynamisch geht, d.h. ich
kann zur Laufzeit den Funktionspointer umbiegen. Stimmt das? Und wenn
ja, wo gibts dazu Informationen?
Ich hab das Referencemanual überflogen, aber nix dazu gefunden
hmm schrieb:> Ich würde gerne eine andere Funktion als die Defaultmäßige bei einem> Interrupt anspringen.
Dann benenne doch deine Foot-Funktion um in SysTick_Handler, oder
schreibe schlicht und ergreifend Foot() in die SysTick_Handler-Funktion
hinein.
hmm schrieb:> Wo muss ich die Funktion eintragen, damit das geht?
Im ISR-Vektor. Wenn du ein Beispielprojekt von ST genommen hast, steht
der in der Datei startup_stm32f40_41xxx.s o.ä.
hmm schrieb:> Ich habe mal irgendwo gelesen, dass das auch dynamisch geht, d.h. ich> kann zur Laufzeit den Funktionspointer umbiegen. Stimmt das?
Ja.
hmm schrieb:> Und wenn> ja, wo gibts dazu Informationen?
Im ARMv7M Architecture Reference Manual. Dazu musst du einen neuen
Interrupt Vektor im RAM anlegen (den im Flash willst du vermutlich nicht
dynamisch ändern), und im VTOR Register die neue Adresse eintragen.
hmm schrieb:> Ich hab das Referencemanual überflogen, aber nix dazu gefunden
Alle Dinge die den ARM Prozessorkern betreffen finden sich im o.g.
Architecture Reference Manual. ST hat die nicht alle in das eigene
STM32F4 Reference Manual kopiert...
hmm schrieb:>> Wo muss ich die Funktion eintragen, damit das geht?> Ich habe mal irgendwo gelesen, dass das auch dynamisch geht, d.h. ich> kann zur Laufzeit den Funktionspointer umbiegen. Stimmt das? Und wenn> ja, wo gibts dazu Informationen?>> Ich hab das Referencemanual überflogen, aber nix dazu gefunden
Das steht eher im Programming Manual für die F3, F4 und L4 Serie. Der
NVIC im M4 kann die Tabelle mit den Interruptvektoren verschieben, man
kann sie also ohne weiteres ins RAM kopieren, und dort dann zur Laufzeit
manipulieren.
Bei den Cortex M0 muss man das anders machen, da ist die Vektortabelle
fix, je nach dem ob man FLASH oder RAM bei 0 einblendet kann man da aber
auch dynamisch änderbare Interruptvektoren realisieren.
Dr. Sommer schrieb:> Dann benenne doch deine Foot-Funktion um in SysTick_Handler
Das will ich nicht, ich finde einfach den Namen (war ja nur ein
Beispiel) nicht so gut.
Dr. Sommer schrieb:> oder> schreibe schlicht und ergreifend Foot() in die SysTick_Handler-Funktion> hinein.
auch nicht so schön, weil ja ich dann in der ISR noch nen
Funktionssprung habe. will ich vermeiden. Unbedingt sogar.
Dr. Sommer schrieb:> Im ISR-Vektor. Wenn du ein Beispielprojekt von ST genommen hast, steht> der in der Datei startup_stm32f40_41xxx.s o.ä.
Ok. Ja das dachte ich mir schon. Nur wo?
1
.weak SysTick_Handler
2
.thumb_set SysTick_Handler,Default_Handler
hier? einfach Default_Handler durch meinen ersetzen?
hmm schrieb:> Dr. Sommer schrieb:>> Dann benenne doch deine Foot-Funktion um in SysTick_Handler>> Das will ich nicht, ich finde einfach den Namen (war ja nur ein> Beispiel) nicht so gut.
Schlecht. Dann musst Du nämlich den Startup Code verstehen um ihn ändern
zu können. Und nein, die oben gepostete Stelle würde nicht ausreichen.
Und ich finde Foot() als Funktionsnamen sehr viel bescheuerter als
SysTick_Handler().
hmm schrieb:> Dr. Sommer schrieb:>> Dann benenne doch deine Foot-Funktion um in SysTick_Handler>> Das will ich nicht, ich finde einfach den Namen (war ja nur ein> Beispiel) nicht so gut.
Die Namen der Interrupt-Handler haben die Leute ausgesucht, die den
Startup-Code geschrieben haben. Schreib dir halt deinen eigenen, dann
kannst du auch die Namen selber aussuchen.
> Dr. Sommer schrieb:>> oder>> schreibe schlicht und ergreifend Foot() in die SysTick_Handler-Funktion>> hinein.>> auch nicht so schön, weil ja ich dann in der ISR noch nen> Funktionssprung habe. will ich vermeiden. Unbedingt sogar.
Wenn du dich da auch nur ein kleines bißchen geschickt anstellst,
inlined der Compiler deine Funktion ohnehin.
wenn du gcc verwendest, funktioniert auch ein "alias"-attribute
1
alias ("target")
2
The alias attribute causes the declaration to be emitted as an alias for another symbol, which must be specified. For instance,
3
4
void __f () { /* Do something. */; }
5
void f () __attribute__ ((weak, alias ("__f")));
6
7
8
defines `f' to be a weak alias for `__f'. In C++, the mangled name for the target must be used. It is an error if `__f' is not defined in the same translation unit.
9
10
Not all target machines support this attribute.
was du aber suchst, ist folgende Stelle in der Datei
startup_stm32f40_41xxx.s:
1
...
2
.word SVC_Handler
3
.word DebugMon_Handler
4
.word 0
5
.word PendSV_Handler
6
.word SysTick_Handler // <==== Das hier ändern
7
8
/* External Interrupts */
9
.word WWDG_IRQHandler /* Window WatchDog */
10
.word PVD_IRQHandler /* PVD through EXTI Line detection */
11
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
12
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
13
.word FLASH_IRQHandler /* FLASH */
14
...
Diese Stelle zu ändern halte ich aber auch nicht für sinnvoll, weil
sonst der Interrupt Vector schnell unleserlich wird.
Bist Du Dir sicher, dass sich niemand ™ für den SysTick_Handler();
interessiert?
Möglicherweise wird der ja von eingebauten Funktionen verwendet;
respektive die zugehörigen Zähler.
Den Zähler einfach tot legen ist dann so als ob versuchst zu sitzen, dir
aber jemand den Stuhl, unter dem Hintern, wegzieht. Nix gut!
Kommst Du aber an die originalen Quellen heran, so kannst Du die
Funktionalität, in Deinen eigenen Handler, einbauen. Ob sich aber dieser
Aufwand lohnt, nur um einen Call und den zugehörigen Return zu sparen...
Amateur schrieb:> Ob sich aber dieser> Aufwand lohnt, nur um einen Call und den zugehörigen Return zu sparen...
Ja da wird wieder gespart, koste es was es wolle.
Und ohne das Hirn zu aktivieren.
hmm schrieb:> Als Beispiel:> AnstattSysTick_Handler();> als Interrupthandler für den SysTick zu nuzten, würde ich gerneFoot();> anspringen.
Deswegen steht ja auch da:
Als Beispiel. Einfach weil es das erste ist, was man typischerweise
nutzt und den meisten klar sein sollte, was damit gemeint ist.
Ob das jetzt das ist oder ein USB-Interrupt oder ein EXTI Interrupt
spielt überhaupt keine Rolle.
Axel S. schrieb:> Wenn du dich da auch nur ein kleines bißchen geschickt anstellst,> inlined der Compiler deine Funktion ohnehin.
Ein kleines bisschen geschickt würde mich interessieren...
Wenn ich meine Interrupt-Routinen in dem vom ST vorgeschlagenen files
(*it.h/*it.c) hinterlege, dann wird dieses file compiliert. Wenn ich
also meine Funktionen, die ich ausgeführt haben will, nicht als inline
schreibe und bekanntmache, dann ist mir schleierhaft, wie der Compiler
(der ja nur ein .c file kompiliert) wissen soll, wie er eine Funktion
(bei der ja nur der Funktionsname präsent ist) inlinen soll. Da bitte
ich mal um Erklärung!
Little B. schrieb:> wenn du gcc verwendest, funktioniert auch ein "alias"-attributealias> ("target")> The alias attribute causes the declaration to be emitted as an alias> for another symbol, which must be specified. For instance,>> void __f () { /* Do something. */; }> void f () _attribute_ ((weak, alias ("__f")));>>> defines `f' to be a weak alias for `__f'. In C++, the mangled name> for the target must be used. It is an error if `__f' is not defined in> the same translation unit.>> Not all target machines support this attribute.>> was du aber suchst, ist folgende Stelle in der Datei> startup_stm32f40_41xxx.s:> ...> .word SVC_Handler> .word DebugMon_Handler> .word 0> .word PendSV_Handler> .word SysTick_Handler // <==== Das hier ändern>> /* External Interrupts */> .word WWDG_IRQHandler /* Window WatchDog> */> .word PVD_IRQHandler /* PVD through EXTI Line> detection */> .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps> through the EXTI line */> .word RTC_WKUP_IRQHandler /* RTC Wakeup through the> EXTI line */> .word FLASH_IRQHandler /* FLASH> */> ...>> Diese Stelle zu ändern halte ich aber auch nicht für sinnvoll, weil> sonst der Interrupt Vector schnell unleserlich wird.
Danke für die ausführliche Antwort. Wenigstens einige sinnvolle
Antworten kriegt man hier doch immer wieder ;)
ui schrieb:> Wenn ich> also meine Funktionen, die ich ausgeführt haben will, nicht als inline> schreibe und bekanntmache, dann ist mir schleierhaft, wie der Compiler> (der ja nur ein .c file kompiliert) wissen soll, wie er eine Funktion> (bei der ja nur der Funktionsname präsent ist) inlinen soll. Da bitte> ich mal um Erklärung!
Wenn du den GCC verwendest und LTO aktivierst, wird beim Linken noch ein
Optimierungsschritt durchgeführt. Dabei werden u.a. (fast?) alle
Funktionen geinlined, die nur 1x aufgerufen werden. Somit verschwindet
auch dein Funktionsaufruf. Im Zweifelsfall die Disassembly bemühen.
Übrigens ist es beim Cortex-M überhaupt kein Problem wenn einzelne ISR's
etwas länger dauern und/oder Funktionsaufrufe durchführen, da man hier
(im Unterschied zu AVR) ja dank Interrupt Prioritäten und
-Verschachtelung dafür sorgen kann dass wichtige Interrupts rechtzeitig
ausgeführt werden.
Dr. Sommer schrieb:> Wenn du den GCC verwendest und LTO aktivierst, wird beim Linken noch ein> Optimierungsschritt durchgeführt. Dabei werden u.a. (fast?) alle> Funktionen geinlined, die nur 1x aufgerufen werden. Somit verschwindet> auch dein Funktionsaufruf. Im Zweifelsfall die Disassembly bemühen.> Übrigens ist es beim Cortex-M überhaupt kein Problem wenn einzelne ISR's> etwas länger dauern und/oder Funktionsaufrufe durchführen, da man hier> (im Unterschied zu AVR) ja dank Interrupt Prioritäten und> -Verschachtelung dafür sorgen kann dass wichtige Interrupts rechtzeitig> ausgeführt werden.
Danke für den Hinweis, das hört sich interessant an.
Problem: Für manche Projekte kann ich den gcc bemühen, bei anderen muss
ich auf proporitäre Compiler von IAR oder Keil ausweichen. Da muss ich
deren Manual mal durchschauen, ob die sowas auch machen (können).
ui schrieb:> Axel S. schrieb:>> Wenn du dich da auch nur ein kleines bißchen geschickt anstellst,>> inlined der Compiler deine Funktion ohnehin.>> Ein kleines bisschen geschickt würde mich interessieren...> Wenn ich meine Interrupt-Routinen in dem vom ST vorgeschlagenen files> (*it.h/*it.c) hinterlege ... Wenn ich> also meine Funktionen, die ich ausgeführt haben will, nicht als inline> schreibe und bekanntmache, dann ...
... ist das genau das Gegenteil von "geschickt".
ST mag ja ganz vernünftige Hardware machen, aber bei Software von denen
habe ich bishher immer nur das Gruseln bekommen.
In diesem Fall würde man einfach in der selben(!) Übersetzungseinheit
den umhüllenden Interrupt-Handler
1
voidSystick_Handler(void)
2
{
3
Foot();
4
}
und die eigentliche Implementierung
1
staticinlinevoidFoot(void)
2
{
3
//irnkwas
4
}
unterbringen. Dann inlined der Compiler das fast immer (lies: nur dann
nicht, wenn man jegliche Optimierung verbietet). Wenn der Code unbedingt
in verschiedenen Übersetzungseinheiten sein muß (warum sollte er?) dann
eben mit LTO wie von Dr. Sommer angemerkt.
hmm schrieb:> Dr. Sommer schrieb:>> Dann benenne doch deine Foot-Funktion um in SysTick_Handler>> Das will ich nicht, ich finde einfach den Namen (war ja nur ein> Beispiel) nicht so gut.>> Dr. Sommer schrieb:>> oder>> schreibe schlicht und ergreifend Foot() in die SysTick_Handler-Funktion>> hinein.>> auch nicht so schön, weil ja ich dann in der ISR noch nen> Funktionssprung habe. will ich vermeiden. Unbedingt sogar.>
Was stört Dich dabei mehr - der Sprung in die Funktion, oder der
zusätzliche stack frame? Das zweitere ist beim Cortex kein Problem, wenn
die letzte Funktion in der Aufrufhierarchie eine leaf Funktion ist, denn
da gibt es eine Optimierung, die an der Stelle den letzten stack frame
eliminiert (näheres im 2. Kapitel hier:
https://www.amazon.de/Embedded-Controller-Grundlagen-industrielle-Anwendungen/dp/3658148497).
Ansonsten wird (wie bereits angemerkt) der Code der aufgerufenen
Funktion in der Regel geinlined.
Aber das sind natürlich nur Krücken, die Annahmen über die Realisierung
und den Compiler treffen und die Lesbarkeit des Codes beeinträchtigen;
am sinnvollsten ist es (wie auch schon angemerkt), deine Funktion direkt
in die IVT einzutragen.
Amateur schrieb:> Bist Du Dir sicher, dass sich niemand ™ für den SysTick_Handler();> interessiert?
Da kann man sich felsenfest sicher sein. Die einzige Instanz, die sich
für den Systick_Handler interessieren darf, ist die Hardware. Du willst
doch nicht etwa eine Interruptfunktion aus irgendeiner C-Quelle heraus
aufrufen??
hmm schrieb:> Ich würde gerne eine andere Funktion als die Defaultmäßige bei einem> Interrupt anspringen.
....
> Wo muss ich die Funktion eintragen, damit das geht?
Sag mal, hast du den garnichts gelesen? Also: Die "Defaultmäßige" steht
normalerweise im Startupcode und sieht bei Leuten, die einen
Standard-startup verwenden etwa so aus:
1
SysTick_HandlerPROC
2
EXPORTSysTick_Handler[WEAK]
3
B.
4
ENDP
Und eben diese "Defaultmäßige" wird durch deine selbstgeschriebene
ersetzt, wenn du selbiger genau denselben Namen gibst und sie nicht
als "weak" kennzeichnest.
Hast du das Prinzip jetzt verstanden? Wenn du dir selber Streß machen
willst, dann mußt du nur eben diesen Namen an den 3 Stellen in gleicher
Weise verändern: in der Vektortafel, an der oben gezeigten "weak"
Prozedur und in deinem eigenen Quellcode.
W.S.