2016-04-19 15 views
0

Ich schreibe ein kleines Framework für meine eingebettete Robotik-Projekt. Wir laufen auf einem Xilinx Zynq FPGA (FPGA und ARM Cortex A9 eingebettet auf einem einzigen Chip)Anfügen einer Methode an eine Interrupt-Routine in C++ 11

Die Idee ist relativ einfach. In meinem main() möchte ich den Interrupt initialisieren und dann die Routine (eine run() Methode) von der Haupt auch aufrufen. Irgendwie muss die run() Methode an den Interrupt angehängt werden, während sie sich an verschiedenen Stellen im Code befinden.

Der Interrupt wird in einer statischen Timer Klasse initialisiert. Innerhalb initInterrupt() ist die interruptRoutine() angebracht, die auch in der Timer Klasse ist. Letztendlich zwingt uns das, dass unser gesamter Code innerhalbinterruptRoutine() laufen muss.

Irgendwie wollen wir eine run() Methode innerhalb der main.cc Datei haben (über der Timer Klasse), die alle Logik und alle anderen Funktionsaufrufe speichert.

Wie können wir das erreichen?

Die main.cc:

int main() { 
    Timer::initInterrupt(); 
    Timer::run([] { 
     // All logic goes here? 
     // Very hopeful thinking that this is possible... 
    }); 
} 

Die Timer-Klasse:

class Timer { 
public: 
    static void initInterrupt(void); 
    static void interruptRoutine(void*); 
    static void run(); 

}; 

    /** 
    * Initialize main interrupt routine 
    */ 
    void initInterrupt(void) { 
    // Declare pointers 
    XScuTimer_Config* ConfigPtr; 
    XScuGic_Config* IntcConfig; 

    // Initialize timers by looking up config and initializing with that config 
    ConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID); 
    XScuTimer_CfgInitialize(&TimerInstance, ConfigPtr, ConfigPtr->BaseAddr); 
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); 
    XScuGic_CfgInitialize(&IntcInstance, IntcConfig, 
      IntcConfig->CpuBaseAddress); 

    // Initialize exception handling 
    Xil_ExceptionInit(); 
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, 
      (Xil_ExceptionHandler) XScuGic_InterruptHandler, &IntcInstance); 

    // Connect interrupt routine to exception handler 
    XScuGic_Connect(&IntcInstance, TIMER_IRPT_INTR, 
      (Xil_ExceptionHandler) interruptRoutine, (void *) (&TimerInstance)); 

    // Enable interrupts 
    XScuGic_Enable(&IntcInstance, TIMER_IRPT_INTR); 
    XScuTimer_EnableInterrupt(&TimerInstance); 

    // Enable exception handler 
    Xil_ExceptionEnable(); 

    // Set auto reload so timer reloads when interrupt is cleared 
    XScuTimer_EnableAutoReload(&TimerInstance); 

    // Set timer value 
    XScuTimer_LoadTimer(&TimerInstance, TIMER_LOAD_VALUE); 

    // Start interrupt 
    XScuTimer_Start (&TimerInstance); 
} 
/** 
* main interrupt routine 
*/ 
inline void Timer::interruptRoutine(void *CallBackRef) { 
    // Define pointer to timer 
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef; 

    // If timer is expired, clear interrupt status 
    if (XScuTimer_IsExpired(TimerInstancePtr)) { 
     XScuTimer_ClearInterruptStatus(TimerInstancePtr); 

     // Currently all the application logic is handled in here 
    } 
} 

inline void Timer::run(Callback){ 
    // We want all our application logic to be handled in here but it has to be called from the main() 
} 

Antwort

0

Entweder ist dies einfacher als Sie denken, oder komplexer als Sie erklärt haben!

Timer::run() tut Sachen: -

inline void Timer::run() 
{ 
... complex stuff... 
} 

Ihre Interrupt-Routine Anrufe run()

