|
|
Interrupts are hardware signals that can be handled by the software. They can be generated by different events, such as exceptions, timer counter overflows, incoming messages on a bus, GPIO level change etc... These events generate Interrupt REQuests (IRQ), which are managed in the software by defining a corresponding IRQHandler() function.
|
|
|
Interrupts are asynchronous **hardware signals** that can be handled in software. They are handled by the CPU 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. Other interrupts have to be explicitly enabled by the software. In STM32 boards this typically means that you have to set a register of the peripheral you are using to tell it to generate an interrupt. For example, for TIMER peripherals in STM32 you ave to set the `DIER` register. You can find this kind of information is the microcontroller's datasheet.
|
|
|
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.
|
|
|
|
|
|
To generate an interrupt on a GPIO rising/falling edge you will have configure the EXTI and SYSCFG->EXTICR registers as follows:
|
|
|
#### 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.
|
|
|
|
|
|
```cpp
|
|
|
// Clear the mask on the wanted line
|
|
|
// 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.
|
|
|
|
|
|
```cpp
|
|
|
// Clear the mask on line 4
|
|
|
EXTI->IMR |= EXTI_IMR_MR4;
|
|
|
|
|
|
// Trigger the interrupt on a falling edge
|
|
|
// Trigger the interrupt on a falling edge of line 4
|
|
|
EXTI->FTSR |= EXTI_FTSR_TR4;
|
|
|
|
|
|
// Trigger the interrupt on a rising edge
|
|
|
// EXTI->RTSR |= EXTI_RTSR_TR0;
|
|
|
// Trigger the interrupt on a rising edge of line 4
|
|
|
// EXTI->RTSR |= EXTI_RTSR_TR4;
|
|
|
|
|
|
EXTI->PR |= EXTI_PR_PR4; // Reset pending register
|
|
|
EXTI->PR |= EXTI_PR_PR4; // Reset pending register of line 4
|
|
|
|
|
|
// Assign the interrupt to a GPIO (in this case PC4)
|
|
|
// Assign the interrupt of line 4 to GPIOC (=> PC4)
|
|
|
SYSCFG->EXTICR[2] = 0x2;
|
|
|
```
|
|
|
|
|
|
Note that each interrupt line can be associated only to a pin ending with the same number: for example, `EXTI4` can be associated either to PA4 or PB4 or PC4 ecc... More details can be found in the SYSCFG section of the STM32 reference manual.
|
|
|
|
|
|
### Step 2: Enable IRQ Handling
|
|
|
|
|
|
After eneable 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.
|
|
|
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.
|
|
|
|
|
|
```cpp
|
|
|
// Enable the interrupt called EXTI4
|
... | ... | @@ -41,7 +60,11 @@ NVIC_SetPriority(EXTI4_IRQn, 15); |
|
|
|
|
|
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](https://en.wikipedia.org/wiki/Name_mangling), in your separate .cpp file.
|
|
|
To do this you will have to write the following code, which contains some magic related to [GCC name mangling](https://en.wikipedia.org/wiki/Name_mangling), in a .cpp file.
|
|
|
|
|
|
:warning: **RESET PENDING BIT REGISTER** in your IRQHandler, otherwise you will enter an infinite loop.
|
|
|
|
|
|
:warning: **DON'T USE BLOCKING OR "HEAVY" FUNCTIONS (e.g. printf)** inside your interrupt. Remember that the CPU will stall for the whole time it will execute your IRQHandler. Most of the times, the only thing you will need in your IRQHandler is to set a global variable or to operate with registers.
|
|
|
|
|
|
```cpp
|
|
|
// You need to define the IRQHandler you want to override as "naked".
|
... | ... | |