1 / 72

第 7 章 移植 μC/OS-II 到 ARM7

第 7 章 移植 μC/OS-II 到 ARM7. RTOS ARM. 第 7 章 目录. 1. μC/OS-II 简介 2. 移植规划 3. 移植 μC/OS-II 4. 移植代码应用到 LPC2000. 第 7 章 目录. 1. μC/OS-II 简介 2. 移植规划 3. 移植 μC/OS-II 4. 移植代码应用到 LPC2000. 7.1 μC/OS-II 简介. 概述.

trory
Download Presentation

第 7 章 移植 μC/OS-II 到 ARM7

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第7章 移植μC/OS-II到ARM7 RTOSARM

  2. 第7章 目录 • 1. μC/OS-II简介 • 2.移植规划 • 3.移植μC/OS-II • 4.移植代码应用到LPC2000

  3. 第7章 目录 • 1. μC/OS-II简介 • 2.移植规划 • 3.移植μC/OS-II • 4.移植代码应用到LPC2000

  4. 7.1 μC/OS-II简介 • 概述 μC/OS-II读做“micro C O S 2”,意为“微控制器操作系统版本2”。μC/OS-II是源码公开的著名实时内核,可用于各类8位、16位和32位单片机或DSP。从μC/OS算起,该内核已有10多年应用史,在诸多领域得到广泛应用。 μC/OS-II是一个完整的、可移植、可固化、可剪裁的占先式实时多任务内核。μC/OS-II使用ANSI C语言编写,包含一小部分汇编代码,使之可以供不同架构的微处理器使用。至今,从8位到64位,μC/OS-II已在超过40种不同架构的微处理器上运行。

  5. µC/OS-II的各种商业应用 • 全世界有数百种产品在应用 • 医疗器械 • 移动电话 • 路由器 • 工业控制 • GPS 导航系统 • 智能仪器 • 更多…

  6. μC/OS-II特点 • 提供源代码:购买作者撰写的《嵌入式实时操作系统μC/OS-II》一书即可获得μC/OS-II V2.52版本的所有源代码,购买此书的其它版本可以获得相应版本的全部源代码。 • 可移植性(portable):μC/OS-II的源代码绝大部分是使用移植性很强的ANSI C写的,将与微处理器硬件相关的汇编语言使用量压缩到最低的限度,以使μC/OS-II便于移植到其它微处理器上。目前,μC/OS-II已经被移植到多种不同架构的微处理器上。 • 可固化(ROMmable):只要具备合适的软硬件工具,就可以将μC/OS-II嵌入到产品中成为产品的一部分。 • 可剪裁(scalable):μC/OS-II使用条件编译实现可剪裁,用户程序可以只编译自己需要的(μC/OS-II的)功能,而不编译不要需要的功能,以减少μC/OS-II对代码空间和数据空间的占用。 • 可剥夺(preemptive):μC/OS-II是完全可剥夺型的实时内核,μC/OS-II总是运行就绪条件下优先级最高的任务。

  7. μC/OS-II特点 • 多任务:μC/OS-II可以管理64个任务,然而,μC/OS-II的作者建议用户保留8个给μC/OS-II。这样,留给用户的应用程序最多可有56个任务。 • 可确定性:绝大多数μC/OS-II的函数调用和服务的执行时间具有确定性,也就是说,用户总是能知道μC/OS-II的函数调用与服务执行了多长时间。 • 任务栈:μC/OS-II的每个任务都有自己单独的栈,使用μC/OS-II的占空间校验函数,可确定每个任务到底需要多少栈空间。 • 系统服务:μC/OS-II提供很多系统服务,例如信号量、互斥信号量、时间标志、消息邮箱、消息队列、块大小固定的内存的申请与释放及时间管理函数等。 • 中断管理:中断可以使正在执行的任务暂时挂起,如果优先级更高的任务被中断唤醒,则高优先级的任务在中断嵌套全部退出后立即执行,中断嵌套层数可达255层。

  8. μC/OS-II特点 • 稳定性与可靠性:μC/OS-II是μC/OS的升级版,μC/OS自1992年以来已经有数百个商业应用。μC/OS-II与μC/OS的内核是一样的,只是提供了更多的功能。2000年7月,μC/OS-II在一个航空项目中得到了美国联邦航空管理局对商用飞机的、符合RTCA DO – 178B标准的认证。这表明,该操作系统的质量得到了认证,可以在任何应用中使用。 作者及其著作

  9. 第7章 目录 • 1. μC/OS-II简介 • 2.移植规划 • 3.移植μC/OS-II • 4.移植代码应用到LPC2000

  10. 7.2 移植规划 • 概述 所谓“移植”,就是使一个实时内核能在其它的微处理器或微控制器上运行。 尽管大部分μC/OS-II的代码是用C语言编写的,但是在编写与处理器硬件相关的代码时还是不得不使用汇编语言。移植的主要工作就是编写这些与处理器硬件相关的代码。 在移植前针对所使用的微处理器进行规划,主要有以下几个方面的考虑:编译器的选择、任务模式的选择和支持的指令集。

  11. 应用程序 (用户代码) μC/OS-II (与处理器无关代码) OS_CORE.c OS_FLAG.c ... μC/OS-II配置 (与应用无关) OS_CFG.H INCLUDES.H μC/OS-II移植 (与处理器相关代码) 软件 硬件 CPU 定时器 • μC/OS-II移植 ——μC/OS-II硬件软件体系结构 移植时需要编写的代码 用于产生系统时钟

  12. 7.2 移植规划 • 编译器的选择 目前,针对ARM处理器核的C语言编译器有很多,如SDT、ADS、IAR、TASKING和GCC等。据了解,目前在国内最流行的是ADS、SDT和GCC。SDT和ADS均为ARM公司自己开发,ADS为SDT的升级版,以后ARM公司不再支持SDT,所以不会选择SDT。GCC虽然支持广泛,很多开发套件使用它作为编译器,但是与ADS比较其编译效率较低,这对充分发挥芯片性能很不利,所以最终使用ADS编译程序和调试。

  13. 7.2 移植规划 • 任务模式的取舍 ARM7处理器核具有用户、系统、管理、中止、未定义、中断和快中断七种模式,其中除用户模式外其它均为特权模式。其中管理、中止、未定义、中断和快中断模式与相应异常相联系,任务使用这些模式不太适合。而系统模式除了是特权模式外,其它与用户模式一样,因而可选为任务使用的模式只有用户模式和系统模式。为了尽量减少任务代码错误对整个程序的影响,缺省的任务模式定为用户模式,可选为系统模式,同时提供接口使任务可以在这两种模式间切换。

  14. 7.2 移植规划 • 支持的指令集 带T变量的ARM7处理器核具有两个指令集(可以参考第3章 ):标准32位ARM指令集和16位Thumb指令集,两种指令集有不同的应用范围。为了最大限度地支持芯片的特性,任务应当可以使用任意一个指令集并可以自由切换,而且不同的任务应当可以使用不同的指令集,这次移植的代码已经实现了这一点。

  15. 第7章 目录 • 1. μC/OS-II简介 • 2.移植规划 • 3.移植μC/OS-II • 4.移植代码应用到LPC2000

  16. 7.3 移植μC/OS-II • 概述 • 要移植一个操作系统到一个特定的CPU体系结构上并不是一件很容易的事情,它对移植者有以下要求: • 1. 对目标体系结构要有很深了解; • 2. 对OS原理要有较深入的了解; • 3. 对所使用的编译器要有较深入的了解; • 4. 对需要移植的操作系统要有相当的了解; • 5. 对具体使用的芯片也要一定的了解。

  17. 参考ARM公司的《ARM体系结构》文档 参考《嵌入式实时操作系统μC/OS-II》一书 参考ADS软件自带的编译器和连接器手册 参考《嵌入式实时操作系统μC/OS-II 》一书 参考具体芯片的数据手册和使用手册 • 概述 • 要移植一个操作系统到一个特定的CPU体系结构上并不是一件很容易的事情,它对移植者有以下要求: • 1. 对目标体系结构要有很深了解; • 2. 对OS原理要有较深入的了解; • 3. 对所使用的编译器要有较深入的了解; • 4. 对需要移植的操作系统要有相当的了解; • 5. 对具体使用的芯片也要一定的了解。 因为第4点的影响是全局性的,它决定移植代码的框架和功能。 所以重点介绍第4点。

  18. 概述 ——移植需要编写的文件 • 根据μC/OS-II的要求,移植μC/OS-II到一个新的体系结构上需要提供2个或3个文件: • OS_CPU.H(C语言头文件) • OS_CPU_C.C(C程序源文件) • OS_CPU_A.ASM(汇编程序源文件) • 其中OS_CPU_A.ASM在某些情况下不需要,但极其罕见。不需要OS_CPU_A.ASM的必须满足以下苛刻条件,而同时满足这些条件的微控制器几乎没有: • 1.可以直接使用C语言开关中断; • 2.可以直接使用C语言编写中断服务程序; • 3.可以直接使用C语言操作堆栈指针; • 4.可以直接使用C语言保存CPU的所有寄存器。

  19. 概述 ——移植代码包括的内容 实际上,还有一个文件很重要,它就是IRQ.INC,它定义了一个汇编宏,它是μC/OS-II for ARM7通用的中断服务程序的汇编与C函数接口代码。时钟节拍中断服务程序也没有移植,因为其与芯片和应用都强烈相关,需要用户自己编写,不过可以通过IRQ.INC简化用户代码的编写。

  20. congfig.h Includes.h UCOS内核文件 用户程序 • 关于头文件includes.h和config.h μC/OS-II要求所有.C文件的都要包含都文件includes.h,这样使得用户项目中的每个.C文件不用分别去考虑它实际上需要哪些头文件。使用INCLUDES.H的缺点是它可能会包含一些实际不相关的头文件,这意味着每个文件的编译时间可能会增加,但却增强了代码的可移植性。 在本移植中另外增加了一个头文件config.h,我们要求所有用户程序必须包含config.h,在config.h中包含includes.h和特定的头文件和配置项。而μC/OS-II的系统文件依然只是包含includes.h,即μC/OS-II的系统文件完全不必改动。所有的配置改变包括头文件的增减均在config.h中进行,而includes.h定下来后不必改动(μC/OS-II的系统文件需要包含的东西是固定的)。这样,μC/OS-II的系统文件需要编译的次数大大减少,编译时间随之减少。

  21. 编写OS_CPU.H ——不依赖于编译的数据类型 μCOS-II不使用C语言中的short、int、long等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。代之以移植性强的整数数据类型,这样,既直观又可移植,不过这就成了必须移植的代码。根据ADS编译器的特性,这些代码如程序清单7.1所示。 typedef unsigned char BOOLEAN; typedef unsigned char INT8U; typedef signed char INT8S; typedef unsigned short INT16U; typedef signed short INT16S; typedef unsigned int INT32U; typedef signed int INT32S; typedef float FP32; typedef double FP64; typedef INT32U OS_STK;

  22. ARM指令集 Thumb指令集 用户模式 系统模式 • 编写OS_CPU.H ——使用软中断SWI作底层接口 μCOS-II运行时,处理器可能处于的状态如下图所示: ARM指令 用户模式 Thumb指令 用户模式 用户任务使用的处理器模式 ARM指令 系统模式 Thumb指令 系统模式 ARM7内核具有的指令集

  23. 编写OS_CPU.H ——使用软中断SWI作底层接口 为了使底层接口函数与处理器状态无关,同时在任务调用相应的函数不需要知道函数位置,本移植使用软中断指令SWI作为底层接口,使用不同的功能号区分不同的函数。软中断功能号分配如下表所示,未列出的为保留功能。

  24. 该调用不返回参数 程序中调用软中断时使用的函数名 软件中断号 • 编写OS_CPU.H ——使用软中断SWI作底层接口 用软中断作为操作系统的底层接口就需要在C语言中使用SWI指令。在ADS中,有一个关键字__swi,用它声明一个不存在的函数,则调用这个函数就在调用这个函数的地方插入一条SWI指令,并且可以指定功能号。同时,这个函数也可以有参数和返回值,其传递规则与一般函数一样。 /* 任务级任务切换函数 */ __swi(0x00) void OS_TASK_SW(void); /* 运行优先级最高的任务 */ __swi(0x01) void _OSStartHighRdy(void); /* 关中断 */ __swi(0x02) void OS_ENTER_CRITICAL(void); /* 开中断 */ __swi(0x03) void OS_EXIT_CRITICAL(void); /* 任务切换到系统模式 */ __swi(0x80) void ChangeToSYSMode(void); /* 任务切换到用户模式 */ __swi(0x81) void ChangeToUSRMode(void); /* 任务代码是ARM代码 */ __swi(0x82) void TaskIsARM(INT8U prio); /* 任务代码是THUMB代码 */ __swi(0x83) void TaskIsTHUMB(INT8U prio);

  25. 编写OS_CPU.H ——堆栈生长方式 μCOS-II使用结构常量OS_STK_GROWTH中指定堆栈的生长方式: 置OS_STK_GROWTH为0表示堆栈从下往上长。 置OS_STK_GROWTH为1表示堆栈从上往下长。 虽然ARM处理器核对于两种方式均支持,但ADS的C语言编译器仅支持一种方式,即从上往下长,并且必须是满递减堆栈,所以OS_STK_GROWTH的值为1。 #define OS_STK_GROWTH 1

  26. 栈底 任务入栈的 其它数据 PC 任务环境开始 LR R12 R11 R10 R9 R8 ... R2 R1 R0 SP OSEnterSum 空闲空间 • 编写Os_cpu_c.c ——OSTaskStkInit( ) 该函数用于初始化任务堆栈,使任务的堆栈看起来就像刚发生中断一样。即任务被执行时,就像从中断返回一样。 在编写此函数之前,必须先确定任务的堆栈结构。而任务的堆栈结构是与CPU的体系结构、编译器有密切的关联。本移植的堆栈结构如下图所示。

  27. 编写Os_cpu_c.c ——OSTaskStkInit( ) OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { OS_STK *stk; opt = opt; stk = ptos; *stk = (OS_STK) task; *--stk = (OS_STK) task; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = (unsigned int) pdata; *--stk = (USER_USING_MODE|0x00); *--stk = 0; return (stk); } 入栈的数据

  28. 编写Os_cpu_c.c ——OSTaskStkInit( ) OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { OS_STK *stk; opt = opt; stk = ptos; *stk = (OS_STK) task; *--stk = (OS_STK) task; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = 0; *--stk = (unsigned int) pdata; *--stk = (USER_USING_MODE|0x00); *--stk = 0; return (stk); } 该数据比较特别,它用于保存该任务关中断的次数,它在调用OS_ENTER_CRITICAL( )时加1,在调用OS_EXIT_CRITICAL( )时减1。 这样每个任务都可以独立控制本任务的中断允许状态,而不会影响其它任务的中断允许状态。因此关中断和开中断就可以嵌套。

  29. 编写Os_cpu_c.c ——软件中断异常服务程序 前面介绍过,操作系统与硬件相关的底层函数使用软件中断作为接口,如下表所示。移植代码中一个重要的工作就是为这些软件中断编写服务程序。

  30. 这两个软件中断使用汇编代码完成 • 编写Os_cpu_c.c ——软件中断异常服务程序 void SWI_Exception(int SWI_Num, int *Regs) { OS_TCB *ptcb; switch(SWI_Num) { case 0x02: // 关中断 ... case 0x03: // 开中断 ... case 0x80: // 任务切换到系统模式 ... case 0x81: // 任务切换到用户模式 ... case 0x82: // 任务代码是ARM代码 ... case 0x83: // 任务代码是Thumb代码 ... default: } }

  31. μC/OS-II的启动多任务环境的函数叫做OSStart(),用户在调用OSStart()之前,必须已经建立了一个或更多任务。OSStart()最终调用函数OSStartHighRdy()运行多任务启动前优先级最高的任务。 void OSStartHighRdy(void) { _OSStartHighRdy(); } 该函数在Os_cpu_a.s文件中实现。 • 编写Os_cpu_c.c ——软件中断异常服务程序

  32. 关中断和开中断是为了保护临界段代码。这些代码与处理器有关,是需要移植的代码。在ARM处理器核中关中断和开中断时通过改变程序状态寄存器CPSR中的相应控制位实现。由于使用了软件中断,程序状态寄存器CPSR保存到程序状态保存寄存器SPSR中,软件中断退出时会将SPSR恢复到CPSR中,所以程序只要改变程序状态保存寄存器SPSR中的相应的控制位就可以了。 • 编写Os_cpu_c.c ——软件中断异常服务程序

  33. 每关闭一次中断,中断关闭计数器加1 每调用一次开中断函数,该计数器减1,为0时允许打开中断 • 编写Os_cpu_c.c ——软件中断异常服务程序 void SWI_Exception(int SWI_Num, int *Regs) { ... case 0x02: // 关中断 __asm { MRS R0,SPSR ORR R0,R0,#NoInt MSR SPSR_c,R0 } OsEnterSum++; break; case 0x03: // 开中断 if (--OsEnterSum == 0) { __asm { MRS R0,SPSR BIC R0,R0,#NoInt MSR SPSR_c,R0 } } break; ... 关闭中断 打开中断

  34. 它们可以在任何情况下使用。它们改变程序状态保留寄存器SPSR的相应位段,而程序状态保留寄存器会在软件中断退出时复制到程序状态寄存器CPSR,任务的处理器模式就改变了。它们可以在任何情况下使用。它们改变程序状态保留寄存器SPSR的相应位段,而程序状态保留寄存器会在软件中断退出时复制到程序状态寄存器CPSR,任务的处理器模式就改变了。 • 编写Os_cpu_c.c ——软件中断异常服务程序

  35. 使用内嵌汇编将处理器模式切换到用户模式 使用内嵌汇编将处理器模式切换到系统模式 • 编写Os_cpu_c.c ——软件中断异常服务程序 void SWI_Exception(int SWI_Num, int *Regs) { ... case 0x80: // 任务切换到系统模式 __asm { MRS R0,SPSR BIC R0,R0,#0x1f ORR R0,R0,#SYS32Mode MSR SPSR_c,R0 } break; case 0x81: // 任务切换到用户模式 __asm { MRS R0,SPSR BIC R0,R0,#0x1f ORR R0,R0,#USR32Mode MSR SPSR_c,R0 } break; ...

  36. 任务可以使用ARM的两种指令集的任意一种运行,但是任务建立时默认的只是一种指令集。如果任务使用的第一条指令与默认的指令集不同,则程序运行错误。所以增加两个函数TaskIsARM()和TaskIsTHUMB()用于改变任务建立时默认的指令集。任务可以使用ARM的两种指令集的任意一种运行,但是任务建立时默认的只是一种指令集。如果任务使用的第一条指令与默认的指令集不同,则程序运行错误。所以增加两个函数TaskIsARM()和TaskIsTHUMB()用于改变任务建立时默认的指令集。 它们都有唯一的参数:需要改变的任务的优先级,值得注意的是,这两个函数必须在相应的任务建立后但还没有运行时调用。 • 编写Os_cpu_c.c ——软件中断异常服务程序

  37. 修改任务堆栈中CPSR的值,在任务重新运行时,切换到ARM指令方式修改任务堆栈中CPSR的值,在任务重新运行时,切换到ARM指令方式 修改任务堆栈中CPSR的值,在任务重新运行时,切换到Thumb指令方式 被设置的任务号一定不能大于最低优先级任务 获取指定任务的任务控制块的地址 • 编写Os_cpu_c.c ——软件中断异常服务程序 ... case 0x82: // 任务代码是ARM代码 if (Regs[0] <= OS_LOWEST_PRIO) { ptcb = OSTCBPrioTbl[Regs[0]]; if (ptcb != NULL) { ptcb -> OSTCBStkPtr[1] &= ~(1 << 5); } } break; case 0x83: // 任务代码是Thumb代码 if (Regs[0] <= OS_LOWEST_PRIO) { ptcb = OSTCBPrioTbl[Regs[0]]; if (ptcb != NULL) { ptcb -> OSTCBStkPtr[1] |= (1 << 5); } } break; ...

  38. 编写Os_cpu_c.c ——软件中断异常服务程序 注意: 这两个函数必须在相应的任务建立后但还没有运行时调用。 如果在低优先级的任务中创建高优先级的任务就十分危险了。此时,解决的方法有三种: (1)高优先级任务使用默认的指令集; (2)改变函数OSTaskCreateHook()使任务默认不是处于就绪状态,建立任务后调用函数OSTaskResume()来使任务进入就绪状态; (3)建立任务时禁止任务切换,调用函数TaskIsARM()或TaskIsTHUMB()后再允许任务切换。

  39. 编写Os_cpu_c.c ——…Hook( )函数 在Os_cpu_c.c文件中还有许多钩子函数,它们在某个特定的系统动作时被调用,允许执行函数中的用户代码。这些函数默认是空函数,用户根据实际情况添加相关代码。它们分别如下表所示。

  40. 7.3 移植μC/OS-II • 编写Os_cpu_a.s 在Os_cpu_a.s文件中有软件中断的汇编接口程序、任务切换程序、OS启动时运行就绪最高优先级任务的程序。

  41. 保护被中断的现场 将要用到的寄存器压栈 判断中断前的指令集类型 ARM 从产生软件中断的指令中提取 中断号 Thumb 根据不同的中断号执行不同的程序 返回被中断的任务 • 编写Os_cpu_a.s ——软件中断汇编接口 在调用软中断之后,处理器切换到ARM指令和管理模式下工作。在执行软件中断服务函数之前,要提取中断号和其它入口参数,这些通过软件中断接口程序完成。 SoftwareInterrupt LDR SP, StackSvc STMFD SP!, {R0-R3, R12, LR} MOV R1, SP MRS R3, SPSR TST R3, #T_bit LDRNEH R0, [LR,#-2] BICNE R0, R0, #0xff00 LDREQ R0, [LR,#-4] BICEQ R0, R0, #0xFF000000 CMP R0, #1 LDRLO PC, =OSIntCtxSw LDREQ PC, =__OSStartHighRdy BL SWI_Exception LDMFD SP!, {R0-R3, R12, PC}^

  42. 编写Os_cpu_a.s ——任务切换代码 μCOS-II是抢占式实时操作系统,得到运行的始终是就绪条件下最高优先级的任务。当处于运行状态的任务因为某种脱离就绪态,或者有其它更高优先级的任务进入就绪态,那么操作系统内核就要运行别的就绪任务,这时需要进行任务切换。 任务切换可能发生的情况有两种: 1.当前运行的任务主动交出CPU控制权,通常发生在等待某个事件或是调用系统延时。调用函数OS_TASK_SW( ); 2.发生中断,使更高优先级的任务进入就绪状态,内核剥夺当前任务的运行资格。即发生在中断退出时。调用函数OSIntCtxSw( )。

  43. 栈底 任务入栈的 其它数据 LR 任务环境开始 R12 R3 R2 R1 R0 SP 空闲空间 • 编写Os_cpu_a.s ——任务切换代码 虽然OS_TASK_SW( )和OSIntCtxSw( )的执行条件不同,但是它们的功能相同,只要稍作处理就可以它们共用一段任务切换代码。这些处理就是保证在执行任务切换前两者的任务现场是一致的。共同执行的任务切换代码是“OSIntCtxSw” 其中OS_TASK_SW( )是通过软件中断0完成的,通过前面的分析,可以知道执行任务切换时的现场环境如下所示,同时R3中保存着SPSR,它是任务中断前CPSR的备份。

  44. 保存当前任务的 寄存器组及其它 修改当前任务的TCB堆栈指针 用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur 恢复新任务的寄存器组及其它 运行新任务 • 编写Os_cpu_a.s ——OSIntCtxSw 流程图

  45. 流程图 PC LR R12 R11 R10 修改当前任务的TCB堆栈指针 R9 恢复之前的模式 读出压栈的数据 R8 R7 R6 用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur R5 切换至系统模式获取任务运行时的各寄存器并将它们压栈 R4 R3 R2 R1 恢复新任务的寄存器组及其它 R0 CPSR OSEnterSum 运行新任务 • 编写Os_cpu_a.s ——OSIntCtxSw OSIntCtxSw ;下面为保存任务环境 LDR R2, [SP, #20] ;获取PC LDR R12, [SP, #16] ;获取R12 MRS R0, CPSR ;保存LR,PC及R4-R12 MSR CPSR_c, #(NoInt | SYS32Mode) MOV R1, LR STMFD SP!, {R1-R2} STMFD SP!, {R4-R12} ;获取R0-R3,并出栈R12和PC寄存器 MSR CPSR_c, R0 LDMFD SP!, {R4-R7} ADD SP, SP, #8 ;保存R0-R3 MSR CPSR_c, #(NoInt | SYS32Mode) STMFD SP!, {R4-R7} ;获取OsEnterSum,并保存CPSR,OsEnterSum LDR R1, =OsEnterSum LDR R2, [R1] STMFD SP!, {R2, R3} ... 保存当前任务的 寄存器组及其它 任务环境保存结束后的栈结构

  46. 保存当前任务的 寄存器组及其它 恢复新任务的寄存器组及其它 运行新任务 • 编写Os_cpu_a.s ——OSIntCtxSw 修改当前任务的TCB堆栈指针 用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur

  47. 用即将运行任务的优先级更新OSPrioCur变量 用即将运行任务的TCB指针更新OSTCBCur变量 • 编写Os_cpu_a.s ——OSIntCtxSw OSIntCtxSw ... ;保存当前任务堆栈指针到当前任务的TCB LDR R1, =OSTCBCur LDR R1, [R1] STR SP, [R1] BL STaskSwHook ;调用钩子函数 ;OSPrioCur <= OSPrioHighRdy LDR R4, =OSPrioCur LDR R5, =OSPrioHighRdy LDRB R6, [R5] STRB R6, [R4] ;OSTCBCur <= OSTCBHighRdy LDR R6, =OSTCBHighRdy LDR R6, [R6] LDR R4, =OSTCBCur STR R6, [R4] ...

  48. 保存当前任务的 寄存器组及其它 修改当前任务的TCB堆栈指针 用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur • 编写Os_cpu_a.s ——OSIntCtxSw 恢复新任务的寄存器组及其它 运行新任务

  49. 恢复新任务的各寄存器 运行新任务 • 编写Os_cpu_a.s ——OSIntCtxSw OSIntCtxSw ... OSIntCtxSw_1 ;从R6指向TCB中获取新任务堆栈指针 LDR R4, [R6] ;调整堆栈指针 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP ADD SP, R4, #68 LDR LR, [SP, #-8] ;进入管理模式,恢复任务的各寄存器和变量 MSR CPSR_c, #(NoInt | SVC32Mode) MOV SP, R4 ;设置堆栈指针 ;获取CPSR和OsEnterSum LDMFD SP!, {R4, R5} ;恢复新任务的OsEnterSum LDR R3, =OsEnterSum STR R4, [R3] ;恢复CPSR MSR SPSR_cxsf, R5 ;运行新任务 LDMFD SP!, {R0-R12, LR, PC }^ 这段代码还被__OSStartHighRdy 函数调用,用于启动最高优先级的就绪任务

  50. 编写Os_cpu_a.s ——OSStartHighRdy μC/OS-II的多任务环境由函数OSStart( ) 启动。用户在调用该函数之前,必须已经建立了一个或更多任务。OSStart()最终调用函数OSStartHighRdy( )运行多任务启动前优先级最高的任务,而它最终是调用__OSStartHighRdy实现的,其代码如下所示: __OSStartHighRdy MSR CPSR_c, #(NoInt | SYS32Mode) ;告诉uC/OS-II自身已经运行 LDR R4, =OSRunning MOV R5, #1 STRB R5, [R4] ;调用钩子函数 BL OSTaskSwHook LDR R6, =OSTCBHighRdy ;取得新任务的TCB指针 LDR R6, [R6] B OSIntCtxSw_1

More Related