Presentation is loading. Please wait.

Presentation is loading. Please wait.

移植μC/OS-Ⅱ.

Similar presentations


Presentation on theme: "移植μC/OS-Ⅱ."— Presentation transcript:

1 移植μC/OS-Ⅱ

2 μC/OS-Ⅱ的移植 移植就是使一个实时内核能在某个微处理器或微控制器上运行。
为了方便移植,大部分的μC/OS-Ⅱ代码是用C语言写的;但仍需要用C和汇编语言写一些与处理器相关的代码,这是因为μC/OS-Ⅱ在读写处理器寄存器时只能通过汇编语言来实现。 由于μC/OS-Ⅱ在设计时就已经充分考虑了可移植性,所以μC/OS-Ⅱ的移植相对来说是比较容易的。

3 μC/OS-Ⅱ的移植 要使μC/OS-Ⅱ正常运行,处理器必须满足以下要求: 1. 处理器的C编译器能产生可重入代码。
3. 处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)。 4. 处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。 5. 处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。 像Motorola 6805系列的处理器不能满足上面的第4条和第5条要求,所以μC/OS-Ⅱ不能在这类处理器上运行。

4 μC/OS-Ⅱ的结构以及与硬件的关系

5 μC/OS-Ⅱ的移植 移植工作包括以下几个内容: 用#define设置一个常量的值(OS_CPU.H)
用C语言编写六个简单的函数(OS_CPU_C.C) 编写四个汇编语言函数(OS_CPU_A.ASM) 根据处理器的不同,一个移植实例可能需要编写或改写50至300行的代码。

6 μC/OS-Ⅱ的移植 一旦代码移植结束,下一步工作就是测试。
这样做有两个好处:第一,避免使本来就复杂的事情更加复杂;第二,如果出现问题,可以知道问题出在内核代码上而不是应用程序。 开始测试的时候可以运行一些简单的任务和时钟节拍中断服务例程。一旦多任务调度成功地运行了,再添加应用程序的任务就是非常简单的工作了。

7 移植开发工具 移植μC/OS-Ⅱ需要一个C编译器,并且是针对用户用的CPU的。

8 移植目录结构和文件 Intel/AMD 80186 \SOFTWARE\uCOS-II\Ix86S \OS_CPU.H \OS_CPU_A.ASM \OS_CPU_C.C \SOFTWARE\uCOS-II\Ix86L Motorola 68HC11 \SOFTWARE\uCOS-II\68HC11 各个微处理器的移植源代码必须在以下两个或三个文件中找到:OS_CPU.H,OS_CPU_C.C,OS_CPU_A.ASM。 汇编语言文件OS_CPU_A.ASM是可选择的,因为某些C编译器允许用户在C语言中插入汇编语言,所以用户可以将所需的汇编语言代码直接放到OS_CPU_C.C中。 一个放置移植实例的目录结构如表所示。这样的目录结构,使用户更容易找到目标处理器的文件。

9 INCLUDES.H文件 INCLUDES.H是一个头文件,它在所有.C文件的第一行被包含。 #include "includes.h"

10 OS_CPU.H文件 用户所必须要做的就是查看编译器手册,并找到对应于μC/OS-Ⅱ的标准C数据类型。
#ifdef OS_CPU_GLOBALS #define OS_CPU_EXT #else #define OS_CPU_EXT extern #endif  /*********************************** * 数据类型 (与编译器相关) ************************************/  typedef unsigned char BOOLEAN; typedef 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; /* 双精度浮点数 typedef unsigned int OS_STK; /* 堆栈入口宽度为16位 用户所必须要做的就是查看编译器手册,并找到对应于μC/OS-Ⅱ的标准C数据类型。

11 OS_CPU.H文件(续) /************************* * 与处理器相关的代码
* 与处理器相关的代码 *************************/  #define OS_ENTER_CRITICAL() ??? /* 禁止中断 */ (3) #define OS_EXIT_CRITICAL() ??? /* 允许中断 */  #define OS_STK_GROWTH /* 定义堆栈的增长方向: 1=向下, 0=向上 */ (4) /绝大多数的微处理器和微控制器的堆栈是从上往下长的,但是某些处理器是用另外一种方式工作的。μC/OS-Ⅱ被设计成两种情况都可以处理。 #define OS_TASK_SW() ??? (5)/μC/OS-Ⅱ从低优先级任务切换到最高优先级任务时调用OS_TASK_SW() 。任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。 上述代码中的???部分,对于不同的微处理器来说,都不一样。例如通过执行STI命令在Intel 80186上禁止中断,并用CLI命令来允许中断。 #define OS_ENTER_CRITICAL() asm CLI #define OS_EXIT_CRITICAL() asm STI

