Presentation is loading. Please wait.

Presentation is loading. Please wait.

第48组:姜立群(SC ) 谭兆路(SC ) 闫 佼(SC )

Similar presentations


Presentation on theme: "第48组:姜立群(SC ) 谭兆路(SC ) 闫 佼(SC )"— Presentation transcript:

1 第48组:姜立群(SC06010023) 谭兆路(SC06010022) 闫 佼(SC06010062)
基于S3C44b0X的μC/OS-Ⅱ移植 第48组:姜立群(SC ) 谭兆路(SC ) 闫 佼(SC )

2 摘要 1.μC/OS-Ⅱ概述 2.移植条件 3. μC/OS-Ⅱ 移植的实现 4.测试移植代码 5.μC /OS-Ⅱ扩展的体系结构
6.Skyeye上模拟演示 2019/1/11

3 1.μC/OS-Ⅱ概述 μC/OS-Ⅱ是由Jean J . Labrosse 先生编写的完整的可移植、固化、裁剪的占先式实时多任务内核。
2019/1/11

4 1.1μC/OS-Ⅱ特点 (1) 公开源代码 μC/OS-Ⅱ对学校和教育的使用完全免费,商业应用的费用相对也很低。 (2) 可移植性
   绝大部分 μC/OS-Ⅱ的源码是用移植性很强的C语言编写写的,和微处理器硬件相关的那部分是用汇编语言写的。汇编语言写的部分已经压到最低限度,使得 μC/OS-Ⅱ便于移植到其他微处理器上。μC/OS-Ⅱ可以在绝大多数 8 位、16 位、32 位以至 64 位微处理器、微控制器、数字信号处理器(DSP)上运行。 只要买一本书就可获得其源代码,且代码清晰易读、注释详尽,组织 有序。 2019/1/11

5 1.1μC/OS-Ⅱ特点(续) (3) 可固化 μC/OS-Ⅱ是为嵌入式应用而设计的,只要有固化手段(C编译、连接、下载和固化),μC/OS-Ⅱ就可以嵌入到产品中成为产品的一部分。 (4) 可裁剪 可以只使用 μC/OS-Ⅱ中应用程序需要的那些系统服务,也就是说某产品可以只使用很少几个 μC/OS-Ⅱ调用,而另一个产品则使用了几乎所有 μC/OS-Ⅱ的功能。这样可以减少产品中的 μC/OS-Ⅱ所需的存储空间(RAM 和 ROM),这种可裁剪性是靠条件编译实现的。 2019/1/11

6 1.1μC/OS-Ⅱ特点(续) (5) 占先式 μC/OS-Ⅱ完全是占先式的实时内核。所以 μC/OS-Ⅱ总是运行就绪条件下优先级最高的任务。大多数商业内核也是占先式的,μC/OS-Ⅱ在性能上和它们类似。 (6) 多任务 μC/OS-Ⅱ可以管理 64 个任务,但是保留 8 个任务给系统,应用程序最多可以有 56 个任务,赋予每个任务的优先级必须是不同的,这意味着μC/OS-Ⅱ不支持时间片轮转调度法(Round-robin Scheduling)。 (7) 可确定性 全部 μC/OS-Ⅱ的函数调用与服务的执行时间具有其可确定性。也就是说,全部 μC/OS-Ⅱ的函数调用与服务的执行时间是可知的。进而言之,μC/OS-Ⅱ系统服务的执行时间不依赖于应用程序任务的多少。 2019/1/11

7 1.1μC/OS-Ⅱ特点(续) (8) 任务栈 每个任务有自己单独的栈, μC/OS-Ⅱ允许每个任务有不同的栈空间,以便压低应用程序对 RAM 的需求。使用 μC/OS-Ⅱ的栈空间校验函数,可以确定每个任务到底需要多少栈空间。 (9) 系统服务 μC/OS-Ⅱ提供很多系统服务,例如邮箱、消息队列、信号量、块大小固定的内存的申请与释放、时间相关函数等。 2019/1/11

8 1.1μC/OS-Ⅱ特点(续) (10) 中断管理 中断可以使正在执行的任务暂时挂起。如果优先级更高的任务被该中断唤醒,则高优先级的任务在中断嵌套全部退出后立即执行,中断嵌套层数可达 255 层。 (11) 稳定性与可靠性 μC/OS-Ⅱ是基于 μC/OS 的,μC/OS 自 1992 年以来已经有好几百个商业应用。μC/OS-Ⅱ与 μC/OS 的内核是一样的,只不过提供了更多的功能。 2019/1/11

9 1.2μC /OSⅡ的软件体系结构 2019/1/11

