Search code examples
cstm32interruptcan-bususart

STM32_lib: Is there a function to pass the IRQ_handler routine to?


I am trying to add an IRQ_handler routine for when there's an interrupt, for example CAN or USART. I wish to avoid writing directly to memory addresses and wonder if there is a function in either CMSIS or STM32_lib that does that job (that I've missed or for some reason can't find). After I've initialized and enabled NVIC, created an IRQ routine, where do I go from there?

To summarize: Can I pass a pointer to this function as an argument somewhere or do I have to do a function pointer to the NVIC_vtor address offset?

void USART1_IRQhandler(void)
{
/* handle IRQ routine*/
}

I've tried searching for IRQ keywords in all the include files put can't really find anything other than IRQn, which only helps me enable interrupts not handle them.

Note: I do not use CubeIDE and only use STM32F4xx_StdPeriph_Driver + CMSIS files


Solution

  • Do you need to be able to set these handlers at run time dynamically which involves relocating the vector table to RAM, or can they be set statically, in which case the vector table can be in flash?

    If the latter then all you need do is use the CMSIS defined handler names (such as your example USART1_IRQhandler(void)) and the linker will build the vector table for you. Then you only need enable the interrupt, the handler address will already be in the vector table.

    If you need to set interrupts at runtime, then create a 256 byte aligned array of pointers to void functions, copy the 256 entries from the default vector table to it (this ensures there is a default handler for everything including the exception handlers), then relocate the vector table to your RAM table by setting . Setting a new vector is then simply a case of setting the appropriate table element indexed by IRQ number to the function pointer you wish to handle the interrupt.

    On Cortex-M regular C functions can be use directly as interrupt handlers, there are no special keywords or extensions necessary.

    Vector table relocation is discussed at https://www.keil.com/pack/doc/CMSIS/Core/html/using_VTOR_pg.html with example code.

    You can also use the CMSIS NVIC_SetVectorTable() function (declared in CMSIS misc.h).

    A function to set a vector in a RAM table is rather trivial:

    void setIrqVector( int irq, void (handler*)(void) )
    {
        ((void(*)(void))SCB->VTOR)[irqn] = handler ;
    }
    

    Which of course will only work if SCB->VTOR refers to a RAM based vector table. You could of course use the function pointer array itself rather than SCB->VTOR, which would avoid the cast, but would mean that the array must have wider scope than necessary. It could be a local static in whatever function that sets the table to SCB->VTOR.

    On STM32 the default (reset state) is for the vector table to be located at 0x08000000 (base address of flash). If that is the vector table your application will use, you have no choice but to have the table generated statically, and no special action is necessary to set a vector at run time.

    You might relocate to a RAM vector table if you needed to select different handlers for a single IRQ at runtime or if somehow the required handlers cannot be determined at runtime. These scenarios are fairly unusual I would say. You would also use relocation, but most likely to a different area of flash if you were implementing a bootloader, where you need to switch from the bootloader's table to that of the loaded application.