0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于GP32开发板上实现μC/OS-II内核的移植

牵手一起梦 来源:单片机与嵌入式系统应用 作者:邵贝贝 , 许庆丰 2020-10-04 17:15 次阅读

作者:邵贝贝 , 许庆丰 , 王云飞

80X86CPU上的移植,相信读者通信学习,已经对移植的过程和步骤有了一定的了解。ΜC/OS-II最初是为摩托罗拉68HC11系列单片机设计的。68HC11系列单片机有外部总线,可以外接RAMROM;而没有外部总线8位MCU。由于RAM容量的限制,移植就存在一定的困难;但对于有些8位的MCU,将μC/OS-II移植到MOTOROLA

MC68H908GP32(以下简称GP32)上。

一、在GP32上移植μC/OS-11的主要问题

在第(4)讲中,介绍过要移植μC/OS-11,目标处理必须满足以下要求:

(1)处理器的C编译器能产生可重入代码;

(2)用C语言就可以打开和关闭中断;

(3)处理器支持中断,并且能产生定时中断(通常在10~100Hz之间);

(4)处理器支持足够的RAM,保存全局变量和作为多任务环境下的任务堆栈。

(5)处理器有将堆栈指针和其他CPU寄存器读出和存储到堆栈或内存中的指令。

编译后的μC/OS-II的内核大约有6~10KB;如果只保留最核心的代码,则最小可压缩到2KB。RAM的占用与系统中的任务数有关,任务堆栈要占用大量的RAM空间,堆栈的大小取决于任务的局部变量、缓冲区大小及可能的中断嵌套的层数。所以,所要移植的系统中必须有足够的RAM资源。而像MOTOROLA6805系列的8位MCU,由于RAM资源太小且堆栈指针是固定的,不能满足上面的第(4)条和第(5)条要求,所以μC/OS-II不能在这类处理器上运行。

GP32是68HC08家庭的成员,具有512字节的片内RAM,32K字节的片内Flash,8MHz总线时钟。内部寄存器包括1个8位累加器A,1个16位索引寄存器X,1个16位堆栈指针寄存器SP,1个16位程序指针寄存器PC及1个8位标志寄存器CCR。与6805系列MCU相比,68HC08系列MCU的堆栈指针为16位,可以自由寻址。这就满足了移植条件(5),且可以使用C编译器生成代码。GP32的用户手册可以在摩托罗拉公司的主页上下载,http://www.motorola.com。

在GP32上移植μC/OS-II的主要困难还是RAM资源太少。为了移植成功,必须采取措施减少RAM的用量,包括限制系统中的任务数量、仔细修改μC/OS-II内核、去掉不使用的部分、限制任务的断嵌套层数、在任务中尽量减少使用局部变量等等。在采取了上述措施后,可将RAM用量减少到最低。但需要提醒的是,为了节省堆栈空间而采用的限制中断嵌套层数的方法将影响系统的实时性能。所以,在GP32上移植μC/OS-II更多的是一种演示,能更好地说明μC/OS-II内核的可裁剪性和灵活性。

在本讲中将介绍一个移植实例。为了减少RAM用量,在本例中只运行了2个用户任务。尽量减池一内核中不必要的模块(包括由箱、消息队列、内存管理等),去掉了任务挂起、唤醒和删除等扩展功能,但仍然支持任务的创建和管理,也保留了信号量模块来用来任务间的通讯。

二、工具和运行环境

要实现μC/OS-II向GP32的移植,需要一个面向MC68HC08的C编译器。使用的是HIWARE公司的C编译器。移植过程同样适用于MC68HC08家庭的其他成员。

三、移植中所需修改的文件

首先是编写整个项目的公共头文件include.h,这个文件定义使用内核中的哪些模块。Include.h会被所有的C源程序引用。还要修改和CPU相关的三个文件,分别是头文件OS_CPU08.H、汇编代码文件OS_CPU08.ASM和C代码文件OS_CPU08.C。

1.include.h文件

include.h是主头文件,在所有后缀名为.C的文件的开始都包含include.h文件。文件中可以内核进行裁剪。在本例中定义如下:

基于GP32开发板上实现μC/OS-II内核的移植

对于不同类型的处理器,还需要改写include.h文件,增加自己的头文件,但必须加在文件末尾。在安装μC/OS-II的时候,附带了几个移植实例,例如,针对Intel

80X86的代码安装到IIL目录下。我们为GP32编写的移植实例可放在IIHC08下,这样,为GP32改写的include.h文件中应该加入下列语句:

