Forum: Mikrocontroller und Digitale Elektronik STM32 Interrupt Handler in statischer Klasse


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Anonymous U. (gastt)


Bewertung
0 lesenswert
nicht lesenswert
Wie teile ich meiner lieben CubeIDE denn mit, dass der SPI Interrupt 
eine bestimmte Funktion einer Klasseninstanz aufrufen soll?

Mein erfolgloser Versuch bisher:

main.cpp
1
int main(void) {
2
    //...
3
    System system;
4
    //...
5
}

system.cpp
1
System::System(){
2
    //...
3
    HAL_SPI_Receive_IT(&hspi1, spi_interface_buffer, 1);
4
}
5
//...
6
extern "C" void System::Callback_SPI_Interface() {
7
  uint32_t dummy = 23;
8
  dummy++;
9
}

stm32l4xx_it.c
1
extern (*Callback_SPI_Interface)(void);
2
//...
3
void SPI1_IRQHandler(void)
4
{
5
    /* USER CODE BEGIN SPI1_IRQn 0 */
6
    Callback_SPI_Interface();
7
    /* USER CODE END SPI1_IRQn 0 */
8
    HAL_SPI_IRQHandler(&hspi1);
9
    /* USER CODE BEGIN SPI1_IRQn 1 */
10
    /* USER CODE END SPI1_IRQn 1 */
11
}

