Подброшу:
enum IRQnumber {
IRQ_ADC1Group1 = 15,
IRQ_CAN1level0 = 16,
IRQ_HET1level2 = 24,
IRQ_MibSPI1level1 = 26,
IRQ_CAN1level1 = 29,
IRQ_CAN2level0 = 35,
IRQ_MibSPI3level1 = 38,
IRQ_CAN2level1 = 42,
IRQ_CAN1IF3 = 44,
IRQ_CAN3level0 = 45,
IRQ_CAN2IF3 = 46,
IRQ_CAN3level1 = 55,
IRQ_MibSPI5level1 = 56,
IRQ_CAN3IF3 = 60,
IRQ_I2C = 66,
IRQ_HET2level2 = 73,
};
enum IRQtype {IRQ = 0, FIQ = 1};
class irq_capable {
public:
irq_capable(IRQnumber irqnumber, IRQtype irqtype);
virtual bool handlerIRQ() = 0;
virtual ~irq_capable();
};
namespace test {
void emulateIRQ(IRQnumber n);
} /* namespace test */
namespace {
irq_capable *registeredObjects[VIM_CHANNELS] = {nullptr};
bool callISR(IRQnumber n) {return registeredObjects[n]->handlerIRQ();}
void adc1Group1Interrupt() ;
void can1Level0Interrupt() ;
void het1Level2Interrupt() ;
void mibspi1Level1Interrupt() ;
void can1Level1Interrupt() ;
void can2Level0Interrupt() ;
void mibspi3Level1Interrupt() ;
void can2Level1Interrupt() ;
void can1IF3Interrupt() ;
void can3Level0Interrupt() ;
void can2IF3Interrupt() ;
void can3Level1Interrupt() ;
void mibspi5Level1Interrupt() ;
void can3IF3Interrupt() ;
void i2cInterrupt() ;
void het2Level2Interrupt() ;
void adc1Group1Interrupt() {callISR(IRQ_ADC1Group1);}
void can1Level0Interrupt() {callISR(IRQ_CAN1level0);}
void het1Level2Interrupt() {callISR(IRQ_HET1level2);}
void mibspi1Level1Interrupt() {callISR(IRQ_MibSPI1level1);}
void can1Level1Interrupt() {callISR(IRQ_CAN1level1);}
void can2Level0Interrupt() {callISR(IRQ_CAN2level0);}
void mibspi3Level1Interrupt() {callISR(IRQ_MibSPI3level1);}
void can2Level1Interrupt() {callISR(IRQ_CAN2level1);}
void can1IF3Interrupt() {callISR(IRQ_CAN1IF3);}
void can3Level0Interrupt() {callISR(IRQ_CAN3level0);}
void can2IF3Interrupt() {callISR(IRQ_CAN2IF3);}
void can3Level1Interrupt() {callISR(IRQ_CAN3level1);}
void mibspi5Level1Interrupt() {callISR(IRQ_MibSPI5level1);}
void can3IF3Interrupt() {callISR(IRQ_CAN3IF3);}
void i2cInterrupt() {callISR(IRQ_I2C);}
void het2Level2Interrupt() {callISR(IRQ_HET2level2);}
typedef void (*voidfunc)();
voidfunc getVectorFromIRQnumber(IRQnumber n)
{
switch (n) {
case IRQ_ADC1Group1: return adc1Group1Interrupt;
case IRQ_CAN1level0: return can1Level0Interrupt;
case IRQ_HET1level2: return het1Level2Interrupt;
case IRQ_MibSPI1level1: return mibspi1Level1Interrupt;
case IRQ_CAN1level1: return can1Level1Interrupt;
case IRQ_CAN2level0: return can2Level0Interrupt;
case IRQ_MibSPI3level1: return mibspi3Level1Interrupt;
case IRQ_CAN2level1: return can2Level1Interrupt;
case IRQ_CAN1IF3: return can1IF3Interrupt;
case IRQ_CAN3level0: return can3Level0Interrupt;
case IRQ_CAN2IF3: return can2IF3Interrupt;
case IRQ_CAN3level1: return can3Level1Interrupt;
case IRQ_MibSPI5level1: return mibspi5Level1Interrupt;
case IRQ_CAN3IF3: return can3IF3Interrupt;
case IRQ_I2C: return i2cInterrupt;
case IRQ_HET2level2: return het2Level2Interrupt;
default: throw exc("Unknown handler for given IRQ number in irq::<anonymous>::getVectorFromIRQnumber");
}
}
} /* anonymous namespace */
irq_capable::irq_capable(IRQnumber irqNumber, IRQtype irqtype)
{
if (registeredObjects[irqNumber]) {
throw exc("IRQ is already registered");
}
registeredObjects[irqNumber] = this;
const voidfunc handler = getVectorFromIRQnumber(irqNumber);
if (IRQ == irqtype) {
registerIRQvectorInVIM(irqNumber, handler);
} else {
registerFIQvectorInVIM(irqNumber, handler);
}
}
irq_capable::~irq_capable()
{
size_t idx = 0;
while (registeredObjects[idx] != this) {
++idx;
}
registeredObjects[idx] = nullptr;
}
namespace test {
void emulateIRQ(IRQnumber n)
{
registeredObjects[n]->handlerIRQ();
}
} /* namespace test */
Пример использования:
namespace {
class iadc : private irq::irq_capable {
volatile adcBase& adc;
uint32_t *ram;
void acknowledgeIRQforGroup1();
uint32_t getDataFromRAM(uint32_t offset);
void init();
void enableGroup1ConversationCompleteIRQ();
void startGroup1ConversationForAllChannes();
void operator=(const iadc& );
iadc(const iadc& );
virtual bool handlerIRQ() override;
public:
iadc(volatile adcBase& adc, uint32_t *ram);
virtual ~iadc(){}
};
iadc::iadc(volatile adcBase &adc, uint32_t *ram) :
irq_capable(irq::IRQ_ADC1Group1, irq::IRQ),
adc(adc),
ram(ram)
{
init();
enableGroup1ConversationCompleteIRQ();
for (size_t i = 0; i < maxChannels; ++i) convertHandlers[i].physObjectHanler = nullptr;
startGroup1ConversationForAllChannes();
}
/*
* Обработчик IRQ по завершению преобразования
*/
bool iadc::handlerIRQ()
{
acknowledgeIRQforGroup1();
for (size_t i = 0; i < maxChannels; ++i) {
ADCdata[i] = getDataFromRAM(i);
}
return false;
}
} /* anonymous namespace */
void create(void *baseAddress, void *RAMaddress)
{
if (ptrIADC) {
throw exc("Error in creating internal ADC: already created");
}
ptrIADC = new iadc(*reinterpret_cast<volatile adcBase *>(baseAddress), reinterpret_cast<uint32_t *>(RAMaddress));
}
Такой подход позволяет изолировать работу с прерываниями: конкретные вектора (жёстко прописанные в VIM) вызывают обработчики для тех объектов, которые зарегистрировали себя как источники прерываний. Легко эмулируется любое прерывание через вызов test::emulateIRQ(), что существенно облегчает тестирование и разработку с использованием TDD. Объекты, которые используют прерывания, могут быть полностью скрыты из глобального пространства имён; вектора прерываний также скрыты из глобального пространства, но имеется возможность эмуляции любого прерывания (я эту фишку использовал при тестировании рабочего кода на ПК). Но у меня Cortex-R4 и VIM, где я могу требуемые вектора прямо в регистры VIM загрузить, в отличие от Cortex-M, там придётся держать вектора в ОЗУ и адрес таблицы векторов указывать в соответствующем системном регистре.
Да, кстати, такому коду абсолютно пофиг на режим работы: ARM или THUMB. Никаких дополнительных ключей компиляции использовать не надо.