inline void Timer::interruptRoutine(void *CallBackRef) { 
    // Define pointer to timer 
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef; 

    // If timer is expired, clear interrupt status 
    if (XScuTimer_IsExpired(TimerInstancePtr)) { 
     XScuTimer_ClearInterruptStatus(TimerInstancePtr); 

    run(); 
} 

Und wollen Sie run() von Haupt nennen ...

int main() { 
    Timer::initInterrupt(); 
    Timer::run(); 
} 

Es ist nicht klar, ob Sie möchten dievermeidenLogik innerhalb der Timer Klasse? Wenn dies der Fall ist, machen Sie run() eine abstrakte virtuelle Methode einer Timer Instanz, dann leiten Sie MyTimer von Timer ab und fügen Sie Ihren Code zu seiner run() Methode hinzu.

+0

Ich denke, es ist ein bisschen komplexer als ich erklärte. Ich möchte nicht, dass meine 'Timer'-Klasse irgendetwas über irgendeine Logik weiß. Es folgt SRP. Der Hauptverantwortliche delegiert alles, aber ich muss irgendwie den ganzen Code bei einem Interrupt ausführen. Das Problem ist, dass der einzige Ort, wo ich es für jetzt tun kann, innerhalb der 'Timer' Klasse in der' interruptRoutine() ' – Ortix92

+0

@ Ortix92 - was ist mit der virtuellen Methode in einer abgeleiteten Klasse und ich erwähnte? Würde das für dich funktionieren? Ansonsten verwende einfach eine reguläre Callback-Funktion. – Roddy

1

Fügen Sie in der Timer-Klasse ein boolesches Flag "Timer abgelaufen" hinzu. Dieses Flag wird vom Timer-Callback gesetzt.

Das Hauptprogramm prüft dann dieses Flag durch eine Getter-Funktion in der Timer-Klasse.

Wenn das Flag gesetzt ist, löscht main das Flag durch eine Setter-Funktion in der Timer-Klasse und führt dann den Code aus, der ausgeführt werden muss.

Bitte beachten Sie, dass der Zugang zur Flagge atomar sein muss! Kein Problem innerhalb der ISR, vorausgesetzt, es kann nicht unterbrochen werden, aber es könnte ein Problem in den erwähnten Setter/Getter-Funktionen sein.

Beachten Sie, dass das Obenstehende vielleicht die gebräuchlichste Art ist, Interrupt zu entwerfen.Sie werden vermeiden wollen, den eigentlichen Code aus dem ISR auszuführen, wenn Ihre Echtzeitanforderungen nicht sehr streng sind.

+0

Da wir mit etwa 20kHz Sampling betreiben, ist unsere Echtzeitanforderung ziemlich schwierig. Aber wir werden es auf jeden Fall versuchen. Können Sie vielleicht ein bisschen Beispielcode zeigen, wie Sie das implementieren können? Vielen Dank! – Ortix92

1

Dies ist möglich, aber Sie benötigen einige Primitive, um vom Interrupt-Handler an die Timer::run()-Methode zu signalisieren. Das erste Problem besteht darin, dass der aktuelle Code keinen this Zeiger für den Timer-Interrupt bereitstellt. Sie scheinen den Code xscutimer.h zu verwenden. Sie haben diesen Code,

// Connect interrupt routine to exception handler 
XScuGic_Connect(&IntcInstance, TIMER_IRPT_INTR, 
     (Xil_ExceptionHandler) interruptRoutine, (void *) (&TimerInstance)); 

Der TimerInstance Wert ist aus dem Code-Schnipsel unklar. Es sollte jedoch eine statische Timer Klasseninstanz sein. Auch Timer::interruptRoutine sollte eine static Methode sein und nicht inline. Dann sollte Ihre Timer Klasse ein XScuTimer Mitglied haben. Außerdem benötigen Sie eine Art von Signalisierung für die Run-Methode und idealerweise eine yield()-Typ-Funktion. Dann würde der Lauf aussehen,

Timer::run() { 
    while(1) { 
    while(no_work) 
     wfi(); //ARM typically has a wfi or wait for interrupt. 
    // do work 
    } 
} 

Die yield ideal hat eine Warten auf Interrupt Anweisung an ihn ist Kernenergieverbrauch zu minimieren. Sie können nur auf no_work drehen, wenn Sie sich nicht um den Stromverbrauch kümmern. Natürlich muss no_work von der Timer::interruptRoutine auf false eingestellt werden.

Die Variable no_work sollte frei von Sperren oder Interruptsafe sein. sig_atomic_t oder andere Mittel (abhängig von Ihrer Arbeit) sind angemessen. Entweder hat das Toolkit diese Dinge oder Sie müssen sie selbst implementieren. Bitte geben Sie einen Verweis auf die von Ihnen verwendete xilinx-Bibliothek an. Diese Frage zu einem global variable and ISR kann nützlich sein. Für Sie Cortex-A-CPU können Sie ldrex und strex mit all ihren Vorbehalten verwenden (nicht so einfach zu bedienen, wie die Leute denken) für ziemlich flexible ISR/Mainline-Signalisierung. Abhängig von Ihrem Compiler/Ihrer Bibliothek kann es durch die C++ lock-free/atomic Funktionalität oder etwas, das in den Xilinx Bibliotheken passend ist, zur Verfügung gestellt werden; Diese können ldrex und strex in der zugrunde liegenden Implementierung verwenden und Sie vermeiden, die Komplexität ihrer Verwendung kennen zu müssen. Die MMU muss verwendet werden, damit sie arbeiten können.

+0

Ich habe meinen Code aktualisiert. Ich habe in der Tat ein Stück Code in der 'initInterrupt() 'fehlt Ich werde auf jeden Fall schauen, was Sie vorgeschlagen haben. Es scheint jedoch immer noch so zu sein, dass die gesamte Arbeit innerhalb der Timer-Klasse ausgeführt wird, was genau das ist, was ich vermeiden möchte (Lesbarkeit und Struktur des Codes). – Ortix92

+0

Wenn Sie den Code in 'Timer' nicht wollen, müssen Sie Informationen an' initInterrupt() 'oder an die' Timer :: run() 'Methode übergeben. Man könnte es einfach 'Timer :: waitToRun()' nennen. Dies setzt den Code in Ihr 'main()' ... 'while (Timer :: waitToRun()) {/ * Hauptcode * /}'. Es sieht so aus, als ob Sie den Code zur Ausführung weitergeben möchten. Ich sehe nicht, was das bringt, außer einer Menge schicker C++ - Syntax, die die Dinge wahrscheinlich nur langsamer und sicherlich komplexer für die meisten Leute machen wird. –

Verwandte Themen