von oooops (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Wenn du eine Klasse innerhalb von main() instanzierst dann
wird die wohl keine andere Funktion jemals zu sehen bekommen.

von Do (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ich weiß nicht ob man eine in C definierte Funktion in einer C++ Klasse 
implementieren kann. Bzw. stelle es mir nicht gerade schön und 
ungeeignet vor. Ansonsten nimm doch Pointer.

von Anonymous U. (gastt)


Bewertung
0 lesenswert
nicht lesenswert
Damit müsste die Funktion über ihren Pointer global erreichbar sein.
1
void (*Callback_ptr)(void);
2
3
void main(void){
4
  System system;
5
  Callback_ptr = &(system.Callback_SPI_Interface);
6
}

Funktioniert aber nicht, da folgender Fehler auftritt:
cannot convert 'void (System::*)()' to 'void (*)()' in assignment

von Vincent H. (vinci)


Bewertung
0 lesenswert
nicht lesenswert
Der Fehlermeldung nach zu urteilen ist deine Funktion nicht statisch.

von Johannes S. (jojos)


Bewertung
2 lesenswert
nicht lesenswert
Die Memberfunktion kann in der Klassendefinition als 'static' deklariert 
werden. Dann sind in der Funktion aber keine Zugriffe auf 
Membervariablen möglich weil der this pointer fehlt. Den kann die ISR 
auch nicht mitgeben, dafür ist dann eine Hilfsfunktion nötig.

von Anonymous U. (gastt)


Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> Die Memberfunktion kann in der Klassendefinition als 'static' deklariert
> werden. Dann sind in der Funktion aber keine Zugriffe auf
> Membervariablen möglich weil der this pointer fehlt. Den kann die ISR
> auch nicht mitgeben, dafür ist dann eine Hilfsfunktion nötig.

Ok, dass funktioniert jetzt. Also es wird fehlerfrei kompiliert. Hier 
der aktuelle Code:

main.cpp
1
// global visible pointer to actual static member function of class
2
void (*Callback_SPI_Interface_Ptr)(void) = &(System::Callback_SPI_Interface);
3
//...
4
int main(void) {
5
    //...
6
    // initialisation of System object
7
    System system;
8
    //...
9
}


system.h
1
class System {
2
private:
3
  //...
4
public:
5
  System();
6
  // set function as a static member
7
  static void Callback_SPI_Interface();
8
};
system.cpp
1
System::System(){
2
    //...
3
    // enable receiving interrupt?
4
    HAL_SPI_Receive_IT(&hspi1, spi_interface_buffer, 1);
5
}
6
//...
7
// make funktion callable from C source code
8
extern "C" void System::Callback_SPI_Interface() {
9
  uint32_t dummy = 99;
10
}


stm32l4xx_it.c
1
// pointer to static member function is declared outside of this file
2
extern (*Callback_SPI_Interface_Ptr)(void);
3
//...
4
void SPI1_IRQHandler(void)
5
{
6
    /* USER CODE BEGIN SPI1_IRQn 0 */
7
    Callback_SPI_Interface_Ptr();
8
    /* USER CODE END SPI1_IRQn 0 */
9
    HAL_SPI_IRQHandler(&hspi1);
10
    /* USER CODE BEGIN SPI1_IRQn 1 */
11
    /* USER CODE END SPI1_IRQn 1 */
12
}

Eine Frage noch. Kann man das so machen. Oder gibt es da elegantere 
Möglichkeiten. Ich bin immer wieder am überlegen, ob nicht C doch 
sinnvoller wäre... Sehr unschlüssig...

von Felix U. (ubfx)


Bewertung
0 lesenswert
nicht lesenswert
Anonymous U. schrieb:
> Ich bin immer wieder am überlegen, ob nicht C doch
> sinnvoller wäre... Sehr unschlüssig...

Ich würde mal sagen wenn es schon an einem ISR scheitert, ist C 
vielleicht doch die bessere Lösung. Schlussendlich kannst du den Komfort 
von C++ eh nicht richtig genießen und musst dir wieder um jede 
Speicherallokation Gedanken machen. Das kannst du auch in C haben. Es 
gibt sicher Projekte, wo der zusätzliche Aufwand Sinn ergibt, aber oft 
ist es auch sinnvoll, einfach beim Bewährten zu bleiben.

von oooops (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Anonymous U. schrieb:
> Eine Frage noch. Kann man das so machen. Oder gibt es da elegantere
> Möglichkeiten.

Warum krampfhaft in der main() eine Klasseninstanz anlegen und
einen globalen Pointer darauf? Gibt es dafür triftige Gründe?

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
Wenn man system global anlegt sind Zugriffe aus C++ so möglich:
https://isocpp.org/wiki/faq/mixing-c-and-cpp#call-cpp

von Anonymous U. (gastt)


Bewertung
0 lesenswert
nicht lesenswert
oooops schrieb:
> Anonymous U. schrieb:
>> Eine Frage noch. Kann man das so machen. Oder gibt es da elegantere
>> Möglichkeiten.
>
> Warum krampfhaft in der main() eine Klasseninstanz anlegen und
> einen globalen Pointer darauf? Gibt es dafür triftige Gründe?

Meine Klassen benötigen eine bereits initialisierte HAL. Ich könnte zwar 
eine init()-Funktion in die Klasse integrieren, aber wenn dieser Code im 
Konstruktor steht, finde ich es schöner.

Felix U. schrieb:
> Es
> gibt sicher Projekte, wo der zusätzliche Aufwand Sinn ergibt, aber oft
> ist es auch sinnvoll, einfach beim Bewährten zu bleiben.

Ich war lange am überlegen, ob ich C oder C++ verwenden sollte. Ich bin 
da eher erstmal zögerlich Neuem gegenüber. Aber dann dachte ich zurück 
an meine Anfänge mit ASM und wie ich mich erst gegen C gesträubt habe 
:-) So here we go..

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
ich benutze mittlerweile auch viel C++ und sehe darin keinen Nachteil. 
Dazu Mbed-os, das ebenfalls sehr konsequent C++ nutzt und auch das API 
bietet alles in Klassen an.
Die Interrupts kann man da auch bequem im Usercode abarbeiten:
Im RAM ist ein Bereich für die Kopie der Interrupt Vektoren reserviert. 
Die ISR kann fest in die Tabelle wenn sie zur Kompilierzeit bekannt ist, 
kann aber zur Laufzeit einfach mit
1
void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector);
umgebogen werden.

Für den Fall das man eine Methode als ISR nutzen möchte gibt es einen 
Helfer names CThunk. Das ist eine Trampolin Funktion die sich einen 
Callback merkt und als ISR arbeitet. Kostet zusätzliche indirekte calls, 
aber die nehme ich für den Luxus in Kauf. Dafür spare ich mir die lange 
universelle Behandlung in der HAL ISR.
Der Vorteil ist, das ich meine Komponente schön kapseln kann und die bei 
Bedarf den ISR Kram implizit behandelt. Es muss keine ISR im Core vorab 
reserviert werden (Arduino) oder eine unübersichtliche 'ich kann alle 
Ints' (HAL) implementiert werden.

Benutzt habe ich das z.B. in einer Timerklasse. Der gebe ich den 
gewünschten Callback mit und intern wird die Interruptbehandlung 
gemacht.

Auszugsweise sieht das so aus, Timerkram weggelassen:
1
class HWTimer {
2
public:
3
    HWTimer(int id, chrono::microseconds  period, Callback<void()> cb, bool onePulseMode);
4
5
    ~HWTimer();
6
    void start(chrono::microseconds period = 0us);
7
    void stop();
8
9
private:
10
    void callTimerFn();
11
12
private:
13
    int _id;
14
    bool _onePulseMode;         
15
    Callback<void()> _cb;       // user callback on timeout
16
    CThunk<HWTimer> _irq;       // helper for setting static irq member function
17
    void *_pConfig;             // generic pointer, used for implementation dependent structures
18
};
19
20
21
HWTimer::HWTimer(int id, chrono::microseconds us_period, Callback<void()> cb, bool onePulseMode) :
22
    _id(id),
23
    _onePulseMode(onePulseMode),
24
    _cb(cb),
25
    _irq(this)
26
{
27
    // set IRQ ISR, use CThunk to handle member function call from static
28
     _irq.callback(&HWTimer::callTimerFn);
29
30
    // activate IRQ in NVIC
31
    NVIC_SetVector(TimIRQTab[id], (uint32_t)_irq);
32
    NVIC_EnableIRQ(TimIRQTab[id]);
33
}
34
35
void HWTimer::callTimerFn()
36
{
37
    if (__HAL_TIM_GET_FLAG((TIM_HandleTypeDef*)_pConfig, TIM_FLAG_UPDATE) == SET) {
38
        __HAL_TIM_CLEAR_FLAG((TIM_HandleTypeDef*)_pConfig, TIM_FLAG_UPDATE);
39
40
        _cb();
41
    }
42
}

Den CThunk könnte man auch aus dem OS rausoperieren, der setzt nichts OS 
spezifisches vorraus. Aber da habe ich keine Ambitionen, das OS hat ja 
noch mehr zu bieten das ich benutzen möchte.

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.