MyRTOS 2025.9.1 更新

    15

项目Gitee地址 * my-rtos-demo

一个基于 Cortex-M4 的多任务调度系统。经过全新架构重构,实现了内核、服务与平台驱动的深度解耦。其结构清晰、API 统一、可移植性强,并提供了的内核扩展钩子,适合作为学习实时操作系统原理、研究可移植系统设计的参考项目。

✨ 功能特性

  • 分层架构
  • Kernel (内核层):平台无关的核心调度与IPC机制。
  • Services (服务层):基于内核API实现的可选功能模块,如日志、监视器等。
  • Platform (平台层):封装所有与硬件相关的代码,实现轻松移植。
  • 多任务调度
  • 基于优先级的抢占式调度。
  • 同优先级时间片轮转调度。
  • IPC机制 (Inter-Process Communication)
  • 消息队列 (Queue):线程安全,支持阻塞、非阻塞及超时。
  • 计数信号量 (Semaphore):用于资源计数与任务同步,支持ISR版本。
  • 互斥锁 (Mutex):包含优先级继承协议以防止优先级反转。
  • 递归互斥锁 (Recursive Mutex):允许同一任务嵌套持有。
  • 任务通知 (Task Notification):轻量级的直接到任务的事件传递机制,支持ISR版本。
  • 中断管理
  • 提供FromISR版本的API,用于在中断服务程序中安全操作内核对象。
  • 支持临界区嵌套。
  • 采用延迟调度机制(PendSV),优化中断响应。
  • 时间管理
  • 基于系统Tick的任务延时。
  • 软件定时器服务,支持周期性与一次性定时器。
  • 内存管理
  • 基于静态内存池的动态内存分配 (MyRTOS_Malloc/MyRTOS_Free)。
  • 线程安全,支持空闲块自动合并以减少碎片。
  • 任务管理
  • 支持任务的动态创建与删除。
  • 实现任务ID的回收与复用。
  • 提供API以获取任务状态与优先级。
  • 系统监控与调试
  • 实时性能监视器 (Monitor):显示任务状态、优先级、栈使用高水位线、CPU占用率等。
  • 堆内存监控:显示总大小、当前剩余及历史最小剩余。
  • HardFault异常信息打印。
  • 内核扩展机制 (Hooks)
  • 通过MyRTOS_RegisterExtension注册回调,可监听内核关键事件(如任务切换、内存分配/释放、系统Tick等)。
  • 为系统调试、性能分析和功能扩展提供了支持。
  • 统一的配置中心
  • 通过单一头文件 (MyRTOS_Config.h) 对内核功能、服务模块和平台资源进行集中配置。

🏗️ 抽象架构

1756694077275.svg

调度流程

1756694077644.svg

📁 目录结构

新的目录结构清晰地体现了分层解耦的设计思想:

MyRTOS-Project/
├── Firmware/                   # 芯片官方库与核心文件
├── MyRTOS/                   # MyRTOS 操作系统源码
│   ├── kernel/                 # 内核实现 (平台无关)
│   │   ├── include/            # 内核私有头文件
│   │   └── MyRTOS.c
│   ├── platform/               # 平台包 (可替换)
│   │   └── GD32F4xx/           # 针对 GD32F4xx 的具体实现
│   │       ├── include/
│   │       └── *.c
│   ├── services/               # 服务实现 (平台无关)
│   │   ├── include/            # 服务模块头文件
│   │   └── *.c
│   └── MyRTOS_Config_Example.h # 供用户参考的配置文件模板
├── Project/                    # Keil MDK 或其他 IDE 的工程文件
└── User/                       # 用户应用代码
    ├── main.c                  # 应用入口
    └── MyRTOS_Config.h         # RTOS 功能及平台配置文件 (用户唯一配置入口)
  • MyRTOS: RTOS的全部源码。
  • kernel: 内核实现。包含任务调度、内存管理、IPC机制等纯软件逻辑,完全与硬件平台无关。它向服务层和用户应用暴露所有核心API。
  • services: 服务实现。包含日志(Log)、系统监视器(Monitor)、软件定时器(Timer)等可选的功能模块。它们依赖内核API,但与具体硬件平台无关。
  • platform: 平台包,是连接上层软件与底层硬件的桥梁,是RTOS可移植性的关键。当前提供了一个针对 GD32F4xx 的实现包,其内部包含了CPU核心移植代码(上下文切换、中断处理)、板级支持代码(如调试串口PutChar实现)等。
  • User: 用户的最终应用代码。
  • main.c: 应用的入口点。现在它变得非常简洁,只需初始化系统,创建初始任务,然后启动调度器。
  • MyRTOS_Config.h: 用户唯一的配置中心。用户通过复制和修改 MyRTOS/MyRTOS_Config_Example.h 模板来创建此文件。它控制着RTOS的所有功能开关、资源限制和平台定义,是整个项目的“指挥棒”。

