Forum: Compiler & IDEs Eine Callback Funktion erstellen, wie?


von Patrick B. (p51d)


Lesenswert?

Hallo miteinander

Ich habe ein kleines Problem. Ich bin momentan am Programmieren einer 
Klasse für ein Projekt (mit einem STM32).
Die STM-Lib stellt auch schon Funktionen und so weiter zur verfügung. 
Wie kann ich es jetzt lösen, dass ein Interrupt eine Funktion in der 
Klasse aufruft, obwohl im Interrupt keine neue Instanz der KLasse 
erstellt werden soll?
Also die Funktion auf eine andere "mappen"?

Besten Dank für die Antwort
MFG
Patrick

von Rene H. (Gast)


Lesenswert?

Hallo Patrick,

ich bin mir nicht sicher ob ich Deine Frage richtig verstanden habe. 
Meinst Du evtl ein Singleton?

So wie
1
   ret = class.getInstance()->function();

Grüsse,
R.

von Patrick B. (p51d)


Lesenswert?

Ich versuchs nochmals zu erklähren:
1
class ComPort{
2
  private:
3
    uint8_t rxBuffer[256];
4
    uint8_t txBuffer[256];
5
6
  public:
7
    Send(void);
8
    Get(void);
9
    TimeoutISR(void);
10
}
11
12
void ComPort::TimeoutISR (void){
13
  // RX-Daten bearbeiten
14
}
15
16
17
void USART1_IRQHandler (void){
18
  TimeoutISR();    // entweder
19
20
  rxBuffer[]...    // oder
21
}
In der Klasse ComPort ist die Funktion "TimeoutISR", die mir ein Flag in 
der Klasse selber setzen soll, oder die Daten aus dem rxBuffer der durch 
einen DMA bedient wird verarbeiten.
Die Funktion USART1_IRQHandler wird vom Controller selber aufgerufen. 
Jetzt kann ich das verarbeiten nicht in diese ISR nehmen, da weder 
rxBuffer noch die Funktion TimeoutISR bekannt sind (die Instanz wird im 
main oder sonst wo gemacht).
Wie kann ich jetzt also erreichen, dass die Funktion TimeoutISR von 
USART1_IRQHandler aufgerufen wird, ohne eine neue Instanz in der ISR zu 
erzeugen?

von Karl H. (kbuchegg)


Lesenswert?

Patrick B. schrieb:

> Wie kann ich jetzt also erreichen, dass die Funktion TimeoutISR von
> USART1_IRQHandler aufgerufen wird, ohne eine neue Instanz in der ISR zu
> erzeugen?

Den Weg zurück schaffst du nur, in dem du der freistehenden Funktion 
eine Möglichkeit gibst, an das Objekt ranzukommen, deren Methoden du 
benutzen willst.

Zb. in dem du eine globale Variable machst (zb. einen Pointer oder ev. 
sogar direkt das Objekt), das auch in USART1_IRQHandler bekannt ist.
Anders geht es nicht. Um eine Memberfunktion eines Objektes aufzurufen, 
benötigst du Zugang zum Objekt.

von Rene H. (Gast)


Lesenswert?

Das würde ich dann wirklich mit einem Singleton lösen.

siehe 
http://de.wikibooks.org/wiki/C++-Programmierung:_Entwurfsmuster:_Singleton

In Deinem Fall:
1
class ComPort{
2
  private:
3
    uint8_t rxBuffer[256];
4
    uint8_t txBuffer[256];
5
6
    static ComPort* instance;
7
  public:
8
    static ComPort* getInstance(void);
9
    Comport () {};
10
11
    Send(void);
12
    Get(void);
13
    TimeoutISR(void);
14
}
15
16
ComPort* ComPort::instance = 0;
17
18
ComPort* ComPort::getInstance(void)
19
{
20
    if (instance == 0) instance = new ComPort();
21
22
    return (instance);
23
}
24
25
26
void ComPort::TimeoutISR (void){
27
  // RX-Daten bearbeiten
28
29
  ComPort::getInstance()->meineFunktion();
30
}

Das ist nur Beispiel Code. :-)

Grüsse,
R.

