Interrupts are asynchronous hardware signals that can be handled in software. They are handled by the microcontroller immediately, therefore interrupting whatever was being executed before the interrupt.
Interrupts can be generated by different events, such as exceptions, timer counter overflows, incoming messages on a bus, GPIO level change etc... All these events generate an Interrupt REQuest (IRQ), which is managed in software by the corresponding IRQHandler().
This page gives some quick information on how to use Interrupts with MIOSIX in STM32 boards.
Step 1: Enable IRQ Generation
Some Interrupts, called unmaskable interrupts, are enabled by default and are handled by the OS, e.g. exceptions.
Other interrupts have to be explicitly enabled. In STM32 boards, this typically means that you have to set a register of the peripheral to tell it to generate an interrupt when a certain event occurs.
1.1 Timer peripherals
For TIMER peripherals in STM32 you ave to set the DIER
register. You can find this kind of information is the microcontroller's datasheet.
// enable interrupt on Update Event (overflow) of TIM2
TIM2->DIER |= TIM_DIER_UIE;
// start the timer
TIM2->SR &= ~TIM_SR_UIF;
TIM2->CR1 |= TIM_CR1_CEN;
1.2 GPIO interrupts (EXTI peripheral)
To generate an interrupt on a GPIO rising/falling edge you will have configure the EXTI and SYSCFG->EXTICR registers. Pins that end with the same number are part of the same interrupt line (e.g. PA1, PB1, PC1 -> line 1). Each line can be associated to only one pin (e.g. for line 1 you can generate an interrupt for PA1 or PB1 etc...).
This is an example of how to enable interrupt generation on the falling edge of PC4
. More details can be found in the SYSCFG section of the STM32 reference manual.
// Clear the mask on line 4
EXTI->IMR |= EXTI_IMR_MR4;
// Trigger the interrupt on a falling edge of line 4
EXTI->FTSR |= EXTI_FTSR_TR4;
// Trigger the interrupt on a rising edge of line 4
// EXTI->RTSR |= EXTI_RTSR_TR4;
EXTI->PR |= EXTI_PR_PR4; // Reset pending register of line 4
// Assign the interrupt of line 4 to GPIOC (=> PC4)
SYSCFG->EXTICR[2] = 0x2;
Step 2: Enable IRQ Handling
After enabling the interrupt generation, you also have to enable the interrupt handling in the NVIC (Nested Vector Interrupt Controller), giving it a priority. More information on NVIC can be found on the datasheet.
// Enable the interrupt called EXTI4
NVIC_EnableIRQ(EXTI4_IRQn);
// Set its priority to 15
NVIC_SetPriority(EXTI4_IRQn, 15);
Step 3: Writing a IRQHandler
In the core/stage_1_boot.cpp
file of your board (located in miosix-kernel/miosix/arch/<ARCH>/<BOARD>/
) all the possible IRQHandlers are defined. They are all defined with __attribute__((weak))
: this means that if you provide your own implementation of the same function in a .cpp file and compile it, the default one will be overridden by yours.
To do this you will have to write the following code, which contains some magic related to GCC name mangling, in a .cpp file.
// You need to define the IRQHandler you want to override as "naked".
// It must have the same name of the IRQHandler in the stage_1_boot file.
void __attribute__((naked)) EXTI4_IRQHandler()
{
saveContext();
// write the mangled name of the function that you want to call
// in the form: _Z<LENGTH_OF_FUNCTION_NAME><FUNCTION_NAME><ARGUMENTS('v'=void)>
asm volatile("bl _Z20EXTI4_IRQHandlerImplv");
restoreContext();
}
// This is the function executed. It has to be declared as "used".
void __attribute__((used)) EXTI4_IRQHandlerImpl()
{
EXTI->PR |= EXTI_PR_PR4; // Always reset pending register!!!
// Write me!
}