📚 API 列表

所有公开 API 均定义在 MyRTOS/kernel/include/MyRTOS.hMyRTOS/services/include/*.h 中。

系统核心 (System Core)

  • void MyRTOS_Init(void) — 初始化系统核心。
  • void Task_StartScheduler(void (*idle_task_hook)(void *)) — 启动任务调度器。
  • uint64_t MyRTOS_GetTick(void) — 获取当前系统Tick。
  • uint8_t MyRTOS_Schedule_IsRunning(void) — 检查调度器是否已启动。
  • void *MyRTOS_Malloc(size_t wantedSize) — 内核内存分配。
  • void MyRTOS_Free(void *pv) — 内核内存释放。

任务管理 (Task Management)

  • TaskHandle_t Task_Create(void (*func)(void *), const char *taskName, uint16_t stack_size, void *param, uint8_t priority) — 创建任务。
  • int Task_Delete(TaskHandle_t task_h) — 删除任务。
  • void Task_Delay(uint32_t tick) — 任务延时。
  • int Task_Notify(TaskHandle_t task_h) — 向任务发送通知。
  • int Task_NotifyFromISR(TaskHandle_t task_h, int *higherPriorityTaskWoken) — 在ISR中向任务发送通知。
  • void Task_Wait(void) — 等待任务通知。
  • TaskState_t Task_GetState(TaskHandle_t task_h) — 获取任务状态。
  • uint8_t Task_GetPriority(TaskHandle_t task_h) — 获取任务优先级。
  • TaskHandle_t Task_GetCurrentTaskHandle(void) — 获取当前任务句柄。

消息队列 (Queue Management)

  • QueueHandle_t Queue_Create(uint32_t length, uint32_t itemSize) — 创建队列。
  • void Queue_Delete(QueueHandle_t delQueue) — 删除队列。
  • int Queue_Send(QueueHandle_t queue, const void *item, uint32_t block_ticks) — 发送数据到队列 (阻塞/非阻塞/超时)。
  • int Queue_Receive(QueueHandle_t queue, void *buffer, uint32_t block_ticks) — 从队列接收数据 (阻塞/非阻塞/超时)。

互斥锁 (Mutex Management)

  • MutexHandle_t Mutex_Create(void) — 创建互斥锁。
  • void Mutex_Delete(MutexHandle_t mutex) — 删除互斥锁。
  • void Mutex_Lock(MutexHandle_t mutex) — 获取互斥锁 (永久等待)。
  • int Mutex_Lock_Timeout(MutexHandle_t mutex, uint32_t block_ticks) — 在指定时间内尝试获取互斥锁。
  • void Mutex_Unlock(MutexHandle_t mutex) — 释放互斥锁。
  • void Mutex_Lock_Recursive(MutexHandle_t mutex) — 递归获取互斥锁。
  • void Mutex_Unlock_Recursive(MutexHandle_t mutex) — 递归释放互斥锁。

信号量 (Semaphore Management)

  • SemaphoreHandle_t Semaphore_Create(uint32_t maxCount, uint32_t initialCount) — 创建计数信号量。
  • void Semaphore_Delete(SemaphoreHandle_t semaphore) — 删除信号量。
  • int Semaphore_Take(SemaphoreHandle_t semaphore, uint32_t block_ticks) — 获取信号量 (P操作)。
  • int Semaphore_Give(SemaphoreHandle_t semaphore) — 释放信号量 (V操作)。
  • int Semaphore_GiveFromISR(SemaphoreHandle_t semaphore, int *higherPriorityTaskWoken) — 在ISR中释放信号量。

内核扩展 (Kernel Extension)

  • int MyRTOS_RegisterExtension(KernelExtensionCallback_t callback) — 注册一个内核事件回调钩子。
  • int MyRTOS_UnregisterExtension(KernelExtensionCallback_t callback) — 注销一个内核事件回调钩子。

核心宏

  • MS_TO_TICKS(ms) — 毫秒转 ticks。
  • TICK_TO_MS(tick) — ticks 转毫秒。
  • MyRTOS_Port_EnterCritical() — 进入临界区。
  • MyRTOS_Port_ExitCritical() — 退出临界区。
  • MyRTOS_Port_Yield() — 手动触发任务调度。

🚀 快速开始 & 示例解析

新的架构通过平台钩子(Platform Hooks)极大地简化了用户应用的组织方式。现在,您只需要在平台层提供的特定函数中填充您的业务逻辑即可。

1. 极简的 `main` 函数

在新的架构下,用户的 `main.c` 变得异常简洁。所有的硬件初始化、服务配置和RTOS启动都由平台层统一管理(非强制)。

int main(void) {
    // 初始化平台层 (它会处理所有底层细节和RTOS服务)
    Platform_Init();

    // 打印启动信息
    LOG_I("Main", "=========   MyRTOS 演示   =========");
    LOG_I("Main", "系统启动中...");

    // 启动RTOS调度器 (由平台层接管)
    Platform_StartScheduler();
    
    return 0; // 永远不会执行到这里
}

2. 平台钩子 (Platform Hooks) - 应用的真正入口

您的应用代码现在被组织在几个平台钩子函数中。`main.c` 的主要工作就是实现这些钩子。

  • void Platform_BSP_Init_Hook(void): 用于初始化特定于您开发板的硬件,如LED、按键等。
  • void Platform_CreateTasks_Hook(void): 这是最重要的钩子,用于创建所有的RTOS对象,包括任务、队列、信号量、定时器等。
  • void Platform_AppSetup_Hook(ShellHandle_t shell_h): 用于在RTOS服务(如Shell)初始化后,进行应用层面的配置,例如注册自定义Shell命令。

下面是 Platform_CreateTasks_Hook 的一个示例,展示了如何在这里集中创建所有应用组件:

void Platform_CreateTasks_Hook(void) {
    /* --- 软件定时器测试 --- */
    single_timer_h = Timer_Create("单次定时器", MS_TO_TICKS(5000), 0, single_timer_cb, NULL);
    perio_timer_h = Timer_Create("周期定时器", MS_TO_TICKS(10000), 1, perio_timer_cb, NULL);
    Timer_Start(single_timer_h, 0);
    Timer_Start(perio_timer_h, 0);

    /* --- 队列测试 (生产者-消费者) --- */
    product_queue = Queue_Create(3, sizeof(Product_t));
    Task_Create(consumer_task, "Consumer", 256, NULL, CONSUMER_PRIO);
    Task_Create(producer_task, "Producer", 256, NULL, PRODUCER_PRIO);
    
    /* --- 信号量测试 (共享资源) --- */
    printer_semaphore = Semaphore_Create(2, 2); // 假设有2台打印机
    Task_Create(printer_task, "PrinterTask1", 256, (void *)"PrinterTask1", PRINTER_TASK_PRIO);
    Task_Create(printer_task, "PrinterTask2", 256, (void *)"PrinterTask2", PRINTER_TASK_PRIO);
    Task_Create(printer_task, "PrinterTask3", 256, (void *)"PrinterTask3", PRINTER_TASK_PRIO);

    /* --- 任务协作 (任务通知) --- */
    a_task_h = Task_Create(a_task, "TaskA", 64, NULL, COLLABORATION_TASKS_PRIO);
    b_task_h = Task_Create(b_task, "TaskB", 64, NULL, COLLABORATION_TASKS_PRIO);
    Task_Notify(a_task_h); // 启动第一个任务

    /* --- 动态任务管理 (创建与删除) --- */
    c_task_h = Task_Create(c_task, "TaskC_dynamic", 1024, NULL, COLLABORATION_TASKS_PRIO);
    d_task_h = Task_Create(d_task, "TaskD_Creator", 256, NULL, COLLABORATION_TASKS_PRIO);
    
    /* --- 其他功能任务 --- */
    high_prio_task_h = Task_Create(high_prio_task, "HighPrioTask", 256, NULL, HIGH_PRIO_TASK_PRIO);
    interrupt_task_h = Task_Create(interrupt_handler_task, "KeyHandlerTask", 128, NULL, INTERRUPT_TASK_PRIO);
}

