0% found this document useful (0 votes)
40 views

(T) EE2028 Topic 9B Interrupt Programming

This document discusses interrupt programming for Cortex-M4 microcontrollers. It describes the various registers involved in interrupt configuration like the ISER, ICER, ISPR, and ICPR registers for enabling, disabling, and clearing interrupts. It also discusses priority registers, active bit registers, and exception handling registers. Finally, it outlines the basic steps to set up and respond to interrupts, including setting priority levels, implementing interrupt service routines, and configuring external devices to trigger interrupts.

Uploaded by

Alex Carmona
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
40 views

(T) EE2028 Topic 9B Interrupt Programming

This document discusses interrupt programming for Cortex-M4 microcontrollers. It describes the various registers involved in interrupt configuration like the ISER, ICER, ISPR, and ICPR registers for enabling, disabling, and clearing interrupts. It also discusses priority registers, active bit registers, and exception handling registers. Finally, it outlines the basic steps to set up and respond to interrupts, including setting priority levels, implementing interrupt service routines, and configuring external devices to trigger interrupts.

Uploaded by

Alex Carmona
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

9B.

Interrupt Programming

Rajesh Panicker

[T]EE2028

Acknowledgement:
- Slides modified from the original slides by A/Prof. C K Tham, ECE, NUS
Interrupt Configuration
◼ Each external interrupt has several associated registers
◼ Set Enable and Clear Enable Registers
◼ Set Pending and Clear Pending Registers
◼ Priority Registers
◼ Active Bit Registers
◼ A number of other registers affect int. processing
◼ Exception-masking registers (PRIMASK, FAULTMASK, and
BASEPRI)
◼ Vector Table Offset Register
◼ Software Trigger Interrupt Register (STIR)
◼ Priority group in Application Interrupt & Reset Control
Register

2
Interrupt Set Enable Registers (ISER) and
Interrupt Clear Enable Registers (ICER)

◼ NVIC_ISER (32 bits, RW) is used to


◼ Enable interrupts
◼ Determine which interrupts are currently enabled
◼ NVIC_ICER (32 bits) is used to
◼ Disable interrupts
◼ Determine which interrupts are currently disabled
◼ NVIC can have more than one NVIC_ISER/NVIC_ICER. Each bit
of the registers is corresponding to one interrupt
◼ Enable/Disable an interrupt:
◼ To enable an interrupt (set enable bit), write 1 to NVIC_ISER
◼ To disable an interrupt (clear enable bit), write 1 to NVIC_ICER

3
Interrupt Set Pending Registers (ISPR) &
Interrupt Clear Pending Registers (ICPR)
◼ NVIC_ISPR (32 bits) register is used to
◼ Force interrupts into pending state
◼ Determine which interrupts are currently pending (R/W)
◼ NVIC_ICPR (32 bits) register is used to
◼ Clear pending interrupts
◼ Determine which interrupts are currently pending (R/W)
◼ NVIC can have more than one NVIC_ISPR/NVIC_ICPR.
Each bit is corresponding to one interrupt input
◼ Interrupt pending status can be accessed through
NVIC_ISPR/NVIC_ICPR
◼ To pend an interrupt, write 1 to corresponding bit in NVIC_ISPR
◼ To cancel a current interrupt pending, write 1 to corresponding bit in
NVIC_ICPR
◼ Writing to NVIC_ISPR has no effect on an int. which is already pending or is
disabled
◼ Writing to NVIC_ICPR has no effect on an active int., unless it is also pending

4
Interrupt Priority Registers (IPR),
Interrupt Active Bit Registers (IABR)
◼ NVIC Interrupt Priority Registers (IPRs) are 8-bit registers, though
only some of the MSBs are implemented depending on the SoC
◼ Accessible as IP[i], where i=0 to 81 for STM32 as there are 82
external interrupts
◼ Each register is used to assign a priority level (from 0 to 255) to
interrupts: 0 is the highest priority

◼ Read NVIC_IABR to determine which interrupts are active. Each


