Can a Bare-Metal System Be Real-Time?
This article is intended for intermediate and advanced C programmers. It explains why bare-metal systems can be real-time systems, how they achieve deterministic behavior without an operating system, and the types of applications for which they are best suited.
1. A Common Misconception
After learning about Real-Time Operating Systems (RTOSes), many beginners naturally arrive at the following conclusion:
A system must have an RTOS to be a real-time system.
At first glance, this seems reasonable.
After all, RTOSes are specifically designed for real-time applications, so it is easy to assume that real-time and RTOS are inseparable.
In reality, this assumption is incorrect.
A system can be:
- Bare-metal and real-time
- RTOS-based and real-time
- Bare-metal and not real-time
- RTOS-based and not real-time
The presence or absence of an operating system does not determine whether a system is real-time.
Instead, the determining factor is much simpler:
Can the system consistently complete its required operations before their specified deadlines?
If the answer is yes, the system is real-time. If the answer is no, the system is not real-time.
Whether those deadlines are achieved using a bare-metal application, an RTOS, or even a real-time version of Linux is a design decision; not the definition of a real-time system.
The purpose of this article is to answer a simple but important question:
Can a bare-metal application satisfy real-time requirements without an operating system?
As we will see, the answer is yes. In fact, many safety-critical and high-performance embedded systems are deliberately designed this way.
2. What Is a Bare-Metal System?
Before discussing whether a bare-metal system can be real-time, let's first understand what bare-metal actually means.
A bare-metal system is an embedded application that runs directly on the hardware, without an operating system.
After the processor resets, the application initializes the hardware and begins executing its own code. There is no operating system between the application and the processor.
A typical bare-metal application looks like this:
Power On
│
▼
Reset Handler
│
▼
Hardware Initialization
│
▼
main()
│
▼
+--------------------+
| Infinite Main Loop |
+--------------------+
▲
│
Interrupts
Unlike an RTOS-based application, there are:
- No tasks
- No scheduler
- No context switching
- No kernel
- No operating system services
The application itself is responsible for deciding what executes and when it executes.
How Does a Bare-Metal Application Work?
Most bare-metal applications consist of two parts:
- A main loop, sometimes called the super loop.
- One or more Interrupt Service Routines (ISRs).
The main loop repeatedly executes the application's logic.
Whenever an external event occurs, such as a timer expiring, a button being pressed, or a communication packet arriving, the processor temporarily suspends the main loop and executes the appropriate interrupt service routine.
Once the interrupt has been handled, execution resumes exactly where the main loop left off.
Why Is It Called "Bare-Metal"?
The term bare-metal emphasizes that the application runs directly on the processor's hardware ("bare metal") without an operating system.
By contrast, an RTOS-based system introduces an additional software layer.
This additional layer provides services such as task scheduling, timers, synchronization, and inter-task communication.
A bare-metal application provides none of these services unless the developer implements them.
The absence of an operating system, however, does not imply that the application cannot satisfy real-time requirements.
That is the topic of the next section.
3. Can a Bare-Metal System Be Real-Time?
The short answer is:
Yes. A bare-metal system can absolutely be a real-time system.
This often surprises beginners because real-time systems are frequently discussed together with Real-Time Operating Systems (RTOSes).
However, it is important to remember that:
Real-time is a property of the application, not the operating system.
If a bare-metal application can consistently complete every critical operation before its required deadline, then it satisfies the definition of a real-time system.
A Simple Example
Consider a motor controller that must update the motor speed every 100 μs.
The application performs the following steps:
Suppose the entire sequence always completes within 80 μs.
Since the required deadline is 100 μs, every execution finishes before the deadline.
The application is therefore real-time, even though it does not use an RTOS.
What Makes It Real-Time?
Notice that the operating system plays no role in this example.
The only question that matters is:
Can the application consistently meet its deadline?
If the answer is yes, the application is real-time.
If the answer is no, it is not.
This relationship can be summarized as follows:
Application Deadline
│
▼
Can Every Deadline Be Met?
│
┌────┴────┐
│ │
Yes No
│ │
Real-Time Not Real-Time
Whether the application is:
- Bare-metal
- RTOS-based
- Linux-based
does not change this definition.
Why Many Real-Time Systems Are Bare-Metal
Many embedded applications are intentionally implemented as bare-metal systems because they are relatively small and perform only a few time-critical operations.
Typical examples include:
- Motor controllers
- Digital power supplies
- LED lighting controllers
- Battery management systems
- Sensor interfaces
These applications often consist of a single control loop and a handful of interrupt service routines.
Because there is very little software overhead, it is relatively easy to analyze their timing and verify that every deadline can be met.
This simplicity is one of the main reasons why bare-metal programming remains popular in many real-time embedded systems.
The next question is naturally:
If bare-metal systems can already be real-time, why do developers use RTOSes at all?
4. How Can a Bare-Metal System Be Real-Time?
If a bare-metal application runs without an operating system, how can it still satisfy real-time requirements?
The answer is that bare-metal applications rely on several hardware and software mechanisms that enable them to respond to events quickly and predictably.
Some of the most important mechanisms are:
- Hardware timers – Generate precise periodic events.
- Hardware interrupts – Notify the processor immediately when an event occurs.
- Interrupt Service Routines (ISRs) – Execute time-critical code in response to interrupts.
- The Super Loop (Main Loop) – Executes non-time-critical background tasks continuously.
- Memory-Mapped I/O – Allows software to communicate directly with hardware peripherals.
- Direct Memory Access (DMA) (if available) – Transfers data between peripherals and memory without occupying the CPU.
The following sections explain how each of these mechanisms contributes to the real-time behavior of a bare-metal system.
Hardware Timers
One of the most important building blocks of a bare-metal real-time system is the hardware timer.
A hardware timer is a dedicated peripheral inside the microcontroller that counts clock pulses independently of the CPU. Because it operates in hardware, it continues counting even while the processor is executing other instructions.
After a programmed time interval expires, the timer can generate a hardware interrupt, notifying the processor that a time-critical operation must be performed.
For example, suppose a motor controller must update its control algorithm every 100 μs.
Instead of repeatedly checking the elapsed time inside the main loop, a hardware timer can automatically generate an interrupt every 100 μs.
This approach offers several advantages:
- High timing accuracy because the timer is driven directly by the hardware clock.
- Consistent periodic execution, regardless of what the main loop is doing.
- No CPU time is wasted continuously checking whether the required time has elapsed.
For these reasons, hardware timers are commonly used for:
- Periodic control loops
- Motor control
- PWM generation
- Periodic sensor sampling
- Communication timeouts
- Periodic system tasks
In many bare-metal applications, the hardware timer serves as the system's heartbeat, periodically triggering time-critical operations at precise and predictable intervals.
Hardware Interrupts and Interrupt Service Routines (ISRs)
While hardware timers generate events at regular intervals, many real-time systems must also respond to external events that occur unpredictably.
Examples include:
- A button being pressed
- A sensor detecting an object
- A UART receiving data
- An ADC completing a conversion
- A CAN message arriving
Continuously checking for these events inside the main loop (a technique known as polling) is inefficient and may introduce unacceptable delays.
Instead, embedded systems use hardware interrupts.
A hardware interrupt is a signal generated by a peripheral that immediately requests the processor's attention.
When an interrupt occurs, the processor temporarily suspends its current execution and automatically executes a special function called an Interrupt Service Routine (ISR).
External Event
│
▼
Hardware Interrupt
│
▼
CPU Suspends
Current Execution
│
▼
Interrupt Service Routine (ISR)
│
▼
Return to Previous Code
Because the processor responds immediately, time-critical events can be handled with very little delay.
For example, a UART receiving a character can immediately trigger an interrupt, allowing the ISR to read the received byte before another one arrives.
Similarly, an ADC can generate an interrupt when a conversion is complete, allowing the application to process the sampled data without continuously polling the ADC status register.
Hardware interrupts and ISRs are therefore essential components of many bare-metal real-time systems, enabling the processor to react quickly to both periodic events (such as timer interrupts) and asynchronous events (such as communication or sensor inputs).
The Super Loop (Main Loop)
In most bare-metal applications, the super loop (also called the main loop) forms the backbone of the software.
After the processor initializes the hardware, it enters an infinite loop that repeatedly executes the application's background tasks.
A typical super loop looks like this:
int main(void)
{
system_init();
while (1)
{
read_sensors();
process_data();
update_display();
check_communication();
}
}
Unlike an RTOS, there is no scheduler deciding which task runs next.
The application itself determines the order in which functions are executed.
Main Loop
Read Sensors
│
▼
Process Data
│
▼
Update Display
│
▼
Check Communication
│
▼
Repeat Forever
The super loop is typically used for non-time-critical operations.
Whenever a time-critical event occurs, such as a timer expiring or data arriving on a communication interface, a hardware interrupt temporarily suspends the main loop, executes the appropriate ISR, and then returns to exactly where the main loop left off.
This simple execution model is one of the reasons bare-metal systems are widely used in embedded applications. With only a single main loop and a small number of interrupt service routines, the software is relatively easy to understand, debug, and verify.
For small embedded applications, the super loop provides an effective way to perform background processing while allowing interrupts to handle time-critical events.
Direct Hardware Access (Memory-Mapped I/O)
Real-time systems often need to interact with hardware immediately. For example, they may need to:
- Start an ADC conversion.
- Update a PWM duty cycle.
- Read a sensor value.
- Transmit a byte over a UART.
- Toggle a GPIO pin.
Any unnecessary software layers between the application and the hardware increase response time.
For this reason, most modern microcontrollers provide Memory-Mapped I/O (MMIO), allowing software to access hardware peripherals directly.
Instead of communicating through an operating system, hardware peripherals are assigned fixed memory addresses. Reading from or writing to these addresses directly controls the peripheral.
For example, in an STM32 microcontroller, writing to the GPIO output register immediately changes the state of an output pin. Similarly, writing to a timer register changes the timer configuration, while reading an ADC register retrieves the latest conversion result.
Memory-mapped I/O is a standard feature found in most modern microcontrollers, including:
- STM32 (ARM Cortex-M)
- NXP LPC series
- Microchip PIC32
- TI MSP430
- ESP32
- Many RISC-V microcontrollers
By allowing software to communicate directly with hardware, MMIO minimizes software overhead and provides fast, predictable access to peripherals. This direct hardware control is one of the reasons bare-metal applications are well suited for many real-time embedded systems.
5. Advantages of Bare-Metal Real-Time Systems
By now, we have seen that bare-metal applications use hardware timers, interrupts, a super loop, and direct hardware access to achieve real-time behavior.
These mechanisms provide several advantages, especially for small and medium-sized embedded applications.
Low Software Overhead
A bare-metal application runs directly on the processor without an operating system.
As a result, there is:
- No kernel
- No task scheduler
- No context switching
- No operating system services
The processor spends nearly all of its time executing the application itself.
Fast Response Time
When a hardware interrupt occurs, the processor immediately transfers control to the corresponding Interrupt Service Routine (ISR).
Since there is no operating-system scheduler involved, critical events can often be serviced with very little delay.
This makes bare-metal programming well suited for applications requiring rapid responses to external events.
Small Memory Footprint
Bare-metal applications contain only the code required by the application.
There is no operating-system kernel occupying Flash or RAM.
This makes bare-metal programming ideal for small microcontrollers with limited resources.
For example, many 8-bit and 32-bit microcontrollers provide only:
- 32 KB Flash
- 2 KB RAM
A bare-metal application can often run comfortably within these limits.
Greater Control Over Hardware
The application directly configures and controls hardware peripherals such as:
- Timers
- GPIO
- UART
- SPI
- I²C
- ADC
- PWM
This allows developers to optimize the system for their specific application without relying on operating-system abstractions.
Easier Timing Analysis
Small bare-metal applications usually have a simple execution model:
- One main loop
- A few interrupt service routines
Because the control flow is straightforward, it is often easier to determine:
- When functions execute
- How frequently they execute
- Whether timing deadlines can be met
This is particularly valuable in applications with strict timing requirements.
Typical Applications
Bare-metal programming is commonly used in:
- Motor controllers
- Power supplies
- Battery management systems
- LED lighting controllers
- Simple automotive modules
- Sensor interfaces
- Consumer appliances
These applications often perform a limited number of well-defined tasks, making a bare-metal architecture both efficient and practical.
In the next section, we will examine the limitations of bare-metal systems and why they become increasingly difficult to manage as application complexity grows.
6. Limitations of Bare-Metal Real-Time Systems
Bare-metal programming works extremely well for many embedded applications. However, as the application grows in size and complexity, managing the software becomes increasingly difficult.
The limitation is not that bare-metal cannot be real-time.
Rather, the limitation is that the application itself becomes harder to organize and maintain.
The Super Loop Becomes Larger
Small applications may consist of only a few functions:
As new features are added, the main loop continues to grow.
Read Sensors
Motor Control
CAN Communication
Ethernet
USB
LCD Display
Logging
Diagnostics
Watchdog
...
Eventually, the super loop becomes long and difficult to manage.
Scheduling Becomes Manual
In a bare-metal application, the developer is responsible for deciding:
- Which function executes first.
- How often each function executes.
- Which functions are interrupt-driven.
- Which functions execute in the main loop.
As the number of software components increases, manually managing this execution order becomes increasingly complex.
Poor Scalability
Adding a new feature often requires modifying the existing main loop.
For example, introducing Bluetooth communication or a file system may require reorganizing the entire application.
As a result, software that was originally simple can become difficult to extend without affecting existing functionality.
Limited Support for Concurrent Activities
Many modern embedded systems perform several independent activities simultaneously, such as:
- Motor control
- Communication
- Data logging
- User interface
- Network connectivity
In a bare-metal application, coordinating these activities usually requires custom scheduling logic, state machines, or extensive use of interrupts.
As the application grows, this approach becomes increasingly difficult to maintain.
More Difficult Team Development
Bare-metal programming works well for small projects developed by one or two engineers.
Larger projects often involve multiple developers working on independent software modules.
Without a task-based software architecture, integrating these modules into a single super loop can become challenging.
The Main Limitation
It is important to remember that these are software engineering challenges, not real-time limitations.
A well-designed bare-metal application can still satisfy strict timing deadlines.
However, as application complexity increases, maintaining and extending the software becomes progressively more difficult.
This is one of the primary reasons why many larger embedded applications adopt a Real-Time Operating System (RTOS), which provides a more structured way to organize software while still supporting real-time execution.
7. If Bare-Metal Can Be Real-Time, Why Use an RTOS?
At this point, a natural question arises:
If a bare-metal application can already satisfy real-time requirements, why do embedded developers use an RTOS?
The answer is simple:
An RTOS is not used to make a system real-time. It is used to make complex software easier to manage.
Whether an application is real-time depends on meeting its deadlines, not on whether it uses an operating system.
Small Applications
Consider a simple motor controller.
It performs only a few tasks:
- Read sensors
- Compute the control algorithm
- Update the PWM output
A single super loop and a few interrupt service routines are often sufficient to meet all timing requirements.
Adding an RTOS in such a system may provide little benefit while increasing memory usage and software complexity.
As Applications Grow
Now consider a more sophisticated embedded product.
It may need to perform:
- Motor control
- CAN communication
- Ethernet networking
- USB communication
- LCD updates
- Data logging
- Fault monitoring
- Firmware updates
Although the application may still have real-time requirements, organizing all of these functions within one super loop becomes increasingly difficult.
What an RTOS Provides
An RTOS helps organize large applications by providing services such as:
- Task scheduling
- Priority-based execution
- Software timers
- Message queues
- Semaphores and mutexes
- Inter-task communication
Instead of manually controlling every part of the application, the developer divides the software into multiple independent tasks, while the RTOS manages their execution.
The Key Difference
It is important to distinguish between these two concepts:
| Real-Time | RTOS |
|---|---|
| A system property | A software component |
| Means meeting timing deadlines | Helps organize application software |
| Can be achieved without an operating system | Is not required for real-time operation |
A bare-metal application can therefore be real-time.
An RTOS simply provides a more structured and scalable way to build larger embedded applications while still allowing them to satisfy their timing requirements.
In the next section, we will compare bare-metal and RTOS-based systems to help determine which architecture is better suited for a particular embedded application.
8. Bare-Metal vs. RTOS
Both bare-metal and RTOS-based systems are widely used in embedded applications. Neither approach is universally better. The choice depends on the application's complexity, timing requirements, available resources, and long-term maintenance needs.
The following table summarizes the main differences.
| Feature | Bare-Metal | RTOS-Based |
|---|---|---|
| Operating System | None | RTOS kernel |
| Program Structure | Super loop + ISRs | Multiple tasks + ISRs |
| Software Complexity | Best for simple applications | Better suited for large applications |
| Memory Usage | Very small | Higher due to the RTOS kernel |
| Software Overhead | Minimal | Additional scheduling and kernel overhead |
| Task Scheduling | Managed by the application | Managed by the RTOS |
| Scalability | Limited | Excellent |
| Maintainability | Can become difficult as projects grow | Easier to organize and extend |
| Development Team | Suitable for small teams | Better for larger teams |
| Real-Time Capability | Yes | Yes |
Which One Should You Choose?
As a general guideline:
Choose bare-metal when:
- The application is relatively small.
- Only a few time-critical tasks are required.
- Memory and processing resources are limited.
- Minimal software overhead is important.
Choose an RTOS when:
- The application consists of many independent software components.
- Multiple tasks must execute concurrently.
- The project is expected to grow over time.
- Software modularity and maintainability are important.
The Most Important Point
It is important not to confuse real-time with RTOS.
A system does not become real-time simply because it uses an RTOS.
Likewise, a bare-metal application is not excluded from being real-time simply because it does not use an operating system.
The defining question remains the same:
Can the application consistently complete its required operations before their specified deadlines?
If the answer is yes, then the system is real-time, regardless of whether it is implemented as a bare-metal application or as an RTOS-based application.
9. Summary
Many beginners assume that a Real-Time Operating System (RTOS) is a requirement for building a real-time system.
As we have seen throughout this article, this is not true.
A bare-metal application can absolutely be a real-time system if it consistently completes its required operations before their specified deadlines.
The key points discussed in this article are summarized below:
- Bare-metal simply means the application runs directly on the hardware without an operating system.
- Real-time means meeting application deadlines; not using a particular software architecture.
- A bare-metal application can achieve real-time behavior using:
- Hardware timers
- Hardware interrupts and ISRs
- A super loop
-
Direct access to hardware peripherals
-
Bare-metal systems are often chosen because they provide:
- Minimal software overhead
- Fast response times
- Small memory footprint
- Direct control of hardware
- As applications become larger and more complex, organizing everything within a single super loop becomes increasingly difficult.
- In such cases, developers often adopt an RTOS; not because bare-metal is no longer real-time, but because an RTOS provides a more structured way to organize complex software.
The most important takeaway is this:
Bare-metal and real-time are not mutually exclusive.
Whether a system is implemented using bare-metal firmware, an RTOS, or even real-time Linux, the defining question remains the same:
Can the system consistently complete its required operations before their specified deadlines?
If the answer is yes, then the system is a real-time system.