10 1.2μC /OS-Ⅱ的软件体系结构 上图给出了μC/OS-Ⅱ的软件体系结构,它包括如下结构:
3)要使同一个内核能适用于不同的硬件体系,就需要在内核和硬件之间添加一个中间层,这就是与处理器相关的代码。所谓移植,就是编写与处理器相关的这部分代码。 2019/1/11

11 2.移植条件 一般来说,能移植μC /OS-Ⅱ的微处理器必须满足以下条件 : (1) 处理器的C编译器能产生可重入型代码。
(2) 处理器支持中断,并且能产生定时中断 。 (3) 用C语言就可以开/关中断。 (4) 处理器能支持一定数量的数据存储硬件堆栈 。 (5) 处理器有将堆栈指针及其他CPU寄存器的内容读出、并存储到堆栈或内存中去的指令。 2019/1/11

12 2.移植条件(续) (1)可重入的代码指的是一段代码(比如:一个函数)可以被多个任务同时调用,而不必担心会破坏数据。也就是说,可重入型函数在任何时候都可以被中断执行,过一段时间以后又可以继续运行,而不会因为在函数中断的时候被其他的任务重新调用,影响函数中的数据。 2019/1/11

13 2.移植条件(续) (2)在μC/OS-Ⅱ中,可以通过: OS_ENTER_CRITICAL( ) OS_EXIT_CRITICAL( )
宏来控制系统关闭或者打开中断。这需要处理器的支持。在S3C44b0X的处理器上,可以设置相应的寄存器来关闭或者打开系统的所有中断。 2019/1/11

14 2.移植条件(续) (3) μC/OS-Ⅱ是通过处理器产生的定时器的中断来实现多任务之间的调度的。 在S3C44b0X的处理器上可以产生定时器中断。 2019/1/11

15 2.移植条件(续) (4) μC/OS-Ⅱ进行任务调度的时候,会把当前任务的CPU寄存器存放到此任务的堆栈中,然后,再从另一个任务的堆栈中恢复原来的工作寄存器,继续运行另一个任务。所以,寄存器的入栈和出栈是μC/OS-Ⅱ多任务调度的基础。 S3C44b0X处理器中有专门的指令处理堆栈,可以灵活的使用堆栈。 2019/1/11

16 2.移植条件(续) 对于S3C44B0X来说,上面的这些条件都是可以满足的。当操作系统和微处理器协商好之后,下面要做的工作才是真正的移植工作! 2019/1/11

17 3.μC/OS-II 移植的实现 实际上,μC /OS-Ⅱ可以简单地看作是一个多任务调度器,在这个任务调度器上添加了与多任务操作系统相关的一些系统服务,如信号量、邮箱等,其90 %的代码是用C语言写的。 移植工作主要集中在多任务切换的实现上,因为这部分代码用来保存和恢复CPU现场(即写/读相关寄存器) ,不能用C语言实现,而只能用汇编语言完成。 μC /OS-Ⅱ 的全部源代码量大约是6 000~7 000行,共16个文件,将其移植到ARM处理器上,需要修改3 个与ARM 体系结构相关的文件:OS_CPU. H,OS_CPU_C. C, OS_CPU_A. ASM,代码量大约是500行。下面分别介绍这3个文件的移植工作。 2019/1/11

18 3.1 OS_CPU.H的移植 在这个文件中主要定义了与编译器和处理器相关的的代码,这部分代码包括数据类型定义、堆栈单位定义、堆栈增长方向定义、关中断和开中断的宏定义以及进行任务切换的宏定义等。需要修改的代码如下: 2019/1/11

19 (1)数据类型的定义 这部分的修改是和所用的编译器相关的,不同的编译器会使用不同的字节长度来表示同一数据类型。 具体定义代码如下:
typedef unsigned char BOOLEAN; typedef unsigned char INT8U; /*8 位无符号整数*/ typedef signed char INT8S; /*8 位有符号整数*/ typedef unsigned short INT16U; /*16 位有符号整数*/ typedef signed short INT16S; /*16 位无符号整数*/ typedef unsigned int INT32U; /*32 位无符号整数*/ typedef signed int INT32S; /*32 位有符号整数*/ typedef float FP32; /*单精度浮点数*/ typedef double FP64; /*双精度浮点数*/ 2019/1/11

20 (2) 堆栈的定义 寄存器在任务切换时将会保存在当前运行任务的堆栈中,所以堆栈单位(即数据类型 OS_STK)应该是和处理器的寄存器长度一致的。 typedef unsigned int OS_STK /*堆栈的单位, 堆栈入口宽度为 32位*/ 2019/1/11

21 (2) 堆栈的定义 堆栈也是和编译器有关的,当进行函数调用时,入口参数和返回地址都会保存在当前任务的堆栈中,编译器的编译选项和由此生成的堆栈指令决定堆栈的增长方向。在μC/OS-Ⅱ中, 用OS_STK_GROWTH 来设置堆栈的增长方向, 其宏定义为: #define OS_STK_GROWTH 1; /* 堆栈从高地址向低地址增长*/ #define OS_STK_GROWTH 0; /* 堆栈从低地址向高地址增长*/ 2019/1/11