external interrupt has a bit in NVIC_IABRs
◼ When CPU starts an interrupt handler, its active bit is set to 1, and
then cleared when return is executed
◼ If two interrupts are nested, both are active
◼ Active = has started, has not finished. An interrupt being active doesn’t
necessarily mean that the processor is currently executing the ISR
◼ NVIC_IABRs for external interrupts are read only
5
Registers for System Exceptions

◼ So far, we saw the various Ixx registers which deals with external
interrupts. For system exceptions, the corresponding controls are
considered a part of the System Control Block (SCB) rather than NVIC
◼ Priority of system exceptions with programmable priority levels are
set in System Handler Priority Registers (SHPRs), accessible as
SHP[i], where i=0 to 11 for all CM4 chips
◼ The Interrupt control and state register (ICSR) can be to
◼ Set and clear the pending status of system exceptions including SysTick,
PendSV, and NMI
◼ Determine the currently executing exception/interrupt number by
reading VECTACTIVE
◼ System handler control and state register (SHCSR)
◼ Usage faults, memory management (MemManage) faults, and bus fault
exceptions are enabled by the SHCSR
◼ The pending status of faults and active status of most system exceptions
are also available from this register
6
PRIMASK, FAULTMASK and BASEPRI

◼ PRIMASK is a 1-bit register (default = 0)


◼ When it is set, it allows only NMI and hard fault exceptions. Other
interrupts and exceptions (having PRI  0) are masked (disabled)
◼ FAULTMASK is a 1-bit register (default = 0)
◼ When it is set, it allows only NMI. All the interrupts and exceptions
including hard fault exception (having PRI  -1) are masked
◼ BASEPRI is an 8-bit register
◼ used to disable all exceptions/interrupts having PRI  BASEPRI. Writing a
0 to BASEPRI disables BASEPRI
void __enable_irq(); // Clear PRIMASK
void __disable_irq(); // Set PRIMASK
void __set_PRIMASK(uint32_t priMask); // Set PRIMASK to value priMask
uint32_t __get_PRIMASK(void);// Read the PRIMASK value
void __set_FAULTMASK(uint32_t faultMask); //Set or clear FAULTMASK
uint32_t __get_FAULTMASK(void); // Read the FAULTMASK value
void __set_BASEPRI(uint32_t basePri); // Set the base priority register
uint32_t __get_BASEPRI(void); // Return the content of the base priority register
__set_BASEPRI(0x60); // Disable interrupts with priorities from 0x60-0xFF

7
Accessing Cortex-M4 NVIC registers
CMSIS functions Description
void NVIC_EnableIRQ(IRQn_Type IRQn) Enables an interrupt or exception.
void NVIC_DisableIRQ(IRQn_Type IRQn) Disables an interrupt or exception.
void NVIC_SetPendingIRQ(IRQn_Type IRQn) Sets the pending status of interrupt or exception to 1.
void NVIC_ClearPendingIRQ(IRQn_Type IRQn) Clears the pending status of interrupt or exception to 0.
Reads the pending status of interrupt or exception. This
uint32_t NVIC_GetPendingIRQ(IRQn_Type
function returns non-zero value if the pending status is
IRQn)
set to 1.
uint32_t NVIC_GetActive(IRQn_Type IRQn); Read the active bit for an external interrupt
void NVIC_SetPriority(IRQn_Type IRQn, Sets the priority of an interrupt or exception with
uint32_t priority) configurable priority level to 1.
Reads the priority of an interrupt or exception with
uint32_t NVIC_GetPriority(IRQn_Type IRQn) configurable priority level. This function return the
current priority level.
void NVIC_SetPriorityGrouping(uint32_t
Set the Priority Grouping in NVIC Interrupt Controller
PriorityGroup);
uint32_t NVIC_EncodePriority (uint32_t Encode the priority for an interrupt. The returned
PriorityGroup, uint32_t PreemptPriority, priority value can be used for NVIC_SetPriority(...)
uint32_t SubPriority); function
The input parameter IRQn is the IRQ number

