Estoy codificando Cortex M0 / M4 en este momento y el enfoque que estamos utilizando en C ++ (no hay una etiqueta C ++, por lo que esta respuesta puede estar fuera del tema) es la siguiente:
Usamos una clase CInterruptVectorTable
que contiene todas las rutinas de servicio de interrupción que se almacenan en el vector de interrupción real del controlador:
#pragma location = ".intvec"
extern "C" const intvec_elem __vector_table[] =
{
{ .__ptr = __sfe( "CSTACK" ) }, // 0x00
__iar_program_start, // 0x04
CInterruptVectorTable::IsrNMI, // 0x08
CInterruptVectorTable::IsrHardFault, // 0x0C
//[...]
}
La clase CInterruptVectorTable
implementa una abstracción de los vectores de interrupción, por lo que puede vincular diferentes funciones a los vectores de interrupción durante el tiempo de ejecución.
La interfaz de esa clase se ve así:
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;
};
Debe realizar las funciones que están almacenadas en la tabla de vectores static
porque el controlador no puede proporcionar un this
puntero ya que la tabla de vectores no es un objeto. Entonces, para solucionar ese problema, tenemos el pThis
puntero estático dentro del CInterruptVectorTable
. Al ingresar a una de las funciones de interrupción estática, puede acceder al pThis
puntero para obtener acceso a los miembros del único objeto de CInterruptVectorTable
.
Ahora en el programa, puede usar el SetIsrCallbackfunction
para proporcionar un puntero de función a una static
función que se llamará cuando ocurra una interrupción. Los punteros se almacenan en el InterruptVectorTable_t virtualVectorTable
.
Y la implementación de una función de interrupción se ve así:
void CInterruptVectorTable::IsrNMI(void) {
pThis->virtualVectorTable.IsrNMI();
}
Entonces eso llamará a un static
método de otra clase (que puede ser private
), que luego puede contener otro static
this
puntero para obtener acceso a las variables miembro de ese objeto (solo una).
Supongo que podría construir e interactuar IInterruptHandler
y almacenar punteros en los objetos, por lo que no necesita el static
this
puntero en todas esas clases. (tal vez lo intentemos en la próxima iteración de nuestra arquitectura)
El otro enfoque funciona bien para nosotros, ya que los únicos objetos permitidos para implementar un controlador de interrupciones son aquellos dentro de la capa de abstracción de hardware, y generalmente solo tenemos un objeto para cada bloque de hardware, por lo que está bien trabajar con static
this
punteros. Y la capa de abstracción de hardware proporciona otra abstracción a las interrupciones, llamada ICallback
que luego se implementa en la capa de dispositivo sobre el hardware.
¿Accede a datos globales? Claro que sí, pero puede hacer que la mayoría de los datos globales necesarios sean privados, como los this
punteros y las funciones de interrupción.
No es a prueba de balas, y agrega gastos generales. Tendrá dificultades para implementar una pila IO-Link con este enfoque. Pero si no está extremadamente apretado con los tiempos, esto funciona bastante bien para obtener una abstracción flexible de las interrupciones y la comunicación en los módulos sin usar variables globales que sean accesibles desde cualquier lugar.