Course contentsShow
← Embedded Systems & Computer Architecture
Lesson 315 of 9364. Peripheral Interfacing and ControlFree lesson

Managing Shared Data Between ISRs and Mainline

Learners will implement volatile variable qualifiers and critical section protection to safely share data between interrupt contexts and the main application loop.

In this lesson, you will learn how to prevent data corruption when your main application loop and interrupt service routines (ISRs) need to access the same variable simultaneously.

The Shared Data Problem

When you read or write a variable in your main() loop, the compiler optimizes that code, often keeping the value in a CPU register for speed. However, an interrupt can trigger at any moment, forcing the CPU to jump to an ISR. If that ISR modifies the same variable, your main() loop will be unaware of the change because it is still working with the old, "cached" value in its register. This mismatch leads to race conditions and unpredictable system behavior.

Using volatile

To fix the compiler optimization issue, we use the volatile keyword. Marking a variable as volatile tells the compiler: "This value can change outside the normal flow of the program, so do not cache it in a register." Every time you access a volatile variable, the CPU must fetch the actual value from its memory address.

// Force the compiler to re-read 'sensor_value' from memory every time
volatile uint16_t sensor_value;

void ADC_IRQHandler(void) {
    // The ISR updates the data directly
    sensor_value = ADC_GetResult();
}

Protecting Critical Sections

volatile ensures the CPU sees the most recent value, but it does not prevent a "read-modify-write" error. If your main() loop is performing a calculation on a variable (like count++) and an interrupt fires halfway through the operation, the interrupt might overwrite the value before main() finishes, causing your update to be lost.

To prevent this, you must protect the variable by disabling interrupts briefly—a process called creating a critical section.

uint16_t current_data;

// Inside main loop:
__disable_irq();           // 1. Block interrupts
current_data = sensor_value; // 2. Access the shared data safely
__enable_irq();            // 3. Re-enable interrupts

By wrapping the access in these blocks, you guarantee that the ISR cannot interrupt the operation, ensuring the data remains consistent and reliable throughout the execution of your program.

Key Takeaway: Use volatile to prevent compiler register-caching and create critical sections with interrupt masking to ensure data integrity during shared access.