8
Interrupt Programming Steps
1. Set up the priority group setting
◼ By default priority group setting is zero (7 MSBs used for preempt priority)
2. Set up the priority level of the interrupt
◼ By default, all interrupts are at priority level 0 (highest)
3. Implement the ISR (details 2 slides later)
4. The interrupting device/peripheral should be configured
to generate an interrupt for the specific condition of interest
◼ The interrupt should be enabled inside the interrupting device - the exact
way in which this is done is device dependent
◼ If the interrupt is from an external device (e.g. a sensor) which is routed
to NVIC through an EXTI, we will need to enable both EXTI interrupts and
the device interrupts
◼ EXTI interrupts are enabled by writing to EXTI_IMR, EXTI_RTSR / EXTI_FTSR
etc.
◼ External device (e.g. sensor) interrupt is enabled by writing to the
appropriate register through the normal data channel connecting the
device to the processor (e.g. I2C) Not to be confused with the interrupts generated
by the I2C interface (I2C_EV and I2C_ER) 9
Interrupt Programming Steps
5. Clear the pending status
◼ This step is optional, but desirable in many circumstances
6. Enable the interrupt at NVIC
◼ The interrupt for the device/peripheral should be enabled in NVIC so that
NVIC can deal with the interrupt when it occurs
◼ Else, the interrupt can go pending, but will not become active
◼ Once everything is set up, interrupt triggering itself typically does
not require any action from software
◼ The interrupt service routine is invoked automatically by the
hardware provided appropriate conditions are met
◼ It is not ‘called’ explicitly from the main program
◼ However, there are circumstances where the software triggers an
interrupt explicitly (e.g. for testing). An external interrupt can be triggered
artificially by
◼ Writing to ISPR
◼ via Software Trigger Interrupt Register (STIR) in NVIC
Exceptions such as SVC (supervisor call) are triggered by software. SVC is used to transfer control to the Operating System (OS) 10
ISR Implementation
1. PUSH all the registers modified by the handler, other than those which are
automatically pushed by the hardware before the handler is invoked (R0–R3, R12,
LR, PC, and xPSR)
◼ If ISR is implemented in C, this is taken care of implicitly
2. If a peripheral generates interrupts due to multiple causes which go into NVIC as a
single interrupt (i.e., the interrupt status bits are OR-ed together internally), identify
the exact cause / reason for the interrupt
◼ This can typically be done by checking the interrupt status bit(s) of the peripherals
◼ EXTI 5-9 are or-ed together and is recognized by NVIC as a single interrupt. Further,
each pin can trigger an interrupt because of rising edge and/or falling edge
◼ Identification of EXTI triggered and cause (rising/falling) can be done by reading EXTI_PR
◼ I2C_EV interrupt can be triggered due to several conditions such as RXNE, TXIS etc.
◼ The exact condition can be determined by checking I2C_ISR
◼ If the interrupt is from an external device (e.g. a sensor) which is routed to NVIC
through an EXTI, we might need to read appropriate register(s) within the device to
find out the cause
◼ This is done through the normal data channel connecting the external device to the
processor – there is no NVIC involvement
◼ The cause of interrupt from accelerometer is identified by reading appropriate registers
through the I2C interface
11
ISR Implementation …
3. Do the BARE MINIMUM required to deal with the cause
◼ If the interrupt was triggered because a new data is available - grab the data
and store it in a buffer
◼ You might also want to set a boolean flag to indicate that a new data is
available. This flag can be polled in the main program, which then does further
processing on the data and clears the flag
◼ As a general rule, the higher the priority/urgency of the interrupt, the more
effort you should put in to make the ISR shorter (execution time)
◼ Generally, an interrupt which has a lower inter-arrival time is more urgent, as

the likelihood of missing it is higher if not processed before it arrives again.


This also depends on the application requirements
◼ Do not use any "blocking" code in the ISR. Blocking code refers to a code
which 'waits' until certain condition is satisfied
◼ e.g., waiting for a button press, HAL_Delay() by a certain amount of time

