适合裸机编程的极简任务调度器
1. Tsk 轻量级任务调度器的出现,为嵌入式开发者提供了介于 "纯裸机状态机" 与 "完整 RTOS" 之间的中间方案;2.用简单的 API 实现功能模块化,降低状态机设计复杂度,提升代码可维护性;
@[TOC]适合裸机编程的极简任务调度器
一、Tsk任务调度器的特点
- 代码体积:核心代码不到 200 行 C 语言;
- 内存占用:每个任务控制块占用内存极小,占用的大小与定义的任务数量有关;
- 调度机制:采用非抢占式时间片轮询(Round-Robin),避免复杂的上下文切换开销,任务切换仅需修改当前任务指针;
二、裸机开发的模块化之痛
在嵌入式系统开发中,裸机环境下的程序设计往往面临着独特的挑战。传统方案依赖状态机实现功能集成,随着业务复杂度提升,代码会逐渐演变为交织的 “面条式逻辑”:事件处理与业务逻辑混杂、模块间耦合紧密、调试定位困难,移植时更需推倒重来。尽管 FreeRTOS 等实时操作系统提供了完善的任务管理方案,但其复杂的内核机制(如抢占式调度、任务同步组件)在资源受限的微控制器(如 8 位 / 16 位 MCU 或低功耗 32 位芯片)上显得臃肿,且对于大多数实时性要求不高的中小项目而言,引入完整 RTOS 可能带来不必要的资源消耗。
如何在 “轻量资源占用” 与 “模块化管理” 之间找到平衡?Tsk 轻量级任务调度器应运而生 —— 它专为裸机环境设计,以极小的内存开销实现任务级模块化管理,让开发者既能享受多任务编程的清晰架构,又无需背负完整 RTOS 的复杂度。
三、Tsk引入模块化架构
传统状态机将功能拆解为枚举状态与条件判断,随着状态数增加,代码可读性呈指数级下降。大多数裸机应用对实时响应要求不是太严格,满足响应需求即可;而 对于高实时性(响应在us级别),可通过硬件中断直接处理。Tsk 引入任务(Task)概念,每个任务是独立的功能模块,降低系统设计的复杂度,实现功能的高内聚低耦合,方便开发维护以及功能移植。
// 任务函数模板:需包含非阻塞式逻辑
void Tsk1LedRun(void)
{
Led0Toggle();
Led1Toggle();
}
// main函数实现 任务的注册,并在循环里实现调度
int main() {
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
TskCreate(500, Tsk1LedRun); // 创建任务1, 500 毫秒翻转一次指示灯
while (1)
{
TskLooperHandle(); // 调度器实现任务的调度
}
return 0;
}
这种设计将复杂的状态转移转化为线性的任务函数,配合全局任务列表管理,使模块间依赖清晰可控,代码维护成本显著降低。
四、Tsk代码介绍
tsk.h 头文件
#ifndef _TSK_H_
#define _TSK_H_
// 设置开销的任务数
#define TSK_FUNC_NUM ( 5 )
// 定义函数指针类型
typedef void (*TSK_FUNC_CALLBACK)(void);
void TimerSoftTick(void);
unsigned long long TimerGetTimeStampMs(void);
unsigned int TimerGetTimeStampSec(void);
unsigned char TskCreate(unsigned int cycleTime, TSK_FUNC_CALLBACK callBack);
void TskDelete(TSK_FUNC_CALLBACK callBack);
void TskLooperHandle(void);
Tsk 使用说明
1. 根据时间需求定义 任务数量 TSK_FUNC_NUM ,数量越多影响内存的占用;
2. TimerSoftTick() 在毫秒定时中断里进行调用;
3. TskCreate() 注册任务调用的周期和回调函数;
4. 若有必要,使用 TskDelete() 进行指定任务回调函数销毁;
5. TskLooperHandle() 在循环里进行调用;
Tsk 应用示例
/* USER CODE BEGIN 0 */
void Tsk1LedRun(void)
{
Led0Toggle();
Led1Toggle();
}
void Tsk2AdcSample(void)
{
// 任务2 数据采集
}
void Tsk3Communicat(void)
{
// 通信处理
}
void Tsk4LogicHandle(void)
{
// 逻辑处理
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
TskCreate(500, Tsk1LedRun); // 创建任务1, 500 毫秒翻转一次指示灯
TskCreate(10, Tsk2AdcSample); // 创建任务2, 10 毫秒采集一次数据
TskCreate(10, Tsk3Communicat); // 创建任务3, 10 毫秒处理一次通信数据
TskCreate(20, Tsk4LogicHandle); // 创建任务4, 20 毫秒处理一次逻辑数据
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
TskLooperHandle();
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
五、与 FreeRTOS 的定位对比

Tsk 并非 RTOS 的替代品,而是针对 “不需要抢占式调度与复杂同步” 场景的最优解。对于需要多任务协作、临界资源保护的项目,仍需引入完整 RTOS;但在中小规模裸机系统中,Tsk 提供了更轻量的模块化方案。
六、重新定义裸机开发范式
Tsk 轻量级任务调度器的出现,为嵌入式开发者提供了介于 “纯裸机状态机” 与 “完整 RTOS” 之间的中间方案:
- 对开发者:用简单的 API 实现功能模块化,降低状态机设计复杂度,提升代码可维护性;
- 对系统:极小的资源占用适配全系列 MCU,中断机制保障实时性,为产品小型化、低功耗设计创造条件;
- 对项目:标准化的任务接口加速功能移植,支持快速迭代与团队协作;
在物联网终端、消费电子等领域,80% 的应用其实并不需要复杂的实时调度,Tsk 正是为这部分场景量身定制的解决方案。
更多推荐



所有评论(0)