I want to use the CAC unit on this thread and my first issue is that there's not any FSP support for that function in our core. There's also not a good way to add the interrupt handler I need without modifying core files. So I started a new thread to doc this part and I'll link it back. This issue is going to repeat itself and I want to have a good set of notes for next time.
Interrupts on the RA4M1
The interrupt controller works differently on the RA4M1 chip on the UNO-R4 than it does on the old AVR micro on the UNO-R3. On the old chip the interrupt vector table was set in memory and there was an entry for each possible interrupt source. If you wanted to service an interrupt then you would set the pointer in the appropriate slot in the vector table to your function and then enable the interrupt in whatever register turned it on.
With the new chip, there are non-maskable interrupts that work the same way as the old chips. These cover things like watchdog timer interrupts and voltage monitors. But there are also a number of ICU Event Link slots that can be used as interrupts where you can select the source. This allows us to have a lot more possible interrupt sources than there are slots in the vector table. We just use the ones we want.
On our RA4M1 we get 32 of these slots controlled by the IELSR[0-31] registers. Each register has a 8 bit selection for the event link. This chooses the interrupt source for that interrupt. For example if you set it to 0x5D then this interrupt will service the overflow interrupt for GPT0.
A list of interrupt sources can be found in Table 13.4
Some of these interrupts can do some neat extra things. For instance, some of them can also trigger the DTC or DMA to automatically transfer data without the CPU. This might be handy for a display driver where you have a timer interrupt that is refreshing a display buffer or something. Using the DMA or DTC that could be done without code.
Interrupt Code
The problem that we have here is that everything is controlled through the IRQManager
class in IRQManager.h
. It contains code that is specific to all sorts of interrupts, but it does not cover them all. For instance, I want to use the CAC interrupt and it is not available here.
I could add code elsewhere to add the even link I want, but I am afraid to mess with this table without letting this IRQManager know. It has an index called last_interrupt_index
so it knows where to put the next IRQ. If I start writing to the table and then some other code adds a new IRQ, then it would overwrite the one I just added.
If we look at all the different entries in addPeripheral
or any of the addTimer...
interrupt functions they all seem to follow a standard pattern. I think the thing to do will be to start here and add a function that allows us to add a general ISR as long as we have the mask.
This is the function I'm adding
// allows to put any maskable interrupt into the table and keep last_interrupt_index up to date.
bool IRQManager::addMaskableInterrupt(uint8_t eventMask, Irq_f fnc /*= nullptr*/) {
/* getting the address of the current location of the irq vector table */
volatile uint32_t *irq_ptr = (volatile uint32_t *)SCB->VTOR;
/* set the displacement to the "programmable" part of the table */
irq_ptr += FIXED_IRQ_NUM;
bool rv = true;
__disable_irq();
*(irq_ptr + last_interrupt_index) = (uint32_t)fnc;
R_ICU->IELSR[last_interrupt_index] = eventMask;
R_BSP_IrqDisable((IRQn_Type)last_interrupt_index);
R_BSP_IrqStatusClear((IRQn_Type)last_interrupt_index);
NVIC_SetPriority((IRQn_Type)last_interrupt_index, 12);
R_BSP_IrqEnable ((IRQn_Type)last_interrupt_index);
last_interrupt_index++;
__enable_irq();
return rv;
}
It compiles. I'll be back to this soon to see if it works.