◼ Any variable/flag modified by the ISR should be qualified as volatile (for


example, volatile uint32_t uwTick;) to ensure that the main function or its
callees always get the latest data, i.e., reads and writes are not optimized
away based on the assumption that it is not modified outside the main function

12
ISR Implementation …
4. Clear the interrupt
◼ Clearing an interrupt is the process of telling/causing the interrupting device to
de-assert the interrupt. The exact way in which it is done is dependent on the
specific peripheral, and on the specific condition inside the peripheral which
triggered the interrupt. This typically involves one of the following
◼ reading a data register within the interrupting device (for data-ready type interrupts)
◼ writing a data register within the device (for transmit register empty type interrupts)
◼ reading a status register (for errors/status interrupts)
◼ writing to an interrupt clear register (for errors/status interrupts)
◼ Some interrupts arrive as pulses of finite duration; doesn’t require any explicit clearing
◼ If the interrupt is from an external device (e.g. a sensor) which is routed to NVIC
through an EXTI, both EXTI int. and the external device int. should be cleared
◼ The clearing process is done through the normal data channel connecting the
external device to the processor – there is no NVIC involvement
◼ The interrupt from accelerometer is cleared by reading / writing appropriate registers
within the accelerometer through I2C interface
◼ Clearing the EXTI interrupt is done by writing to EXTI_PR through the internal bus
◼ Clearing pending status ≠ disabling an interrupt ≠ clearing an interrupt
5. POP all the registers pushed at the beginning of the handler before exiting

13
Only the NVIC side (dealing with interrupts) configuration is shown.
EXTI configuration (generation of interrupts) was covered in Chapter 6

Example (Demo)
SetPriority() expects right-aligned priority, but IPR is left-aligned

NVIC_SetPriorityGrouping(5);
◼ PriorityGroup=5
NVIC_SetPriority(EXTI15_10_IRQn, 0x0C);
◼ PreemptPriority=0b11 // Set priority level to 0xC0 = 1100 0000
◼ SubPriority=0b00 NVIC_ClearPendingIRQ(EXTI15_10_IRQn);
◼ Full IPR contents: NVIC_EnableIRQ(EXTI15_10_IRQn);
◼ 0b11000000 = 0xC0

#include "stm32l475xx.h " //contains definition of __NVIC_PRIO_BITS, xxxIRQns etc.


int main(void) {
uint32_t ans, PG=5, PP=0b11, SP=0b00;
NVIC_SetPriorityGrouping(5);
ans = NVIC_EncodePriority(PG,PP,SP); // ans = 0x0C
NVIC_SetPriority(EXTI15_10_IRQn,ans);
/* Priority in the IPR for IRQn = 40 set to 0xC0 (SetPriority shifts it up for us)
NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
*/
}
14
Example (Demo)
stm32l4xx_it.c
void EXTI15_10_IRQHandler(void) __weak void HAL_GPIO_EXTI_Callback(uint16_t
{ GPIO_Pin)
/* For vector table to be set up correctly, {
the interrupt handler name should match the /* Prevent unused argument(s) compilation
interrupt handler name in warning */
startup_stm32l475vgtx.s */ (void)(GPIO_Pin);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11); /* NOTE: This function should not be
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); modified, when the callback is needed, the
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14); HAL_GPIO_EXTI_Callback could be implemented
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15); in the user file (main.c) */
} }

void HAL_GPIO_EXTI_IRQHandler(uint16_t main.c


GPIO_Pin)
{ // Implemented by the user in main.c
/* EXTI line interrupt detected */ // this one overrides the _weak function
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != with the same name
0x00u) void HAL_GPIO_EXTI_Callback(uint16_t
{ GPIO_Pin)
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); {
HAL_GPIO_EXTI_Callback(GPIO_Pin); // Actual handler implementation here
} // following the guidelines in slide 11-13
} }

15
THE END
Questions?

16

You might also like