#include “iiHC08_CPU08.ASM”

#include “iiHC08_CPU08.C”

#include “iiHC08_CPU08.H”

2.OS_CPU08.H文件

OS_CPU08.H文件中定义了硬件相关的基本信息

/*数据类型*/

typedef unsigned char BOOLEAN;

typedef unsigned char INT8U;

typedef signed char INT8S;

typedef unsigned short INT16U;

typedef signed short INT16S;

typedef unsigned long INT32U;

typedef signed long INT32S;

/*定义堆栈增长方向*/

#define OS_STK_GROWTH1 /*堆栈由高地址向低地址增长*/

/*定义堆栈单位*/

#define OS_STK INT8U

/*定义进入临界代码区开关中断宏*/

#define OS_ENTER_CRITICAL() asm sei

#define OS_EXIT_CRITICAL() asm cli

#define OS_TASK_SW() asm swi

(1)数据类型

由于不同的处理器有不同的字长,μC/OS-II的移植需要重新定义一系列的数据结构。具体字长还和使用的C编译器有关。在GP32中堆栈是按字节操作的,堆栈数据类型OS_STK声明为8位。μC/OS-II中所有任务的堆栈都必须用OS_STK声明。

(2)代码临界区

μC/OS-II在进入系统临界代码区之前要关闭中断,等到退出临界区后再打开,从而保护核心数据不被多任务环境下的其他任务或中断破坏。在GP32中,开关中断可以通过汇编指令CLI和SEI来实现。所以μC/OS-II中的宏OS_ENTER_CRITICAL()定义为指令SEI,OS_EXIT_CRITICAL()定义为指令CLI。

(3)堆栈增长方向

GP32的堆栈是由高地址向低地址方向增长的,所以常量OS_STK_GPOWTH必须设置为1。

(4)OS_TASK_SW()函数的定义

在μC/OS-II中,OS_TASK_SW()用来实现任务切换。就绪任务的堆栈初始化应该模拟一次中断发生后的样子,堆栈中应该按进栈次序设置好各个寄存器的内容。OS_TASK_SW()函数模拟一次中断过程,在中断返回的时候进行任务切换。GP32中可采用软中断指令SWI实现任务切换。中断服务程序的入口点必须指向汇编函数OSCtxSw()。

OS_TASK_SW()的定义:

#define OS_TASK_SW() asm swi

3.OS_CPU08.ASM文件

μC/OS-II的移植需要改写OS_CPU08.ASM中的4个函数:OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()和OSTickISR()。

(1)OSStartHighRdy()函数

该函数由SStart()函数调用,功能是运行优先级最高的就绪任务。在调用OSStart()之前,必须先调用OSInit(),并且已经至少创建了一个任务。为了启动任务,OSStartHighRdy()首先找到当前就绪的优先级最高的任务(OSTCBHighRdy中保存有优先级最高任务的任务控制块-TCB的地址),并从任务的任务控制块(OS_TCB)中找到指向堆栈的指针,然后从堆栈中弹出全部寄存器的内容,运行RTE中断返回。由于任务创建时堆栈的结构就是按中断后的堆栈结构初始化的,执行RET指令后就切换到新任务(有关μC/OS-II的任务切换机制,请参考系列讲座的第2讲)。对于OSStartHighRdy的代码,我们采用在C中嵌入汇编的方法编写。需要说明的是,由于GP32中有512字节RAM,所以地址指针必须是16位的;而GP32中累加寄存器A为8位,所以用累加器A传递地址必须进行两次读入、输出操作。

Void OSStartHighRdy(void)

{asm

{

jsr OSTaskSwHook //调用用户定义接口函数

lda OSRunning //设置OSRunning变量,标志进入多任务模式

inca

sta OSRunning

ldx OSTCBHighRdy //取得最高优先级就绪任务TCB地址

stx OSTCBCur //保存到OSTCBCur中

pshx

ldx OSTCBHighRdy:1//保存地址的第二个字节

stx OSTCBCur:1

pulh

lda 0,X //载放就绪任务堆栈指针

psha

ldx 1,X //载入就绪任务堆栈指针第二个字节

pulh

txs

pulh //恢复索引寄存器内容

rti //中断返回,运行新任务

}}

(2)OSCtxSw()函数