22 (3)中断与临界区代码 μC/OS-Ⅱ和其它所有的实时内核一样,在访问操作系统的临界区之前必须关闭中断,访问之后开中断。这可以保证μC/OS-Ⅱ的临界区代码不会被多个任务或中断服务程序同时访问,避免造成共享数据(μC/OS-Ⅱ的全局变量)的不一致性。 对于不同的处理器和编译器,实现开,关中断的方法可能不一样。所以为了方便移植,μC/OS-Ⅱ提供了两个宏定义:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(), 用这两个宏定义屏蔽开, 关中断的不同实现。 2019/1/11

23 (3)中断与临界区代码 相关宏定义如下: #define OS_TASK_SW() OSCtxSw() /*任务切换*/ #define OS_EXIT_CRITICAL() ARMDisabIeInt() /*关闭中断*/ #define OS_ENTER_CRITICAL() ARMEnableInt() /*开启中断*/ OS_TASK_SW()函数用来实现任务切换。μC/OS-Ⅱ会调用 OSCtxSw()函数实现任务切换。OSCtxSw()需要对寄存器进行操作,加上考虑到效率的因素,OSCtxSw()用 ARM 汇编来实现。具体实现过程将在OS_CPU_A. ASM中详细讲述。 OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL() 采用汇编来实现禁止和允许中断,具体实现为 ARMDisableInt()和 ARMEnableInt()。 2019/1/11

24 (3)中断与临界区代码 在μC/OS-Ⅱ移植过程中很关键的一个问题就是OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL ()这一对宏定义如何实现的问题。 最简单的方法是仅用关中断指令实现宏OS_ENTER_CRITICAL() ,仅用开中断指令实现宏OS_EXIT_CRITICAL()。 2019/1/11

25 (3).中断与临界区代码 用以下的汇编子程序完成ARMDisabIeInt()函数来实现关中断:
STMFD SP!,{R0,LR} //R0,LR入栈 MRS R0,CPSR //CPSR->R0需要修改R0 ORR R0,R0,#0XC0 //修改CPSR的运行IRQ位和FIQ位 SWI UpdateCPSR //使修改生效 LDMFD SP!,{RO,PC} 2019/1/11

26 (3).中断与临界区代码 程序状态寄存器组成图 2019/1/11

27 (3).中断与临界区代码 注意子程序中改变中断状态的方法,这里用了一条软件中断指令。因为,当程序运行在用户模式时,不能用MSR指令来改变CPSR的内容,所以,必须用一条软件中断指令进入管理模式来改变中断状态。 软件中断服务程序如下: UpdateCPSR MSR SPSR_cxsf,R0 MOVS PC,LR 2019/1/11

28 (3).中断与临界区代码 其中,寄存器R0用于传递参数,包含新的CPSR。这里是利用在管理模式下改变其SPSR(软件中断前用户模式下的CPSR)的值达到改变用户模式下CPSR的目的。指令MOVS PC,R14在将返回地址赋给PC时, 同时也将管理模式下的SPSR值赋给用户模式的CPSR。 2019/1/11

29 (3).中断与临界区代码 用类似的方法可以实现开中断,而且中断状态的改变也必须用软件中断实现。 2019/1/11

30 (3).中断与临界区代码 这种方法可以减少中断延迟时间,但它可能存在一点小问题,就是如果程序在调用OS_ENTER_CRITICAL() 之前,中断已经被禁止,那么在调用OS_EXIT_CRITICAL () 之后,中断被允许,这可能不是程序所期望的。因此这种简单的实现方法对某些情况不适用。 2019/1/11

31 (3).中断与临界区代码 OS_ENTER_CRITICAL() 和OS_EXIT_CRITICAL ()的具体实现要根据原来的中断禁止/允许的状态来决定的。对原方案的改进是在退出临界区的时候会恢复进入之前的状态,使用堆栈保存状态和恢复状态。 具体的方法是在实现宏OS_ENTER_CRITICAL() 的时候,先将当前程序的中断状态保存到堆栈, 然后关中断, 而宏OS_EXIT_CRITICAL ()的实现只需将堆栈中的中断状态恢复。这样,就不会破坏程序原来的中断状态,但是这种实现会增加中断的延迟时间。 2019/1/11

