轻量级裸机任务调度器,不用 RTOS。
每个任务只需填三个函数指针(init / periodic / on_event),调度器自动按时机调用。
支持消息队列通信、1ms 精确定时。
把整个项目文件夹丢给 AI(Claude、GPT 等),再附上一句你的需求,AI 就能自动为你的 MCU 工程生成多任务框架:
把这个 TTask 模板集成到我的 STM32G0 项目中。
我的项目有:
• 按键扫描(4个按键,10ms 轮询)
• LED 指示灯(2个,状态指示)
• 串口协议解析(115200bps,不定长帧)
• 继电器控制(3路,带过零检测)
帮我生成对应的 task_xxx.c 和 timer.c 移植代码。
原理: 仓库中的 SKILL.md 包含了调度器的完整知识(API、任务模式、消息通信、移植方法、注意事项),AI 阅读后就能精准理解并替你写出适配代码。你只需要把生成的 task_xxx.c 放到工程里注册即可。
💡 这就是本项目的主要使用方式——不用自己读源码,交给 AI 理解框架然后自动输出你需要的任务代码。
TTask-MultiTask-Template/
├── README.md ← 本文件
├── SKILL.md ← 完整技能文档(含教程、示例、FAQ)
├── User/
│ ├── main.c ← 主入口(只需 3 行)
│ └── scheduler/
│ ├── ptask.h / .c ← Protothreads + 消息队列(底层)
│ ├── ttask.h / .c ← 通用任务模板(上层 API)
│ ├── timer.h ← 硬件定时器接口
│ ├── tasks.h ← 任务 ID 定义
│ └── tasks.c ← 任务集中注册
├── examples/ ← 6 个示例任务
│ ├── task_led.c ← LED 闪烁
│ ├── task_key.c ← 按键扫描 + 消息发送
│ ├── task_echo.c ← 串口回显 + 定时报告 + 事件接收
│ ├── task_ota.c ← OTA 升级(纯事件驱动)
│ ├── task_sensor.c ← 传感器读取(慢周期)
│ └── task_relay.c ← 继电器定时控制
└── port/ ← 移植指南
└── PORTING.md ← 各平台移植说明
三步移植:
在你的工程中创建一个 timer.c,实现:
#include "scheduler/ptask.h"
void Timer_Init_1ms(void)
{
// 配置你的 MCU 硬件定时器,产生 1ms 中断
// 在定时器中断中调用 PTASK_Tick();
}
// 定时器中断服务函数
void TIMx_IRQHandler(void)
{
// 清除中断标志
PTASK_Tick(); // ← 驱动调度器时基
}各平台的参考实现见 port/PORTING.md。
复制 examples/task_led.c 到你的 User/ 目录,按需修改:
#include "scheduler/ttask.h"
static void my_init(void) { /* 初始化外设 */ }
static void my_periodic(void) { /* 周期执行的逻辑 */ }
static void my_event(uint16_t event, uint16_t param, void *data) {
/* 收到消息时处理 */
}
TTask task_my = {
.init = my_init,
.periodic = my_periodic,
.interval = 100, // 每 100ms 执行一次
.on_event = my_event,
};在 scheduler/tasks.h 加一行 ID:
#define TASK_ID_MY 6 /* 你的新任务 */在 scheduler/tasks.c 加一行注册:
extern TTask task_my;
TTASK_Register(&task_my); /* TASK_ID_MY = 6 */| API | 说明 |
|---|---|
TTASK_Init() |
初始化调度器 |
TTASK_Register(&task) |
注册任务,返回 ID |
TTASK_Run() |
主循环调用,驱动调度 |
TTASK_SendEvent(id, param) |
发送消息 |
TTASK_SendEventISR(id, param) |
从中断发送消息 |
PTASK_GetTick() |
获取 1ms 时基计数值 |
| 模式 | 实现方式 | 适用场景 |
|---|---|---|
| 周期执行 | 填 .periodic + .interval |
LED 闪烁、按键扫描、传感器轮询 |
| 事件驱动 | 填 .on_event,不填 periodic |
OTA 升级、串口命令响应 |
| 混合 | 两者都填 | 继电器控制(周期检查超时 + 事件触发开关) |
// 发消息
TTASK_SendEvent(TASK_ID_ECHO, TASK_PARAM(1, 1)); // 按键1单击
// 收消息(定义 on_event 回调即可)
static void my_event(uint16_t event, uint16_t param, void *data) {
uint8_t key = TASK_KEY(param); // 获取按键号
uint8_t evt = TASK_EVT(param); // 获取事件类型
}| 特性 | 说明 |
|---|---|
| 零 RTOS 依赖 | 纯 C 实现,只需 1ms 硬件定时器 |
| 低 RAM 占用 | 每任务约 20 字节 |
| 补偿式调度 | last_run += interval,消除时间漂移 |
| 消息队列 | 每任务 8 条深度,环形缓冲 |
| 中断安全 | SendEventISR 关中断保护 |
| uint32_t 溢出安全 | 差值计算,溢出后仍正确 |
// 改造前:超级循环
while(1) {
KEY_Scan();
USART_Echo();
Sensor_Read();
LED_Toggle();
Delay_Ms(10);
}
// 改造后:拆成 4 个 TTask
TTask task_key = { .periodic=KEY_Scan, .interval=10 };
TTask task_echo = { .periodic=USART_Echo, .interval=1 };
TTask task_sensor = { .periodic=Sensor_Read, .interval=1000 };
TTask task_led = { .periodic=LED_Toggle, .interval=500 };
// main 只剩一行
while(1) TTASK_Run();基于 WCH 官方 CH32V303 EVT 例程的调度器实现,MIT 许可证。