OSCtxSw()是一个任务级的任务切换函数(在任务中调用,区别于在中断程序中调用的OSIntCtxSw())。在GP32上实现,可通过执行一条软中断指令SWI来实现任务切换。软中断向量指向OSCtxSw()。在μC/OS-II中,如果任务调用了某个函数,而该函数的执行结果可能造成系统任务新调度(例如试图唤醒一个优先级更高的任务),则在函数的末尾会调用OSSched();如果OSSched()将查找当前就绪的优先级最高的任务,若不是当前任务,则判断是否需要进行任务调度,并找到该任务控制块OS_TCB的地址,将该地址拷贝到变量OSTCBHighRdy中,然后通过宏OS_TASK_SW()执行软中断进行任务切换。在此过程中,变量OSTCBCur始终包含一个指向当前运行任务OS_TCB的指针。OSCtxSw()的汇编代码如下:

Void OSCtxSw(void)

{asm

{pshh //保存X寄存器

tsx

pshx

pshh

dx OSTCBCur //载入当前任务的TCB指针

pshx

ldx OSTCBCur:1 //载入TCB的第二个字节

pulh

pula

sta 0,x //保存当前堆栈指针

pula

sta 1,x

jsr OSTaskSwHook //调用用户定义的接口函数

lda OSPrioHighRdy //设置OSPrioCur=OSPrioHighRdy

sta OSPrioCur

pshx

ldx OSTCBHighRdy:1

stx OSTCBCur:1

pulh

lda 0,x //载入堆栈指针

psha

ldx,1,x

pulh

txs

pulh //恢复索引寄存器内容

rti //中断返回,切换任务

}}

(4)OSTickISR()函数

在μC/OS-II中,当调用OSStart()启动多任务环境后,时钟中断的使用是非常重要的。在时钟中断程序中负责处理所有与定时相关的工作,如任务的延时、等待操作等等。在时钟中断中将查询处于等待状态的任务,判断是否延时结束,否则将重新进行任务调度。

为GP32编写的函数OSTickISR()的代码如下:

void OSTickISR()void{

asm{

pshh

LDA T1SC

BCLR 7,T1SC //允许中断嵌套

}

OsintEnter(); /*标志进入中断*/

OSTimeTick(); /*调用时钟节拍函数*/

OSlntExit(); /*标志退出中断*/

Asm{

Pulh

Rti

}}

和μC/OS-II中的其他中断服务程序一样,OSTickISR()首先在被中断任务堆栈中保存CPU寄存器的值,然后调用OSIntEnter()。μC/OS-II要求在中断服务程序开头调用OSIntEnter(),其作用是将记录中断嵌套层数的全局变量OSIntNesting加1。如果不调用OSIntEnter(),直接将OSIntNesting加1也是允许的。随后,OSTickISR()调用OSTimeTick(),检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。在OSTickISR()的最后调用OSIntExit(),如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层,OSIntExit()将进行任务调度。注意:如果进行了任务调度,OSIntExit()将不同志返回调用者,而是用新任务的堆栈中的寄存器数值恢复CPU现场,然后用IRET实现任务切换。如果当有中断不是中断嵌套的最后一层,或中断中没有改变任务的就绪状态,OSIntExit()将返回调用者OSTickISR(),最后OSTickISR()返回被中断的任务。

4.OS_CPU08.C文件

μC/OS-II的移植需要用户在OS_CPU08.C中定义6个函数:

OSTaskStkInit()

OSTaskCreateHook()

OSTaskDelHook()

OSTaskSwHook()

OSTaskStatHook()

OSTimeTickHook()

实际需要定义的只有OSTaskStkInit()函数,其他5个函数需要声明,但不一定有实际内容。这5个函数都是用户定义,所以OS_CPU08.C中只有定义,没有给出代码。如果用户需要使用这些函数,请将文件OS_CFG.H中的#define

constant OS_CPU_HOOKS_EN设为1,设为0表示不使用这些函数。

OSTaskStkInit()函数由任务创建函数OSTaskCreate()或OSTaskCreateExt()调用,用来初始化任务的堆栈。初始状态的堆栈模拟发生一次中断后的堆栈结构,按照中断后的进栈次序预留各个寄存器存储空间;而中断返回地址指向任务代码的起始地址。当调用OSTaskCreate()或OSTaskCreateExt()创建一个新任务时,需要传递的参数是:任务代码的起始地址、参数指针(pdata)、任务堆栈顶端的地址、任务的优先级。OSTaskCreateExt()还需要一些其他参数,但与OSTaskStkinit()没有关系。OSTaskStkInit()只需要以上提到的3个参数(task、pdata和ptos)。堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈栈顶指针,OSTaskCreate()OSTaskCreateExt()将指针保存在任务的OS_TCB中。

