Forum: Mikrocontroller und Digitale Elektronik ARM (STM32F4) - eigene Interrupt Routine-> umbiegen des Funktionspointer


von hmm (Gast)


Lesenswert?

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

von Dr. Sommer (Gast)


Lesenswert?

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

von rmu (Gast)


Lesenswert?

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.

von hmm (Gast)


Lesenswert?

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?

von Jim M. (turboj)


Lesenswert?

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

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Jim M. (turboj)


Lesenswert?

Man könne übrigens auch einfach den Präprozessor nutzen:
1
#define Foot SysTick_Handler
2
3
4
void Foot()
5
{
6
7
}

Etwas ganz ähnliches benutze ich um Code projektübergreifend auf 
verschiedene USARTs anzupassen.

von Steffen R. (steffen_rose)


Lesenswert?

Jim M. schrieb:
> Man könne übrigens auch einfach den Präprozessor nutzen:
> #define Foot SysTick_Handler

Macht sich beim Debuggen blöd, finde ich.

von Little B. (lil-b)


Lesenswert?

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.

von Amateur (Gast)


Lesenswert?

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

von STMApprentice (Gast)


Lesenswert?

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.

von ui (Gast)


Lesenswert?

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

von Dr. Sommer (Gast)


Lesenswert?

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.

von ui (Gast)


Lesenswert?

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

von Axel S. (a-za-z0-9)


Lesenswert?

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
void Systick_Handler(void)
2
{
3
    Foot();
4
}

und die eigentliche Implementierung
1
static inline void Foot(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.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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_Handler PROC
2
                EXPORT  SysTick_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.

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.