RTThreadIoTOS · 2021年01月28日

μC/OS-II兼容层——让基于μC/OS-II开发的应用层无感地迁移到RT-Thread操作系统

文章转载于微信公众号:RTThread物联网操作系统
作者:满鉴霆

1 概述

image.png

这是一个针对RT-Thread国产操作系统的μCOS-II操作系统兼容层,可以让基于美国Micriμm公司的μCOS-II操作系统的项目快速、无感地迁移到RT-Thread操作系统上。在兼容层的设计、编写上尊重原版μC/OS-II,保证原版μC/OS-II的原汁原味。

uCOS-II兼容层的设计和编写融合了uCOS-III兼容层的编写经验,同时与uCOS-III兼容层进行交叉检查,确保两个兼容层的可靠性。

支持版本:μC/OS-II 2.00-2.93全部版本

代码仓库地址:

https://github.com/mysterywol...

欢迎star!

1.1 RT-Thread的其他RTOS兼容层

RT-Thread操作系统的μCOS-III兼容层:https://github.com/mysterywol...

1.2 本兼容层适合于

  • 之前学习过μCOS-II操作系统,意图转向学习RT-Thread国产操作系统。本兼容层可以帮您用已有的μCOS-II编程经验和习惯快速将项目跑起来,日后在应用过程中深入熟悉RT-Thread的API函数,逐步向RT-Thread过度,降低您的学习门槛和时间成本。有了本兼容层,对RT-Thread API以及编程风格的不熟悉再也不是您学习RT-Thread的阻力!
  • 现有任务(线程)模块采用μCOS-II编写,想要用在基于RT-Thread的工程上
  • 老项目需要从μCOS-II操作系统向RT-Thread操作系统迁移
  • 当需要快速基于RT-Thread开发产品,但是工程师之前均采用μC/OS开发,从未用过RT-Thread的开发经验。本兼容层可以帮助让工程师快速基于μC/OS-II开发经验开发产品,简化软件的重用、缩短微控制器新开发人员的学习过程,并缩短新设备的上市时间。
  • 避免在从μCOS-II迁移到RT-Thread时,由于μCOS-II的编程经验导致的思维定式引发的错误,这种错误一般很难被发现

例如:

  1. 软件定时器参数的不同
  2. 任务堆栈的数据类型不同

1.3 版本详细信息

image.png

1.4 官网

RT-Thread:

https://www.rt-thread.org/   

文档中心:

https://www.rt-thread.org/doc...

μCOS-II:

https://www.micrium.com/  
文档中心:

https://doc.micrium.com/pages...

2 使用

2.1 Keil-MDK仿真工程

本仿真工程是基于STM32F103平台。

Keil工程路径:rt-thread-3.1.3/bsp/stm32f103/Project.uvprojx

需要提前安装好RT-Thread Nano-3.1.3 Keil支持包.

注意:调试串口使用的是USART2,不是USART1

image.png

2.2 迁移步骤

(如果使用的是RT-Thread Nano版请参见以下步骤;若使用RT-Thread完整版可以直接跳转至Env工具自动化配置到工程中章节

  1. 将uCOS-II文件夹内的所有文件都加入到你的工程中,最好保持原有文件夹的结构。相较于原版μCOS-II增加了os_rtwrap.c文件,负责对RT-Thread和μCOS-II的转换提供支持。
  2. 配置os_cfg.h 每个选项的配置说明和原版μCOS-II一致,若有不同,我已经在注释中有所解释。 
    原版μCOS-II配置说明可参见: 
    a)《嵌入式实时操作系统μC/OS-II(第二版)》北京航空航天大学出版社 邵贝贝等译  
    b) Micriμm公司μCOS-II在线文档
  3. μCOS-II原版定时器回调函数是在定时器线程中调用的,而非在中断中调用,因此要使用μCOS-II兼容层的软件定时器,需要将rtconfig.h中的宏定义RT_USING_TIMER_SOFT置1。
  4. 由于兼容层采用rt-thread内核自带的堆内存分配方式,因此免去了原版uCOS-II中配置任务以及各内核对象内存池大小的步骤,遂需要在rtconfig.h中定义RT_USING_MEMHEAP

2.3 os\_cfg.h配置文件