3. 功能示例解析 (摘自 `main.c`)

生产者-消费者模型 (消息队列)

producer_task 任务生产数据并发送到队列,而 consumer_task 任务从队列中等待并接收数据。

typedef struct { uint32_t id; uint32_t data; } Product_t;
static QueueHandle_t product_queue;

void producer_task(void *param) {
    Product_t product = {0, 100};
    while (1) {
        product.id++;
        LOG_I("生产者", "产品 ID %lu 已发送", product.id);
        Queue_Send(product_queue, &product, MS_TO_TICKS(100));
        Task_Delay(MS_TO_TICKS(2000));
    }
}

void consumer_task(void *param) {
    Product_t received_product;
    while (1) {
        if (Queue_Receive(product_queue, &received_product, MYRTOS_MAX_DELAY) == 1) {
            LOG_I("消费者", "接收到产品 ID %lu", received_product.id);
        }
    }
}
共享资源管理 (计数信号量)

模拟多个任务竞争有限的资源(如2台打印机)。任务必须先获取信号量才能“打印”,使用后释放信号量供其他任务使用。

static SemaphoreHandle_t printer_semaphore; // 创建时设置为 Semaphore_Create(2, 2)

void printer_task(void *param) {
    const char *taskName = (const char *) param;
    while (1) {
        LOG_D(taskName, "正在等待打印机...");
        if (Semaphore_Take(printer_semaphore, MYRTOS_MAX_DELAY) == 1) {
            LOG_I(taskName, "获取到打印机, 开始打印 (耗时3秒)...");
            Task_Delay(MS_TO_TICKS(3000));
            LOG_I(taskName, "打印完成, 释放打印机.");
            Semaphore_Give(printer_semaphore);
        }
        Task_Delay(MS_TO_TICKS(500));
    }
}
任务协作与同步 (任务通知)