32 3.2 OS_CPU_C.C的移植 在此文件中, 要求我们必须编写10 个简单的C 函数。
OStaskStkInit(); /*任务堆栈初始化函数*/ OStaskCreateHook ();/*任务建立接口函数 */ OStaskDelHook ();/*任务删除接口函数*/ OStaskSwHook ();/*任务切换接口函数 OStaskIdleHook ();空闲任务接口函数 OStaskStatHook ();统计任务接口函数 OSTimeTickHook ();时钟节拍接口函数 OStaskHookBegin ();系统初始化开始接口函数 OStaskHookEnd ();系统初始化结束接口函数 OSTCBInitHook ();控制块初始化接口函数 唯一必要的函数是OsTaskStkInt()函数。其他9 个函数必须声明, 但可以不包含任何代码。 2019/1/11

33 3.2 OS_CPU_C.C的移植 OSTaskStkInit( )是用户建立任务时系统内部自己调用的, 对用户任务堆栈进行初始化。在ARM7TDMI体系结构下,任务堆栈空间由高至低依次保存着PC, LR, R12, R11, ..., R1, R0, CPSR,SPSR,如图所示。当任务堆栈初始化完成后,OSTaskStkInit( )返回新的堆栈指针STK。 2019/1/11

34 ARM7体系结构的寄存器位置 2019/1/11

35 执行OSTaskCreate ( )时,会调用OSTaskStkInit ( )的初始化过程,然后通过OSTCBInit ( )函数调用,将返回的SP指针保存到该任务的TCB 块中。初始状态的堆栈是模拟了一次中断后的堆栈结构,使建立好的进入就绪任务的堆栈与系统发生中断、并且将环境变量保存完毕时的设置与堆栈结构一致。因为任务创建后并不是直接就获得执行,而是通过OSSched ( )函数进行调度分配,当满足执行条件后才能获得执行。为了使调度简单一致,就预先将该任务的PC指针和返回地址LR都指向函数入口,以便被调度时从堆栈中恢复到刚开始运行时的CPU现场。 2019/1/11

36 3.2 OS_CPU_C.C的移植 OSTaskStkInit()函数具体代码如下:
void *OSTaskStkInit (void (*task)(void *pd),void *pdata, void *ptos, INT16U opt) { unsigned int *stk; opt = opt; /* 因为'opt' 变量没有用到,防止编译器产生警告*/ stk = (unsigned int *)ptos; /*装载堆栈指针*/ 2019/1/11

37 3.2 OS_CPU_C.C的移植 /* 为新任务创建上下文,使用满递减堆栈*/
*--stk = (unsigned int) task; /* pc */ *--stk = (unsigned int) task; /* lr */ *--stk = 0; /* r12 */ *--stk = 0; /* r11 */ *--stk = 0; /* r10 */ *--stk = 0; /* r9 */ *--stk = 0; /* r8 */ *--stk = 0; /* r7 */ *--stk = 0; /* r6 */ *--stk = 0; /* r5 */ *--stk = 0; /* r4 */ *--stk = 0; /* r3 */ *--stk = 0; /* r2 */ *--stk = 0; /* r1 */ 2019/1/11

38 3.2 OS_CPU_C.C的移植 *--stk = (unsigned int) pdata; /* r0,在ARM编译器里第一个参数使用R0传递 */ *--stk = (SVC32MODE|0x0); /* cpsr IRQ, 关闭FIQ */ *--stk = (SVC32MODE|0x0); /* spsr IRQ, 关闭FIQ */ return ((void *)stk); } 2019/1/11

39 3.2 OS_CPU_C.C的移植 后面的函数为钩子函数,如果没有特殊需求,可以简单地将它们都实现为空函数。以后可再根据需要再加以实现:
void OSTaskSwHook (void) void OSTaskStatHook (void) void OSTimeTickHook (void) 但是void OSTaskCreateHook ()和 void OSTaskDelHook ()有时却不能简单的定义为空函数。 2019/1/11

40 3.2 OS_CPU_C.C的移植 OSTaskCreateHook()在µC/OS中没有,在由µC/OS 向µC/OS-II升级时,定义一个空函数就可以了。注意其中的赋值语句,如果不把Ptcb赋给Ptcb,有些编译器会产生一个警告错误,说定义的Ptcb变量没有用到。 OSTaskCreateHook() #if OS_CPU_HOOKS_EN OSTaskCreateHook(OS_TCB *ptcb) { ptcb = ptcb; //防止编译时出现警告 } #endif 2019/1/11

41 void OSTaskDelHook ()与void OSTaskCreateHook ()的定义类似。
用户还应该使用条件编译管理指令来处理这个函数。只有在OS_CFG.H 文件中将OS_CPU_HOOKS _EN设为1时,OSTaskCreateHook()的代码才会生成。这样做的好处是允许用户移植时可在不同文件中定义钩子函数。 void OSTaskDelHook ()与void OSTaskCreateHook ()的定义类似。 2019/1/11