1#define  OS_TMR_CFG_TICKS_PER_SEC  10u   /* Rate at which timer management task runs (Hz) */

在原版μCOS-II中,该宏定义定义了软件定时器的时基信号,这与RT-Thread的软件定时器有本质的不同,在RT-Thread中,软件定时器的时基信号就等于OS Ticks。因此为了能够将μCOS-II软件定时器时间参数转为RT-Thread软件定时器的时间参数,需要用到该宏定义。请使该宏定义与原工程使用μCOS-II时的该宏定义参数一致。需要注意的是,虽然在兼容层中定义了软件定时器的时基频率,但是在兼容层内部使用的RT-Thread软件定时器的时基频率等同于OS Ticks,因此OS_TMR结构体的.OSTmrMatch成员变量其保存的数值是以OS Ticks频率来计算的。

由于兼容层采用rt-thread内核自带的堆内存分配方式,因此免去了原版uCOS-II中配置任务以及各内核对象内存池大小的步骤,遂相关宏定义在兼容层中均被删除

2.4 运行

2.4.1 手动初始化流程

本兼容层完全兼容官方给出的标准初始化流程,如果您兼容老项目,μCOS-II初始化部分无需做任何修改。

2.4.2 自动初始化流程

如果您在应用层中不想手动初始化本兼容层,可以在rtconfig.h文件中定义PKG_USING_UCOSII_WRAPPER_AUTOINIT宏定义。请参见 [4.2.1章节](#4.2.1 Enable uCOS-II wrapper automatically init)(如无特殊要求,建议采用该种方式)。

2.5 注意

1、μCOS-II的任务堆栈大小单位是sizeof(CPU_STK),而RT-Thread的线程堆栈大小单位是sizeof(rt_uint8_t),虽然在兼容层已经做了转换,但是在填写时一定要注意,所有涉及到μCOS-II的API、宏定义全部是按照μCOS-II的标准,即堆栈大小为sizeof(CPU_STK)切勿混搭!这种错误极其隐晦,一定要注意!下面是混搭的错误示例

 1ALIGN(RT_ALIGN_SIZE) 2static rt_uint8_t thread2_stack[1024];//错误:混搭RT-Thread的数据类型定义线程堆栈 3 4OSTaskCreateExt(task, 5               0, 6               &task_stack[TASK_SIZE-1], 7               TASK_PRIO, 8               0, 9               &task_stack[0],10               sizeof(thread2_stack),//任务堆栈大小(错误:这个参数的单位是sizeof(CPU_STK))11               0,12               OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);

下面是正确写法

 1#define THREAD_STACK_SIZE       256 //正确,要通过宏定义单独定义堆栈大小,单位为sizeof(CPU_STK) 2ALIGN(RT_ALIGN_SIZE) 3   static CPU_STK thread2_stack[THREAD_STACK_SIZE];//正确,使用uCOS-II自己的数据类型定义任务堆栈 4 5OSTaskCreateExt(task, 6               0, 7               &task_stack[TASK_SIZE-1], 8               TASK_PRIO, 9               0,10               &task_stack[0],11               THREAD_STACK_SIZE,//任务堆栈大小(正确)12               0,13               OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);

2、本兼容层文件内含有中文注释,编码格式ANSI - GB2312,并非UTF-8编码。

3 接口

3.1 新增的API

额外实现OSMutexCreateEx()函数,该函数并不在uCOS-II原版的函数中,OSMutexCreate()函数中第一个参数prio在兼容层中没有任何意义,因此该函数将OSMutexCreate()函数中的第一个参数略去,以方便用户使用。原因是由于uCOS-II的实现方式过于落后,不支持相同任务在同一优先级。推荐用户使用这个API:

1OS_EVENT  *OSMutexCreateEx (INT8U  *perr);

额外实现OSQCreateEx()函数,该函数并不在uCOS-II原版的函数中,OSQCreateEx()函数中第一个参数size在本兼容层中没有意义,因此该函数将OSQCreateEx()函数中的第一个参数略去,以方便用户使用。推荐用户使用这个API:

1OS_EVENT  *OSQCreateEx (INT16U    size);

3.2 没有实现兼容的API (仅2个)

 1INT8U         OSTaskCreate            (void           (*task)(void *p_arg), 2                                       void            *p_arg, 3                                       OS_STK          *ptos, 4                                       INT8U            prio); 5 6INT16U        OSEventPendMulti        (OS_EVENT       **pevents_pend, 7                                       OS_EVENT       **pevents_rdy, 8                                       void           **pmsgs_rdy, 9                                       INT32U           timeout,10                                       INT8U           *perr);

3.3 钩子函数

μCOS-II的钩子函数仅对μCOS-II兼容层负责。 即如果你注册了OSTaskDelHook函数,他仅会在调用OSTaskDel函数时被调用,不会在调用rt_thread_detach函数时被调用(这个由RTT的钩子函数负责)。这样做是为了层次分明,防止μCOS-II兼容层插手RT-Thread内部事务。

μCOS-II的钩子函数在两个文件中实现:os_cpu_c.capp_hooks.c 。按照μCOS-II的思想,os_cpu_c.c提供原始的钩子函数(即这些钩子函数被相应的函数直接调用),该文件以及其内部的钩子函数是移植工程师编写的内容,应用工程师不应该操作这个文件的内容,os_cpu_c.c文件的钩子函数提供相应的函数指针供app_hooks.c文件内的钩子函数注册和使用,这个文件内的钩子函数应用工程师是可以操作的。换句话说,我们有什么需要在钩子函数中调用的函数,应该放在app_hooks.c文件中。

以下原版μCOS-II钩子函数将予以取消,由RT-Thread接管相关钩子函数接管:

1void          OSTaskReturnHook          (OS_TCB *p_tcb);2void          OSTaskSwHook              (void);3void          OSTimeTickHook            (void);

同时,上述钩子函数对应的应用级钩子函数也被取消:

1void  App_TaskReturnHook (OS_TCB  *p_tcb);2void  App_TaskSwHook (void);3void  App_TimeTickHook (void);

3.4 统计任务(OS\_TaskStat())

在μCOS-II中,统计任务是一个系统任务,通过OS_TASK_STAT_EN宏决定是否开启,可以在系统运行时做一些统计工作。CPU的利用率用一个0-100之间的整数表示(对应0% - 100%)。

但是RT-Thread并没有统计任务,因此需要创建一个任务来兼容原版μCOS-II的统计任务,完成上述功能。该统计任务会在兼容层初始化时自动创建,用户无需干预。用户仅需调用OSCPUUsage全局变量即可获取当前的CPU使用率,CPU使用率的计算策略和原版μCOS-II完全一致。

3.5 任务控制块、内核对象控制块(结构体)

本兼容层尽可能的兼容任务、内核对象控制块(结构体)的每个成员变量,确保迁移过来的老程序如果直接访问这些结构体的成员变量也是可以直接运行,无需做修改的(尽管直接访问结构体的成员变量μCOS-II官方并不建议甚至十分反对)。

例如,OS_TCB结构体的各个成员变量如下,可以看到,其包含了原版绝大多数成员变量。如果不用兼容原版成员变量,可以定义宏PKG_USING_UCOSII_WRAPPER_TINY,可以看到OS_TCB结构体大幅度缩减。也可以将OS_TASK_PROFILE_ENOS_TASK_NAME_ENOS_CFG_TASK_REG_TBL_SIZE 关闭以进一步裁剪。

 1typedef struct os_tcb { 2    struct rt_thread OSTask; 3    OS_STK          *OSTCBStkPtr;           /* Pointer to current top of stack                         */ 4 5#if OS_TASK_CREATE_EXT_EN > 0u 6    void            *OSTCBExtPtr;           /* Pointer to user definable data for TCB extension        */ 7    OS_STK          *OSTCBStkBottom;        /* Pointer to bottom of stack                              */ 8    INT32U           OSTCBStkSize;          /* Size of task stack (in number of stack elements)        */ 9    INT16U           OSTCBOpt;              /* Task options as passed by OSTaskCreateExt()             */10    INT16U           OSTCBId;               /* Task ID (0..65535)                                      */11#endif1213    struct os_tcb   *OSTCBNext;             /* Pointer to next     TCB in the TCB list                 */14    struct os_tcb   *OSTCBPrev;             /* Pointer to previous TCB in the TCB list                 */1516#if OS_TASK_CREATE_EXT_EN > 0u17#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)18    OS_TLS           OSTCBTLSTbl[OS_TLS_TBL_SIZE];19#endif20#endif2122#ifndef PKG_USING_UCOSII_WRAPPER_TINY23#if (OS_EVENT_EN)24    OS_EVENT        *OSTCBEventPtr;         /* Pointer to           event control block                */25#endif26#if (OS_FLAG_EN > 0u)27    OS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */28#endif29    INT32U           OSTCBDly;              /* Nbr ticks to delay task or, timeout waiting for event   */30#endif31    INT8U            OSTCBStat;             /* Task      status                                        */32    INT8U            OSTCBStatPend;         /* Task PEND status                                        */33    INT8U            OSTCBPrio;             /* Task priority (0 == highest)                            */3435#if OS_TASK_DEL_EN > 0u36    INT8U            OSTCBDelReq;           /* Indicates whether a task needs to delete itself         */37#endif3839#if OS_TASK_PROFILE_EN > 0u40    OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */41    INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */42#endif4344#if OS_TASK_NAME_EN > 0u45    INT8U           *OSTCBTaskName;46#endif4748#if OS_TASK_REG_TBL_SIZE > 0u49    INT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];50#endif51} OS_TCB;

3.6 全局变量

目前,本兼容层可以使用以下μCOS-II原版全局变量(位于ucos_ii.h)。这些全局变量的具体含义请参见[2.2节](#2.2 迁移步骤)中所列举出的参考资料。

 1#if OS_TASK_STAT_EN > 0u 2OS_EXT  INT8U             OSCPUUsage;               /* Percentage of CPU used                          */ 3OS_EXT  INT32U            OSIdleCtrMax;             /* Max. value that idle ctr can take in 1 sec.     */ 4OS_EXT  INT32U            OSIdleCtrRun;             /* Val. reached by idle ctr at run time in 1 sec.  */ 5OS_EXT  BOOLEAN           OSStatRdy;                /* Flag indicating that the statistic task is rdy  */ 6OS_EXT  OS_STK            OSTaskStatStk[OS_TASK_STAT_STK_SIZE];      /* Statistics task stack          */ 7#endif 8 9#define OSIntNesting      rt_interrupt_get_nest()   /* Interrupt nesting level                         */1011#define OSLockNesting     rt_critical_level()       /* Multitasking lock nesting level                 */1213#define OSPrioCur rt_thread_self()->current_priority       /* Priority of current task                 */1415OS_EXT  BOOLEAN           OSRunning;                       /* Flag indicating that kernel is running   */1617OS_EXT  INT8U             OSTaskCtr;                       /* Number of tasks created                  */1819#if OS_TASK_STAT_EN > 0u20OS_EXT  volatile  INT32U  OSIdleCtr;                       /* Idle counter                             */21#endif2223#ifdef OS_SAFETY_CRITICAL_IEC6150824OS_EXT  BOOLEAN           OSSafetyCriticalStartFlag;25#endif2627#define OSTCBCur         ((OS_TCB*)rt_thread_self())       /* Pointer to currently running TCB         */28OS_EXT  OS_TCB           *OSTCBFreeList;                   /* Pointer to list of free TCBs             */29OS_EXT  OS_TCB           *OSTCBList;                       /* Pointer to doubly linked list of TCBs    */30OS_EXT  OS_TCB           *OSTCBPrioTbl[OS_LOWEST_PRIO + 1u];    /* Table of pointers to created TCBs   */31OS_EXT  OS_TCB            OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS];   /* Table of TCBs                  */3233#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)34OS_EXT  OS_MEM           *OSMemFreeList;            /* Pointer to free list of memory partitions       */35OS_EXT  OS_MEM            OSMemTbl[OS_MAX_MEM_PART];/* Storage for memory partition manager            */36#endif3738#if OS_TASK_REG_TBL_SIZE > 0u39OS_EXT  INT8U             OSTaskRegNextAvailID;     /* Next available Task register ID                 */40#endif4142#if OS_TIME_GET_SET_EN > 0u43#define   OSTime          rt_tick_get()             /* Current value of system time (in ticks)         */44#endif4546#if OS_TMR_EN > 0u47#define   OSTmrTime       rt_tick_get()             /* Current timer time                              */48#endif

4 Env工具自动化配置到工程中

4.1 配置方法

uCOS-II兼容层在RT-Thread Nano版中需要手动添加到工程中,但如果使用RT-Thread完整版,则可以通过Env工具进行自动化添加到工程中。方法如下:

1RT-Thread online packages2    system packages --->3        [*] Micrium: Micrium software products porting for RT-Thread --->4            [*] uCOS-II Wrapper --->5                [*]   Enable uCOS-II wrapper automatically init6                [*]   Enable uCOS-II wrapper tiny mode7                Version (latest)  --->8

4.2 可选功能说明

4.2.1 Enable uCOS-II wrapper automatically init

uCOS-II兼容层支持按照uCOS-II原版的初始化步骤进行初始化,但是在有些情况,用户不想手动初始化uCOS-II兼容层,想要直接运行应用层任务或模块,则可以使用该宏定义。在rtconfig.h中定义本宏定义后,在RT-Thread初始化完成并进入到main线程之前会自动将uCOS-II兼容层初始化完毕,用户仅需要专注于uCOS-II的应用级任务即可。

若将该功能开启,则会在rtconfig.h文件中中定义PKG_USING_UCOSII_WRAPPER_AUTOINIT宏。在os_rtwrap.c文件中的以下函数将被使能并在RT-Thread初始化时自动执行

若没有使用完整版(即nano版)也想使用本功能,可以在rtconfig.h中手动添加定义宏定义PKG_USING_UCOSII_WRAPPER_AUTOINIT

 1/** 2 * 自动初始化 3 * uCOS-II兼容层支持按照uCOS-II原版的初始化步骤进行初始化,但是在有些情况, 4 * 用户不想手动初始化uCOS-II兼容层,想要直接运行应用层任务或模块,则可以使用该 5 * 宏定义。在rtconfig.h中定义本宏定义后,在RT-Thread初始化完成并进入到main线程之前 6 * 会自动将uCOS-II兼容层初始化完毕,用户仅需要专注于uCOS-II的应用级任务即可。 7 * The wrapper supports uCOS-II standard startup procedure. Alternatively, 8 * if you want to run uCOS-II apps directly and ignore the startup procedure,  9 * you can choose this option.10 */11#ifdef PKG_USING_UCOSII_WRAPPER_AUTOINIT12static int rt_ucosii_autoinit(void)13{14    OSInit();                                       /*uCOS-II操作系统初始化*/15    OSStart();                                      /*开始运行uCOS-II操作系统*/1617#if OS_TASK_STAT_EN > 0u18    OSStatInit();19#endif20    return 0;21}22INIT_PREV_EXPORT(rt_ucosii_autoinit);23#endif

4.2.2 Enable uCOS-II wrapper tiny mode

如果你在使用过程中不需要兼容任务/内核对象结构体的成员变量,可使能该选项。ENV将自动在rtconfig.h文件中定义PKG_USING_UCOSII_WRAPPER_TINY宏定义。该模式可满足所有API的基本兼容需求,建议勾选该选项

5 其他

5.1 联系方式

维护:Meco Man

联系方式:jiantingman@foxmail.com

5.1.2 个人GitHub主页

https://github.com/mysterywol... 

推荐阅读
【20210122期AI简报】保姆级深度学习环境配置指南、寒武纪首颗AI训练芯片亮相
ART-PI-重力感应无线智能小车第二弹-----OneNet+板载wifi控制电机转动
ART-PI重力感应无线智能小车第一弹——ART-PI扩展板设计

原文链接:https://mp.weixin.qq.com/s/w_0o9F3x60FWgjQUUOaJ3Q
转载已获授权,禁止二转
推荐阅读
关注数
8071
内容数
181
小而美的物联网操作系统,经过14年的累积发展,RT-Thread 已经拥有一个国内最大的嵌入式开源社区,同时被广泛应用于能源、车载、医疗、消费电子等多个行业,累积装机量超过4亿台,成为国人自主开发、国内最成熟稳定和装机量最大的开源 RTOS。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息