Follow our official account to keep the embedded knowledge flowing!
1. Global Variables
Global variables are often regarded as “bad design,” but in embedded systems, they are the most performant communication method. The key is not to avoid their use, but rather to design them correctly.
The issue with global variables is not performance, but rather access control. Their essence is zero-copy shared memory, which is the source of their performance advantage.
Underlying Mechanism of Data Races
Why do data races occur? The essence lies in non-atomic operations being interrupted.
This operation:
volatile uint32_t counter = 0;
counter++; // Appears atomic, but is not
In ARM Cortex-M, this operation is compiled to:
LDR r0, [counter_addr] ; Read current value
ADD r0, r0, #1 ; Increment by 1
STR r0, [counter_addr] ; Write back to memory
If an interrupt occurs between LDR and STR, and the interrupt handler also modifies counter, data loss will occur.
Strategies for Atomic Operations
Strategy 1: Disable Interrupts (suitable for simple data)

Strategy 2: Lock-Free Algorithms (suitable for high-frequency access)
A lock-free design based on memory barriers, used in high-frequency data acquisition systems:

2. Callback Functions
The essence of callback functions is inversion of control, which transfers the decision of “when to execute” from the caller to the callee. This mechanism has unique value in embedded systems.
Underlying Principles of Callback Mechanisms
Traditional function calls are push model: actively calling a function to get a result. Callback functions are pull model: passively waiting for an event to trigger.
This difference brings fundamental architectural advantages:
- Temporal Decoupling: Event generation and processing can occur at different times
- Spatial Decoupling: Event sources and processors can be in different modules
- Many-to-Many Relationships: One event can trigger multiple processors
Deadly Traps of Callback Functions
Trap 1: Blocking Operations in Callbacks
This is the most common mistake I have seen. In a 1kHz sensor sampling system, if the callback function includes a 2ms LCD operation, the system load will reach 200%!
Trap 2: Recursive Callback Calls
A more insidious problem is recursive callbacks. A sensor callback triggers an alarm, and the alarm function reads the sensor again, creating infinite recursion.
Trap 3: Memory Allocation in Callback Functions
Calling malloc in an interrupt context is disastrous, yet many engineers unconsciously do this in callbacks.
High-Performance Callback Design Patterns
Efficient callback design patterns:

Core Design Principles:
- Synchronous Callbacks: Only perform data copying and flag setting, time < 10μs
- Asynchronous Callbacks: Complex processing placed in a task queue, executed by low-priority tasks
- Priority Management: Critical callbacks executed first to ensure real-time performance
This design perfectly combines the flexibility of callback functions with the real-time nature of the system.
3. Asynchronous Communication
The core value of asynchronous communication lies not in technical implementation, but in the shift in system thinking: from “immediate processing” to “delayed processing,” from “synchronous waiting” to “asynchronous response”.
The Essence of Asynchronous Communication: Decoupling in Time Dimension
Synchronous communication is like phone calls: both parties must be online simultaneously, one party blocks waiting for the other to respond.
Asynchronous communication is like email: the sender delivers the message and immediately returns, while the receiver processes it at their own pace.
This difference has profound implications in embedded systems:
Problems with Synchronous Mode:
- High-priority tasks are blocked by low-priority operations
- System response time is unpredictable
- Strong temporal coupling between modules, making it difficult to scale
Advantages of Asynchronous Mode:
- Temporal decoupling: producers and consumers operate independently
- Buffering mechanism: smooth processing of burst data
- Priority isolation: high-priority tasks are unaffected
Design of Message Queues
Traditional dynamic memory allocation methods are anti-patterns in embedded systems:
- Memory fragmentation leads to unpredictable allocation failures
- malloc/free is not reentrant in interrupts
- Memory leak risks are hard to control
Better Design:
- Pre-allocation Strategy: Allocate all memory at system startup
- Fixed-size Messages: Avoid complexities from variable lengths
- Circular Buffers: Efficient FIFO implementation
- Lock-Free Algorithms: Avoid the overhead and deadlock risks of mutexes

Timing Analysis of Asynchronous Communication

Asynchronous communication transforms temporal coupling into spatial coupling, trading a small amount of memory for system real-time performance and scalability. This is a fundamental shift in embedded system architecture design.
4. Summary of Engineering Experience

Core Judging Criteria:
- Real-time Performance First: Strict real-time requirements determine the upper limit of communication methods
- Moderate Complexity: Avoid over-designing for optimization
- Maintainability: Team understanding and maintenance are more important than extreme performance
Common Traps
Trap 1: Callback Hell
Nested callback functions create complex call chains that are difficult to debug and maintain.

Solution Approach: Introduce an event queue to convert synchronous callbacks into asynchronous event handling.

Trap 2: Priority Inversion
High-priority tasks are blocked by low-priority operations.

Solution Approach: Strictly distinguish between synchronous and asynchronous operations; high-priority paths must not include blocking operations.

You may also like:
Lightweight circular buffer management library suitable for embedded systems!
Git interactive rebase to modify commit descriptions
Singleton pattern: The guardian of global state consistency in embedded systems
Embedded field: The ultimate showdown between Linux and RTOS!
Embedded software advancement guide, let’s level up together!