42 3.4 OS_CPU_A.ASM的移植 为了方便移植,大部分的 μC/OS-Ⅱ代码是用 C 语言写的,但仍需要用 C 和汇编语言写一些与处理器相关的代码,这是因为μC/OS-Ⅱ在读写处理器寄存器时只能通过汇编语言来实现。这部分代码都放在了OS_CPU_A.ASM文件中。在此文件中需改写4 个简单汇编语言函数。 (1)OSStartHighRdy()函数 (2)OSCtxSw( )函数 (3)OSIntCtxSw()函数 (4)OSTickISR()函数 2019/1/11

43 (1)OSStartHighRdy()函数
操作系统初始化后,开始执行系统内第一个最高优先级的任务。对于第一个执行的任务,不需要进行上下文保存,只需恢复任务上下文即可。第一个任务的执行是通过调用汇编子程序OSStartHighRdy 实现的。此子程序首先调用钩子函数OSTaskSwHook(),完成用户扩展的操作系统功能,然后将OSRunnig 的值置为真,表示开始任务的执行,从而保证任务切换操作的正确执行。紧接着从具有最高优先级的任务控制块中取得任务的堆栈指针,初始化堆栈指针寄存器SP 。然后恢复CPSR,恢复其它的寄存器,开始执行最高的优先级的任务。 注意,这里CPRS的恢复也必须用前面所述的软件中断来实现,以保证有效性。 2019/1/11

44 (1)OSStartHighRdy()函数
具体代码: LDR r4,addr_OSTCBCur; /*得到当前任务的 TCB 地址*/ LDR r5,addr_OSTCBHighRdy; /*得到高优先级任务的 TCB 地址*/ LDR r5,[r5]; /*得到堆栈指针*/ LDR sp,[r5]; /*切换到新的堆栈*/ STR r5,[r4]; /*设置新的当前任务的 TCB 地址*/ LDMFD sp!,{r4}; MSR CPSR_cxsf,r4; LDMFD sp!, {r4} ; 从栈顶得到新的声明 MSR CPSR_cxsf, r4 LDMFD sp!,{r0-r12,lr,pc}; /*开始新的任务*/ END 2019/1/11

45 (2)OSCtxSw( )函数 任务级的任务切换
任务级的上下文切换,当任务因为被阻塞而主动请求CPU调度时被执行,由于此时的任务切换在非异常模式下进行,因此区别于中断级别的任务切换。它的工作是先将当前任务的CPU现场保存到该任务堆栈中,然后获得最高优先级任务的堆栈指针,从该堆栈中恢复此任务的CPU现场,使之继续执行。这样就完成了一次任务切换 2019/1/11

46 (2)OSCtxSw( )函数 任务级的任务切换
任务级上下文切换是由汇编子程序OSCtxSw()实现。它是由操作系统的任务调度函数OSSched()调用,所有的操作都在用户模式下完成(除了使用软件中断进入管理模式改变CPSR)在整个过程中中断是关闭的,因为在上下文切换过程中要访问操作系统的全局变量。 2019/1/11

47 (2)OSCtxSw( )函数 任务级的任务切换
OSCtxSw()函数是一个任务级的任务切换函数。软中断向量指向此函数。在μC/OS-Ⅱ中, 如果任务调用了某个函数, 而该函数的执行结果可能造成系统任务的重新调度, 则在函数的末尾会调用OSSched(),OSSched()查找当前就绪最高优先级的任务, 如果不是当前任务, 则找该任务的TCB 地址, 并复制到变量OSTcbHighRdy()中, 然后通过宏OS_TASK_SW( ) 执行软中断调用OSCtxSw()进行任务切换。变量OSTCBCur 始终包含指向当前运行任务TCB的指针。 2019/1/11

48 具体代码如下: STMFD sp!, {lr} ; 保存PC 指针 STMFD sp!, {lr} ; 保存lr 指针
STMFD sp!, {r0-r12} ;保存寄存器文件和ret 地址 MRS r4, CPSR STMFD sp!, {r4} ; 保存当前PSR MRS r4, SPSR STMFD sp!, {r4} LDR r4, addr_OSPrioCur LDR r5, addr_OSPrioHighRdy LDRB r6, [r5] STRB r6, [r4] ; 得到当前任务的TCB 地址 2019/1/11

49 具体代码 LDR r4, addr_OSTCBCur LDR r5, [r4]
STR sp, [r5] ; 保存栈指针在占先任务的TCB 上 ,取得高优先级任务的TCB 地址 LDR r6, addr_OSTCBHighRdy LDR r6, [r6] LDR sp, [r6] ;得到新任务的堆栈指针 ; OSTCBCur = OSTCBHighRdy STR r6, [r4] ; 设置当前新任务的TCB 地址 LDMFD sp!, {r4} MSR SPSR_cxsf, r4 MSR CPSR_cxsf, r4 LDMFD sp!, {r0-r12, lr, pc} 2019/1/11