von Karl H. (kbuchegg)


Lesenswert?

Rene H. schrieb:
> Das würde ich dann wirklich mit einem Singleton lösen.
>
> siehe
> http://de.wikibooks.org/wiki/C++-Programmierung:_Entwurfsmuster:_Singleton

Ja, ist eine Möglichkeit.
Schwieriger wirds, wenn es nicht nur einen COM-Port gibt, sondern deren 
mehrerer.
Das kann dann soweit gehen, dass man aktiv in main ein bestimmtes Objekt 
benennt und an den IRQ-Handler bindet, in dem man zu jedem IRQ-Handler 
eine zugehörige Pointer-Variable schafft (oder auch eine Liste von 
derartigen Pointern), in die diese Verknüpfung eingetragen wird, so dass 
der IRQ-Handler über den Pointer eine Memberfunktion aufrufen kann.

Ist halt immer eine Frage dessen, wie universell man das ganze benötigt. 
Wenns ganz allgemein sein soll, muss man mehr Aufwand treiben.

von Rene H. (Gast)


Lesenswert?

Das ist natürlich so. Die Fragestellung gab aber keinen Hinweis dazu. Im 
Gegenteil. Ich vermute, dass das Singleton Pattern bereits die Frage 
"überbeantwortet".

Manchmal wäre es wirklich gut wenn man wüsste mit welchem Wissen die 
Frage gestellt wird, zum Beispiel auf einer Skala 1-10. Je nach dem 
erschliesst sich das Ziel der Frage einem genauer und man kann adäquater 
Antworten.

Würde ich eine Analog Frage stellen, dann wäre es sicher für die 
Antworten hilfreich wenn sie wüssten, wenig Ahnung und könnten dann 
entsprechend Antworten :-).
Nur als Beispiel.

Grüsse,
R.

von Tom K. (ez81)


Lesenswert?

>Das würde ich dann wirklich mit einem Singleton lösen.
..., weil es ausgeschlossen ist, dass es jemals einen zweiten COM-Port 
geben könnte. Solchen Kot voller GetInstance() habe ich mal geerbt. All 
mein Hass... Von der Untestbarkeit ganz zu schweigen. Nicht persönlich 
nehmen. </polemik>

Wie Karl Heinz schrieb, bitte irgendwie so lösen:
1
// global
2
ComPort* pComportForIRQ(NULL);
3
4
void USART1_IRQHandler (void)
5
{
6
    pComportForIRQ->TimeoutISR();    // entweder
7
    pComportForIRQ->rxBuffer[]...    // oder
8
}
9
10
int main()
11
{
12
    ComPort dingens;
13
    pComportForIRQ = &dingens;
14
    //usw
15
}

von Rene H. (Gast)


Lesenswert?

Tom K. schrieb:
>>Das würde ich dann wirklich mit einem Singleton lösen.
> ..., weil es ausgeschlossen ist, dass es jemals einen zweiten COM-Port
> geben könnte. Solchen Kot voller GetInstance() habe ich mal geerbt. All
> mein Hass... Von der Untestbarkeit ganz zu schweigen. Nicht persönlich
> nehmen. </polemik>
>

Hi Tom,

nein, ich nehm das nicht persönlich, dafür bin ich schon zu lange 
Entwickler. Das Argument von mehreren COM Ports ist berechtigt, war aber 
kein Inhalt in der Fragestellung. Deshalb meine Antwort.
(Siehe das letzte Post)

Nichts desto trotz, was meinst Du mit Untestbarkeit. Ich verstehe das 
nicht wirklich. Das lässt sich sauber Debuggen und man kann es ebenso 
ordentlich mit Unit Tests abfangen.

Grüsse,
R.

PS: Die Gassensprache ist bei mir nicht nötig, ich verstehe dennoch was 
Du meinst :-). Sorry, dieser Seitenhieb musste jetzt sein. Nicht 
persönlich nehmen. ;-)

von Tom K. (ez81)


Lesenswert?

Hallo Rene,

die Sprache war wirklich daneben, sorry. Inhaltlich bleibe ich dabei, 
dass Singletons meistens hässlich sind ;).

