How Does an Embedded System Handle Real-Time Tasks?

How does an embedded system handle real-time tasks?

The first time the concept of “real-time” really clicked for me was watching a slow-motion video of a car’s airbag deploying. From the moment the crash sensor detects impact to the moment the airbag is fully inflated, only tens of milliseconds pass — and every single one of those milliseconds is accounted for by design. That’s not “fast” in some vague sense. It’s a hard, engineered guarantee. That distinction — between “usually fast” and “guaranteed within a deadline” — is the entire foundation of real-time embedded systems.

What “Real-Time” Actually Means

In everyday language, “real-time” often just means “fast” or “immediate.” In embedded systems engineering, it means something much more precise: a real-time system is one where correctness depends not just on the logical result of a computation, but on the time at which that result is produced. Being right, but late, counts as a failure.

graph LR
    A[Event Occurs] --> B[Processing]
    B --> C{Result Ready Before Deadline?}
    C -->|Yes| D[Correct - System Succeeds]
    C -->|No| E[Failure - Even If Logically Correct]

Hard, Firm, and Soft Real-Time Systems

Not all real-time requirements are equally strict. Embedded engineers generally classify tasks into three categories:

  • Hard real-time: Missing a deadline is a total system failure, potentially catastrophic. Example: an airbag deployment controller, a pacemaker’s pacing signal.
  • Firm real-time: Occasionally missing a deadline is tolerated, but the result becomes useless if it’s late. Example: a video frame that arrives too late to display in sequence.
  • Soft real-time: Missing a deadline degrades quality but doesn’t cause failure. Example: a slightly delayed sensor reading in a non-critical logging system.
graph TD
    A[Real-Time Task Types] --> B[Hard Real-Time<br/>Missed deadline = system failure]
    A --> C[Firm Real-Time<br/>Missed deadline = useless result]
    A --> D[Soft Real-Time<br/>Missed deadline = degraded quality]

Determinism: The Core Requirement

The key property that makes real-time behavior possible is determinism — the guarantee that a given operation will always take a predictable, bounded amount of time, no matter what else is happening in the system. This is fundamentally different from general-purpose computing, where task scheduling optimizes for average throughput, not worst-case timing.

Embedded engineers achieve determinism through several techniques:

1. Avoiding Unbounded Operations

Dynamic memory allocation (malloc), for instance, can take a variable and unpredictable amount of time depending on memory fragmentation. Many hard real-time systems avoid it entirely, using fixed, pre-allocated memory pools instead.

2. Careful Interrupt Design

As discussed in the article on interrupts, keeping ISRs short and predictable prevents one event from unpredictably delaying the handling of another, more time-critical one.

3. Priority-Based Scheduling

When multiple tasks compete for CPU time, real-time systems use priority-based scheduling to guarantee that the most time-critical tasks always get processor time first.

A Practical Example: Motor Control Loop

Consider a brushless motor controller that needs to update its control output every 100 microseconds to maintain smooth, stable operation.

sequenceDiagram
    participant Timer as Hardware Timer (100us period)
    participant ISR as Control Loop ISR
    participant Sensor as Position Sensor
    participant PWM as PWM Output

    loop Every 100 microseconds
        Timer->>ISR: Trigger interrupt
        ISR->>Sensor: Read rotor position
        ISR->>ISR: Compute control algorithm (PID)
        ISR->>PWM: Update PWM duty cycle
        ISR->>Timer: Return, wait for next tick
    end

If this loop takes 90 microseconds most of the time but occasionally spikes to 150 microseconds because of some unrelated task hogging the CPU, the motor control becomes unstable — possibly damaging the motor or the driven mechanism. This is why real-time embedded engineers obsess over worst-case execution time (WCET), not just average performance.

Code Example: A Bounded, Predictable Control Loop

#define CONTROL_PERIOD_US  100

void TIM2_IRQHandler(void) {
    if (TIM2->SR & TIM_SR_UIF) {
        TIM2->SR &= ~TIM_SR_UIF;      // Clear interrupt flag

        int16_t position = read_encoder();          // Bounded time
        int16_t error = target_position - position;
        int16_t output = pid_update(error);          // Fixed-time computation, no loops with variable iteration count
        set_pwm_duty(output);                        // Bounded time

        // No malloc, no unbounded loops, no blocking calls in this ISR
    }
}

Every operation inside this ISR is chosen specifically because it executes in a fixed, predictable amount of time — no dynamic memory allocation, no unbounded loops, no blocking I/O calls that might stall unpredictably.

Scheduling Approaches for Real-Time Tasks

Bare-Metal Superloop with Timed Sections

For simple systems, a “superloop” architecture manually manages timing using hardware timers, checking flags set by interrupts.

int main(void) {
    while (1) {
        if (control_loop_flag) {
            control_loop_flag = 0;
            run_control_loop();
        }
        if (comm_flag) {
            comm_flag = 0;
            handle_communication();
        }
    }
}

This works for simple systems but becomes unwieldy and hard to guarantee timing for as complexity grows.

RTOS-Based Preemptive Scheduling

For more complex systems with multiple concurrent responsibilities, a real-time operating system (RTOS) provides a proper task scheduler, letting each responsibility live in its own task with its own priority.