50 (3)OSIntCtxSw()函数 中断级的任务切换
ARM处理器在跳到中断服务程序时,自动关闭IRQ中断。为保证IRQ中断的可重入性,即一个IRQ中断程序能被另一个IRQ中断,需要使用ARM提供的系统模式。因此中断服务程序需要运行在系统模式下。 为了能够跟踪中断服务程序,操作系统提供了两个函数,一个是OSIntEnter(),在开始运行实际的中断服务之前调用;另一个是OSIntExit(),在退出中断服务时调用。由于中断服务程序可能会激活一个更高优先级的任务,所以,OSIntExit()对就绪任务队列进行检查,如果有更高优先级的任务且任务调度允许,则调用中断级的上下文切换例程,保存被中断的任务,运行高优先级的任务。 2019/1/11

51 (3)OSIntCtxSw()函数 中断级的任务切换
OSIntCtxSw()过程用于实现中断级的上下文切换,它由OSIntExit()调用。OSIntCtxSw()的主要功能是将被中断的任务的执行环境保存到其堆栈中,而被中断的任务堆栈指针的保存、高优先级任务执行环境的恢复与执行,由例程CmnCtxSw()完成。 因此,实现OSIntCtxSw()的关键在于如何收集被中断任务的上下文。首先必须清楚,从中断发生到中断级上下文切换的过程中,例程的调用关系和处理器模式的变化情况。如图所示: 2019/1/11

52 调用关系和处理器模式的变化 2019/1/11

53 调用关系和处理器模式的变化 从上图可知,被中断任务的返回地址PC、通用寄存器R0-R12以及程序状态寄存器CPSR被保存在IRQ堆栈中,而连接寄存器LR_usr被保存在系统模式的堆栈中(实际上就是被中断任务的堆栈中)。由于中断级的任务切换发生在系统模式,为了访问IRQ堆栈,必须把处理器状态改变到IRQ模式并禁止中断,获取IRQ堆栈指针,将其保存到通用寄存器,然后回到系统模式并禁止中断。 这样,在系统模式下可以访问到被中断任务的返回地址、R0-R12的值以及CPRS的值,而且LR_usr的值也已被保存在系统模式的堆栈中。从而,在系统模式下完成了被中断任务执行环境的保存。 (在返回系统模式之前,还必须调整IRQ堆栈指针,清除IRQ堆栈的无用数据) 还有需要注意的一点,就是在系统模式堆栈中存取LR_usr时,必须调整堆栈指针,由于例程OSIntExit()需要调用例程OSIntCtxSw(),从而需要保存一些寄存器的值到系统模式的堆栈。 2019/1/11

54 具体代码如下: STMFD sp! ,{lr}; /* 保存PC 指针*/
STMFD sp! ,{r0- r12}; /* 保存寄存器文件和RET 地址*/ STMFD sp! ,{r4}; /* 保存当前PSR*/ LDR r4,addr_OSTCBCur; /* 得到当前任务的TCB 地 址*/ STR sp,[r5]; /* 保存栈指针在占先任务的TCB 上*/ LDR r6,addr_OSTCBHighRdy;/* 取得高优先级任务 的TCB 地址*/ LDR sp,[r6]; /* 得到新任务的堆栈指针*/ LDMFD sp!,{r4} ; /* 设置当前新任务的TCB 地址*/ 2019/1/11

55 (4)OSTickISR()函数 当时钟中断(IRQ)到来时,处理器跳转到时钟中断服务程序,时钟中断服务程序主要调用函数OSTimeTick(),这个函数处理与系统时钟相关的工作,如将每个任务的等待时间减1、更新系统时间等等。 时钟中断服务程序由汇编子程序OSTickISR()实现。因为OSTickISR()的具体实现与中断级上下文切换的实现有很大的关系,所以对其具体的实现步骤进行较详细的分析: 2019/1/11

56 (4)OSTickISR()函数 1)它首先保存被中断程序的上下文环境。将被中断程序的返回地址、通用寄存器以及程序状态寄存器保存到IRQ堆栈中,此时,IRQ堆栈中的内容如图所示(堆栈指针寄存器指向内容为CPSR的堆栈单元)。IRQ堆栈中保存的寄存器内容非常重要,因为它影响中断级上下文切换的实现。在保存被中断任务执行环境的时候,中断级上下文切换必须从IRQ堆栈中读取相关的寄存器内容。 2019/1/11

57 IRQ 堆栈的内容 2019/1/11