Der geerbte Code war Bildverarbeitung; die Kamera (von der es nur eine 
geben sollte und gab) war hinter einem Singleton versteckt und wurde von 
diversen Objekten (Belichtungsregelung, Objekterkennung u.s.w.) 
angesprochen. Unittests des Objekterkennungsteils (oder was ich wollte: 
brute force Parameter ausprobieren) mit gespeicherten Beispielbildern 
wären sehr einfach gewesen, wenn die Objekterkennung die Abhängigkeiten 
von Belichtungsregelung und Kamera per dependency injection verpasst 
bekommen hätte. Dann hätte man ihr einfach ein nichtstuendes 
Belichtungsregelungsobjekt und eine gespeicherte Bilder ausspuckende 
Mock-Kamera unterschieben können, ohne im existierenden Code 
herumzupfuschen. So wie es war, wären Unittests nicht ohne 
angeschlossene Kamera gelaufen oder hätten massive Änderungen an zig 
Stellen erfordert, die mir bei drohender Deadline und ohne existierende 
Unittests zu riskant waren.

Auch für den OP wäre es vielleicht praktisch, zum Debuggen des 
Kommunikationsprotokolls einen gefälschten Serial Port zu mocken, der, 
ohne die Hardware zu berühren, vorgegebene Ergebnisse liefert. Ein 
Singleton macht sowas schwieriger oder erfordert eine Menge Setter, um 
dessen state nur zu Testzwecken von außen anzupassen, was ich ziemlich 
unsauber finde. Oder sowas, ohne ComPort irgendwie zu verändern:
1
// Testen, ob die ISR funktioniert.
2
class FakeComPort: public ComPort
3
{
4
  public:
5
    void Send(void){ }
6
    void Get(void){ }
7
    void TimeoutISR(void){ToggleLED(2);}
8
};
9
10
//...
11
12
//ComPort dingens;
13
FakeComPort dingens;
14
pComportForIRQ = &dingens;

von Rene H. (Gast)


Lesenswert?

Hallo Tom

Tom K. schrieb:
> die Sprache war wirklich daneben, sorry.

Kein Problem :-).

Ja, dann verstehe ich das in etwa. Man muss aber auch sagen, dass ein 
Singleton nicht generell schlecht sein muss. Ein Pattern macht nicht 
überall Sinn wo man es einsetzen könnte.
Auch erwarte ich von einer kommerziellen Software, dass ordentliche 
Klassen- und Sequenzdiagramme vorhanden sind, damit die Wartung 
einfacher wird.
Gut, das ist schön Wetter denken. Ich glaube in meinen 24 Jahren 
Erfahrungen als Entwickler kam das in etwas zwei mal vor.

Grüsse,
René

von Patrick B. (p51d)


Lesenswert?

Die Variante mit dem Singleton reicht momentan aus. Später soll sie aber 
dann umgebaut werden, damit mehere Comports realisiert werden können.

Eine Frage noch:
Wie werden eigentlich "Event-Funktionen" (weiss nicht ob man diesen so 
sagt) bei der nächst tieferen Instanz "registriert"?
Soll heissen, in einer Klasse (z.B. Comport) werden für die benötigten 
Interrupts passende Funktionen bereitgestellt. Wie kann man jetzt neue 
Eventhandler machen, die dann in den entsprechenden Klassenfunktion 
enden?

USART1_IRq -> ComPort1_IRqHandler
USART2_IRq -> ComPort2_IRqHandler
SysTick_Irq -> ComPort1_Ticks
            -> ComPort2_Ticks

Das ganze soll dann noch möglichst dynamisch erweiterbar sein.
Beim PC können ja auch z.B. Tastendruck-Events programmiert werden, oder 
neu erstellt werden.

Sorry, aber ich steige momentan noch nicht so ganz durch mit dem 
abstackten denken.

Besten Dank für die Hilfe
MFG

von Rene H. (Gast)


Lesenswert?

Auf die Gefahr hin, dass ich jetzt als Pattern Fuzzi abgestempelt werde 
und man mich zereisst. Guck mal nach dem Observer Pattern.

Grüsse,
R.

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.