Summary of Methods for Implementing Multithreading in Single Chip Microcontrollers


Multithreading enables microcontrollers to handle concurrent tasks efficiently. Here are the primary implementation approaches:
1. Real-Time Operating Systems (RTOS)
Popular RTOS Options
RTOS | License | Key Features | Best For |
FreeRTOS | MIT | Lightweight, portable | General-purpose |
Zephyr | Apache 2.0 | Modular, IoT-focused | Connected devices |
ThreadX | Proprietary | Hard real-time | Automotive/medical |
RT-Thread | Apache 2.0 | Middleware-rich | Complex IoT systems |
Implementation Example (FreeRTOS):
c
void vTask1(void *pvParameters) {
while(1) {
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void main() {
xTaskCreate(vTask1, "LED_Task", 128, NULL, 1, NULL);
vTaskStartScheduler();
}
2. Bare-Metal Multitasking
A. Cooperative Scheduling
c
typedef struct {
void (*task)(void);
uint32_t interval;
uint32_t last_run;
} Task;
Task tasks[] = {
{LED_Update, 100}, // Runs every 100ms
{Sensor_Read, 500}
};
void Scheduler_Run() {
uint32_t now = HAL_GetTick();
for(int i=0; i<2; i++) {
if(now - tasks[i].last_run >= tasks[i].interval) {
tasks[i].task();
tasks[i].last_run = now;
}
}
}
B. Time-Triggered Architecture
Uses hardware timers to invoke tasks at precise intervals
Benefits:
Predictable timing
No stack overflows
Minimal CPU overhead
3. Interrupt-Driven Concurrency
c
volatile uint8_t adc_ready = 0;
void ADC_IRQHandler() {
adc_ready = 1; // Set flag
}
void main() {
while(1) {
if(adc_ready) {
Process_ADC_Data();
adc_ready = 0;
}
// Other non-blocking code
}
}
4. Hybrid Approaches
RTOS + Interrupts
Critical tasks: Handled by ISRs
Background tasks: Managed by RTOS
Coroutines
c
void coroutine1() {
static int state = 0;
switch(state) {
case 0: /* Initialization */ state++; break;
case 1: /* Task step 1 */ state++; break;
// ...
}
}
Comparison Table
Method | RAM Usage | Timing Precision | Complexity | Best Use Case |
RTOS | Medium-High | High | Medium | Complex systems |
Cooperative | Low | Low | Simple | Resource-constrained |
Time-Triggered | Low | Very High | Medium | Timing-critical |
Interrupts | Low | Very High | Medium | Event-driven |
Key Considerations
Stack Management
RTOS: Each thread needs dedicated stack
Bare-metal: Shared stack (risk of overflow)
Synchronization
Use mutexes/semaphores in RTOS
Disable interrupts for critical sections in bare-metal
Debugging Tools
FreeRTOS: Tracealyzer
Bare-metal: Logic analyzer for task timing
Advanced Techniques
Memory Protection Units (MPU): Isolate task memory (Cortex-M)
Dynamic Priority Adjustment: Change task priorities at runtime
Task Watchdogs: Monitor for stuck tasks
For most modern 32-bit MCUs (STM32, ESP32), an RTOS provides the best balance of features and maintainability. 8-bit systems often benefit from simpler cooperative schedulers.
Subscribe to my newsletter
Read articles from ampheo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