12 OS_CPU_A.ASM文件 μC/OS-Ⅱ的移植实例要求用户编写四个简单的汇编语言函数:
OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()、OSTickISR() 如果用户的编译器支持插入汇编语言代码的话,用户就可以将所有与处理器相关的代码放到OS_CPU_C.C文件中,而不必再拥有一些分散的汇编语言文件。

13 OSStartHighRdy() void OSStartHighRdy (void)
要想运行最高优先级任务,用户所要做的是将所有处理器寄存器按顺序从任务堆栈中恢复出来,并且执行中断的返回。 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 task's stack; Execute a return from interrupt instruction; } 注意:OSStartHighRdy()必须调用OSTaskSwHook(),因为用户正在进行任务切换的部分工作——用户在恢复最高优先级任务的寄存器。而OSTaskSwHook()可以通过检查OSRunning来知道是OSStartHighRdy()在调用它(OSRunning为FALSE)还是正常的任务切换在调用它(OSRunning为TRUE)。 OSStartHighRdy()还必须在最高优先级任务恢复之前和调用OSTaskSwHook()之后设置OSRunning为TRUE。

14 OSCtxSw() 任务级的切换问题是通过发软中断命令或依靠处理器执行陷阱指令来完成的。中断服务例程,陷阱或异常处理例程的向量地址必须指向OSCtxSw()。 如果当前任务调用μC/OS-Ⅱ提供的系统服务,并使得更高优先级任务处于就绪状态,μC/OS-Ⅱ就会借助上面提到的向量地址找到OSCtxSw()。 在系统服务调用的最后,μC/OS-Ⅱ会调用OSSched(),并由此来推断当前任务不再是要运行的最重要的任务了。OSSched()先将最高优先级任务的地址装载到OSTCBHighRdy中,再通过调用OS_TASK_SW()来执行软中断或陷阱指令。 注意:变量OSTCBCur早就包含了指向当前任务的任务控制块(OS_TCB)的指针。软中断 (或陷阱) 指令会强制一些处理器寄存器(比如返回地址和处理器状态字)到当前任务的堆栈中,并使处理器执行OSCtxSw()。

