0. QuarKTS介绍
QuarkTS是一个专为资源高度受限的微控制器(8 位、16 位和 32 位 MCU)设计的开源、轻量级协作式多任务嵌入式操作系统(OS / 任务调度内核)。
与 FreeRTOS、RT-Thread 等重量级抢占式 RTOS 相比,QuarkTS 的定位更偏向于事件驱动型的超轻量级运行环境,其核心架构与特性如下:
一、 核心架构与特点
1. 共享栈设计与极低 RAM 占用
传统 RTOS(如 FreeRTOS):每个任务(Task)必须分配一块独立的任务堆栈(Stack,通常几百字节到几 KB),多开几个任务对小容量 RAM 的 MCU(如 2KB~8KB RAM)负担极大。
QuarkTS:采用运行至完成(Run-to-Completion)机制,所有任务共享同一个系统调用栈。系统启动一个任务仅消耗几十字节的任务控制块(TCB)内存,在极小 RAM 甚至 8 位单片机(PIC、AVR、STM8、8051)上也能轻松运行。
2. 调度机制:协作式 + 事件驱动
非抢占协作式:任务默认由事件触发或周期调度,任务执行完毕后主动退出交出 CPU 控制权。虽然避免了复杂的汇编级上下文切换(Context Switch),但要求每个任务不能有长延时死等(如禁止直接调用
delay_ms())。优先级与队列支持:支持任务优先级,当多个事件同时发生时,高优先级任务优先排队执行。
3. 开箱即用的丰富外围组件
QuarkTS 虽小,但内部封装了一套完整的嵌入式工程工具链,无需外挂第三方库:
分层状态机(HSM - Hierarchical State Machine):原生支持状态嵌套、进出动作,非常适合处理复杂的业务逻辑(如汽车灯光模式切换、按键状态机)。
软件定时器(Software Timers):只需提供一个物理 Tick(如 SysTick),即可派生无限数量的低开销软定时器。
任务间通信(IPC):内建消息队列(Queues)、通知(Notifications)和事件标志(Event Flags)。
内存池管理(Memory Pools):提供固定尺寸的安全内存池,杜绝堆碎片化。
简易 CLI 命令行接口:自带命令行解析器,方便串口调试。
4. 纯 ANSI-C 编写,极高移植性
核心代码不依赖特定芯片架构的汇编指令(如中断压栈切换),只要支持标准 C 语言编译器,基本只需对接一个系统硬件时钟节拍(Tick 函数),即可在任意 MCU 上跑起来。
1. 先需要搞到QuarkTS的源码
github仓库链接如下
kmilo17pet/QuarkTS: 一个支持优先级协作调度、时间控制、任务间通信原语、分层状态机和 CoRoutines 的开源嵌入式应用操作系统。 --- kmilo17pet/QuarkTS: An open-source OS for embedded applications that supports prioritized cooperative scheduling, time control, inter-task communications primitives, hierarchical state machines and CoRoutines.https://github.com/kmilo17pet/QuarkTS
你Clone完会发现 QuarkTS\src\os 底下什么也没有 我代码呢? 我内核呢??
作者有一个小巧思 他分离了主仓库和子仓库 :
- 纯净内核仓库(只给单片机用):
- 仓库地址其实是:
github.com/TECREA/QuarkTS - 这个仓库里极其干净,只有 16 个
.c和 20 个.h,没有任何杂质,专供我们这种嵌入式工程师引用。
- 仓库地址其实是:
- 综合开发大仓库(刚才 Clone 的这个):
- 仓库地址是:
github.com/kmilo17pet/QuarkTS - 这是作者自己的完整工作台。它里面包含了:
- 内核代码(通过子模块链接到仓库 1);
- 官方用户手册(通过子模块链接到说明书仓库);
- 电脑仿真测试工具(x86 单元测试、代码质量检查等)。
- 仓库地址是:
破解方法如下:
招式 1:克隆时直接带递归参数(最省事,一步到位)
git clone --recursive https://github.com/kmilo17pet/QuarkTS.git
加了--recursive(递归)参数,Git 就会自动把仓库里嵌套的所有子模块仓库全部一口气下载齐全!
招式 2:如果已经 clone 完了,在文件夹里补一条命令
git submodule update --init --recursive
此时就可以发现我们的关键文件就有了
2. 复制源码中的内核文件到我们的工程文件夹中
首先需要复制的文件如下
└── QuarkTS/ ├── include/ <-- 整个 include 文件夹(里面是 QuarkTS.h, qconfig.h 等) ├── qkernel.c <-- 调度器核心 ├── qtasks.c <-- 任务管理 ├── qclock.c <-- 时间基准 ├── qfsm.c <-- 状态机 ├── qqueues.c <-- 队列 ├── qstimers.c <-- 软件定时器 ├── qresponse.c ├── qmemmang.c ├── qlists.c ├── qioutils.c ├── qedgecheck.c ├── qcritical.c ├── qcoroutine.c ├── qbsbuffers.c └── qatcli.c也就是说把你Clone的QuarkTS-7.3.3下的include文件夹和所有.c文件拷到你自己的工程文件夹下 你可以自己建自己的文件夹方便管理 我这里使用的是 RTOS/QuarkTS
然后还要把你Clone的QuarkTS-7.3.3下的这个路径里面的 qconfig.h 这个文件放在**include文件夹内 补好最后一张拼图
可以参考一下我的工程目录 include文件夹内 还多一个qconfig.h 哦!
我的工程目录参考如下
3. 打通构建系统(CMakeLists.txt)
在CMakeLists.txt中加入
加上 RTOS/ QuarkTS / include
加上 RTOS/ QuarkTS / * .c (每个人的放的路径和名字不一样,我放我的作为参考)
加完之后保存 然后用cmake构建一下 发现文件已经被加入进去了
而且发现整个 QuarkTS 内核编进来,居然只占用了56 个字节的 RAM!这就是在 6KB 小芯片上选协作式单堆栈操作系统的意义所在!
4. 由你手写板级胶水层(xxx.c 和 xxx.h)
那么如何把刚刚加进去的操作系统和我们的单片机代码耦合起来,让他们正常工作呢,这时候就需要自己写一个驱动,把他们两“粘起来” 。
我这里的驱动文件名称就命名为app_os.c和.h了,自己看懂自己新建的文件即可。
主要任务如下(5ms任务是我自己加的 每个
人任务不同):
├── 配置硬件 1ms SysTick 心跳
├── 创建 5ms 车灯任务句柄(qTask_t)与回调函数(把 ADC_5ms_Task 和 Output_5ms_Task 放进去)
└── 编写 Init_OS( ):设置 1ms 时基 -> 注册任务 -> qOS_Run() 启动调度
我以我的简单嵌入式车灯代码为例:
第一步:新建App/app_os.h
#ifndef _APP_OS_H_ #define _APP_OS_H_ /* 启动车灯操作系统调度 */ void Init_App_OS(void); #endif /* _APP_OS_H_ */第二步:新建App/app_os.c
#include "app_os.h" #include "QuarkTS.h" #include "cps32k21x.h" #include "adc_drv.h" #include "output.h" #define SYSTEM_CLOCK_HZ (48000000UL) // 芯片主频 48MHz #define OS_TICK_HZ (1000UL) // OS 滴答频率 1000Hz (1ms 节拍) /* 1. 定义 5ms 车灯任务控制块(句柄) */ static qTask_t m5ms_task; /* 2. 编写 5ms 任务回调函数(把之前裸机 while(1) 里的业务搬进来) */ static void M5ms_Task_Callback(qEvent_t e) { (void)e; ADC_5ms_Task(); // 5ms 采样滤波 Output_5ms_Task(); // 5ms 状态机决策 } /* 3. 空闲任务回调(没有任务就绪时执行,留空即可) */ static void Idle_Callback(qEvent_t e) { (void)e; } /* 4. 配置单片机硬件 SysTick(每 1ms 触发一次 SysTick_Handler 中断) */ static void Hardware_SysTick_Init(void) { SysTick_Config(SYSTEM_CLOCK_HZ / OS_TICK_HZ); // 48000 个周期 = 1ms } /* 5. 胶水层主入口函数 */ void Init_App_OS(void) { /* 5.1 初始化 QuarkTS 内核时基 (0.001f 表示以 1ms 为基准) */ qOS_Setup( NULL , 0.001f, Idle_Callback NULL ); /* 5.2 添加 5ms 周期任务 (间隔 0.005 秒,周期运行,默认使能) */ qOS_Add_Task(&m5ms_task, M5ms_Task_Callback, qMedium_Priority, 0.005f, qPeriodic, qEnabled, NULL); /* 5.3 启动硬件 1ms 滴答定时器 */ Hardware_SysTick_Init(); /* 5.4 正式移交权力,启动操作系统调度循环(永不返回!) */ qOS_Run(); }第三步:在 App/interrupt.c 注入心跳
找到第 26 行原本空着的SysTick_Handler函数,把 QuarkTS 的心跳推动函数注入进去:
#include "QuarkTS.h" // 记得顶部引用一下头文件 extern void qClock_SysTick(void); void SysTick_Handler(void) { qClock_SysTick(); /* 每 1ms 中断触发一次,推动 QuarkTS 内核时间指针前进 1ms */ }5. 在 main.c 中交接指挥权 !
└── 初始化完硬件后,调用 Init_OS( ),进入操作系统调度循环!
直接上代码
#include "main.h" #include "adc_drv.h" #include "gpio_drv.h" #include "output.h" #include "app_os.h" // 包含我们的胶水层头文件 int main(void) { ErrorStatus status = ERROR; status = RCC_SysClk_SwitchTo_PLL(RCC, PLL_INPUT_SOURCE_HIRC, SET_PLL_FREQ_48MHz); while (status != SUCCESS) { status = RCC_SysClk_SwitchTo_PLL(RCC, PLL_INPUT_SOURCE_HIRC, SET_PLL_FREQ_48MHz); } /* 1. 单片机外设硬件初始化 */ Init_GPIO(); Init_ADC(); Init_Output(); /* 2. 移交控制权给操作系统(进入 qOS_Run 循环,不再返回) */ Init_App_OS(); /* 3. 保底死循环 */ while (1) { ; } }