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

168 Views Asked by At

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

3

There are 3 best solutions below

0
Clifford On BEST ANSWER

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.

4
Lundin On

ARM uses somewhat confusing nomenclature here speaking of "IRQ". IRQ simply means interrupt request. It is normally used in the context of normal "maskable" interrupts. A function executed upon interrupt and called by the hardware is known as a interrupt service routine (ISR).

All interrupt service routines need to be registred in an interrupt vector table. On Cortex M this table is (together with default stack pointer etc) located from address 0 and upwards. Upon receiving a hardware interrupt trigger, the MCU will call the routine registred in the vector table - so if you didn't register one, nothing will happen.

CMSIS provides "weak linkage" names for all default interrupt service routines in the vector table. "Weak linkage" meaning that the default ISR will only get linked unless the programmer didn't write a routine with the very same name - in which case that one will get linked instead.

So you should use the recommended name when writing your ISR and placing it inside a driver. Depending on compiler, you may have to use non-standard keywords to mark something as an ISR. Since ISRs are called by the MCU and not by the program, they generally have an unique calling convention and the MCU core is responsible for stacking certain things upon calling the ISR. In addition, the C compiler might stack additional registers.

And for the same reasons, you can/should never call and ISR from your application. And ISR takes no parameters and return no value. All communication with it happens through file scope variables.

So you should only need to define a function void USART1_IRQhandler (void) in your driver and implement it according to best practices - which is another story.


0
wek On

In SPL (Standard Peripheral Library, i.e. the now deprecated "original" library from ST), you have startup files mentioned by others in [STM32F4xx_DSP_StdPeriph_Lib_V1.6.0]\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates[tolchain-specific subdirectories]

JW