How to Build a Fully Decoupled Time‑Slice Scheduling Framework for Microcontrollers
This article explains why a solid program architecture is essential for embedded projects, introduces a lightweight time‑slice scheduling framework that separates OS logic from user code, and provides complete C source files—including an intrusive doubly‑linked list implementation—to help developers quickly create and manage tasks on a microcontroller.
Why a Program Framework Matters
Many beginners write code piece‑by‑piece without an overall design, which leads to tangled code, frequent bugs, and huge time loss when adding new features. Investing time in a clear architecture at the start makes later development faster, debugging easier, and overall code more maintainable.
Design of the Time‑Slice Scheduling Framework
The framework follows an operating‑system‑style approach: user tasks are registered through simple interface functions and then managed by the scheduler, achieving low coupling between business logic and the OS layer. Users only need to include the header file and call the provided APIs; no changes to the library are required.
Demo Application
The demo creates two host threads on a PC to simulate a microcontroller environment: one thread generates timer interrupts that drive the time‑slice, and the other runs the scheduler loop.
#include <thread>
#include <stdio.h>
#include <windows.h>
#include "timeslice.h"
// Create 5 task objects
TimesilceTaskObj task_1, task_2, task_3, task_4, task_5;
// Task functions
void task1_hdl(){ printf(">> task 1 is running ...
"); }
void task2_hdl(){ printf(">> task 2 is running ...
"); }
void task3_hdl(){ printf(">> task 3 is running ...
"); }
void task4_hdl(){ printf(">> task 4 is running ...
"); }
void task5_hdl(){ printf(">> task 5 is running ...
"); }
void task_init(){
timeslice_task_init(&task_1, task1_hdl, 1, 10);
timeslice_task_init(&task_2, task2_hdl, 2, 20);
timeslice_task_init(&task_3, task3_hdl, 3, 30);
timeslice_task_init(&task_4, task4_hdl, 4, 40);
timeslice_task_init(&task_5, task5_hdl, 5, 50);
timeslice_task_add(&task_1);
timeslice_task_add(&task_2);
timeslice_task_add(&task_3);
timeslice_task_add(&task_4);
timeslice_task_add(&task_5);
}
void timeslice_exec_thread(){ while(true){ timeslice_exec(); } }
void timeslice_tick_thread(){ while(true){ timeslice_tick(); Sleep(10); } }
int main(){
task_init();
printf(">> task num: %d
", timeslice_get_task_num());
printf(">> task len: %d
", timeslice_get_task_timeslice_len(&task_3));
timeslice_task_del(&task_2);
printf(">> delete task 2
");
printf(">> task 2 exists: %d
", timeslice_task_isexist(&task_2));
// ... additional add/delete operations ...
printf("
========timeslice running===========
");
std::thread thread_1(timeslice_exec_thread);
std::thread thread_2(timeslice_tick_thread);
thread_1.join();
thread_2.join();
return 0;
}The program output (shown below) demonstrates that tasks can be added, removed, and queried without touching the core scheduler code.
Core Framework Files
The framework consists of a header ( timeslice.h) that defines the task structure, state enum, and public API, and a source file ( timeslice.c) that implements the scheduler, tick handling, and task management functions.
#ifndef _TIMESLICE_H
#define _TIMESLICE_H
#include "./list.h"
typedef enum { TASK_STOP, TASK_RUN } IsTaskRun;
typedef struct timesilce {
unsigned int id;
void (*task_hdl)(void);
IsTaskRun is_run;
unsigned int timer;
unsigned int timeslice_len;
ListObj timeslice_task_list;
} TimesilceTaskObj;
void timeslice_exec(void);
void timeslice_tick(void);
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);
void timeslice_task_add(TimesilceTaskObj* obj);
void timeslice_task_del(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_num(void);
unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);
#endif #include "./timeslice.h"
static LIST_HEAD(timeslice_task_list);
void timeslice_exec(){
ListObj* node;
TimesilceTaskObj* task;
list_for_each(node, ×lice_task_list){
task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
if(task->is_run == TASK_RUN){
task->task_hdl();
task->is_run = TASK_STOP;
}
}
}
void timeslice_tick(){
ListObj* node;
TimesilceTaskObj* task;
list_for_each(node, ×lice_task_list){
task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
if(task->timer != 0){
task->timer--;
if(task->timer == 0){
task->is_run = TASK_RUN;
task->timer = task->timeslice_len;
}
}
}
}
unsigned int timeslice_get_task_num(){ return list_len(×lice_task_list); }
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len){
obj->id = id;
obj->is_run = TASK_STOP;
obj->task_hdl = task_hdl;
obj->timer = timeslice_len;
obj->timeslice_len = timeslice_len;
}
void timeslice_task_add(TimesilceTaskObj* obj){ list_insert_before(×lice_task_list, &obj->timeslice_task_list); }
void timeslice_task_del(TimesilceTaskObj* obj){ if(timeslice_task_isexist(obj)) list_remove(&obj->timeslice_task_list); }
unsigned char timeslice_task_isexist(TimesilceTaskObj* obj){
unsigned char isexist = 0;
ListObj* node;
TimesilceTaskObj* task;
list_for_each(node, ×lice_task_list){
task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
if(obj->id == task->id) isexist = 1;
}
return isexist;
}
unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj){ return obj->timeslice_len; }Intrusive Doubly‑Linked List (Linux‑style)
The scheduler relies on an intrusive list implementation similar to the one used in the Linux kernel and RT‑Thread OS. The list header ( list.h) defines the node structure and a set of macros for insertion, removal, and traversal.
#ifndef _LIST_H
#define _LIST_H
#define offset_of(type, member) (unsigned long)&((type*)0)->member
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offset_of(type, member)))
typedef struct list_structure { struct list_structure* next; struct list_structure* prev; } ListObj;
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) ListObj name = LIST_HEAD_INIT(name)
void list_init(ListObj* list);
void list_insert_after(ListObj* list, ListObj* node);
void list_insert_before(ListObj* list, ListObj* node);
void list_remove(ListObj* node);
int list_isempty(const ListObj* list);
unsigned int list_len(const ListObj* list);
#define list_entry(node, type, member) container_of(node, type, member)
#define list_for_each(pos, head) for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, n, head) for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next)
#endif #include "list.h"
void list_init(ListObj* list){ list->next = list->prev = list; }
void list_insert_after(ListObj* list, ListObj* node){
list->next->prev = node;
node->next = list->next;
list->next = node;
node->prev = list;
}
void list_insert_before(ListObj* list, ListObj* node){
list->prev->next = node;
node->prev = list->prev;
list->prev = node;
node->next = list;
}
void list_remove(ListObj* node){
node->next->prev = node->prev;
node->prev->next = node->next;
node->next = node->prev = node;
}
int list_isempty(const ListObj* list){ return list->next == list; }
unsigned int list_len(const ListObj* list){
unsigned int len = 0;
const ListObj* p = list;
while(p->next != list){ p = p->next; len++; }
return len;
}Conclusion
The presented framework offers a lightweight, fully decoupled time‑slice scheduler that can be dropped into any C‑based microcontroller project. By leveraging an OS‑style API and an intrusive linked‑list backbone, developers gain modularity, easy task management, and high execution efficiency without needing a full operating system.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
