嵌入式实时操作系统uCOSII第8章 移植μCOS-Ⅱ.pdf
《嵌入式实时操作系统uCOSII第8章 移植μCOS-Ⅱ.pdf》由会员分享,可在线阅读,更多相关《嵌入式实时操作系统uCOSII第8章 移植μCOS-Ⅱ.pdf(20页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第八章第八章 移植移植C/OS-这一章介绍如何将C/OS-移植到不同的处理器上。所谓移植,就是使一个实时内核能在某个微处理器或微控制器上运行。为了方便移植,大部分的C/OS-代码是用 C 语言写的;但仍需要用 C 和汇编语言写一些与处理器相关的代码,这是因为C/OS-在读写处理器寄存器时只能通过汇编语言来实现。由于C/OS-在设计时就已经充分考虑了可移植性,所以C/OS-的移植相对来说是比较容易的。如果已经有人在您使用的处理器上成功地移植了C/OS-,您也得到了相关代码,就不必看本章了。当然,本章介绍的内容将有助于用户了解C/OS-中与处理器相关的代码。要使C/OS-正常运行,处理器必须满足以
2、下要求:1.处理器的 C 编译器能产生可重入代码。2.用 C 语言就可以打开和关闭中断。3.处理器支持中断,并且能产生定时中断(通常在 10 至 100Hz 之间)。4.处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。5.处理器有将堆栈指针和其它 CPU 寄存器读出和存储到堆栈或内存中的指令。像 Motorola 6805 系列的处理器不能满足上面的第 4 条和第 5 条要求,所以C/OS-不能在这类处理器上运行。图 8.1 说明了C/OS-的结构以及它与硬件的关系。由于C/OS-为自由软件,当用户用到C/OS-时,有责任公开应用软件和C/OS-的配置代码。这本书和磁盘包含了所有与处
3、理器无关的代码和 Intel 80 x86 实模式下的与处理器相关的代码(C 编译器大模式下编译)。如果用户打算在其它处理器上使用C/OS-,最好能找到一个现成的移植实例,如果没有只好自己编写了。用户可以在正式的C/OS-网站 www.COS-.com 中查找一些移植实例。图图 8.1 C/OS-II 硬件和软件体系结构硬件和软件体系结构 如果用户理解了处理器和 C 编译器的技术细节,移植C/OS-的工作实际上是非常简单的。前提是您的处理器和编译器满足了C/OS-的要求,并且已经有了必要工具。移植工作包括以下几个内容:?用#define 设置一个常量的值(OS_CPU.H)?声明 10 个数据
4、类型(OS_CPU.H)?用#define 声明三个宏(OS_CPU.H)?用 C 语言编写六个简单的函数(OS_CPU_C.C)?编写四个汇编语言函数(OS_CPU_A.ASM)根据处理器的不同,一个移植实例可能需要编写或改写 50 至 300 行的代码,需要的时间从几个小时到一星期不等。一旦代码移植结束,下一步工作就是测试。测试一个象C/OS-一样的多任务实时内核并不复杂。甚至可以在没有应用程序的情况下测试。换句话说,就是让内核自己测试自己。这样做有两个好处:第一,避免使本来就复杂的事情更加复杂;第二,如果出现问题,可以知道问题出在内核代码上而不是应用程序。刚开始的时候可以运行一些简单的任
5、务和时钟节拍中断服务例程。一旦多任务调度成功地运行了,再添加应用程序的任务就是非常简单的工作了。8.00 开发工具开发工具 如前所述,移植C/OS-需要一个 C 编译器,并且是针对用户用的 CPU 的。因为C/OS-是一个可剥夺型内核,用户只有通过 C 编译器来产生可重入代码;C 编译器还要支持汇编语言程序。绝大部分的 C 编译器都是为嵌入式系统设计的,它包括汇编器、连接器和定位器。连接器用来将不同的模块(编译过和汇编过的文件)连接成目标文件。定位器则允许用户将代码和数据放置在目标处理器的指定内存映射空间中。所用的 C 编译器还必须提供一个机制来从 C 中打开和关闭中断。一些编译器允许用户在
6、C 源代码中插入汇编语言。这就使得插入合适的处理器指令来允许和禁止中断变得非常容易了。还有一些编译器实际上包括了语言扩展功能,可以直接从 C 中允许和禁止中断。8.01 目录和文件目录和文件 本书所付的磁盘中提供了C/OS-的安装程序,可在硬盘上安装C/OS-和移植实例代码(Intel 80 x86 实模式,大模式编译)。我设计了一个连续的目录结构,使得用户更容易找到目标处理器的文件。如果想增加一个其它处理器的移植实例,您可以考虑采取同样的方法(包括目录的建立和文件的命名等等)。所有的移植实例都应放在用户硬盘的SOFTWARECOS-目录下。各个微处理器或微控制器的移植源代码必须在以下两个或三
7、个文件中找到:OS_CPU.H,OS_CPU_C.C,OS_CPU_A.ASM。汇编语言文件 OS_CPU_A.ASM 是可选择的,因为某些 C 编译器允许用户在 C语言中插入汇编语言,所以用户可以将所需的汇编语言代码直接放到 OS_CPU_C.C 中。放置移植实例的目录决定于用户所用的处理器,例如在下面的表中所示的放置不同移植实例的目录结构。注意,各个目录虽然针对完全不同的目标处理器,但都包括了相同的文件名。Intel/AMD 80186 SOFTWAREuCOS-IIIx86S OS_CPU.H OS_CPU_A.ASM OS_CPU_C.C SOFTWAREuCOS-IIIx86L OS
8、_CPU.H OS_CPU_A.ASM OS_CPU_C.CMotorola 68HC11 SOFTWAREuCOS-II68HC11 OS_CPU.H OS_CPU_A.ASM OS_CPU_C.C 8.02 INCLUDES.H 在第一章中曾提到过,INCLUDES.H 是一个头文件,它在所有.C 文件的第一行被包含。#include includes.h INCLUDES.H 使得用户项目中的每个.C 文件不用分别去考虑它实际上需要哪些头文件。使用 INCLUDES.H 的唯一缺点是它可能会包含一些实际不相关的头文件。这意味着每个文件的编译时间可能会增加。但由于它增强了代码的可移植性,所
9、以我们还是决定使用这一方法。用户可以通过编辑 INCLUDES.H 来增加自己的头文件,但是用户的头文件必须添加在头文件列表的最后。8.03 OS_CPU.H OS_CPU.H 包括了用#defines 定义的与处理器相关的常量,宏和类型定义。OS_CPU.H 的大体结构如程序清单 L8.1 所示。程序清单程序清单 L 8.1 OS_CPU.H.#ifdef OS_CPU_GLOBALS#define OS_CPU_EXT#else#define OS_CPU_EXT extern#endif /*数据类型*(与编译器相关)*/typedef unsigned char BOOLEAN;typ
10、edef unsigned char INT8U;/*无符号8位整数 */(1)typedef signed char INT8S;/*有符号8位整数 */typedef unsigned int INT16U;/*无符号16位整数 */typedef signed int INT16S;/*有符号16位整数 */typedef unsigned long INT32U;/*无符号32位整数 */typedef signed long INT32S;/*有符号32位整数 */typedef float FP32;/*单精度浮点数 */(2)typedef double FP64;/*双精度浮点
11、数 */typedef unsigned int OS_STK;/*堆栈入口宽度为16位*/*与处理器相关的代码*/#define OS_ENTER_CRITICAL()?/*禁止中断 */(3)#define OS_EXIT_CRITICAL()?/*允许中断 */#define OS_STK_GROWTH 1 /*定义堆栈的增长方向:1=向下,0=向上*/(4)#define OS_TASK_SW()?(5)8.03.01 与编译器相关的数据类型 8.03.01 与编译器相关的数据类型 因为不同的微处理器有不同的字长,所以C/OS-的移植包括了一系列的类型定义以确保其可移植性。尤其是,C/
12、OS-代码从不使用 C 的 short,int 和 long 等数据类型,因为它们是与编译器相关的,不可移植。相反的,我定义的整型数据结构既是可移植的又是直观的L8.1(2)。为了方便,虽然C/OS-不使用浮点数据,但我还是定义了浮点数据类型L8.1(2)。例如,INT16U 数据类型总是代表 16 位的无符号整数。现在,C/OS-和用户的应用程序就可以估计出声明为该数据类型的变量的数值范围是 065535。将C/OS-移植到 32 位的处理器上也就意味着 INT16U 实际被声明为无符号短整型数据结构而不是无符号整型数据结构。但是,C/OS-所处理的仍然是 INT16U。用户必须将任务堆栈的
13、数据类型告诉给C/OS-。这个过程是通过为 OS_STK 声明正确的 C 数据类型来完成的。如果用户的处理器上的堆栈成员是 32 位的,并且用户的编译文件指定整型为 32 位数,那么就应该将 OS_STK 声明位无符号整型数据类型。所有的任务堆栈都必须用 OS_STK 来声明数据类型。用户所必须要做的就是查看编译器手册,并找到对应于C/OS-的标准 C 数据类型。8.03.02 OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()8.03.02 OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()与所有的实时内核一样,C/OS-需要先禁止中断
14、再访问代码的临界段,并且在访问完毕后重新允许中断。这就使得C/OS-能够保护临界段代码免受多任务或中断服务例程(ISRs)的破坏。中断禁止时间是商业实时内核公司提供的重要指标之一,因为它将影响到用户的系统对实时事件的响应能力。虽然C/OS-尽量使中断禁止时间达到最短,但是C/OS-的中断禁止时间还主要依赖于处理器结构和编译器产生的代码的质量。通常每个处理器都会提供一定的指令来禁止/允许中断,因此用户的 C 编译器必须要有一定的机制来直接从 C中执行这些操作。有些编译器能够允许用户在 C 源代码中插入汇编语言声明。这样就使得插入处理器指令来允许和禁止中断变得很容易了。其它一些编译器实际上包括了语
15、言扩展功能,可以直接从 C 中允许和禁止中断。为了隐藏编译器厂商提供的具体实现方法,C/OS-定 义 了 两 个 宏 来 禁 止 和 允 许 中 断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()L8.1(3)。OS_ENTER_CRITICAL();/*C/OS-II 临界代码段*/OS_EXIT_CRITICAL();方法 1 方法 1 执行这两个宏的第一个也是最简单的方法是在 OS_ENTER_CRITICAL()中调用处理器指令来禁止中断,以及在 OS_EXIT_CRITICAL()中调用允许中断指令。但是,在这个过程中还存在着小小的问题。如果用户在禁止中
16、断的情况下调用C/OS-函数,在从C/OS-返回的时候,中断可能会变成是允许的了!如果用户禁止中断就表明用户想在从C/OS-函数返回的时候中断还是禁止的。在这种情况下,光靠这种执行方法可能是不够的。方法 2 方法 2 执行 OS_ENTER_CRITICAL()的第二个方法是先将中断禁止状态保存到堆栈中,然后禁止中断。而执行 OS_EXIT_CRITICAL()的时候只是从堆栈中恢复中断状态。如果用这个方法的话,不管用户是在中断禁止还是允许的情况下调用C/OS-服务,在整个调用过程中都不会改变中断状态。如果用户在中断禁止的时候调用C/OS-服务,其实用户是在延长应用程 序 的 中 断 响 应
17、时 间。用 户 的 应 用 程 序 还 可 以 用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来保护代码的临界段。但是,用户在使用这种方法的时候还得十分小心,因为如果用户在调用象 OSTimeDly()之类的服务之前就禁止中断,很有可能用户的应用程序会崩溃。发生这种情况的原因是任务被挂起直到时间期满,而中断是禁止的,因而用户不可能获得节拍中断!很明显,所有的 PEND 调用都会涉及到这个问题,用户得十分小心。一个通用的办法是用户应该在中断允许的情况下调用C/OS-的系统服务!问题是:哪种方法更好一点?这就得看用户想牺牲些什么。如果用户并不关心在调用C/OS-服务
18、后用户的应用程序中中断是否是允许的,那么用户应该选择第一种方法执行。如果用户想在调用C/OS-服务过程中保持中断禁止状态,那么很明显用户应该选择第二种方法。给用户举个例子吧,通过执行 STI 命令在 Intel 80186 上禁止中断,并用 CLI 命令来允许中断。用户可以用下面的方法来执行这两个宏:#define OS_ENTER_CRITICAL()asm CLI#define OS_EXIT_CRITICAL()asm STI CLI 和 SCI 指令都会在两个时钟周期内被马上执行(总共为四个周期)。为了保持中断状态,用户需要用下面的方法来执行宏:#define OS_ENTER_CRI
19、TICAL()asm PUSHF;CLI#define OS_EXIT_CRITICAL()asm POPF 在这种情况下,OS_ENTER_CRITICAL()需要 12 个时钟周期,而 OS_EXIT_CRITICAL()需要另外的 8 个时钟周期(总共有 20 个周期)。这样,保持中断禁止状态要比简单的禁止/允许中断多花 16 个时钟周期的时间(至少在 80186 上是这样的)。当然,如果用户有一个速度比较快的处理器(如 Intel Pentium),那么这两种方法的时间差别会很小。8.03.03 OS_STK_GROWTH 8.03.03 OS_STK_GROWTH 绝大多数的微处理器
20、和微控制器的堆栈是从上往下长的。但是某些处理器是用另外一种方式工作的。C/OS-被设计成两种情况都可以处理,只要在结构常量 OS_STK_GROWTH L8.1(4)中指定堆栈的生长方式(如下所示)就可以了。置 OS_STK_GROWTH 为 0 表示堆栈从下往上长。置 OS_STK_GROWTH 为 1 表示堆栈从上往下长。8.03.04 OS_TASK_SW()8.03.04 OS_TASK_SW()OS_TASK_SW()L8.1(5)是一个宏,它是在C/OS-从低优先级任务切换到最高优先级任务时被调用的。OS_TASK_SW()总是在任务级代码中被调用的。另一个函数 OSIntExit
21、()被用来在 ISR 使得更高优先级任务处于就绪状态时,执行任务切换功能。任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。在C/OS-中,处于就绪状态的任务的堆栈结构看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。换句话说,C/OS-要运行处于就绪状态的任务必须要做的事就是将所有处理器寄存器从任务堆栈中恢复出来,并且执行中断的返回。为了切换任务可以通过执行 OS_TASK_SW()来产生中断。大部分的处理器会提供软中断或是陷阱(TRAP)指令来完成这个功能。ISR 或是陷阱处理函数(也叫做异常处理函数)的向量地址必须指向汇编语
22、言函数 OSCtxSw()(参看 8.04.02)。例如,在 Intel 或者 AMD 80 x86 处理器上可以使用 INT 指令。但是中断处理向量需要指向 OSCtxSw()。Motorola 68HC11 处理器使用的是 SWI 指令,同样,SWI 的向量地址仍是OSCtxSw()。还有,Motorola 680 x0/CPU32 可能会使用 16 个陷阱指令中的一个。当然,选中的陷阱向量地址还是 OSCtxSw()。一些处理器如 Zilog Z80 并不提供软中断机制。在这种情况下,用户需要尽自己的所能将堆栈结构设置成与中断堆栈结构一样。OS_TASK_SW()只会简单的调用 OSCt
23、xSw()而不是将某个向量指向 OSCtxSw()。C/OS 已经被移植到了 Z80 处理器上,C/OS-也同样可以。8.04 OS_CPU_A.ASM C/OS-的移植实例要求用户编写四个简单的汇编语言函数:OSStartHighRdy()OSCtxSw()OSIntCtxSw()OSTickISR()如果用户的编译器支持插入汇编语言代码的话,用户就可以将所有与处理器相关的代码放到 OS_CPU_C.C 文件中,而不必再拥有一些分散的汇编语言文件。8.04.01 OSStartHighRdy()8.04.01 OSStartHighRdy()使就绪状态的任务开始运行的函数叫做 OSStart
24、(),如下所示。在用户调用 OSStart()之 前,用 户 必 须 至 少 已 经 建 立 了 自 己 的 一 个 任 务(参 看 OSTaskCreate()和OSTaskCteateExt()。OSStartHighRdy()假设 OSTCBHighRdy 指向的是优先级最高的任务的任务控制块。前面曾提到过,在C/OS-中处于就绪状态的任务的堆栈结构看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。要想运行最高优先级任务,用户所要做的是将所有处理器寄存器按顺序从任务堆栈中恢复出来,并且执行中断的返回。为了简单一点,堆栈指针总是储存在任务控制块(即它的 OS_TCB)的开头。换
25、句话说,也就是要想恢复的任务堆栈指针总是储存在 OS_TCB 的 0 偏址内存单元中。void OSStartHighRdy(void)Call user definable OSTaskSwHook();Get the stack pointer of the task to resume:Stack pointer=OSTCBHighRdy-OSTCBStkPtr;OSRunning=TRUE;Restore all processor registers from the new tasks stack;Execute a return from interrupt instructio
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 嵌入式实时操作系统uCOSII第8章 移植COS- 嵌入式 实时 操作系统 uCOSII 移植 COS
限制150内