Chapter 16: Interrupts part 2

šŸ“š CMPE1250: Interrupts Part 2

By the end of this lesson, students will be able to:

  • Explain what interrupts are and why embedded systems rely on them
  • Compare polling and interrupt‑driven designs
  • Identify the components involved in interrupt handling (peripheral, NVIC, ISR)
  • Locate IRQ numbers and ISR names in STM32 documentation
  • Configure and enable a basic interrupt on the STM32G0
  • Write a minimal, correct Interrupt Service Routine (ISR)

1ļøāƒ£ What Are Interrupts?

An interrupt is a hardware‑driven event that temporarily pauses normal program execution so the CPU can respond to something important. When an interrupt occurs:

  • The CPU finishes the current instruction
  • It saves its execution context
  • It jumps to a special function called an Interrupt Service Routine (ISR)
  • The ISR handles the event
  • The CPU restores context and resumes where it left off This allows the microcontroller to react immediately to events such as:
  • Incoming serial data
  • Timer overflows
  • External pin changes
  • ADC conversion completion Interrupts are the embedded world’s version of: ā€œStop what you’re doing — something needs attention right now.ā€

2ļøāƒ£ Why Use Interrupts Instead of Polling?

Polling requires the CPU to constantly check if something has happened:

while (1) 
{
    if (USART2->ISR & USART_ISR_RXNE) {
        // new data arrived
    }
}

This wastes CPU time and risks missing fast events. Interrupts solve this:

  • The CPU can do useful work (or sleep)
  • Events are handled immediately
  • No constant checking
  • More deterministic timing
  • Lower power consumption Interrupts are essential for responsive, real‑time systems.

3ļøāƒ£ Interrupt Masking and Priorit

Masking

  • Interrupts can be individually enabled or disabled
  • Disabled interrupts can still go pending, but they won’t fire
  • Some interrupts (like NMI) cannot be masked

Priority If two interrupts occur at the same time, the one with higher priority is serviced first. The STM32G0 uses a simple priority scheme:

  • Lower IRQ number → higher natural priority
  • Priorities can be changed, but beginners rarely need to This ensures that critical events (e.g., emergency stop) preempt less important ones (e.g., UART RX).

4ļøāƒ£ The Interrupt Workflow (The 5‑Step Pattern)

Every interrupt on the STM32 follows the same pattern:

  1. Configure the peripheral
  2. Enable the peripheral’s interrupt source
  3. Enable the NVIC IRQ
  4. Write the ISR with the correct name
  5. Globally enable interrupts

This pattern will be used throughout the course.

5ļøāƒ£ Finding the Vector Table and IRQ Numbers

To write an ISR, you need two things:

🧩 A. The Interrupt Vector Table

The vector table maps interrupt sources to function names.

Example entry (from stm32g0b1xx_Vectors.s):

DCD     USART2_IRQHandler     ; USART2 global interrupt

This tells you:

  • The ISR must be named USART2_IRQHandler

  • You cannot invent your own name

SEGGER Embedded Studio includes this file under System Files.

🧩 B. IRQ Numbers (IRQn_Type)

To enable an interrupt using CMSIS, you need its IRQ number:

NVIC_EnableIRQ(USART2_IRQn);

These are defined in:

  • stm32g0b1xx.h (part of the STM32G0 package)

  • Enum: IRQn_Type

Example:

USART2_IRQn = 28,   /*!< USART2 global interrupt */

You can quickly jump to the definition using Go to Definition in SEGGER

6ļøāƒ£ CMSIS NVIC Function

  • CMSIS provides portable functions to control interrupts:
  • Global interrupt control
__disable_irq();   // Disable all maskable interrupts
__enable_irq();    // Enable all maskable interrupts

NVIC control Common functions include:

  • NVIC_EnableIRQ(IRQn)

  • NVIC_DisableIRQ(IRQn)

  • NVIC_SetPendingIRQ(IRQn)

  • NVIC_ClearPendingIRQ(IRQn)

  • NVIC_GetPendingIRQ(IRQn)

  • NVIC_SetPriority(IRQn, priority)

These functions abstract away CPU‑specific assembly instructions

7ļøāƒ£ Writing an ISR (Interrupt Service Routine)

An ISR is always:

  • A void function

  • With no parameters

  • Using the exact name from the vector table

Example

</pre> void USART2_IRQHandler(void) { if (USART2->ISR & USART_ISR_RXNE) { char c = USART2->RDR; // reading clears the flag // process character } } </pre>

ISR Rules

  • Keep it short
  • Clear the interrupt flag
  • Never block (no long loops, no delays)
  • Avoid heavy computation

8ļøāƒ£ Putting It All Together — USART RX Example

  • Step 1 — Configure USART2 (baud rate, pins, enable RX)

  • Step 2 — Enable RX interrupt

USART2->CR1 |= USART_CR1_RXNEIE;

  • Step 3 — Enable NVIC IRQ

NVIC_EnableIRQ(USART2_IRQn);

  • Step 4 — Write the ISR
void USART2_IRQHandler(void)
{
    if (USART2->ISR & USART_ISR_RXNE) {
        char c = USART2->RDR;
        // handle character
    }
}
  • Step 5 — Enable global interrupts
__enable_irq();

šŸ“Œ Summary

Interrupts allow the microcontroller to react immediately to important events without wasting CPU time.

To use them effectively, you must:

  • Know the peripheral’s interrupt flag
  • Enable the interrupt in the peripheral
  • Enable the corresponding NVIC IRQ
  • Write the ISR with the correct name
  • Clear the flag inside the ISR
  • Keep the ISR short and efficient

This foundation prepares you for deeper topics such as:

  • NVIC priority
  • Tail‑chaining
  • DMA interrupts
  • Timer interrupts
  • SysTick
  • Multi‑source IRQs