Ich codiere derzeit für Cortex M0 / M4 und der Ansatz, den wir in C ++ verwenden (es gibt kein C ++ - Tag, daher ist diese Antwort möglicherweise nicht zum Thema gehörend), lautet wie folgt:
Wir verwenden eine Klasse, CInterruptVectorTable
die alle Interrupt-Serviceroutinen enthält, die im tatsächlichen Interrupt-Vektor der Steuerung gespeichert sind:
#pragma location = ".intvec"
extern "C" const intvec_elem __vector_table[] =
{
{ .__ptr = __sfe( "CSTACK" ) }, // 0x00
__iar_program_start, // 0x04
CInterruptVectorTable::IsrNMI, // 0x08
CInterruptVectorTable::IsrHardFault, // 0x0C
//[...]
}
Die Klasse CInterruptVectorTable
implementiert eine Abstraktion der Interruptvektoren, sodass Sie zur Laufzeit verschiedene Funktionen an die Interruptvektoren binden können.
Die Schnittstelle dieser Klasse sieht folgendermaßen aus:
class CInterruptVectorTable {
public :
typedef void (*IsrCallbackfunction_t)(void);
enum InterruptId_t {
INTERRUPT_ID_NMI,
INTERRUPT_ID_HARDFAULT,
//[...]
};
typedef struct InterruptVectorTable_t {
IsrCallbackfunction_t IsrNMI;
IsrCallbackfunction_t IsrHardFault;
//[...]
} InterruptVectorTable_t;
typedef InterruptVectorTable_t* PinterruptVectorTable_t;
public :
CInterruptVectorTable(void);
void SetIsrCallbackfunction(const InterruptId_t& interruptID, const IsrCallbackfunction_t& isrCallbackFunction);
private :
static void IsrStandard(void);
public :
static void IsrNMI(void);
static void IsrHardFault(void);
//[...]
private :
volatile InterruptVectorTable_t virtualVectorTable;
static volatile CInterruptVectorTable* pThis;
};
Sie müssen die Funktionen erstellen, die in der Vektortabelle gespeichert sind, static
da der Controller keinen this
Zeiger bereitstellen kann, da die Vektortabelle kein Objekt ist. Um dieses Problem zu pThis
umgehen, haben wir den statischen Zeiger in der CInterruptVectorTable
. Bei Eingabe einer der statischen Interrupt-Funktionen kann auf den pThis
Zeiger zugegriffen werden, um Zugriff auf Mitglieder des einen Objekts von zu erhalten CInterruptVectorTable
.
Jetzt können Sie im Programm SetIsrCallbackfunction
mit den einen Funktionszeiger auf eine static
Funktion setzen, die aufgerufen werden soll, wenn ein Interrupt auftritt. Die Zeiger werden in der gespeichert InterruptVectorTable_t virtualVectorTable
.
Und die Implementierung einer Interrupt-Funktion sieht folgendermaßen aus:
void CInterruptVectorTable::IsrNMI(void) {
pThis->virtualVectorTable.IsrNMI();
}
Das ruft also eine static
Methode einer anderen Klasse auf (die es sein kann private
), die dann einen anderen static
this
Zeiger enthalten kann, um Zugriff auf Mitgliedsvariablen dieses Objekts zu erhalten (nur eine).
Ich denke, Sie könnten ähnliche IInterruptHandler
Zeiger auf Objekte erstellen und mit diesen static
this
verknüpfen und speichern, sodass Sie den Zeiger in all diesen Klassen nicht benötigen . (Vielleicht versuchen wir das in der nächsten Iteration unserer Architektur)
Der andere Ansatz funktioniert gut für uns, da die einzigen Objekte, die einen Interrupt-Handler implementieren dürfen, diejenigen innerhalb der Hardware-Abstraktionsschicht sind und wir normalerweise nur ein Objekt für jeden Hardware-Block haben, so dass es gut funktioniert, mit static
this
-zeigern zu arbeiten. Und die Hardware-Abstraktionsschicht bietet noch eine weitere Abstraktion für Interrupts, ICallback
die dann in der Geräteschicht über der Hardware implementiert wird.
Greifen Sie auf globale Daten zu? Sicher, aber Sie können die meisten benötigten globalen Daten wie die this
Zeiger und die Interrupt-Funktionen privat machen .
Es ist nicht kugelsicher und erhöht den Overhead. Mit diesem Ansatz haben Sie Schwierigkeiten, einen IO-Link-Stack zu implementieren. Wenn Sie jedoch nicht sehr zeitlich begrenzt sind, funktioniert dies recht gut, um eine flexible Abstraktion von Interrupts und Kommunikation in Modulen zu erhalten, ohne globale Variablen zu verwenden, auf die von überall aus zugegriffen werden kann.