STM32 External Interrupt
STM32 External Interrupt
STM32 External Interrupt
In my earlier course based on STM32 GPIOs I showed how to flash a LED with variable
delay times. That example was based on polling method where the code continuously
monitored the logic state of a GPIO input pin attached to a push button to determine
the delay amount. Obviously, that won’t be an efficient technique when a program will
be of a considerable size and complexity. This is simply so because the CPU will have to
check the GPIO’s logic state every time the super-loop (while (1) loop in the main
function) repeats and the push button will also not be responsive during the software
delay function calls. Thus, the overall performance is poor and not real-time. To get rid
of these issues, we’ll need to use external interrupts – a vital feature in every common
microcontroller.
STM32F1xx series are ARM Cortex M3 based MCUs. The Cortex M3 based MCUs have a
sophisticated and yet easy to use interrupt system called the Nested Vectored Interrupt
Controller (NVIC). It ensures low latency and high performance. There are several
features of the NVIC and these are handled by the compiler. Our job is simply to enjoy
the lightning-fast interrupt responses owing to the NVIC. In many MCUs’ interrupt
system, interrupt priority can be set and Reset has the highest interrupt priority over
anything else. The same things go for STM32s too. However, at present I’m not going
to go that deep as that’s not needed for now. In some upcoming post maybe, I’ll discuss
the NVIC in details. As per STM32’s reference manuals for more information on
exceptions and NVIC programming read Chapter 5 Exceptions and Chapter 8 Nested
Vectored Interrupt Controller of the ARM Cortex-M3 Technical Reference Manual. There
are other interrupts that are related to RTC, timer, etc. We won’t also look into them in
this post. We will learn about them when we learn about the related hardware with
them.
1
Internal Structure
There are 16 external interrupt lines with separate interrupt vector addresses that
are connected with GPIO pins. Thus, there are 16 multiplexers connected to the
NVIC and are named as External Interrupt/Event Controllers, EXTI0, EXTI1, etc. up
to EXTI15. We know from our previous encounters with STM32s that STM32 GPIOs
are 16bit wide and so we can see the reason why there are 16 lines of EXTI. GPIO
pins of the same order are grouped together and connected to an EXTI channel. For
instance, EXTI1 is connected to PA1, PB1, etc. and not like PA0, PA1, etc. This is
why at any given instance we can have an external interrupt in only one of
connected GPIO pins of that EXTI mux. For example, when we need to use EXTI2,
we can use either PA2, PB2, PC2, etc. but not PA2, PB2, etc. simultaneously. Thus,
an entire GPIO port or port pins from different GPIO ports can be configured as
external interrupts. Interrupts in all pins is an unnecessary stuff. We can also decide
when to sense an interrupt – on rising/falling or both edges. All these settings are
Spratley stored, allowing high flexibility.
The EXTI lines 0 – 4 are individually and directly connected to the NVIC interface
while the remaining higher order EXTI lines are grouped into two – one ranging from
EXTI 5 to 9 and the other from 10 – 15. The upper EXTI pins are thus not individually
and directly connected to the NVIC. Owing to this EXTI 0 – 4 have separate unique
interrupt vector addresses while the rest have two separate interrupt vector
2
addresses. This grouping doesn’t have too much serious issues with normal external
interrupt uses and in fact I found out that it is rather useful as I don’t have to code
for separate interrupt routines. A good example of its use is with an interface. One
such routine can take care of digital inputs coming from a keyboard or joystick. In
my example code this is what I demonstrated.
Registers:
The interrupt system of STM32 is not too much complex and only a few registers
need to be taken care of after setting GPIO pins. The first thing to do is to setup
which EXTI lines to use and which port pins to use.
AFIO_EXTICRx registers determine this. However please note that we must enable
AFIO functionality from RCC_APB2ENR register first before can use it. If you can
remember from my post on GPIOs, I stated that in the code the very first thing we
do is simply enable the peripherals we need by setting appropriate RCC_APB bits.
As seen in the register map above EXTI lines are grouped as 4 bits and value of
these 4 bits select port pins. 0000 means PA[x] pin, 0001 PB[x] pin and so on.
Secondly, we need to select the edge needed to invoke an interrupt. Triggering edge
can be rising, falling or both. This is done be setting the required bits
of EXTI_FTSR and EXTI_RTSR registers. By default, the bits in these registers are
cleared and so no edge is selected after reset. To select falling edge
required EXTI_FTSR bits are set. Similarly, to select rising edge
required EXTI_RTSR bits are set. To detect both edges both registers are set.
The next thing to do is to unmask only those interrupts that we need. This is done
by setting needed bits of the EXTI_IMR register.
Finally, we need to set the NVIC in order to enable interrupt. We just need to specify
the sources of interrupt request. In MikroC for ARM compiler this process is simply
3
done with just two lines of code. We also need an interrupt service routine more
commonly known as ISR function to tell the MCU what to do during interrupt. Inside
the interrupt we need to check which interrupt is pending by reading the interrupt
request pending register – EXTI_PR. After reading the EXTI_PR bit that caused
the interrupt we have to clear the pending request by setting that bit.
Application: In this application we will use timer 2 to manage the refresh time and
the external interruption which may arise from the PB2 pin on a falling edge and
the result will be displayed simultaneously on the low D-port and the TFT screen
to figure out this aspect of structured programming. We will also split the
application into 3 complementary files to facilitate their implementation phase in
a smooth manner.
1) Create the header file: “Delay_F107.h” in which this set of functions should be contained.
void TIM2_Config (void);
void Delay_US (unsigned int us);
void Delay_MS (unsigned int ms);
/*****************************************************/
2) The C language definition of all function in the previous header file.
#include "Delay_F107.h"
void TIM2_Config (void)
{
/************** STEPS TO FOLLOW *****************
1. Enable Timer clock
2. Set the prescalar and the ARR
3. Enable the Timer, and wait for the update Flag to set
************************************************/
RCC_APB1ENR.TIM2EN=0x1; // enable clock for TIM2
TIM2_ARR = 65535; // ARR value
TIM2_PSC = 72-1; // Prescalar value
TIM2_CR1.EN=1; // enable timer
while (TIM2_SR.UIF==0); // !(TIM2_SR & (1<<0))UIF: Update interrupt flag..
}
4
TIM2_CNT = 0;
while (TIM2_CNT < us);
}
5
void Interrupt_Config (void)
{
/*************>>>>>>> STEPS FOLLOWED <<<<<<<<************
1. Enable the AFIO CLOCK bit in RCC register
2. Configure the EXTI configuration Regiter in the AFIO
3. Disable the EXTI Mask using Interrupt Mask Register (IMR)
4. Configure the Rising Edge / Falling Edge Trigger
5. Set the Interrupt Priority
6. Enable the interrupt
********************************************************/
RCC_APB2ENR |= (1<<0); // Enable AFIO CLOCK
AFIO_EXTICR1 = 0x00000100; // -> EXTI2 line for PB2
EXTI_IMR = 0x00000004; // Bit[2] = 1 -> Disable the Mask on EXTI 2
EXTI_RTSR = 0x00000000; // Disable Rising Edge Trigger for PB2
EXTI_FTSR =0x00000004; // Enable Falling Edge Trigger for PB2
NVIC_SetIntPriority (IVT_INT_EXTI2, _NVIC_INT_PRIORITY_LVL1);
NVIC_IntEnable(IVT_INT_EXTI2); // Enable Interrupt
EnableInterrupts();
}
4) Moreover, we used the TFT screen to display the counter value of the number of
interruptions occurring so that the application is explicit and intelligible.