58 (4)OSTickISR()函数 2)执行标识中断结束的指令。这是通过向AIC(高级中断控制器)的中断结束命令寄存器写一个任意数实现的。
3)切换到系统模式并允许IRQ和FIQ中断。为了允许中断的嵌套,在开始中断服务以前,必须进入系统模式。因为如果在IRQ模式下允许中断嵌套,那么,当下一个IRQ中断发生的时候,就会破坏当前中断的执行环境。 2019/1/11

59 (4)OSTickISR()函数 4)保存系统模式下相关的寄存器。主要是连接寄存器LR_usr(),因为系统模式和用户模式共用寄存器组,该操作实际上是把LR_usr()保存到了用户模式的堆栈中(被中断任务的堆栈中),这个寄存器的内容在中断级任务切换的时候要用到,所以必须保存。 2019/1/11

60 (4)OSTickISR()函数 5)在调用OSTimeTick()函数之前,先调用操作系统提供的OSIntEnter()函数,以使操作系统对中断程序进行跟踪。调用OSTimeTick()之后,再调用函数OSIntExit(),通知操作系统中断完成。如果就绪队列中有更高优先级的任务且允许任务调度,OSIntExit()则进行中断级任务切换(在后面论述)。如果没有更高优先级的任务,则恢复系统模式下保存的寄存器,并切换到IRQ模式且禁止中断,恢复被中断任务的上下文环境,重新执行被中断的任务。 2019/1/11

61 具体代码如下: STMDB SP!,{r0-r11,lr} MRS R0, CPSR ORR R0, R0, #0x80 ; 设置中断禁止标
MRS CPSR_cxsf, r0 ;中断结束 LDR R0, =I_ISPC LDR R1, =BIT_TIMER0 STR R1, [R0] BL IrqStart BL OSTimeTick BL IrqFinish LDR R0, =need_to_swap_context LDR R2, [R0] CMP R2, #1 LDREQ PC, =_CON_SW 2019/1/11

62 完成上述工作后, μC/OS-Ⅱ的就移植代码编写完毕,但是否可以正常运行还需要进行移植测试,以保证其在S3C44B0X板上可以正常运行。
2019/1/11

63 4.测试移植代码 当用户为自己的处理器做完μC/OS-Ⅱ的移植后,紧接着的工作就是验证移植的μC/OS-Ⅱ能否正常的工作,而这可能是移植中最复杂的一步。应该首先不加任何应用代码来测试移植好的μC/OS-Ⅱ,也就是说,应该首先测试内核自身的运行状况。这样做的原因是: (1)尽量避免将事情复杂化; (2)如果有些部分没有正常工作,可以明白是移植本身的问题,而不是应用代码产生的问题。 如果已经将2个基本的任务和节拍中断运行起来,那么接下来添加应用任务是很简单的事情。 2019/1/11

64 4.测试移植代码(续) 可以使用各种不同的技术测试自己的移植工作,这取决于用户个人在嵌入式系统方面的经验和对处理器的理解。一般情况下通过四个步骤测试移植代码: 4.1确保C编译器、汇编编译器及链接器正常工作 4.2验证OSTskStkInit() 和OSStartHighRdy()函数 4.3验证OSCtxSw() 函数 4.4 验证OSIntCtxSw()和OSTickISR()函数 2019/1/11

65 4.1确保C编译器、汇编编译器及链接器正常工作
当修改完需要根据CPU更改的文件后,紧接着要把这些文件和μC/OS-Ⅱ中与处理器无关的文件一同编译和链接。显然这个步骤取决于使用的编译器,在这期间是无代码的测试。最小的测试程序如下: 2019/1/11

66 #include “includes.h” Void main(void) { osinit(); Osstart(); }
2019/1/11

67 4.2验证OSTskStkInit() 和OSStartHighRdy()函数
如果上一步的测试完成了,就可以测试移植好的μC/OS-Ⅱ代码,真正的测试工作就可以开始了。在OS_CFG.H文件中设置OS_TASK_STAT_EN为0,只让一个空闲任务OS_TaskIdle()运行,检查是否出错.如果调试器在OS_TaskIdle()的循环中运行,且在无限循环中已经执行几次,那么就已经验证了OSTskStkInit() 和OSStartHighRdy()函数是成功的。 2019/1/11

68 4.3验证OSCtxSw() 函数 如果上一步测试成功,这一步代码验证就比较容易了。已经因为知道由OSTskStkInit() 初始化的堆栈结构是正确的。在这步测试中,添加一个应用程序,并不断切换到空闲任务。在此之前,应该保证已经正确设置了软中断向量或指令陷阱TRAP向量,使它指向OSCtxSw() 。测试程序如下: 2019/1/11

69 ostaskcreate(testtask,(void*)0,&testtask[99],0); Osstart(); }
#include “includes.h” Void main(void) { osinit(); ostaskcreate(testtask,(void*)0,&testtask[99],0); Osstart(); } void testtask(void *pdata) {pdata= pdata; While(1){ ostimedly(1);} 2019/1/11

