自制 RTOS

目录

0. 配置

1. 体系架构

2. 内存管理

3. 任务/调度器

3. IPC

3.1 信号量

3.2 互斥锁

3.3 消息队列

4. 临界区保护

4.1. 全局中断

4.2. 挂起调度器

4.3. 互斥锁

5. 软件定时器

6. 支持 Log 日志分级

7. Trace

8. Shell

9. Demo


都说,不会写 RTOS 的程序员不是好厨师,那么就写点东西吧;

V1.0 版本大概的组织是这样的:

这个 RTOS 命名为 MxOS;

项目地址为:

https://gitee.com/stephenzhou-tech/mx-os

0. 配置

MxOS 相关的配置全部都在 os_configs.h 文件中,配置是自注释的,看命名就知道干嘛用的,提供的配置并不算丰富,省去了很多无意义的配置;

1. 体系架构

最先适配了 Cortex-M4 (带 FPU) 的体系架构(后面会适配 Cortex-A 系列和 RISC-V);和体系架构相关的代码放在 arch 目录下,主要是和 CPU(MCU)紧密相关的部分,包含一些中断开关,中断初始化,OS Tick 心跳,CPU(MCU)上下文的定义和上下文切换的汇编等等,这部分独立抽象出来,将接口定义完整,以便进行日后的体系架构的扩展;

针对 Cortex-M4 需要注意一点的是,Specification 里面提到的 FPU Lazy stacking 模式,在使用 OS 的情况下,最好打开它,可以提高上下文切换的效率,至于什么是 Lazy stacking,打开它有什么好处,以及如何打开它,请自行阅读 Cortex-M4 的 Specification;

2. 内存管理

一个简单的内存管理模型,以预定义的内存 heap 为管理内容,所有的内存分配都在这进行;提供内存分配和释放的接口,分配按照 Bestfit 原则进行分配,释放的时候,进行相邻内存合并操作;为了进一步减少内存碎片,一些模块的描述符(IPC部分)都使用了预定义数组的方式,用户根据实际的设计情况,合理的定义描述符的个数(比如你的设计可能用到 3 个 Mutex Lock,那就定义 3 个);

内存分配释放的过程类似如下所示:

 这里的蓝色部分是内存初始对齐后砍掉的部分,红色部分是每一块内存的描述符;后续会考虑实现类似 Slab 的东东;

3. 任务/调度器

RTOS 需要及时的调度出当前处于 Ready 并且优先级最高的任务来执行,MxOS 支持最大 32 级任务优先级定义,抢占式调度;

任务优先级的值越高代表优先级越高,同等优先级的任务轮流执行;

默认会启动一个 Idle 任务;

支持任务延时,挂起;

支持主动让出 CPU;

支持一定程度的堆栈溢出检查;

支持动态切换优先级;

支持挂起调度器;

3. IPC

IPC 的描述符资源都是静态定义,根据使用的具体情况,用户来定义,以减小内存碎片;

3.1 信号量

支持计数信号量和二进制信号量用于任务之间,任务与中断之间的同步;

支持 TryWait、和 WaitTimeout 操作;

3.2 互斥锁

支持互斥锁用于临界区的互斥访问,为了更好的 RT 特性,使用优先级继承的方式来减少优先级翻转带来的影响;

支持 TryWait、和 WaitTimeout 操作;

3.3 消息队列

提供一种任务与任务,中断与任务的数据传输的消息队列机制;

4. 临界区保护

提供3中临界区保护方式:

4.1. 全局中断

支持可嵌套的,给予全局中断开关的临界区保护方式;

4.2. 挂起调度器

支持挂起调度器的临界区保护方式;

4.3. 互斥锁

支持基于互斥锁的临界区保护方式;

5. 软件定时器

支持创建软件定时器,软件定时器以一个高优先级任务的方式运行,支持 Auto Reload 和 Oneshot 的定时器模式,超时Handler运行在任务上下文;

6. 支持 Log 日志分级

日志等级分为 4 级输出,输出等级定义越高,低等级的 log 便被阻止输出:

ERROR、WARNING、INFO、DEBUG

7. Trace

支持 Trace OS,根据需要添加 Trace point;

8. Shell

支持外挂 Shell,并注册到内核(当前已经注册了一个开源的 Shell);

适配 Shell 需要板级支持,具体适配方式参考发布链接的 Readme;

现目前 Shell 已经支持了几个命令:

 

task : 查看当前任务状态

mem : 查看当前内存情况

cls : 清屏

help : 帮助

9. Demo

OS_Uint32_t task1_handle = 0;
OS_Uint32_t task2_handle = 0;
OS_Uint32_t task3_handle = 0;

int main(void)
{
    PlatformInit();

    OS_API_KernelInit();

    OS_Uint32_t TaskInputParam = 1;
    TaskInitParameter Param;
    OS_Memset(Param.Name, 0x00, CONFIG_TASK_NAME_LEN);
    Param.Name[0] ='T';
    Param.Name[1] ='1';
    Param.Priority = 1;
    Param.PrivateData = &TaskInputParam;
    Param.StackSize = 1024;
    Param.TaskEntry = TASK1_FUNC;
    OS_API_TaskCreate(Param, (void *)&task1_handle);

    Param.Name[0] ='T';
    Param.Name[1] ='2';
    Param.Priority = 2;
    Param.TaskEntry = TASK2_FUNC;
    OS_API_TaskCreate(Param, (void *)&task2_handle);

    Param.Name[0] ='T';
    Param.Name[1] ='3';
    Param.Priority = 3;
    Param.TaskEntry = TASK3_FUNC;
    OS_API_TaskCreate(Param, (void *)&task3_handle);

    OS_API_KernelStart();

    while(1);
}

版权声明:本文为CSDN博主「爱洋葱」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhoutaopower/article/details/122229253

生成海报
点赞 0

爱洋葱

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

驱动led --GPIO控制

GPIO引脚操作方法概述 硬件知识_LED原理图不同主芯片控制GPIO引脚的方法概述具体单板控制GPIO引脚的方法详解具体单板LED程序的编写与实验汇编与机器码编程知识_进制编程知识_字节序_位操作编写C程序控制LED解析C程序的内部机制完善

时钟周期和机器周期之间的关系

时钟周期(小) 时钟周期:单片机时钟控制的基本时间单位。以微秒(μs)作单位。 时钟周期受时钟晶体振荡频率(以Mhz作单位)(可以简单理解为晶振频率&#xf

18_SPI编程

第十八章 SPI编程(有误) 18.1 SPI接口简介 ​ SPI(Serial Peripheral Interface)接口是全双工的同步串行通讯总线,支持通过多个不同的片选信号来连接多个外设。