Lesson 15: Interrupts

What is Interrupts?

You can read this article: "Interrupts" for detailed information about interrupt.


Interrupts on the TI Tiva LaunchPad

When an interrupt occurs, the ARM Cortex CPU will do the following sequence of five events:

  1. Finish the current instruction
  2. Push eight registers on the stack: R0, R1, R2, R3, R12, R14 (LR), R15 (PC) and xPSR with R0 on top. If the floating point unit (FPU) is active, an additional 18 words will be pushed on the stack. The stack frame layout is shown as the following diagram
  3. The EXC_RETURN value is loaded into the R14 (LR) on interrupt entry. The EXC_RETURN bits [31:5] are all set. Bits [4:1] provide information on the return stack, bit [0] is always 1 on Cortex-M meaning Thumb mode. Click here to see the detail EXC_RETURN values.
  4. The xPSR [7:0] (IPSR ISR Number) is set to the vector number being processed
  5. The R15 (PC) is loaded with the address of the ISR (vector)

 irq stack s


Nested Vectored Interrupt Controller, NVIC

Interrupts on the Cortex-M are controlled by the Nested Vectored Interrupt Controller (NVIC). The NVIC supports:

  • 78 interrupts
  • A programmable priority level of 0 ~ 7 for each interrupt
    A higher level corresponds to a lower priority, so level 0 is the highest interrupt priority
  • Grouping of priority values into group priority and sub-priority fields
    This grouping divides each interrupt priority register entry into two fields:
    • An upper field that defines the group priority
    • A lower field that defines a sub-priority within the group
  • Interrupt tail-chaining
    This mechanism speeds up exception servicing. On completion of an exception handler, if there is a pending exception that meets the requirements for exception entry, the stack pop is skipped and control transfers to the new exception handler.
  • An external Non-maskable interrupt (NMI)

The following definitions for NVIC registers are used in Keil μVision:

  • NVIC->IP[240]: Interrupt Priority Register NVIC_PRIn (RW)
  • NVIC->ISER[8]: Interrupt Set Enable Register NVIC_ENn (RW)
  • NVIC->ICER[8]: Interrupt Clear Enable Register NVIC_DISn (RW)
  • NVIC->ISPR[8]: Interrupt Set Pending Register NVIC_PENDn (RW)
  • NVIC->ICPR[8]: Interrupt Clear Pending Register NVIC_UNPENDn (RW)
  • NVIC->IABR[8]: Interrupt Active bit Register NVIC_ACTIVEn (WO)
  • NVIC->STIR: Software Trigger Interrupt Register NVIC_SWTRIG (WO)

Interrupt Vectors

Each interrupt (including exceptions, resets, software interrupts, and hardware interrupts) has an associated 32-bit vector that points to the memory location where the ISR handling the interrupt is located. 

In the Keil C, the vectors are defined in the Startup.s file.

Interrupt Priority

The NVIC_PRIn_R registers provide 3-bit priority fields for each interrupt. This allows the interrupt priority level for each device from 0 to 7, with 0 being the highest priority.

n = Integer( Interrupt Number / 4 )

p = ( Interrupt Number % 4 ) =
0: INTA (bit.7 to bit.5)
1: INTB (bit.15 to bit.13)
2: INTC (bit.23 to bit.21)
3: INTD (bit.31 to bit.29)

Bits in PRIn Register 31 ~ 29 28 ~ 24 23 ~ 21 20 ~ 16 15 ~ 13 12 ~ 8 7 ~ 5 4 ~ 0
INTD 00000 INTC 00000 INTB 00000 INTA 00000
Interrupt Number Interrupt [4n + 3] Interrupt [4n + 2] Interrupt [4n + 1] Interrupt [4n]

NVIC->IP[n] |= (PriorityNumber << INTp_BIT);

For example, if the system needs to setup priority for PWM0 Generator 0 interrupt.The interrupt number of PWM0 Generator 0 is 10. Calculate n value:

n = Integer( 10 / 4 ) = 2  ==> PRI2 register

p = ( 10 % 4) = 2 ==> INTC

The priority level must be write to NVIC_PRI2_R [23:21] register.
The following C code shows how to set priority 3 for ADC Sequence 0 interrupt :

NVIC->IP[2] |= (3UL << 21);

Enable Interrupt Through NVIC

After set the priority for the interrupt, then the interrupt must be enabled in the NVIC_ENm_R register.

Each NVIC_ENm_R register has 32-bits, and each bit controls one interrupt number. Using the following formula to find out NVIC_ENm_R  register number and the bit number to enable the interrupt on NVIC.

m = interrupt number / 32

b = interrupt number % 32

NVIC->ISER[m] |= (1UL << b);

For example, too enable ADC Sequence 0 interrupt on the NVIC. The interrupt number of ADC Sequence 0 is 48.

m = 48 / 32 =1   ==> EN1 register

b = 48 % 32 = 16  ==> bit 16

You need to set NVIC_EN1_R [16] = 1 to enable interrupt for ADC Sequence 0.
The following C code shows how to enable interrupt for ADC Sequence 0 :

NVIC->ISER[1] = (1UL << 16);

Disable Interrupt Through NVIC

To disable interrupts, you need to write '1' to the corresponding bit in the NVIC_DISm_R register.

NVIC->ICER[1]  = (1UL << 16);

Interrupt Service Routine or Interrupt Handler

You need to implement an ISR or Handler to process the interrupt request. The following code shows an ISR for GPIO Port B that looks like a regular function with no input parameters and no return value.

void GPIOB_Haqndler(void) {
    // Your ISR code for GPIO Port B here
}

Keil μVision has defined the handler name for each interrupt request. You must use exactly the same handler name as defined. Check the name of the Interrupt Handler from the above Interrupt Table (or find it in the startup_TM4Cxx.s file).

Most of ISR must acknowledge the interrupt in software by clearing the flag that caused the interrupt. After clear the interrupt trigger flag, the peripheral can accept the next interrupt request to CPU.