Step-by-step guide to building an efficient time-sharing OS for microcontrollers! Here’s how to solve the challenges of multitasking concurrency.
In microcontroller development, have you ever faced the dilemma of needing to handle urgent tasks like real-time data collection and device control, while also managing regular operations such as data analysis and command responses that can be processed less frequently, like once a minute or hour? This kind of multitasking with varying priorities often leads to response delays and resource conflicts. In fact, an efficient time-sharing operating system can easily solve this problem. Today, we will take you through the principles and practical implementation of building a time-sharing OS for microcontrollers, which even beginners can directly apply!

1. Core Logic of Time-Sharing OS: Frontend and Backend + Dual-Driver Mode
The core of the microcontroller time-sharing operating system is to efficiently support multitasking with limited hardware resources. The key lies in two major designs: “task prioritization” and “precise triggering”.

2. Practical Code: Building a Usable Time-Sharing OS from Scratch
After understanding the principles, let’s dive into a practical code framework! The following is designed based on the STM32 microcontroller, implementing five levels of time-sharing tasks at 10ms, 100ms, 1s, 1min, and 1h. Beginners can directly replace the “user program” section.
1. Main Function Framework: Background Task Loop Execution
The main function is responsible for initializing devices and then continuously calling different periodic background tasks, with a very simple structure:
int main(void) {
SystemInit(); // System initialization
DeviceInit(); // Device initialization (including timer configuration)
while (1) // Background loop
{
TenMSTask(); // 10ms periodic task
HundredMSTask(); // 100ms periodic task
OneSTask(); // 1s periodic task
OneMTask(); // 1min periodic task
}
}
2. Device Initialization: Timer + Watchdog Configuration
The core of initialization is configuring a 10ms timer (for time-sharing timing) and a watchdog (to prevent the program from hanging):
void DeviceInit(void)
{
__disable_irq(); // Disable interrupts for safe initialization
Gpio_Init(); // GPIO initialization
TIM3_10MS_Configuration(); // Configure 10ms timer
IWDG_Config(); // Enable independent watchdog
__enable_irq(); // Enable interrupts
IWDG_ReloadCounter(); // Feed the watchdog to avoid reset during initialization
}
3. Timer Interrupt: The “Time Scale” for Time-Sharing Tasks
Through the 10ms timer interrupt, different periodic task flags are generated, effectively assigning an “execution schedule” to each task:
typedef struct
{
volatile unsigned char _1MS_Count;
volatile unsigned char _10MS_Count;
volatile unsigned char _100MS_Count;
volatile unsigned char _1S_Count;
volatile unsigned char _1MINUTE_Count;
volatile unsigned char _1MS_Flag;
volatile unsigned char _10MS_Flag;
volatile unsigned char _100MS_Flag;
volatile unsigned char _1S_Flag;
volatile unsigned char _1MINUTE_Flag;
volatile unsigned char _1HOUR_Flag;
} Timer0_Manager_t;
Timer0_Manager_t Timer0_Manager;
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update))
{
Timer0_Manager._10MS_Flag = TRUE;
Timer0_Manager._10MS_Count++; /*10MS timing variable accumulation*/
if (Timer0_Manager._10MS_Count > 9) /*10MS*/
{
Timer0_Manager._10MS_Count = 0;
Timer0_Manager._100MS_Flag = TRUE;
Timer0_Manager._100MS_Count++; /*100MS timing variable accumulation*/
if (Timer0_Manager._100MS_Count > 9) /*100MS */
{
Timer0_Manager._100MS_Count = 0;
Timer0_Manager._1S_Flag = TRUE;
Timer0_Manager._1S_Count++; /*1 second timing variable accumulation*/
if(Timer0_Manager._1S_Count > 60 ) /*1S */
{
Timer0_Manager._1S_Count = 0; /*1 second timing variable reset*/
Timer0_Manager._1MINUTE_Flag = TRUE; // Minute flag set
Timer0_Manager._1MINUTE_Count++; /*1 minute timing variable accumulation*/
if(Timer0_Manager._1MINUTE_Count > 60 ) /*1MINUTE */
{
Timer0_Manager._1MINUTE_Count = 0; /*1 minute timing variable reset*/
Timer0_Manager._1HOUR_Flag = TRUE; // Hour flag set
}
}
}
}
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
4. Time-Sharing Task Functions: Execute Corresponding Logic Based on Flags
Each task function checks the corresponding time flag, and when the flag is true, it executes the user program and resets the flag afterward:
void TenMSTask(void)
{
if (Timer0_Manager._10MS_Flag == TRUE)
{
Timer0_Manager._10MS_Flag = FALSE;
// User program
}
}
void HundredMSTask(void)
{
if (Timer0_Manager._100MS_Flag == TRUE)
{
Timer0_Manager._100MS_Flag = FALSE;
// User program
}
}
void OneSTask(void)
{
if (Timer0_Manager._1S_Flag == TRUE)
{
Timer0_Manager._1S_Flag = FALSE;
// User program
IWDG_ReloadCounter(); /* Feed the watchdog */
}
}

3. Core Advantages of This Time-Sharing OS
High resource utilization: No complex scheduling algorithms are needed; multitasking is achieved through time-sharing using timers, suitable for resource-limited microcontrollers;
Controllable real-time performance: By using interrupt priorities and time flags, high-priority tasks can respond promptly;
Extremely extensible: Adding new tasks only requires adding corresponding periodic flags and task functions without needing to restructure the framework;
Maximum stability: Integrated independent watchdog to prevent program crashes, suitable for harsh scenarios like industrial control.
This time-sharing OS framework has been validated in multiple microcontroller projects. What multitasking challenges have you encountered in your development? Feel free to leave a comment, and we will break down more practical optimization techniques in the future!
If you find this useful, remember to like + follow, and share it with your embedded development friends! Follow me for more microcontroller tutorials!