C-Interrupt zu Events in C++ Klasse



  • Hallo miteinander

    Ich stehe momentan vor einem grösseren Problem:
    Ein Mikrocontroller hat eine umfangreiche Peripherie-Lib, die leider in C geschrieben ist. Der Treiber und die Klasse die ich schreiben soll, sollen C++ sein. Soweit so gut, da die Lib schon für einen Mix vorgesehen war (name mangling).
    Die Struktur soll in etwa so sein: Mikrocontroller->Lib in C->TreiberKlasse (C++)->ComKlasse (C++)

    Auf der C-Ebene sind Interrupt-Funktionen, die in einer "Vektor-Tablell" gespeichert sind.
    Wie kann ich jetzt eine Funktion in der TreiberKlasse an die Interrupt-Funktionen in C binden ohne neue Instanzen zu erstellen? Sprich es soll eine ComPort1-Instanz erstellt werden und alle C-Interrutps die den ComPort1 betreffen in die Klasse "leiten"?

    Wie kann ich Events in der Treiberklasse erstellen, die dann in der ComKlasse resultieren? (Ein Rx Interrupt auf C-Ebene soll über ein C++ Event der TreiberKlasse in der ComKlasse endet, damit ich nicht pollen muss)
    In etwa so:

    ComKlasse->RxEvent += new Eventhandler(ComKlRxEvent);
    private void ComKlRxEvent(){
    }
    

    Ich hoffe, ich habe mich verständlich ausgedrückt.
    Besten Dank für die Hilfe
    MFG
    P51D



  • Wenn die ISRs nicht ausufern sollen, bleibt ja nur der Weg über eine EventQueue, die in der Endlosschleife des Hauptprogramms gepollt wird, oder für die zur Behandlung irgendwie ein sehr niedrig-priorisierter Timer-Interrupt verwendet wird, meiner Meinung nach.
    Mich würden auch alle gangbaren Wege interessieren!

    Achso, Du willst also alles im Kontext der ISR-Routine abhandeln?
    1)
    Jede ISR-Routine hat eine ihr zugeordnete Callback-Liste. Hier trägt sich der Treiber ein.

    Jeder Treiber führt eine Liste mit zugeordneten Clients.

    Das ist ja so einfach, daher meine Frage: Geht es Dir hier eigentlich nur darum, wie man in C++ Events umsetzt?



  • Hier wären ein paar weitere Informationen nötig, z.B. auf was für einem OS/Kernel/... das laufen soll, und vor allem auch wie die Interrupt-Handler aussehen.

    ad 2)
    Events gibt's in C++ nicht. Man kann wie Decimad schon geschrieben hat Listen von Callback-Funktionen verwenden, plus evtl. Parameter die diesen Callback-Funktionen mitgegeben werden sollen.
    Wie du den "nicht pollen" Teil umsetzt kommt dann auf einige Faktoren an.
    Welche Synchronisierungs-Primitives stehen zur Verfügung?
    Hast du Threads oder nur Fiber oder gar überhaupt nichts dergleichen?
    Soll der "Event-Handler" Code im Kontext des Interrupts ausgeführt werden (eher unüblich), oder später als Deferred-Procedure-Call?

    uswusf.

    ad 1)
    Woher sollen wir wissen wie das geht? Es gibt viele verschiedene Wege wie das in der Library genau umgesetzt sein kann. Nur die Info "Vektor-Tablell(e)" ist da viel zu wenig.

    Grundsätzlich wird es aber vermutlich darauf hinauslaufen dass du eine freie Funktion implementierst, und die Adresse dieser Funktion in einen Slot des Interrupt-Tables (= die "Vektor-Tablell(e)") reinschreibst.

    Diese Funktion muss dann irgendwie an einen Zeiger auf dein "ComPort1" Objekt kommen. Das "irgendwie" ist dabei im einfachsten Fall eine globale Variable bzw. ein globales Array. (Gibt ja nur eine fixe Anzahl an Interrupts, also kann man hier problemlos mit globalen Variablen arbeiten.)

    Also sinngemäss sowas:

    ComPort1* g_comPort1;
    
    void MyComPort1InterruptHandler()
    {
        g_comPort1->InterruptHandler();
    }
    
    // ...
    
    void ComPort1::InterruptHandler()
    {
        QueueDeferredProcedureCall(); // bzw. was auch immer
    }
    
    // ...
    
    void ComPort1::HookUpInterruptHandler()
    {
        g_comPort1 = this;
        g_theMightyInterruptVector[THE_MIGHTY_SLOT_INDEX] = &MyComPort1InterruptHandler;
    }
    

    Jetzt fehlt natürlich noch ein Haufen Synchronisierung und was nicht noch, aber das ist alles abhängig von der Lib bzw. dem Kernel/Framework/... das du verwendest.



  • hustbaer schrieb:

    Hier wären ein paar weitere Informationen nötig, z.B. auf was für einem OS/Kernel/... das laufen soll, und vor allem auch wie die Interrupt-Handler aussehen.

    Das ist ja genau das Problem: Bei einem OS gibt es viele Möglichkeiten, aber auf dem Mikrocontroller ist kein OS vorhanden. Soll heissen ich schreibe eine Firmware auf c++-Basis (rudimentär).

    Ok, zur Interrupt-Vektortabelle: Dies ist im Prinzip nur eine Tabelle, bei der Addressen hinterlegt sind, die von den Interrupts angesprungen werden.
    Eine Interrupt-Funktion sieht z.B. so aus:

    void USART1_IRqHandler (void)
    {
       // User- code
       USART_ClearITPendingBit(RS485USART, USART_IT_RTO);
    }
    

    @Deicimad
    Ich habe bis jetzt noch nie mit Events gearbetet, darum ist es für mich nicht ganz so einfach, wie für dich.



  • Wichtig wäre vielleicht, was der Compiler für Deinen Microcontroller unterstützt... Gibt da viele, die mit Templates nicht so grüne werden. Das hat natürlich große auswirkungen darauf, wie man die Events implementieren könnte.



  • Templates werden unterstützt
    http://supp.iar.com/FilesPublic/UPDINFO/004916/arm/doc/EWARM_DevelopmentGuide.ENU.pdf

    Ich habe auch noch etwas gelesen, dass man dies über Observer machen könnte, nur habe ich leider keine Ahnung wie ich dies dann realisieren muss.



  • P51D schrieb:

    Das ist ja genau das Problem: Bei einem OS gibt es viele Möglichkeiten, aber auf dem Mikrocontroller ist kein OS vorhanden. Soll heissen ich schreibe eine Firmware auf c++-Basis (rudimentär).

    Naja, ist denn gar nichts vorhanden?
    Nichtmal irgend ein minimaler Kernel? Scheduling/Threading Support in einer Libray? Überhaupt nix?



  • Nein. Wie gesagt, es soll eine Firmware werden und es läuft kein OS.

    Normalerweise werden die Mikrocontroller auch in C programmiert, aber der Wunsch kam, dass es möglichst portabel sein soll (verschiedene Kontroller, Architekturen...)



  • Nichts wird automatisch oder ausschließlich portabler, nur weil man C++ verwendet. Du kannst auch in C eine Ebene um das Hardwarenahe basteln, hinter der dann die Implementierungen ausgetauscht werden. Wenn Du in C geübter bist, wirst Du dort mit aller Wahrscheinlichkeit technisch bessere Abstraktionen produzieren.
    Im Microcontroller-Bereich würde ich sogar soweit gehen und sagen: Wenn Du kein C++ verwendest, wirst du portabler sein. C++ hat abgesehen von std::function, dass es erst seit C++11 gibt, keinen eingebauten Callbacksupport der über einfache Funktionszeiger hinaus geht. Da müsste man dann auf boost::signal oder ähnliches ausweichen. Da das aber so krass den Templateprozessor bemüht, fällt es für den portablen embedded-bereich aus, schätze ich.
    Für Deinen Fall bleibt also nur, Observer-Schnittstellen für die einzelnen Treiber festzulegen und sie dann im Treiber in einer Liste zu verwalten, die während einer ISR abgewandert wird.


Log in to reply