Void*OSTaskStklint(void(*task)(void*pd),void*pdata,void*ptos,INT16U

opt)

{

INT16U *stk;

stk=(INT16U*)ptos; /*保存堆栈指针*/

*--stk=(INT16U)(task); /保存程序计数器内容*/

*--stk=(INT16U)(0x00); /初始化X和A寄存器内容*/

--stk=(INT16U)(0x00); /*初始化CCR和H寄存器*/

return((void*)stk);

}

其余的几个函数:OSTaskCreateHook()、OSTaskDelHook()、OSTaskSwHook()、OSTaskStatHook和OSTimeTickHook()均由用户自定义。

四、制作用户自己的项目

在为内核编写了上述与硬件相关的代码以后,用户就可以为自己的项目编写实际的代码了。在本例中,用户任务共有两个。任务1在初始化时钟中断以后,就进入了一人死循环。在这个循环里,任务1一方面以1s(秒)为周期改变并行I/O口PORTA第0个引脚的输出电压,另一方面每隔4s便向任务2发送1个信号。而任务2则始终等待任务1发来的信号,一旦收到信号,便改变并行I/O口PORTA第1个引脚的输出电压。具体的代码如下:

/*****************************************

* EXE2.C

*******************************************/

#include<hidef.h>

#include “includes.h”

Byte PORTA @0x0000; /*并口A地址$0000*/

Byte DDRA @0x0004; /*并口A方向寄存器地址$0004*/

Byte T1SC @0x0020; /*定时器控制寄存器地址$0020*/

Byte T1MODH@0x0023; /*定时器模式寄存器地址$0023*/

OS_EVENT *Semaphore;

#define TASK_STK_SIZE 64 /*任务堆栈大小64字节*/

INT8U Task1Stk[TASK_STK_SIZE]; /*定义任务1堆栈*/

INT8U Task2Stk[TASK_STK_SIZE]; /*定义任务2堆栈*/

Void Hardwareinit(void);

Void Task1(void*pdata)

{int count=0;

/*int count=0;

/*初始化定时器*/

asm{

LDA #0x50

STA T1SC

LDHX #0x0333 //设定定时器间隔100ms

STHX T1MODH

CLI

}

for(;;){

PORTA&=0xFE;

OSTimeDly(5); /*延时0.5s*/

PORTA|=0x01;

/*延时0.5s*/

DSTimeDly(5);

Count++;

If(count= =4){

OSSemPost(Semaphore);

Count=0;

}

}}

void Task2(void *pdata)

{

Byte err;

For(;;){

OSSemPend(Semaphore,0,&err);

PORTA&=0xFD:

OSSemPend(Semaphore,0,&err);

PORTA|=0x02;

}

}

void main(void){

Hardwarelnit(); /*完成硬件的初始化工作*/

Oslint(); /*初始化多任务环境*/

Semaphore=OSSemCreate(0);

OSTaskCreate(Task1,(void*)0,(void*)&Task1Stk

[TASK_STK_SIZE],10);

OSTaskCreate(Task2,void*)0,(void*)&Task2Stk

[TASK_STK_SIZE],9);

OSStart();

}

在主程序main()中,用户必须先调用OSInit(),然后创建各个任务和信号量等,最后调用OSStart(),以启动内核运行,开始正常的任务调度。

本例中尽量减小了对RAM的需求:假如中断嵌套层数不超过三层,所需事件只有一个,即只需要一个事件控制块;应用中对μC/OS-II提供的功能进行最大限度的裁剪,能不用的尽量不用。采用了上述措施后,μC/OS-II的RAM使用情况大致如下:μC/OC-II所使用的全局变量占用22字节,事件控制块占用12字节。此外,当系统初始化时,还需要最小30字节的系统堆栈用于初始化TCB,并传递参数。以上为μC/OS-II中系统所必需的RAM,计64字节。

假设给每个任务分配64个字节的堆栈空间,其中用于任务控制块17字节,允许三层中断嵌套要用18字节,任务切换时的栈结构要用8字节。由于程序是用C语言编写的,确切的子函数嵌套调用层数是不知道的。如果不使用本身需要缓冲区的C语言函数,如printf()等,任务中程序调用所产生的嵌套层数不超过10层,则64字节中还能分析给任务局部变量的空间只剩下1字节了。这是能分配任务的最小RAM空间了。

综上所述,GP32的512字节RAM可分为8个64字节的RAM块。如果运行4个任务,能留给应用程序的RAM也只剩下128字节了。如果在GP32上运行μC/OS-II,且不多于8个任务,则任务调度表可以再简化,不需要调度64个任务,只调度8个任务就可以了。