15 OSCtxSw()的原型程序 void OSCtxSw(void) { 保存处理器寄存器;
{ 保存处理器寄存器; 将当前任务的堆栈指针保存到当前任务的OS_TCB中: OSTCBCur->OSTCBStkPtr = Stack pointer; 调用用户定义的OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; 得到需要恢复的任务的堆栈指针: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; 将所有处理器寄存器从新任务的堆栈中恢复出来; 执行中断返回指令; } 上述这些代码必须写在汇编语言中,因为用户不能直接从C中访问CPU寄存器。注意在OSCtxSw()和用户定义的函数OSTaskSwHook()的执行过程中,中断是禁止的。

16 OSIntCtxSw() OSIntExit()通过调用OSIntCtxSw()来从ISR中执行切换功能,所有的处理器寄存器都被正确地保存到了被中断的任务的堆栈之中。 实际上除了我们需要的东西外,堆栈结构中还有其它的一些东西。OSIntCtxSw()必须要清理堆栈,这样被中断的任务的堆栈结构内容才能满足我们的需要。

17 在ISR执行过程中的堆栈内容 当中断来临的时候,处理器会结束当前的指令,识别中断并且初始化中断处理过程,包括将处理器的状态寄存器和返回被中断的任务的地址保存到堆栈中(1)。 接着,CPU会调用正确的ISR,要求用户的ISR在开始时要保存剩下的处理器寄存器(2)。 μC/OS-Ⅱ要求用户的ISR在完成中断服务的时候调用OSIntExit(),导致调用者的返回地址被保存到被中断的任务的堆栈中(3)。 处理器的状态寄存器会被保存到被中断的任务的堆栈中(4)。 调用OSIntCtxSw()使返回地址被保存到被中断的任务的堆栈中(5)。

18 OSIntCtxSw()的原型程序 OSIntExit(), OSIntCtxSw()过程中压入堆栈的多余内容;
void OSIntCtxSw(void) { 调整堆栈指针来去掉在调用: OSIntExit(), OSIntCtxSw()过程中压入堆栈的多余内容; 将当前任务堆栈指针保存到当前任务的OS_TCB中: OSTCBCur->OSTCBStkPtr = 堆栈指针; 调用用户定义的OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; 得到需要恢复的任务的堆栈指针: 堆栈指针 = OSTCBHighRdy->OSTCBStkPtr; 将所有处理器寄存器从新任务的堆栈中恢复出来; 执行中断返回指令; } 这些代码必须写在汇编语言中,因为用户不能直接从C语言中访问CPU寄存器。

19 OSTickISR() μC/OS-Ⅱ要求用户提供一个时钟资源来实现时间的延时和期满功能。时钟节拍应该每秒钟发生10-100次。可以使用硬件时钟,也可以从交流电中获得50/60Hz的时钟频率。 用户必须在开始多任务调度后(调用OSStart()后)允许时钟节拍中断。即用户应该在OSStart()运行后,μC/OS-Ⅱ启动运行的第一个任务中初始化节拍中断。

20 时钟节拍ISR的原型程序 void OSTickISR(void) {保存处理器寄存器;
调用OSIntEnter()或者直接将 OSIntNesting加1;  调用OSTimeTick();  调用OSIntExit(); 恢复处理器寄存器; 执行中断返回指令; } 这些代码必须写在汇编语言中,因为用户不能直接从C语言中访问CPU寄存器。

21 OS_CPU_C.C文件 μC/OS-Ⅱ的移植实例要求用户编写六个简单的C函数:
OSTaskStkInit()、OSTaskCreateHook()、OSTaskDelHook()、OSTaskSwHook()、OSTaskStatHook()、OSTimeTickHook() 唯一必要的函数是OSTaskStkInit(),其它五个函数必须得声明但没必要包含代码。

22 OSTaskStkInit() OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInit()来初始化任务的堆栈结构,因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。 假定pdata会被编译器保存到堆栈中,OSTaskStkInit()就会简单的模仿编译器的这种动作,将pdata保存到堆栈中(1)。 处理器至少得将程序计数器的值(中断返回地址)和处理器的状态字存入堆栈(2)。 接着,用户需要将剩下的处理器寄存器保存到堆栈中(3)。 一旦用户初始化了堆栈,OSTaskStkInit()就需要返回堆栈指针所指的地址(4)。

23 OSTaskCreateHook() 当用OSTaskCreate()或OSTaskCreateExt()建立任务的时候就会调用OSTaskCreateHook(),该函数允许用户或使用用户的移植实例的用户扩展μC/OS-Ⅱ的功能。 当μC/OS-Ⅱ设置完了自己的内部结构后,会在调用任务调度程序之前调用OSTaskCreateHook()。该函数被调用的时候中断是禁止的,因此用户应尽量减少该函数中的代码以缩短中断的响应时间。

24 OSTaskDelHook() 当任务被删除的时候就会调用OSTaskDelHook()。该函数在把任务从μC/OS-Ⅱ的内部任务链表中解开之前被调用。 当OSTaskDelHook()被调用的时候,它会收到指向正被删除任务的OS_TCB的指针,这样它就可以访问所有的结构成员了。

25 OSTaskSwHook() 当发生任务切换的时候调用OSTaskSwHook()。不管任务切换是通过OSCtxSw()还是OSIntCtxSw()来执行的都会调用该函数。 OSTaskSwHook()可以直接访问OSTCBCur 和OSTCBHighRdy,因为它们是全局变量。OSTCBCur指向被切换出去的任务的OS_TCB,而OSTCBHighRdy指向新任务的OS_TCB。 注意在调用OSTaskSwHook()期间中断一直是被禁止的。因为代码的多少会影响到中断的响应时间,所以用户应尽量使代码简化。

26 OSTaskStatHook() OSTaskStatHook()每秒钟都会被OSTaskStat()调用一次。
用户可以用OSTaskStatHook()来扩展统计功能。例如,用户可以保持并显示每个任务的执行时间,每个任务所用的CPU份额,以及每个任务执行的频率等等。

27 OSTimeTickHook() OSTaskTimeHook()在每个时钟节拍都会被OSTaskTick()调用。
实际上,OSTaskTimeHook()是在节拍被μC/OS-Ⅱ真正处理,并通知用户的移植实例或应用程序之前被调用的。

28 OSTaskCreate() and OSTaskCreateExt()
OSTaskCreateHook() void OSTaskCreateHook(OS_TCB *ptcb) File Called from Code enabled by OS_CPU_C.C OSTaskCreate() and OSTaskCreateExt() OS_CPU_HOOKS_EN

29 OSTaskDelHook() void OSTaskDelHook(OS_TCB *ptcb) File Called from
File Called from Code enabled by OS_CPU_C.C OSTaskDel() OS_CPU_HOOKS_EN

30 OSCtxSw() and OSIntCtxSw()
OSTaskSwHook() void OSTaskSwHook(void) File Called from Code enabled by OS_CPU_C.C OSCtxSw() and OSIntCtxSw() OS_CPU_HOOKS_EN

31 OSTaskStatHook() void OSTaskStatHook(void) File Called from
File Called from Code enabled by OS_CPU_C.C OSTaskStat() OS_CPU_HOOKS_EN

32 OSTimeTickHook() void OSTimeTickHook(void) File Called from
File Called from Code enabled by OS_CPU_C.C OSTimeTick() OS_CPU_HOOKS_EN


Download ppt "移植μC/OS-Ⅱ."

Similar presentations


Ads by Google