Using
Synchronization
Mechanisms
to Protect
Shared Resources
in
MCU-Constrained
Systems
Table of Contents
Table of Contents
1. Introduction
2. Understanding the Problem of Shared
Resources
3. Using volatile for Shared Resources
4. Disabling and Restoring Interrupts (cli() and
sei())
5. Using
ATOMIC_BLOCK(ATOMIC_RESTORESTA
TE) for Critical Sections
6. Mutex Simulation in MCU-Based Systems
7. Protecting Shared Resources with Double
Buffering
8. Using Semaphores for Synchronization
9. Best Practices for Efficient Synchronization
Resources
Table of Contents
3. Using volatile for Shared Resources
4. Disabling and Restoring Interrupts (cli() and
sei())
5. Using
ATOMIC_BLOCK(ATOMIC_RESTORESTA
TE) for Critical Sections
6. Mutex Simulation in MCU-Based Systems
7. Protecting Shared Resources with Double
Buffering
8. Using Semaphores for Synchronization
9. Best Practices for Efficient Synchronization
in Embedded Systems
10.Conclusion
1. Introduction
1. Introduction
In microcontroller-based systems, where
hardware resources are limited,
synchronization is crucial to ensure data
integrity when multiple execution contexts
(such as interrupts, state machines, or tasks)
access shared resources. Without proper
synchronization, race conditions, inconsistent
data, and unpredictable system behavior can
occur.
This article explores essential
synchronization mechanisms for protecting
shared resources in AVR MCUs and similar
constrained embedded systems. We will
discuss techniques such as volatile variables,
occur. 1. Introduction
This article explores essential
synchronization mechanisms for protecting
shared resources in AVR MCUs and similar
constrained embedded systems. We will
discuss techniques such as volatile variables,
atomic operations, mutexes, semaphores,
and double buffering, providing real-world
examples and trade-offs between different
approaches.
Different synchronization techniques are used
depending on the system constraints,
resource access patterns, and real-time
requirements. Let’s explore the most
effective methods.
2. Understanding
the Problem of
Shared Resources
2. Understanding the Problem of
Shared Resources
What are Shared Resources?
In an embedded system, a shared resource is
any variable, memory location, or hardware
peripheral accessed by multiple execution
contexts. Examples include:
• Global variables modified by an ISR and
used in the main loop.
• I2C/SPI peripherals accessed by multiple
state machines.
• ADC readings updated in an ISR while the
main program processes the data.
• UART buffers shared between an
interrupt handler and a background
task.
• 2.
ADC readings updated
Understanding theinProblem
an ISR while
of the
Shared
main program Resources
processes the data.
• UART buffers shared between an
interrupt handler and a background
task.
2. Understanding the Problem of
Shared Resources
Why is Synchronization Needed?
Without proper synchronization:
1. Race Conditions: Two tasks accessing a
shared resource simultaneously can cause
unpredictable results.
2. Data Corruption: Multi-byte values
modified by an ISR can result in
inconsistent data reads.
3. System Instability: Uncontrolled
concurrent access may lead to undefined
behavior, causing the program to crash or
enter an infinite loop.
3. Using volatile
for Shared
Resources
3. Using volatile for Shared
Resources
When to Use volatile
A variable declared volatile tells the compiler
not to optimize it, ensuring it is always fetched
from memory. It is essential for:
• Interrupt-driven variables that change
outside the main execution flow.
• Memory-mapped registers for hardware
peripherals.
Example:
Using volatile to Access an ISR-Updated
Variable
• Memory-mapped registers for hardware
3. Using volatile for Shared
peripherals. Resources
Example:
Using volatile to Access an ISR-Updated
Variable
When volatile is NOT Enough
• Multi-byte variables (e.g., 16-bit or 32-bit on
an 8-bit MCU) may require atomic access.
3. Using volatile for Shared
Resources
When volatile is NOT Enough
• Multi-byte variables (e.g., 16-bit or 32-bit on
an 8-bit MCU) may require atomic access.
• Does NOT provide mutual exclusion (other
contexts may still modify the variable).
• Needs additional protection mechanisms
for safe concurrent access.
4. Disabling and
Restoring
Interrupts (cli()
and sei())
4. Disabling and Restoring
Interrupts (cli() and sei())
For short critical sections, you can disable
interrupts before accessing shared resources
and re-enable them afterward.
Example:
Protecting a Shared Variable with cli() and
sei()
Trade-offs
• Simple and effective for small critical
interrupts before accessing shared resources
4. Disabling and Restoring
and re-enable them(cli()
Interrupts afterward.
and sei())
Example:
Protecting a Shared Variable with cli() and
sei()
Trade-offs
• Simple and effective for small critical
sections.
• Disables all interrupts, which can impact
real-time performance if the critical section
• is too long.
5. Using
ATOMIC_BLOCK(
ATOMIC_RESTOR
ESTATE) for
Critical Sections
5. Using
ATOMIC_BLOCK(ATOMIC_RESTO
RESTATE) for Critical Sections
A more refined approach is using
ATOMIC_BLOCK(ATOMIC_RESTORESTATE
), which disables interrupts only temporarily
and restores their original state afterward.
Example:
Reading a Multi-Byte Sensor Value Atomically
), which disables 5. Using only temporarily
interrupts
ATOMIC_BLOCK(ATOMIC_RESTO
andRESTATE)
restores their
fororiginal stateSections
Critical afterward.
Example:
Reading a Multi-Byte Sensor Value Atomically
Advantages:
• Safer than cli()/sei() because it restores the
previous interrupt state.
• Ensures atomic access to shared
5. Using
ATOMIC_BLOCK(ATOMIC_RESTO
RESTATE) for Critical Sections
Advantages:
• Safer than cli()/sei() because it restores the
previous interrupt state.
• Ensures atomic access to shared
resources. Not suitable for longer critical
sections (still blocks interrupts briefly).
6. Mutex
Simulation in
MCU-Based
Systems
6. Mutex Simulation in MCU-
Based Systems
A mutex (mutual exclusion lock) ensures that
only one task accesses a shared resource at
a time.
Example:
Implementing a Mutex with Interrupt
Protection
Implementing a Mutex with Interrupt
6. Mutex Simulation in MCU-
Protection Based Systems
Advantages:
• Prevents simultaneous access to shared
resources.
6. Mutex Simulation in MCU-
Based Systems
Advantages:
• Prevents simultaneous access to shared
resources.
• Requires careful management to prevent
deadlocks.
7. Protecting
Shared Resources
with Double
Buffering
7. Protecting Shared Resources
with Double Buffering
How It Works
One buffer (active buffer) is used for reading.
Another buffer (working buffer) is updated in
the background.
Buffer swapping ensures consistency.
Example:
Double Buffering for Sensor Readings
the background.
7. Protecting Shared Resources
Buffer swapping ensuresBuffering
with Double consistency.
Example:
Double Buffering for Sensor Readings
Advantages:
• Prevents data corruption during updates.
• No need for disabling interrupts during
reads.
8. Using
Semaphores for
Synchronization
8. Using Semaphores for
Synchronization
Semaphores ensure one process accesses a
resource at a time, but they also allow
signaling between tasks.
Example:
Using a Semaphore to Synchronize ISR and
Main Loop
Using a Semaphore to Synchronize ISR and
8. Using Semaphores for
Main Loop Synchronization
Advantages:
• Efficient for ISR and main loop
coordination.
• Needs careful handling to avoid race
conditions.
9. Best Practices
for Efficient
Synchronization
in Embedded
Systems
9. Best Practices for Efficient
Synchronization in Embedded
Systems
• Minimize shared global variables to reduce
synchronization complexity.
• Use the lightest possible synchronization
method based on MCU constraints.
• Optimize ISR execution time to reduce lock
contention.
• Avoid unnecessary disabling of interrupts
to maintain system responsiveness.
10. Conclusion
10. Conclusion
Protecting shared resources is critical in MCU-
constrained systems to avoid race conditions,
data corruption, and system instability. By
choosing the right synchronization
mechanism—whether atomic operations,
mutexes, double buffering, or semaphores—
developers can build reliable and efficient
embedded applications.