# multitasks-os **Repository Path**: liuniansihuo/multitasks-os ## Basic Information - **Project Name**: multitasks-os - **Description**: 基于时间轮询的多任务及任务间通讯和驱动软件框架。 特性:多任务之间完全独立,任务之间通过消息进行通讯,每个任务有独立的时间片,驱动架构分层与分离。具有模块化,移植性,可复用性,软件分层设计的特点。 - **Primary Language**: C - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 5 - **Forks**: 4 - **Created**: 2023-06-17 - **Last Updated**: 2025-08-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: mcu, os, 多任务, 嵌入式, IOT ## README [TOC] ## 一. multitasks 简介和使用 ### 1.1 multitasks-os是什么 1. multitasks-os是基于前后台的 ` 多任务及任务间通讯`和 `驱动软件框架 `。 2. multitasks-os软件框架,具有``模块化,移植性,可复用性,软件分层设计``的特点。 ### 1.2 软件框架框图 ![软件框架框图](https://s21.ax1x.com/2025/02/14/pEuvwqJ.png) 1. 每个任务一般分为三层:`task层,bsp层,driver层`。一个driver层可以被多个bsp层使用。一个bsp层也可以被多个task层使用。 2. 任务消息提供接口,负责任务之间发送数据或获取数据。 3. CPU一直执行各个任务的主函数,相当于任务占用CPU计算资源。 4. timer定时器,work工作队列,可以灵活的占用CPU和放弃CPU。 5. 芯片SDK库,CMISS,startup由不同芯片提供。 ### 1.3 工程文件组织结构 ```c |-- mcu:芯片底层库文件相关等 |-- ch32f20x |-- stm32f10x |-- MDK:工程 |-- tool:编译后处理脚本工具,比如合并bootloat和app,app文件处理,格式转换 |-- MDK.uvoptx MDK.uvprojx: MDK工程文件 |-- os:软件框架代码和功能组件等 |-- common: 移植框架代码时,根据不同mcu进行修改的文件 |-- cpu.h:芯片的宏定义和库头文件等 |-- os_task_driver.c.h:框架任务所需的driver层,外设时钟开启,看门狗等。 |-- os_tick.c.h:软件框架的系统tick,系统时基默认为1ms. |-- compent:软件功能组件,纯软件。 |-- bitmap.c.h: 位处理 |-- fifo.c:队列,仅支持unsignedchar 类型,用于一些字符流的协议处理。 |-- list.c.h: 链表 |-- QueueRing.c.h:队列,用于各种数据类型 |-- work_base.c.h:工作队列功能接口。 |-- tick_work.c.h:精确的时基work,该任务处理放在系统时基中断中。 |-- work.c.h:work队列。 |-- timer.c.h:定时器功能。 |-- doc:说明文档 |-- main.c:main函数文件,不做修改。 |-- os_sys.c.h:所有任务处理相关的函数和任务消息处理宏定义。 |-- os_task.c:系统任务。 |-- os.h sys_config.h typede.h:类型定义,功能宏定义, |-- user:用户任务文件夹 |-- task_def.h:任务消息枚举定义,任务枚举定义 |-- project.h:具体项目中的一些公共定义,比如宏定义,结构体定义等 |-- key switch ...:具体的任务文件夹 |-- usart log ...:公共的驱动功能 ``` > 说明:对于软件框架的移植,只需修改os/common/下的文件即可,前提是已经有一个正常的MDK工程了。 ### 1.4 任务添加和删除 1.4.1 新建一个任务,先在` user/task_def.h` 文件中添加任务枚举,按照如下模版,建立一个新任务,并自动添加到任务队列中。 ``` typedef enum TASKID_E { TASKID_SYS = 0U, TASKID_KEY, TASKID_SWITCH, TASKID_BEEP, TASKID_MAX, }E_TASKID; ``` ```c CONSTRUCTOR_DEFAUTL() void syssvc_task_create(void) { struct task_s task = TASK_DEFAULT_INIT(); task.ID = TASKID_SYS; task.state = TASK_STAT_FREE; task.TaskTime = 0; task.run = syssvc_main_run; task.sleep_init = syssvc_peripheral_sleep; task.msg_process= syssvc_message_process; task.task_tick = NULL; task.is_used = 1; task.task_init = syssvc_variable_init; sys_task_add(&task); } ``` > 任务枚举值越小,则`task.task_init`就先执行,对于任务初始化顺序有要求的,可调整枚举顺序。 1.4.2 删除一个任务,屏蔽 CONSTRUCTOR_DEFAUTL() 这一行即可。 ```c //CONSTRUCTOR_DEFAUTL() void syssvc_task_create(void) { struct task_s task = TASK_DEFAULT_INIT(); } ``` ### 1.5 任务结构体说明 ```c struct task_s { E_TASKID ID;//任务ID,全局唯一 task_stat_e state;//任务状态, SysTick_t TaskTime; void (*run)(void);//任务主运行函数 void (*msg_process)(E_DATAID , const u32 );//消息处理函数 void (*task_tick)(void);//任务级,tick处理函数 void (*task_init)(void);//任务初始化函数 unsigned char is_used;//任务使用标志 }; ``` ### 1.6 任务数据结构设计 每个任务都有一个全局的结构体数据,该任务所有的全局变量数据在该结构体中定义。且应该定义为 **static** ```c struct key_task_s { SysTick_t time; u32 test3_cnt[11]; u32 test4_cnt[11]; u32 test6_cnt[11]; u8 key_test; E_KEY key0; E_KEY key1; u32 test1; int test4; unsigned char test6; float test7; }; struct key_task_s key_task={0}; ``` ### 1.7 任务如何划分呢 通常一个项目产品中,根据产品功能划分多个模块,一般就是多个任务。任务划分的颗粒度可大可小,根据实际情况和经验进行设计。 ## 二: 任务消息简介和使用 ### 2.1 任务消息说明 从理论上,软件设计时,要划分为多个模块或任务,每个任务应该是独立的,但是任务之间要有信息交换,所谓的信息,就是任务的数据,尤其是全局数据,如一个任务要使用其他任务的数据,就要使用那个任务的全局变量,这样任务就不独立了,增加耦合性。于是任务消息就为了解决任务之间互相引用变量,彻底隔离任务耦合性。 ### 2.2 任务消息使用说明 1. 任务之间发送消息,必须通过宏定义 msg_send(DatID,data, ...)msg_recv(DatID,data,id) * msg_send(DatID,data, ...) :发送消息宏定义,把本地数据发送到其他任务。 * msg_recv(DatID,data,id) :接受消息宏定义,把指定任务数据接收到本地任务。 2. 任务消息分为单参数和多参数。也分为一对一消息,一对多消息,广播消息。也可分为数据消息,地址消息。 * 单参数:即发送一个变量的数据,可以是数据也可以是地址。可以直接使用data,或使用MSG()宏定义,当时直接使用data时,需要自己转换处理; * 多参数:即同时发送多个变量的数据,可以是数据也可以是地址。其中data必须使用MSG()宏定义。 * 当直接发送数据时,一般发送基本数据类型的数据,char ,unsigned char, short int ,unsigned short int,int, unsigned int,因为这些数据可以向上兼容强制转换。其他类型的如,浮点型,结构体等,必须发送地址了。 * msg_recv 只能是一对一消息。一对多消息,在参数列表中指定要发送的任务ID。广播消息,参数仅一个且为 TASKID_MAX。 * 数据消息,data或MSG内容为数据的消息。地址消息,data或MSG内容为地址的消息 3. 使用MSG(),必须和宏定义处理MSG_D() MSG_P() MSG_R() 成对使用。 * MSG_D() MSG_P() 属于 msg_send的数据处理,MSG_D()是发送数据的,MSG_P()是发送地址的,里面的内容要保持一致,要是地址就都是地址。不能混合使用。 * MSG_R() 属于msg_recv的数据处理,里面的内容必须是地址。 4. 当新建一个消息时,必须在task_def.h文件中的 E_DATAID 枚举中定义。 然后在发送数据的任务,编写代码进行发送数据,在接受数据的任务的消息处理函数中处理消息。 ```c typedef enum DATID_ENUM { DATID_NULL = 0x1000, DATID_EEPROM_TEST1, DATID_EEPROM_TEST7, DATID_EEPROM_TEST1_4_6_7_READ, DATID_EEPROM_TEST1_4_6_7_WRITE, DATID_EEPROM_SAVE, DATID_STATUS_LED_ON_OFF, DATID_STATUS_LED_BLINK, DATID_MAX, }E_DATAID; ``` ### 2.3 任务消息举例说明 1. 单参数 数据 消息。比如按键任务向IO任务发送,LED使能或关闭。 * 发送端 ```c msg_send(DATID_STATUS_LED_ON_OFF,0, TASKID_SWITCH); msg_send(DATID_STATUS_LED_ON_OFF,1, TASKID_SWITCH); ``` * 接收端 ```c case DATID_STATUS_LED_ON_OFF: if(0==data) led_off(STATUS_LED); else if(1==data) led_on(STATUS_LED); break; ``` 2. 多参数 数据 消息。比如比如按键任务向IO任务发送,LED闪烁功能。 * 发送端 ```c msg_send(DATID_STATUS_LED_BLINK,MSG(200,400), TASKID_SWITCH); ``` * 接收端 ```c case DATID_STATUS_LED_BLINK: { u32 on_ms , off_ms; MSG_D(on_ms,off_ms); led_pwm(STATUS_LED,on_ms, off_ms); } break; ``` 3. 多参数 地址消息,比如按键任务发送eeprom任务更新数据功能。 * 发送端 ```c msg_send(DATID_EEPROM_TEST1_4_6_7_WRITE,MSG(&key_task.test1,&key_task.test4,&key_task.test6,&key_task.test7), TASKID_EEPROM,TASKID_SWITCH); ``` * 接收端 ```c case DATID_EEPROM_TEST1_4_6_7_WRITE: MSG_P(eeprom_task.data.D.test1,eeprom_task.data.D.test4,eeprom_task.data.D.test6,eeprom_task.data.D.test7); break; ``` 4. 多参数 接收消息,比如按键任务接收更新 eeprom任务 数据功能 * 发送端 ```c msg_recv(DATID_EEPROM_TEST1_4_6_7_READ,MSG(&key_task.test1,&key_task.test4,&key_task.test6,&key_task.test7),TASKID_EEPROM); ``` * 接收端 ```c case DATID_EEPROM_TEST1_4_6_7_READ: MSG_R(eeprom_task.data.D.test1,eeprom_task.data.D.test4,eeprom_task.data.D.test6,eeprom_task.data.D.test7); break; ``` ## 三:软件框架使用规范 1. task层,bsp层,driver层,每一层向上层提供接口函数,下层可以使用注册回调使用上层函数接口。 2. 仅driver层包含芯片SDK库头文件,bsp层,task层不包含。比如IO类任务。 ![](https://s21.ax1x.com/2025/02/12/pEuC0nU.png) 3. 工程编译除引用第三方外,不要有Waring. ![](https://s21.ax1x.com/2025/02/12/pEuCdXT.png) 4. 从工程中删除某一个任务后,工程仍然可以编译他通过。 [gitee仓库](https://gitee.com/liuniansihuo/multitasks-os) https://gitee.com/liuniansihuo/multitasks-os ## 联系方式 ![](https://s21.ax1x.com/2025/03/04/pEJCKFP.jpg)