graph TD
    A[RTOS Scheduler] --> B[Task: Motor Control<br/>Priority: Highest]
    A --> C[Task: Sensor Fusion<br/>Priority: High]
    A --> D[Task: Communication<br/>Priority: Medium]
    A --> E[Task: Logging<br/>Priority: Low]

I explore RTOS concepts in depth in the dedicated RTOS article, but the key idea here is that the scheduler guarantees higher-priority, more time-critical tasks preempt lower-priority ones whenever necessary.

Worst-Case Execution Time (WCET) Analysis

Serious real-time engineering involves formally analyzing or measuring the absolute worst-case time any critical code path could take — accounting for cache misses, pipeline stalls, interrupt latency, and every possible branch through the code. This WCET figure is then used to prove, mathematically, that all deadlines can be met even under worst-case conditions — a discipline that goes well beyond “it seemed fast enough when I tested it.”

graph LR
    A[Code Path Analysis] --> B[Best Case Time]
    A --> C[Average Case Time]
    A --> D[Worst Case Execution Time - WCET]
    D --> E{WCET < Deadline?}
    E -->|Yes| F[System Meets Real-Time Requirement]
    E -->|No| G[Redesign Needed]

Jitter: The Silent Enemy of Real-Time Systems

Even when average timing looks fine, jitter — variation in the time between events that should be perfectly periodic — can cause serious problems in systems like motor control or audio processing, where consistency matters as much as speed. Real-time embedded design pays close attention to minimizing jitter, often by using dedicated hardware timers rather than software-based delay loops, and by ensuring higher-priority tasks aren’t blocked by lower-priority ones (a problem known as priority inversion).

Handling Priority Inversion

Priority inversion occurs when a high-priority task is blocked waiting for a resource held by a low-priority task, while a medium-priority task runs freely in between — effectively letting a lower-priority task delay a higher-priority one indirectly. RTOSes address this with mechanisms like priority inheritance, where a low-priority task temporarily “borrows” the priority of the higher-priority task it’s blocking, ensuring it finishes and releases the resource quickly.

sequenceDiagram
    participant High as High-Priority Task
    participant Med as Medium-Priority Task
    participant Low as Low-Priority Task (holds shared resource)

    Low->>Low: Acquires shared resource
    High->>Low: Waits for resource (blocked)
    Med->>Med: Runs (would normally preempt Low)
    Note over High,Low: Without priority inheritance,<br/>High is delayed by Med indirectly
    Low->>Low: Priority temporarily raised to High's level
    Low->>Low: Finishes quickly, releases resource
    High->>High: Proceeds immediately

Real-World Examples of Real-Time Embedded Tasks

  • Automotive: Anti-lock braking systems must respond within milliseconds to wheel-slip detection.
  • Medical devices: Infusion pumps must deliver precisely timed doses; deviations can be dangerous.
  • Industrial control: Robotic arms need tightly synchronized motor updates to avoid mechanical damage or safety incidents.
  • Aerospace: Flight control surfaces must respond to pilot or autopilot input within strict, certified timing bounds.
  • Audio processing: Digital audio systems must process samples fast enough to avoid audible glitches (buffer underruns).

Frequently Asked Questions

Does “real-time” mean “very fast”? Not exactly. It means predictable and bounded, not necessarily blazing fast. A system that reliably responds within 50 milliseconds every single time is “real-time,” even if a general-purpose computer could theoretically respond faster on average but with unpredictable spikes.

Do all embedded systems need real-time behavior? No. Many embedded systems — a simple digital picture frame, for example — have no hard timing requirements at all. Real-time design techniques are applied specifically when the application genuinely depends on timing correctness.

Can a system running Linux be real-time? Standard Linux is not a hard real-time OS, because its scheduler optimizes for overall throughput and fairness, not worst-case guarantees. Real-time variants like PREEMPT_RT patch the kernel to provide better real-time guarantees, and they’re used in some industrial applications, though dedicated RTOSes remain more common for hard real-time embedded tasks.

Summary

An embedded system handles real-time tasks by prioritizing predictability and bounded response times over raw average-case speed. This involves careful software design — avoiding unbounded operations, minimizing jitter, keeping interrupt service routines short — combined with priority-based scheduling, whether implemented manually in a bare-metal superloop or managed by a real-time operating system. The goal isn’t just to be fast; it’s to guarantee, with engineering rigor, that critical operations always complete within their required deadlines, even under worst-case conditions.

References and Further Reading

  • ARM Cortex-M Real-Time Programming Guide — https://developer.arm.com/documentation
  • FreeRTOS Real-Time Concepts Documentation — https://www.freertos.org/Documentation/RTOS_book.html
  • STM32 Timers and Real-Time Application Notes — https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html
  • Espressif ESP32 FreeRTOS Integration Guide — https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/freertos-smp.html
  • PREEMPT_RT Linux Real-Time Project — https://wiki.linuxfoundation.org/realtime/start
Total
1
Shares

Leave a Reply

Previous Post
What is real-time operating system (RTOS) in the context of embedded systems?

What Is a Real-Time Operating System (RTOS) in the Context of Embedded Systems?

Next Post
What is the purpose of an interrupt in an embedded system?

What Is the Purpose of an Interrupt in an Embedded System?

Related Posts