Download presentation
Presentation is loading. Please wait.
1
嵌入式系统 —RTEOS μC/OS-II 的移植
2006年6月9日
2
主要内容 移植规划 μC/OS-II的移植 嵌入式系统的初始化
3
移植规划---概述 尽管大部分μC/OS-II的代码是用C语言编写的,但是在编写与处理器硬件相关的代码时还是不得不使用汇编语言。
所谓“移植”,就是使一个实时内核能在其它的微处理器或微控制器上运行。 尽管大部分μC/OS-II的代码是用C语言编写的,但是在编写与处理器硬件相关的代码时还是不得不使用汇编语言。 移植的主要工作就是编写这些与处理器硬件相关的代码。 操作系统的移植大体可以分为两个层次: 跨体系结构的移植 针对特定处理器的移植
4
移植规划 在移植前针对所使用的微处理器进行规划,主要有以下几个方面的考虑: 编译器的选择 任务模式的选择 支持的指令集
5
移植规划(续) 编译器的选择 针对ARM处理器核的C语言编译器有很多,如SDT、 ADS、IAR、TASKING和GCC等
目前在国内最流行的是ADS、SDT和GCC SDT和ADS均为ARM公司自己开发,ADS为SDT的升级版,以后ARM公司不再支持SDT,故不选择SDT。GCC虽然支持广泛,很多开发套件使用它作为编译器,但是与ADS比较其编译效率较低,这对充分发挥芯片性能不利 考虑使用ADS编译程序和调试
6
ARM的工作模式 ARM处理器有7种操作模式: 用户模式(usr) - 正常的程序执行模式
快速中断模式(fiq) - 支持高速数据传输或通道处理 中断模式(irq) - 用于通用中断处理 管理员模式(svc) - 操作系统的保护模式. 中止模式(abt) - 支持虚拟内存和/或内存保护等异常 系统模式(sys) - 支持操作系统的特殊用户模式(运行操作系统任务) 未定义模式(und) - 支持硬件协处理器的软件仿真 除了用户模式外,其他模式均可视为特权模式
7
移植规划(续) 任务模式的取舍 ARM7处理器核具有上述七种模式,其中除用户模式外其它均为特权模式。其中管理、中止、未定义、中断和快中断模式与相应异常相联系,任务使用这些模式不太适合。 系统模式除了是特权模式外,其它与用户模式一样,因而可选为任务使用的模式只有用户模式和系统模式。 为了尽量减少任务代码错误对整个程序的影响,缺省的任务模式定为用户模式,可选为系统模式,同时提供接口使任务可以在这两种模式间切换。
8
移植规划(续) 支持的指令集 带T变量的ARM7处理器核具有两个指令集: 标准32位ARM指令集 16位Thumb指令集
两种指令集有不同的应用范围。 为了最大限度地支持芯片的特性,任务应当可以使用任意一个指令集并可以自由切换,而且不同的任务应当可以使用不同的指令集
9
移植μC/OS-II 概述 1. 对目标体系结构要有很深了解; 2. 对OS原理要有较深入的了解; 3. 对所使用的编译器要有较深入的了解;
要移植一个操作系统到一个特定的CPU体系结构并不是一件很容易的事情,它对移植者有以下要求: 1. 对目标体系结构要有很深了解; 2. 对OS原理要有较深入的了解; 3. 对所使用的编译器要有较深入的了解; 4. 对需要移植的操作系统要有相当的了解; 5. 对具体使用的芯片也要一定的了解
10
概述 要移植一个操作系统到一个特定的CPU体系结构上并不是一件很容易的事情,它对移植者有以下要求: 1. 对目标体系结构要有很深了解;
2. 对OS原理要有较深入的了解; 3. 对所使用的编译器要有较深入的了解; 4. 对需要移植的操作系统要有相当的了解; 5. 对具体使用的芯片也要一定的了解。 参考ARM公司的《ARM体系结构》文档 要移植一个操作系统到一个特定的CPU体系结构上并不是一件很容易的事情,它对移植者有以下要求: 1. 对目标体系结构要有很深了解; 2. 对OS原理要有较深入的了解; 3. 对所使用的编译器要有较深入的了解; 4. 对需要移植的操作系统要有相当的了解; 5. 对具体使用的芯片也要一定的了解。 参考《嵌入式实时操作系统μC/OS-II 》一书 参考ADS软件自带的编译器和连接器手册 参考《嵌入式实时操作系统μC/OS-II 》一书 因为第4点的影响是全局性的,它决定移植代码的框架和功能。 所以重点介绍第4点。 参考具体芯片的数据手册和使用手册
11
主要内容 移植规划 μC/OS-II的移植 嵌入式系统的初始化
12
C/OS-II的文件结构
13
μC/OS-II移植 ——μC/OS-II硬件软件体系结构 应用程序 (用户代码) μC/OS-II (与处理器无关代码)
OS_CORE.c OS_FLAG.c ... μC/OS-II配置 (与应用相关) OS_CFG.H INCLUDES.H μC/OS-II移植 (与处理器相关代码) CPU 定时器 硬件 软件 移植时需要编写的代码 用于产生系统时钟
14
移植C/OS-II满足的条件 处理器的C编译器能产生可重入代码 在程序中可以打开或者关闭中断
处理器支持中断,并且能产生定时中断(通常在10—100Hz之间) 处理器支持能够容纳一定量数据的硬件堆栈(通常是几千字节) 处理器有将堆栈指针和其他CPU寄存器的内容存储和读出到堆栈(或者内存)的指令
15
什么是可重入代码 可重入的代码指的是一段可以被多个任务同时调用,而不必担心会破坏数据的代码(比如:一个函数)
即:可重入型函数在任何时候都可以被中断执行,过一段时间以后又可以继续运行,而不会因为在函数中断的时候被其他的任务重新调用,而影响函数中的数据
16
可重入代码举例 程序1:可重入型函数 void swap(int *x, int *y) { int temp; temp=*x;
*x=*y; *y=temp; }
17
非可重入代码举例 程序2:非可重入型函数 int temp; void swap(int *x, int *y) { temp=*x;
*x=*y; *y=temp; }
18
不可重入函数被中断破坏
19
如何使函数具有可重入性 使Swap()函数具有可重入性的条件: 把Temp定义为局部变量 调用Swap()函数之前关中断,调用 后再开中断
用信号量禁止该函数在使用过程中 被再次调用
20
概述 ——移植需要编写的文件 根据μ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的所有寄存器。
21
概述 ——移植代码包括的主要内容 移植内容 类型 所属文件 描述 BOOLEAN、INT8U、INT8S、 … 数据类型 OS_CPU.H 与编译器无关的数据类型 OS_STK 堆栈的数据类型 OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL() 宏 开关中断的代码 OS_STK_GROWTH 常量 定义堆栈的增长方向 OS_TASK_SW 函数 任务切换时执行的代码 OSTaskStkInit() OS_CPU_C.C 任务堆栈初始化函数 OSInitHookBegin()、OSInitHookEnd()、 μC/OS-II在执行某些操作时调用的用户函数,一般为空 OSStartHighRdy() *OS_CPU_A.ASM 进入多任务环境时运行优先级最高的任务, OSIntCtxSw() 中断退出时的任务切换函数 OSTickISR() 中断服务程序 时钟节拍中断服务程序 实际上,还有一个文件很重要,它就是IRQ.INC,它定义了一个汇编宏,它是μC/OS-II for ARM7通用的中断服务程序的汇编与C函数接口代码。时钟节拍中断服务程序也没有移植,因为其与芯片和应用都强烈相关,需要用户自己编写,不过可以通过IRQ.INC简化用户代码的编写。
22
关于头文件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的系统文件需要编译的次数大大减少,编译时间随之减少。 congfig.h UC/OS内核文件 Includes.h 用户程序
23
设置与处理器和编译器相关的代码 OS_CPU.H中定义了与编译器相关的数据类型。比如:INT8U、INT8S等。
与 ARM处理器相关的代码,使用OS_ENTER_CRITICAL() 和OS_EXIT_CRITICAL() 宏开启/关闭中断 设置堆栈的增长方向 :堆栈由高地址向低地址增长
24
编写OS_CPU.H ——堆栈生长方式 μC/OS-II使用结构常量OS_STK_GROWTH中指定堆栈的生长方式:
虽然ARM处理器核对于两种方式均支持,但ADS的C语言编译器仅支持一种方式,即从上往下长,并且必须是满递减堆栈,所以OS_STK_GROWTH的值为1。 #define OS_STK_GROWTH 1
25
编写OS_CPU.H ——不依赖于编译的数据类型
μC/OS-II不使用C语言中的short、int、long等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。代之以移植性强的整数数据类型,这样,既直观又可移植,不过这就成了必须移植的代码。根据ADS编译器的特性,这些代码如下程序清单所示(与编译有关)。 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;
26
设置includes.h typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; typedef signed char INT8S; typedef unsigned int INT16U; typedef signed int INT16S; typedef unsigned long INT32U; typedef signed long INT32S; typedef float FP32; typedef double FP64; typedef unsigned long OS_STK; typedef unsigned long OS_CPU_SR; extern int INTS_OFF(void); extern void INTS_ON(void); #define OS_ENTER_CRITICAL() { cpu_sr = INTS_OFF(); } #define OS_EXIT_CRITICAL() { if(cpu_sr == 0) INTS_ON(); } #define OS_STK_GROWTH /*从高向低*/
27
程序状态寄存器(CPSR) 条件位: 中断禁止位: I = 1: 禁止 IRQ. N = 1-结果为负,0-结果为正或0
27 31 N Z C V Q 28 6 7 I F T mode 16 23 8 15 5 4 24 f s x c U n d e f i n e d J 条件位: N = 1-结果为负,0-结果为正或0 Z = 1-结果为0,0-结果不为0 C =1-进位,0-借位 V =1-结果溢出,0结果没溢出 Q 位: 仅ARM 5TE/J架构支持 指示增强型DSP指令是否溢出 J 位 J = 1: 处理器处于Jazelle状态 中断禁止位: I = 1: 禁止 IRQ. F = 1: 禁止 FIQ. T Bit 仅ARM xT架构支持 T = 0: 处理器处于 ARM 状态 T = 1: 处理器处于 Thumb 状态 Mode位(处理器模式位): 0b10000 User 0b10001 FIQ 0b10010 IRQ 0b10011 Supervisor 0b10111 Abort 0b11011 Undefined 0b11111 System
28
编写OS_CPU.H ——使用软中断SWI作底层接口 μC/OS-II运行时,处理器可能处于的模式如下图所示: ARM指令 用户模式
Thumb指令 用户模式 Thumb指令集 ARM指令集 用户模式 系统模式 用户任务使用的处理器模式 ARM指令 系统模式 Thumb指令 系统模式 ARM7内核具有的指令集
29
编写OS_CPU.H ——使用软中断SWI作底层接口
功能号 接口函数 简介 0x00 void OS_TASK_SW(void) 任务级任务切换函数 0x01 _OSStartHighRdy(void) 运行优先级最高的任务,由OSStartHighRdy产生 0x02 void OS_ENTER_CRITICAL(void) 关中断 0x03 Void OS_EXIT_CRITICAL(void) 开中断 0x80 Void ChangeToSYSMode(void) 任务切换到系统模式 0x81 Void ChangeToUSRMode(void) 任务切换到用户模式 0x82 Void TaskIsARM(INT8U prio) 任务代码是ARM代码 0x83 Void TaskIsTHUMB(INT8U prio) 任务代码是THUMB代码
30
编写OS_CPU.H ——使用软中断SWI作底层接口
用软中断作为操作系统的底层接口就需要在C语言中使用SWI(SoftWare Interrupt)指令。在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); 该调用不返回参数 程序中调用软中断时使用的函数名 软件中断号
31
编写OS_CPU_C.C μC/OS-II的移植要求用户编写10个C函数:
OSTaskStkInit(): OSTaskCreat()和OSTaskCreatExt()通过调用 本函数,初始化任务的栈结构 OSTaskCreateHook():每当添加任务时由OS_TCBInit( )函数调用 OSTaskDelHook(): 任务被删除后由OSTaskDel()调用 OSTaskSwHook(): 任务切换时两种情况均会调用该函数 OSTaskIdleHook():OSTaskIdle()函数可调用该函数实现CPU低功耗模式 OSTimeTickHook():本函数在每个时钟节拍都会被OSTimeTick()调用 OSInitHookBegin():进入OSInit()函数后本函数会立即被调用 OSInitHookEnd(): OSInit()函数返回之前被调用 OSTCBInitHook():OS_TCBInit( )在调用OSTaskCreateHook()之前将先 调用本函数 唯一必要的函数是OStaskStkInit(),其他9个函数必须声明,但不一定要包含任何代码
32
编写OS_CPU_C.C ——OSTaskStkInit( )
该函数用于初始化任务堆栈,使任务的堆栈看起来就像刚发生中断一样。即任务被执行时,就像从中断返回一样。 在编写此函数之前,必须先确定任务的堆栈结构。而任务的堆栈结构是与CPU的体系结构、编译器有密切的关联。本移植的堆栈结构如下图所示。 任务入栈的 其它数据 LR R12 R11 R10 R9 R8 ... 栈底 任务环境开始 SP PC R2 R1 R0 OSEnterSum 空闲空间
33
编写OS_CPU_C.C ——OSTaskStkInit( ) 入栈的数据 栈底 任务环境开始 SP
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 = (unsigned int) pdata; *--stk = (USER_USING_MODE|0x00); return (stk); } 入栈的数据 任务入栈的 其它数据 LR R12 R11 R10 R9 R8 ... 栈底 任务环境开始 SP PC R2 R1 R0 OSEnterSum 空闲空间
34
编写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 = (unsigned int) pdata; *--stk = (USER_USING_MODE|0x00); return (stk); } 该数据比较特别,它用于保存该任务关中断的次数,它在调用OS_ENTER_CRITICAL( )时加1,在调用OS_EXIT_CRITICAL( )时减1。 这样每个任务都可以独立控制本任务的中断允许状态,而不会影响其它任务的中断允许状态。因此关中断和开中断就可以嵌套。
35
编写OS_CPU_C.C ——软件中断异常服务程序 操作系统与硬件相关的底层函数使用软件中断作为接口,如下表所示。
移植代码中一个重要的工作就是为这些软件中断编写服务程序 功能号 接口函数 简介 0x00 void OS_TASK_SW(void) 任务级任务切换函数 0x01 _OSStartHighRdy(void) 运行优先级最高的任务,由OSStartHighRdy产生 0x02 void OS_ENTER_CRITICAL(void) 关中断 0x03 Void OS_EXIT_CRITICAL(void) 开中断 0x80 Void ChangeToSYSMode(void) 任务切换到系统模式 0x81 Void ChangeToUSRMode(void) 任务切换到用户模式 0x82 Void TaskIsARM(INT8U prio) 任务代码是ARM代码 0x83 Void TaskIsTHUMB(INT8U prio) 任务代码是THUMB代码
36
编写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: } 功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码 这两个软件中断使用汇编代码完成 功能号 接口函数 简介 0x00 void OS_TASK_SW(void) 任务级任务切换函数 0x01 _OSStartHighRdy(void) 运行优先级最高的任务,由OSStartHighRdy产生 0x02 void OS_ENTER_CRITICAL(void) 关中断 0x03 Void OS_EXIT_CRITICAL(void) 开中断 0x80 Void ChangeToSYSMode(void) 任务切换到系统模式 0x81 Void ChangeToUSRMode(void) 任务切换到用户模式 0x82 Void TaskIsARM(INT8U prio) 任务代码是ARM代码 0x83 Void TaskIsTHUMB(INT8U prio) 任务代码是THUMB代码
37
编写OS_CPU_C.C ——软件中断异常服务程序
μC/OS-II的启动多任务环境的函数叫做OSStart(),用户在调用OSStart()之前,必须已经建立了一个或更多任务。OSStart()最终调用函数OSStartHighRdy()运行多任务启动前优先级最高的任务。 void OSStartHighRdy(void) { _OSStartHighRdy(); } 该函数在Os_cpu_a.s文件中实现。 功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码
38
编写OS_CPU_C.C ——软件中断异常服务程序
关中断和开中断是为了保护临界段代码。这些代码与处理器有关,是需要移植的代码。在ARM处理器核中关中断和开中断时通过改变程序状态寄存器CPSR中的相应控制位实现。由于使用了软件中断,程序状态寄存器CPSR保存到程序状态保存寄存器SPSR中,软件中断退出时会将SPSR恢复到CPSR中,所以程序只要改变程序状态保存寄存器SPSR中的相应的控制位就可以了。 功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码
39
编写OS_CPU_C.C ——软件中断异常服务程序 关闭中断 每关闭一次中断,中断关闭计数器加1 打开中断
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 功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码 关闭中断 每关闭一次中断,中断关闭计数器加1 每调用一次开中断函数,该计数器减1,为0时允许打开中断 打开中断
40
编写OS_CPU_C.C ——软件中断异常服务程序
功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码 它们可以在任何情况下使用。它们改变程序状态保留寄存器SPSR的相应位段,而程序状态保留寄存器会在软件中断退出时复制到程序状态寄存器CPSR,任务的处理器模式就改变了。
41
编写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: // 任务切换到用户模式 BIC R0,R0,#0x1f ORR R0,R0,#USR32Mode 功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码 使用内嵌汇编将处理器模式切换到系统模式 使用内嵌汇编将处理器模式切换到用户模式
42
编写OS_CPU_C.C ——软件中断异常服务程序
功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码 任务可以使用ARM的两种指令集的任意一种运行,但是任务建立时默认的只是一种指令集。如果任务使用的第一条指令与默认的指令集不同,则程序运行错误。所以增加两个函数TaskIsARM()和TaskIsTHUMB()用于改变任务建立时默认的指令集。 它们都有唯一的参数:需要改变的任务的优先级,值得注意的是,这两个函数必须在相应的任务建立后但还没有运行时调用。
43
编写OS_CPU_C.C ——软件中断异常服务程序 修改任务堆栈中CPSR的值,在任务重新运行时,切换到ARM指令方式
... 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代码 ptcb -> OSTCBStkPtr[1] |= (1 << 5); 被设置的任务号一定不能大于最低优先级任务 功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码 获取指定任务的任务控制块的地址 修改任务堆栈中CPSR的值,在任务重新运行时,切换到ARM指令方式 修改任务堆栈中CPSR的值,在任务重新运行时,切换到Thumb指令方式 CPSR:程序状态寄存器
44
编写OS_CPU_C.C ——软件中断异常服务程序 注意: 这两个函数必须在相应的任务建立后但还没有运行时调用。
功能号 简介 0x00 任务级任务切换函数 0x01 运行优先级最高的任务,由OSStartHighRdy产生 0x02 关中断 0x03 开中断 0x80 任务切换到系统模式 0x81 任务切换到用户模式 0x82 任务代码是ARM代码 0x83 任务代码是THUMB代码 注意: 这两个函数必须在相应的任务建立后但还没有运行时调用。 如果在低优先级的任务中创建高优先级的任务就十分危险了。此时,解决的方法有三种: (1)高优先级任务使用默认的指令集; (2)改变函数OSTaskCreateHook()使任务默认不是处于就绪状态,建立任务后调用函数OSTaskResume()来使任务进入就绪状态; (3)建立任务时禁止任务切换,调用函数 TaskIsARM()或TaskIsTHUMB()后再允许任务切换。
45
编写OS_CPU_C.C ——…Hook( )函数
函数名 被执行条件 OSInitHookBegin( ) 在开始执行OSInit( )函数时被执行 OSInitHookEnd( ) 在OSInit()函数结束时被执行 OSTaskCreateHook( ) 在任务建立时被调用 OSTaskDelHook( ) 在任务删除时被调用 OSTaskSwHook( ) 在进行任务切换时被调用 OSTaskStatHook( ) 被统计任务调用,每秒执行一次 OSTCBInitHook( ) 在建立所有的TCB后,由OSTCBInit函数调用 OSTimeTickHook( ) 每个时钟节拍产生后被调用 OSTaskIdleHook( ) 由空闲任务调用
46
移植μC/OS-II 编写OS_CPU_A.ASM 在OS_CPU_A.ASM文件中有: 软件中断的汇编接口程序 任务切换程序
47
OS_CPU_A.ASM μC/OS-II的移植要求用户编写4个汇编语言函数:
OSStartHighRdy() OSCtxSw() OSIntCtxSw() OSTickISR() 如果编译器支持插入行汇编代码,就可以将所有与处理器相关的代码放到OS_CPU_C.C文件中,而不必再有单独的汇编语言文件
48
启动多任务环境,运行就绪的最高优先级任务
编写OS_CPU_A.ASM ——软件中断汇编接口 在调用软中断之后,处理器切换到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}^ 保护被中断的现场 将要用到的寄存器压栈 判断中断前的指令集类型 从产生软件中断的指令中提取 中断号 ARM Thumb 中断号 执行程序 进行任务切换 1 启动多任务环境,运行就绪的最高优先级任务 其它 软件中断服务函数 根据不同的中断号执行不同的程序 返回被中断的任务
49
编写OS_CPU_A.ASM ——任务切换代码
μC/OS-II是抢占式实时操作系统,得到运行的始终是就绪条件下最高优先级的任务。当处于运行状态的任务因为某种原因进入就绪态,或者有其它更高优先级的任务进入就绪态,操作系统内核就要运行别的就绪任务,这时需要进行任务切换。
50
编写OS_CPU_A.ASM ——任务切换代码
任务切换可能发生的情况有两种: 1.当前运行的任务主动交出CPU控制权,通常发生在等待某个事件或是调用系统延时。调用函数OS_TASK_SW( ) 2.发生中断,使更高优先级的任务进入就绪状态,内核剥夺当前任务的运行资格。即发生在中断退出时。调用函数OSIntCtxSw( )
51
编写OS_CPU_A.ASM ——任务切换代码
虽然OS_TASK_SW( )和OSIntCtxSw( )的执行条件不同,但是它们的功能相同,只要稍作处理就可以它们共用一段任务切换代码。这些处理就是保证在执行任务切换前两者的任务现场是一致的。共同执行的任务切换代码是“OSIntCtxSw” 其中OS_TASK_SW( )是通过软件中断0完成的,通过前面的分析,可以知道执行任务切换时的现场环境如下所示,同时R3中保存着SPSR,它是任务中断前CPSR的备份。 任务入栈的 其它数据 LR R12 R3 R2 R1 R0 空闲空间 栈底 任务环境开始 SP SPSR:程序状态保留寄存器 CPSR:当前程序状态寄存器
52
用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur
编写OS_CPU_A.ASM ——OSIntCtxSw 流程图 保存当前任务的 寄存器组及其它 修改当前任务的TCB堆栈指针 用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur 恢复新任务的寄存器组及其它 运行新任务
53
编写OS_CPU_A.ASM ——OSIntCtxSw 流程图 保存当前任务的 寄存器组及其它 修改当前任务的TCB堆栈指针 恢复之前的模式
用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur 恢复新任务的寄存器组及其它 运行新任务 流程图 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 STMFD SP!, {R4-R7} ;获取OsEnterSum,并保存CPSR,OsEnterSum LDR R1, =OsEnterSum LDR R2, [R1] STMFD SP!, {R2, R3} ... PC LR R12 R11 R10 R9 R7 R5 R4 R3 R2 R1 R0 R6 R8 CPSR OSEnterSum 保存当前任务的 寄存器组及其它 切换至系统模式获取任务运行时的各寄存器并将它们压栈 恢复之前的模式 读出压栈的数据 任务环境保存结束后的栈结构
54
用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur
编写OS_CPU_A.ASM ——OSIntCtxSw 保存当前任务的 寄存器组及其它 恢复新任务的寄存器组及其它 运行新任务 修改当前任务的TCB堆栈指针 用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur
55
编写OS_CPU_A.ASM ——OSIntCtxSw ;保存当前任务堆栈指针到当前任务的TCB
... ;保存当前任务堆栈指针到当前任务的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] 用即将运行任务的TCB指针更新OSTCBCur变量 用即将运行任务的优先级更新OSPrioCur变量
56
用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur
编写OS_CPU_A.ASM ——OSIntCtxSw 保存当前任务的 寄存器组及其它 修改当前任务的TCB堆栈指针 用将要运行任务的优先级和TCB指针更新OSPrioHighRdy和OSTCBCur 恢复新任务的寄存器组及其它 运行新任务
57
这段代码还被__OSStartHighRdy
编写OS_CPU_A.ASM ——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, R ;设置堆栈指针 ;获取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 函数调用,用于启动最高优先级的就绪任务 运行新任务
58
编写OS_CPU_A.ASM ——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
59
编写OS_CPU_A.ASM 通过前面的分析,我们可以画出下面这张结构图: 中断退出时任务切换 任务级任务切换 启动多任务
xxx_Handler 任务级任务切换 OS_TASK_SW() 启动多任务 OSStartHighRdy() OSIntCtxSw _OSStartHighRdy OSIntCtxSw_1 OS_CPU_A.ASM文件
60
移植μC/OS-II 关于中断及时钟节拍 在本移植中,IRQ是受μC/OS-II管理的中断,而对于FIQ不做处理,这是为了提高FIQ的响应速度。由于各种ARM芯片的中断系统不一样,各个用户的目标板也不一样,对于中断和时钟节拍是需要进一步移植的代码。为此编写一个汇编宏,它是μC/OS-II for ARM7通用的中断服务程序的汇编与C函数接口代码。 注:在不受管理的中断服务程序中不能调用任何系统函数。
61
关于中断及时钟节拍 流程图 保存当前任务的 寄存器组 中断嵌套数加1 切换到系统模式 执行中断服务程序 关中断 执行OSIntExit( )
切换到IRQ模式 判断是否需要进行任务切换 切换任务或返回
62
关于中断及时钟节拍 流程图 保存当前任务的 寄存器组 中断嵌套数加1 切换到系统模式 执行中断服务程序 关中断 执行OSIntExit( )
切换到IRQ模式 判断是否需要进行任务切换 切换任务或返回
63
切换到系统模式,如果是第一次中断还要设置堆栈指针
关于中断及时钟节拍 MACRO $IRQ_Label HANDLER $IRQ_Exception_Function EXPORT $IRQ_Label ; 输出的标号 IMPORT $IRQ_Exception_Function ; 引用的外部标号 $IRQ_Label SUB LR, LR, # ; 计算返回地址 STMFD SP!, {R0-R3, R12, LR} ; 保存任务环境 MRS R3, SPSR ; 保存状态 ; 保存用户状态的R3,SP,LR,不能回写 STMFD SP, {R3, SP, LR}^ LDR R2, =OSIntNesting LDRB R1, [R2] ADD R1, R1, #1 STRB R1, [R2] SUB SP, SP, #4*3 ; 切换到系统模式 MSR CPSR_c, #(NoInt | SYS32Mode) CMP R1, #1 LDREQ SP, =StackUsr ... 压栈保存被中断任务的运行环境 中断嵌套层数加1 切换到系统模式,如果是第一次中断还要设置堆栈指针
64
关于中断及时钟节拍 流程图 中断服务程序 (ISR) 在ISR中可以打开中断实现中断嵌套 保存当前任务的 寄存器组 中断嵌套数加1
切换到系统模式 执行中断服务程序 关中断 执行OSIntExit( ) 在ISR中可以打开中断实现中断嵌套 切换到IRQ模式 判断是否需要进行任务切换 切换任务或返回
65
使OSEnterSum为1,防止在OSIntExit函数中打开中断
关于中断及时钟节拍 MACRO $IRQ_Label HANDLER $IRQ_Exception_Function EXPORT $IRQ_Label ; 输出的标号 IMPORT $IRQ_Exception_Function ; 引用的外部标号 $IRQ_Label ... BL $IRQ_Exception_Function ; 切换到系统模式 MSR CPSR_c, #(NoInt | SYS32Mode) ; OsEnterSum,使OSIntExit退出时中断关闭 LDR R2, =OsEnterSum MOV R1, #1 STR R1, [R2] BL OSIntExit ; 因为中断服务程序要退出,所以OsEnterSum=0 MOV R1, #0 ; 切换回irq模式,并恢复用户状态的R3,SP,LR MSR CPSR_c, #(NoInt | IRQ32Mode) LDMFD SP, {R3, SP, LR}^ ; 注意不能回写 执行中断服务程序(ISR) 关闭中断 使OSEnterSum为1,防止在OSIntExit函数中打开中断 通知系统,中断退出 使OSEnterSum为0,运行中断打开 恢复任务的R3、SP和LR
66
关于中断及时钟节拍 流程图 保存当前任务的 寄存器组 中断嵌套数加1 切换到系统模式 执行中断服务程序 关中断 执行OSIntExit( )
切换到IRQ模式 判断是否需要进行任务切换 切换任务或返回
67
比较当前任务是否为就绪的最高优先级任务,
关于中断及时钟节拍 MACRO $IRQ_Label HANDLER $IRQ_Exception_Function EXPORT $IRQ_Label ; 输出的标号 IMPORT $IRQ_Exception_Function ; 引用的外部标号 $IRQ_Label ... LDR R0, =OSTCBHighRdy LDR R0, [R0] LDR R1, =OSTCBCur LDR R1, [R1] CMP R0, R1 ADD SP, SP, #4*3 MSR SPSR_cxsf, R3 ; 不进行任务切换 LDMEQFD SP!, {R0-R3, R12, PC}^ ; 进行任务切换 LDR PC, =OSIntCtxSw MEND 比较当前任务是否为就绪的最高优先级任务, 如果不是则进行切换 设置堆栈指针和恢复SPSR 进行任务切换或者恢复执行被中断任务
68
关于中断及时钟节拍 流程图 中断服务程序 (ISR) 在ISR中可以打开中断实现中断嵌套 保存当前任务的 寄存器组 中断嵌套数加1
切换到系统模式 执行中断服务程序 关中断 执行OSIntExit( ) 在ISR中可以打开中断实现中断嵌套 切换到IRQ模式 判断是否需要进行任务切换 切换任务或返回
69
中断服务程序的编写 因为中断发生时肯定是允许中断的,所以如果用户在清除中断源之前调用μC/OS-II的系统服务函数就很可能会造成芯片的中断系统工作异常而使程序工作异常。因此在函数开始处关闭中断,或者直接给变量OSEnterSum赋1。如果用户程序没有这种情况,则不需要这个操作。在执行OS_EXIT_CRITICAL( )后,中断重新打开,如果在接下来的用户处理程序中发生中断,就可以实现中断嵌套。 void ISR(void) { OS_ENTER_CRITICAL()或直接给变量OsEnterSum赋1; 清除中断源; 通知中断控制器中断结束: 开中断: OS_EXIT_CRITICAL(); 用户处理程序; }
70
主要内容 移植规划 μC/OS-II的移植 嵌入式系统的初始化
71
初始化程序的下载执行 宿主机 目标机 1)通过编程器将可执行目标文件烧写到BootROM(ROM、EPROM、FLASH)等;
2)通过串行口和网口下载执行目标文件,要求宿主机系统上有数据传输工具程序、目标机装载器、嵌入式监视器或目标机系统上的调试代理。 3)通过JTAG或BDM接口下载; 宿主机 目标机
72
嵌入式系统的初始化过程
73
嵌入式系统的初始化过程 硬件初始化阶段 1、复位向量 ENTRY b ResetHandler ;for debug
b HandlerUndef ;handlerUndef b HandlerSWI ;SWI interrupt handler b HandlerPabort ;handlerPAbort b HandlerDabort ;handlerDAbort b ;handlerReserved b HandlerIRQ b HandlerFIQ
74
嵌入式系统的初始化过程(2) 2、最小硬件初始化 硬件初始化阶段 1)设置适当的寄存器,使嵌入式处理器处于一个已知的状态:
获得CPU的类型; 获得或设置CPU的时钟频率。 2)禁止中断和高速缓存 3)初始化内存控制器、内存芯片和高速缓存单元,包括: 得到内存的开始地址; 得到内存的大小; 如果有要求,则还需要进行主存测试;
75
嵌入式系统的初始化过程(3) 3、其余硬件初始化 硬件初始化阶段 1)引导代码调用合适的函数对目标机系统上的全部硬件部件进行初始化,包括:
建立执行处理程序 初始化中断处理程序 初始化总线接口 初始化板级外设得到内存的开始地址;
76
嵌入式系统的初始化过程(4) 4、RTOS初始化 RTOS初始化阶段 1)RTOS初始化 2)RTOS对象和服务初始化 任务 信号量 定时器
中断 内存管理 3)RTOS任务堆栈初始化 4)RTOS扩展部件初始化 5)启动RTOS
77
ARM7TDMI系统初始化的一般过程 启动(系统上电/复位) 从程序入口点 关闭中断 初始化时钟等硬件相关寄存器 初始化存储器系统
初始化C所需要的存储器空间 调用C入口函数
78
一、设置程序入口指针 上电复位后直接到程序入口点执行,入口点一般为一个跳转表,跳转到复位处理程序处开始执行ARM7TDMI系统的初始化;
启动程序首先必须定义入中指针,而且整个应用程序只有一个入口指针 例:AREA Boot,CODE,READONLY ENTRY /*设置程序入口指针*/
79
二、设置中断向量 ARM要求中断向量必须设置在从OX00000000地址开始,连续8*4字节的地址空间;
向量表包含一系列跳转指令,跳转到相应的中断服务程序; 对各未用中断,使其指向一个含返回指令的哑函数,以防止错误中断引起系统的混乱;
80
中断向量表 FIQ 0x1C 外部快速中断 IRQ 0x18 一般外部中断 (Reserved) 0x14 保留 Data Abort
数据异常 Frefetch Abort 0x0C 预取指异常 Software int 0x08 软件中断 Undef 0x04 未定义指令中断 Reset 0x00 复位中断
81
中断向量表的程序 AREA Boot,CODE,READONLY ENTRY B Reset_handler B Undef_Handler
B SWI_Handler B PreAbort_Handler B ;for reserved interrupt,stop here B IRQ_handler B FIQ_handler
82
三、初始化时钟和设置相关的寄存器 通过设置时钟控制器来确定CPU的工作频率,设置中断控制寄存器屏蔽中断
83
四、初始化存储器系统 存储器类型和时序配置(参考芯片手册,设置与内存映射相关的寄存器)
一个复杂的系统可能存在多种存储器类型的接口,需要根据实际的系统设计对此加以正确配置。对同一种存储器类型来说,也因为访问速度的差异,需要不同的时序设置。 通常Flash 和SRAM 同属于静态存储器类型,可以合用同一个存储器端口; 而DRAM 因为动态刷新和地址线复用等特性,通常配有专用的存储器端口。 存储器端口的接口时序优化是非常重要的,影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能地快;但同时又要考虑由此带来的稳定性问题。只有根据具体选定的芯片,进行多次的测试之后,才能确定最佳的时序配置。
84
存储器地址分布 有些系统具有非常灵活的存储器地址分配特性,进行存储器初始化设计的时候一定要根据应用程序的具体要求来完成地址分配。
一种典型的情况是启动ROM 的地址重映射(remap)。当一个系统上电后程序将自动从0 地址处开始执行,因此在系统的初始状态,必须保证在0 地址处存在正确的代码,即要求0 地址开始处的存储器是非易性的ROM 或Flash 等。但是因为ROM 或Flash 的访问速度相对较慢,每次中断发生后都要从读取ROM 或Flash 上面的向量表开始,影响了中断响应速度。因此有的系统便提供一种灵活的地址重映射方法,可以把0 地址重新指向到RAM 中去。在这种地址映射的变化过程当中,程序员需要仔细考虑的是程序的执行流程不能被这种变化所打断。
85
ROM地址的重映射 RAM Flash 0x0204 0x0200 (remap) 0x0100 (boot code)
(Reset_handler) …… B Reset_Handler 0x0000 Flash 0x0204 RAM
86
为保证重映射之后提供正确的中断入口地址,在重映射之前就必须把中断和异常向量表拷贝到内部RAM中。其程序实现如下:
ROM地址重映射的实现 为保证重映射之后提供正确的中断入口地址,在重映射之前就必须把中断和异常向量表拷贝到内部RAM中。其程序实现如下: mov r8,#RAM_BASE_BOOT //RAM_BASE_BOOT是重映射前内部RAM区地址 add r9, pc,#-(8+.-VectorTable) //VectorTale是异常向量表入口 ldmia r9!, {r0-r7} //读8个异常向量 stmia r8!, {r0-r7} //保存8个异常向量到RAM区 ldmia r9!, {r0-r4} //读5个异常处理程序绝对地址 stmia r8!, {r0-r4} //保存5个异常处理程序绝对地址到RAM区
87
五、初始化堆栈 ARM处理器有好几种运行状态(模式),各种状态都需要有自己的堆栈,所以需要分别为这些堆栈分配空间并设置好各自的堆栈指针
每一种状态的堆栈指针寄存器(SP)都是独立的(System 和User 模式使用相同的SP 寄存器)。因此对程序中需要用到的每一种模式都要给SP 寄存器定义一个堆栈地址。方法是改变状态寄存器CPSR内的状态位,使处理器切换到不同的状态,然后给SP 赋值。(意不要切换到User模式进行User 模式的堆栈设置,因为进入User 模式后就不能再操作CPSR 回到别的模式了。可能会对接下去的程序执行造成影响。) 一般堆栈的大小要根据需要而定,但是要尽可能给堆栈分配快速和高带宽的存储器。堆栈性能的提高对系统整体性能的影响是非常明显的。
88
堆栈初始化代码示例 MRS R0, CPSR ; CPSR -> R0
BIC R0, R0, #MODEMASK ; 安全起见,屏蔽模式位以外的其它位 ORR R1, R0, #IRQMODE ; 把设置模式位设置成需要的模式(IRQ) MSR CPSR_cxsf, R ; 转到IRQ 模式 LDR SP, =UndefStack ; 设置SP_irq ORR R1,R0,#FIQMODE MSR CPSR_cxsf, R ; FIQMode LDR SP, =FIQStack ORR R1, R0, #SVCMODE MSR CPSR_cxsf, R ; SVCMode LDR SP, =SVCStack
89
六、初始化应用程序执行环境 映像一开始总是存储在ROM/Flash 里面的,其RO 部分既可以在ROM/Flash里面执行,也可以转移到速度更快的RAM 中去;而RW 和ZI 这两部分必须是需要转移到可写的RAM 里去的。所谓应用程序执行环境的初始化,就是完成必要的从ROM 到RAM 的数据传输和内容清零。
90
六、初始化C环境 在目标文件中,代码、数据放在不同的段中。源文件编译链接生成含.data、.text段的目标文件,且链接器生成的.data段是以系统RAM为参考地址 故在系统启动时需要拷贝ROM或FLASH中的.data段到RAM,以完成对RAM的初始化。在初始化期间应将系统需要读写的数据和变量从ROM拷贝到RAM里运行
91
链接器产生的符号表 符号由链接器自动产生,只读段(read-only RO)就是代码段,读写段(read-write RW)是已经初始化的全局变量,而零初始化段(zero-initialized section ZI)中存放未初始化的全局变量;
92
初始化C环境(2) C环境初始化,就是利用上述符号初始化RW和ZI段以使后面使用的全局变量的C程序正常运行;
这里有两个循环,第一个循环把预初始化的数据段RW(位于代码段的后面)复制到RAM中,另一个循环把未初始化的数据段ZI初始化为0,也就是实现把从ROM中的.data段拷贝到RAM,对ZI段内的数据初始化为0,以完成对C环境的实始化;
93
初始化C环境(3)
94
改变处理器模式 除用户模式以外,其他6 种模式都是特权模式。因为在初始化过程中许多操作需要在特权模式下才能进行(比如CPSR 的修改),所以要特别注意不能过早地进入用户模式。一般地,在初始化过程中会经历以下一些模式变化:
95
七、调用C程序 对main函数的调用进入uc/OS的入口,通过这个入口就进入uC/OS的主函数,启动对uC/OS的初始化 例
IMPORT Main b Main ;C Entry
96
uC/OS系统的初始化 完成了前面的硬件初始化和运行环境的相关设置后,进入Main(), Main()是uC/OS的入口函数,启动对uC/OS的初始化
97
ARM的硬件抽象层——uHALuC/OS
ARM公司为操作系统的开发提供了一个硬件抽象层HAL,称为uHAL; 从结构上看,uHAL是一组库程序,需要说明的是,uHAL并不是专门为uC/OS准备的,甚至也不是专为操作系统内核准备的; uHAL只是个针对ARM核的函数库; uC/OS是建立在uHAL的基础之上的;
98
uC/OS系统的初始化(2)
99
ARMTargetInit()函数结构 ARMTargetInit() 调uHAL打印接口打印系统信息 调用uHAL函数禁止所有中断
uHAL函数对ARM计数器初始化 结束
100
uHAL的功能 uHAL的作用之一是在操作系统本身进入正常运行之前,为系统提供基本的输入输出手段,例如uHALr_printf()等;
通过uHAL_ResetMMU(),将MMU设置在一个确定的初始状态; 通过ARMDisable()关闭中断; 通过uHAL_InitInterrupts()设置中断向量处理程序; 通过uHAL_InitTimer()对系统使用的计数器进行初始化
101
ARMTargetStart()的分析 创建了任务之后,ARMTargetStart()调用uHALr_InstallSystemTimer()创建一个系统时钟,为时钟中断做好准备;
Similar presentations