a_taskb_task 通过互相发送通知来交替执行,展示了轻量级的任务间同步。

void a_task(void *param) {
    while (1) {
        Task_Wait(); // 等待B的通知
        LOG_I("Task A", "被唤醒,开始工作...");
        Task_Delay(MS_TO_TICKS(1000));
        LOG_I("Task A", "工作完成,唤醒 Task B");
        Task_Notify(b_task_h);
    }
}

void b_task(void *param) {
    while (1) {
        Task_Wait(); // 等待A的通知
        LOG_I("Task B", "被唤醒,开始工作...");
        Task_Delay(MS_TO_TICKS(1000));
        LOG_I("Task B", "工作完成,唤醒 Task A");
        Task_Notify(a_task_h);
    }
}
动态任务管理

c_task 运行5次后会调用 Task_Delete(NULL) 删除自身。d_task 负责监控 c_task 的状态,并在其被删除后重新创建它。

void c_task(void *param) {
    uint16_t index = 0;
    while (1) {
        index++;
        LOG_D("Task C", "正在运行, 第 %d 次", index);
        if (index == 5) {
            LOG_W("Task C", "运行5次后删除自己.");
            c_task_h = NULL; // 清除全局句柄
            Task_Delete(NULL); // 删除自身
        }
        Task_Delay(MS_TO_TICKS(1000));
    }
}

void d_task(void *param) {
    while (1) {
        if (c_task_h == NULL) {
            LOG_I("Task D", "检测到Task C不存在, 准备重新创建...");
            Task_Delay(MS_TO_TICKS(3000));
            c_task_h = Task_Create(c_task, "TaskC_dynamic", 1024, NULL, COLLABORATION_TASKS_PRIO);
        }
        Task_Delay(MS_TO_TICKS(1000));
    }
}
中断安全API (ISR From API)

硬件中断服务程序(ISR)通过调用 FromISR 版本的API(如 Semaphore_GiveFromISR)安全地与RTOS任务交互,唤醒正在等待的任务。

// 中断服务程序
void EXTI0_IRQHandler(void) {
    if (exti_interrupt_flag_get(EXTI_0) != RESET) {
        exti_interrupt_flag_clear(EXTI_0);
        int higherPriorityTaskWoken = 0;

        // 从ISR中释放信号量
        Semaphore_GiveFromISR(isr_semaphore, &higherPriorityTaskWoken);
        
        // 如果唤醒了更高优先级的任务,请求进行一次上下文切换
        MyRTOS_Port_YieldFromISR(higherPriorityTaskWoken);
    }
}

// 等待中断信号量的任务
void isr_test_task(void *param) {
    LOG_I("ISR测试", "启动并等待信号量...");
    while (1) {
        if (Semaphore_Take(isr_semaphore, MYRTOS_MAX_DELAY) == 1) {
            LOG_I("ISR测试", "成功从按键中断获取信号量!");
        }
    }
}

⚙️ 调度机制

MyRTOS 的调度机制核心基于 Cortex-M4 的 SysTick 与 PendSV 中断,实现了优先级抢占与时间片轮转。

  • SysTick 中断: 作为系统心跳,周期性地触发,用于更新系统Tick计数和处理任务延时。
  • 就绪列表 (Ready List): 按优先级组织,存放所有可以立即运行的任务。
  • 调度器 (Scheduler): 在需要进行任务切换时(如SysTick中断、任务阻塞、任务唤醒),从就绪列表中选择优先级最高的任务作为下一个要运行的任务。对于同优先级的任务,采用时间片轮转(Round-Robin)方式调度。
  • PendSV 中断: 用于执行实际的上下文切换。它是一个可悬起、低优先级的中断,确保上下文切换不会在其他重要中断处理期间发生,从而降低了中断延迟。 -* 空闲任务 (Idle Task): 当没有其他任何就绪任务时,CPU 执行空闲任务。通常可在此任务中执行低功耗处理或系统监控。

📜 参考

👤 作者

A_XiaoXiu

消息盒子

# 暂无消息 #

只显示最新10条未读和已读信息