责任编辑:gt

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 处理器
    +关注

    关注

    68

    文章

    18250

    浏览量

    222049
  • 寄存器
    +关注

    关注

    30

    文章

    5025

    浏览量

    117706
  • 存储
    +关注

    关注

    12

    文章

    3854

    浏览量

    84651
收藏 人收藏

    评论

    相关推荐

    μCOS-II操作系统在各种处理器移植

    仍需使用汇编语言来完成一些和处理器相关的操作,例如读写处理器、寄存器时只能使用汇编语言来实现。因此,将μC/OS-II操作系统移植到目标处理器
    发表于 08-14 09:46

    uC/OS-II实时操作系统移植技巧

    和可靠性,除此以外,μC/OS-II的鲜明特点就是源码公开,便于移植和维护。 在μC/OS-II官方的主页
    发表于 07-17 07:07

    μC/OS-II移植方法

    概述嵌入式操作系统μC/OS-II是一个公开源代码的占先式多任务的微内核RTOS,其特点可以概括为以下几个方面:公开源代码,代码结构清晰、明了,注释详尽,组织有条理,可移植性好,可裁剪
    发表于 07-25 08:14

    μC/OS-II的中断按键怎么处理?

    μC/OS-II是一个完整的、可移植、可裁减的占先式实时多任务内核。本文主要讨论了μC/OS-II
    发表于 03-23 08:13

    μC/OS-II在P89V51RD2是怎样实现移植的?

    μC/OS-II是什么?μC/OS-II内核结构是由哪些组成的?什么是P89V51RD2微处理器?μC
    发表于 04-27 06:18

    如何实现μC/OS-II在AT89C51移植

    μC/OS-II在AT89C51移植实现过程是怎样进行的?μ
    发表于 04-27 07:08

    如何实现μC/OS-II系统的移植

    如何实现μC/OS-II系统的移植
    发表于 04-28 06:01

    怎么将uC/OS-II移植到STM32F103ZET6开发板

    怎么将uC/OS-II移植到STM32F103ZET6开发板?其移植步骤有哪些呢?
    发表于 11-02 06:14

    在GD32F310K-START开发板实现ucois-II移植的设计

    1、ucos-II在GD32F310开发板移植介绍μC/OS-II由Micrium公司提供,
    发表于 08-22 16:26

    μC/OS-II 在Nios上的移植

    首先介绍嵌入式实时操作系统μC/OS-II 和Nios 嵌入式处理器, 分析μC/OS-II 移植对目标处理器的要求, 重点介绍μC/OS-II 在Nios 处理器上
    发表于 03-08 09:31 90次下载

    μC OS-II 在S3C44B0X 处理器上的移植

    介绍实时操作系统μC/OS-II 的特点和内核结构,给出μC/OS-II 在Samsung 嵌入式S3C44B0X ARM7 微处理器上的移植的步骤及详细相关代码,同时阐述μC/
    发表于 05-15 13:34 18次下载

    μC OS-II 在Nios 上的移植1

    首先介绍嵌入式实时操作系统μC/OS-II 和Nios 嵌入式处理器, 分析μC/OS-II 移植对目标处理器的要求, 重点介绍μC/OS-II 在Nios 处理器上的
    发表于 05-16 14:32 28次下载

    μC/OS-II在PC机上移植的设计与实现

    本文介绍了μC/OS-II移植到PC机上的过程,使μC/OS-II应用系统程序在PC机上脱离DOS或Windows环境运行。着重介绍了Bootloader引导程序和EXE程序启动代码的设计与
    发表于 09-02 08:51 14次下载

    μC/OS-II在S3C44BOX处理器上的移植

     摘要:介绍实时操作系统μC/OS-II的特点和内核结构,给出μC/OS-II在Samsung嵌入式S3C44BOX ARM7微处理器上的移植的步骤及详细相关代码,同时阐
    发表于 03-11 12:39 1218次阅读
    μC/<b class='flag-5'>OS-II</b>在S3C44BOX处理器上的<b class='flag-5'>移植</b>

    μC/OS-II在EP7312上的移植

    μC/OS-II在EP7312上的移植首先介绍μC/OS-II操作系统的特点,重点分析μC/OS-II在EP7312上的移植方法,介绍μC/
    发表于 06-16 11:24 1036次阅读
    μC/<b class='flag-5'>OS-II</b>在EP7312上的<b class='flag-5'>移植</b>