70 如果OSCtxSw()并没有将用户带入OS_TaskIdle(),则应检查原因,纠正OSCtxSw()中的错误。
现在单步执行进入OSTimeDly()。 OSTimeDly()调用OS_Sched()函数,而OS_Sched()调用汇编语言编写的OSCtxSw() 函数。当进入OSCtxSw() 的代码,看到TestTask()任务的寄存器已经保存到它的任务堆栈,而OS_TaskIdle()的寄存器被调入CPU. 如果OSCtxSw()并没有将用户带入OS_TaskIdle(),则应检查原因,纠正OSCtxSw()中的错误。 2019/1/11

71 4.4 验证OSIntCtxSw()和OSTickISR()函数
OSIntCtxSw()很像OSCtxSw() ,且比OSCtxSw()简单。实际上,OSIntCtxSw()中的多数代码可从OSCtxSw()中得到。在此测试之前,应该保证时钟中断向量指向了时钟节拍中断服务子程序,然后,初始化始终节拍并中断。 2019/1/11

72 5. 基于μC /OS-Ⅱ扩展RTOS的体系结构
 μC /OS-Ⅱ提供的仅仅是一个任务调度的内核,将其移植到S3C44b开发板上以后,要想实现一个相对完整、实用的RTOS,还需要进行相当多的扩展性工作。这部分工作主要包括:建立文件系统、为外部设备建立驱动程序并规范相应的API函数、创建图形用户接口( GUI)函数、建立其他实用的应用程序接口(API)函数等。基于μC /OS-Ⅱ内核扩展的软件整体框如下图所示 2019/1/11

73 基于μC /OS-Ⅱ内核扩展的RTOS的整体框
2019/1/11

74 6.在Skyeye上模拟演示 两个方案: 1)Windows方案
在windows上先安装cygwin模拟linux环境,然后在cygwin上安装Skyeye。 此方案操作相对简单,但可能存在的问题比较多。只能安装较低版本的Skyeye。 2019/1/11

75 Make error 截图 2019/1/11

76 2)Linux方案 层次结构:PC硬件->Windows->VMware Workstation->Linux->Skyeye-> μC /OS-Ⅱ->task1 & task2 实验步骤:(从安装Skyeye开始) ① 解压源码包 # tar xjvf skyeye tar.bz2 ②进入解压SkyEye目录,配置SkyEye # ./configure --target=arm-elf --prefix=/usr/local ③然后是编译和安装 # make # make install 2019/1/11

77 2)Linux方案 ④测试Skyeye安装是否成功 ⑤安装arm-elf-tools交叉编译工具 ⑥将μC/OS-Ⅱ部署到Skyeye
#./arm-elf-tools sh ⑥将μC/OS-Ⅱ部署到Skyeye 从Skyeye的网站上下载的ucosii4Skyeye-v1.8.4就是已经修改好的μC/OS-Ⅱ。 #tar zxf ucosii4skyeye tgz #cd ucosii4skyeye #make cleanall #export OSTYPE=linux-gnu #make config #make dep #make 2019/1/11

78 2)Linux方案 ⑦测试μC/OS-Ⅱ是否配置成功 ⑧运行两个任务Task1、Task2 #skyeye simple_test.elf
(SkyEye)target sim (SkyEye)load (SkyEye)run μC/OS-Ⅱ的一个简单的程序开始在SkyEye上运行,按Ctrl+C终止运行,键入quit退出skyeye。 ⑧运行两个任务Task1、Task2 两个任务Task1、Task2的优先级分别为1、2。Task1先运行,从屏幕获取字符后,进入等待信号量状态,这时Task2运行,将获取的字符打印出来,然后发送一个信号量,Task1获取信号量后继续运行,如此反复。 #skyeye ucos_test.elf 2019/1/11

79 总结 移植μC/OS-Ⅱ到S3C44b0X上,既是对μC/OS-Ⅱ的学习和实验, 同时也是对S3C44b0X芯片编程的实践。 μC/OS-Ⅱ作为一个优秀的实时操作系统已经被移植到各种体系结构的微处理器上, 也是目前较为常用的公开源码的实时内核。从这里入手学习嵌入式系统开发的基本概念,以及在S3C44b0X上运行RTOS,能够更深入地了解嵌入式开发的流程,并且为继续在S3C44b0X上为μC/OS-Ⅱ实时内核添加TCP/IP协议和MMU等功能模块奠定良好的基础。 再有一点就是,使我们的理论学习更有针对性。在做这个实验的过程中才真正体验到自己理论知识的匮乏。 2019/1/11


Download ppt "第48组:姜立群(SC ) 谭兆路(SC ) 闫 佼(SC )"

Similar presentations


Ads by Google