在PROTEUS中使用ARM处理器及uCOS-II移植理解

更新时间:2023-10-15 05:27:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

嵌入式系统课程论文

在PROTEUS中使用ARM处理器及uC/OS-II移植理解

Rein Lee

一.嵌入式系统概述

通过本次嵌入式系统课程的学习,我了解了嵌入式系统的概念。所谓嵌入式系统,是指用于执行独立功能的专用计算机系统,它由包括微处理器、定时器、微控制器、存储器、传感器等一系列微电子芯片与器件,和嵌入在存储器中的微型操作系统、控制应用软件组成,共同实时诸如实时控制、监视、管理、移动计算、数据处理等各种自动化处理任务。嵌入式系统以应用为中心,以微电子技术、控制技术和通讯技术为基础,强调硬件软件的协同性与整合性,软件与硬件可裁减,以满足系统对功能、成本、体积和功耗等要求。 1.1 嵌入式系统的硬件特征

嵌入式系统的硬件必须根据具体的应用任务,以功耗、成本、体积、可靠性、处理能力等为指标来选择。嵌入式系统的核心是系统软件和应用软件。由于存储空间有限,因而要求软件代码紧凑、可靠,大多对实时性有严格的要求。

早期的嵌入式系统设计方法,通常是采用“硬件优先”原则。在粗略估计软件任务需求的情况下,首先进行硬件设计与实现。然后在此硬件平台上,再进行软件设计。因为很难充分利用硬件软件资源,取得最佳性能的效果。同时,一旦在测试时发现问题,需求对设计进行修改时,整个设计流程将重新进行,对成本和设计周期的影响很大。这种传统的设计方法只能改善硬件/软件各自的性能,在有限的设计空间不可能对系统做出较好的性能综合优化,在很大程度上依赖于设计者的经验和反复实验。

随着电子系统功能的日益强大和微型化,系统设计涉及的问题越来越多,难度也越来越大。硬件和软件也不再是截然分开的两个概念。因而出现了软硬件协同的设计方法。在系统目标要求下,协同设计软硬件体系结构,以最大限度地挖掘系统软硬件能力,得到高性能低代价的优化设计方案。 1.2 嵌入式操作系统

目前流行的嵌入式操作系统可以分为两类:一类是从运行在个人电脑上的操作系统向下移植到嵌入式系统中,形成的嵌入式系统,如微软公司的Windows CE,SUN公司的Java操作系统,嵌入式Linux等。

另一类是实时操作系统,如WindRiver公司的VxWorks,ISI的pSOS,ATI的Nucleus,和免费公开源代码的uC/OS-II等。

二.在Proteus中使用ARM处理器

由于Proteus中只支持LPC系列的ARM处理器,在这里只是简单的列举出LPC2124的一些特性:

LPC2124是基于一个支持实时仿真和跟踪的16/32位ARM7TDMI-S CPU的微处理器,并带有256k的嵌入的高速Flash存储器和16k的片那静态RAM。128位宽度的存储器接口和独特的加速结构使得32位代码能够在最大的时钟速率下运行。对代码规模有严格控制的应用可使用16位Thumb模式,将使得代码规模降低超过30%,而性能的损失却很小。

LPC2124片那Boot装载程序实现在系统编程(ISP)和在应用编程(IAP)。1ms可以编程512字节。整片擦除只需要400ms。此外还有4路A/D转换器,转换时间低于2.24us;2个32位定时器,6路PWM输出、RTC、看门狗和多个串

第 1 页 共 7 页

嵌入式系统课程论文

行接口。LPC系列微处理器的抗干扰能力强,在很多应用中得到了使用。

三.软件分析

1.LPC的Memory Map、Remap和LPC2124的Bootblock程序

Memory Map是把芯片中、芯片外的Flash、RAM、外设、BootBlock等进行统一编址,用地址来表示对象。LPC系列ARM处理器的这个地址是出厂时,由厂家规定的,用户只能访问,而不能进行更改。

Remap和Boot,个人理解如下:

在Reset信号周期内,LPC2124运行芯片内部自带的Bootblock程序,复位信号过后才是运行用户的程序。

LPC系列ARM处理器的Bootblock被固化在最高的Flash块中,运行时是被映射到0x7FFFE000~0x7FFFFFFF区域,这个程序是厂家写入的,它由任何复位硬件激活,在任何复位后都会先执行Boot装载程序。之所以要把BootBlock程序放在Flash块的顶端,是因为各芯片的Flash大小不一致,厂家为了BootBlock在芯片中的位置固定,在编址的2G靠前的位置虚拟划分一个区域作为BootBlock区域。这就是Remap。

BootBlock的工作如下:

1.判断P0.14是否为低,如果为低,进入ISP模式。

2.若P0.14不为低,要判断Boot(1:0)这两个脚,如果为11,要设置

MEMAP=1,即运行内部Flash。否则设MEMAP=3,运行外部Flash。 3.如果是运行外部Flash,那需要把外部Flash的起始地址重新映射到

0x00000000,以便复位信号过后就开始运行用户程序。

个人理解BootBlock相当于PC中的BIOS,由厂家固化,上电后首先完成映射,即把它的地址映射到0x00000000处,当初始化完成后,就运行内部Flash或者外部Flash的程序,通常要以向量表开头,这个过程就是Remap,也就是把内部ROM或者外部Flash的地址映射到0x00000000处。

2.启动代码分析

运行完BootBlock程序之后就是要运行用户自己编写的启动代码了。启动代码中最重要的是异常向量表。这个表包括复位、未定义指令、软中断、预取指中止、数据中止、IRQ中断和FIQ中断。 Reset

LDR PC, ResetAddr

LDR PC, UndefinedAddr LDR PC, SWI_Addr LDR PC, PrefetchAddr LDR PC, DataAbortAddr DCD 0xb9205f80 ;插入用户代码有效签名 LDR PC, [PC, #-0xff0] ;从VIC处取得IRQ入口地址 LDR PC, FIQ_Addr

ResetAddr DCD ResetInit UndefinedAddr DCD Undefined SWI_Addr DCD SoftwareInterrupt

第 2 页 共 7 页

嵌入式系统课程论文

PrefetchAddr DCD PrefetchAbort DataAbortAddr DCD DataAbort Nouse DCD 0 IRQ_Addr DCD 0 FIQ_Addr DCD FIQ_Handler Undefined

B Undefined ;未定义指令 PrefetchAbort

B PrefetchAbort ;取指令中止 ;取数据中止 DataAbort

B DataAbort ;快速中断

未定义指令、取指令中止、快速中断使得PC不跳转,进入死循环,等待看门狗复位用户程序。对于ARM9,有MMU单元,应该在PrefetchAbort和DataAbort处进行处理,然后返回到发生异常的指令处重新执行一次。 FIQ_Handler

STMFD SP!, {R0-R3, LR} ;ADS编译器会自动保存R4-R11 IMPORT FIQ_Exception ;也可以在startup.s的开头引入

BL FIQ_Exception ;调用C语言中的中断服务程序

LDMFD SP!, {R0-R3, LR} ;出栈 SUBS PC, LR, #4

如果在CPSR中没有禁止中断,那么每执行一条指令,处理器会检查是否有FIQ或者IRQ中断发生。若有中断发生,当且仅当PC指更新后,即处理器对下一条指令进行取指发生后,处理器才会进入到中断的服务程序中。因此保存在LR中的指是PC-4,此处的PC是更新后的PC,由于ARM处理器是三级流水线的结构,因此返回地址是LR-4,返回到的地址是将要执行,但是被中断了指令。

LDR只能实现当前PC 4KB范围内的跳转,而在LDR的不远处用DCD定义一个字,而在这个字里存放最终的异常服务程序的入口地址,可以实现在4GB范围内的全范围跳转。要求整个向量表的累加和为0。

__user_initial_stackheap函数是对用户的堆栈进行初始化,对它的调用是在__main中完成的,对编程人员不可见。__main是ADS编译系统提供的一个库函数,使用__main标号引导系统时,必须将应用程序的入口定义为main()。__main完成代码和数据的复制,并且把ZI数据区清零。这一步当代码和数据区在存储和运行时处于不同的存储器位置时有意义。__main保证了系统在进入用户的应用程序前自动完成了系统调用。但是如果所有的初始化过程都已经被用户代码显式地被完成,如堆栈初始化、加载映象、执行映象、RW、ZI数据的复制等,那么用户应用程序的入口函数可以任意定义any_name(),完成初始化后,直接B any_name即可。个人建议使用B __main的方式,防止疏忽出现不必要的错误,比如出现程序中的全局变量没有被正确的初始化等错误。

3.IRQ.S分析

通过定义宏:

$IRQ_Label HANDLER $IRQ_Exception_Function来实现LPC 2124的多个中断源共用一段异常处理代码的目的。

第 3 页 共 7 页

嵌入式系统课程论文

SUB LR, LR, #4 ; 计算返回地址 STMFD SP!, {R0-R3, R12, LR} ; 保存任务环境 进入IRQ中断,首先计算中断的返回地址,将LR减4,然后将R0-R3,R12,LR压入堆栈。由于进入了IRQ中断,因此堆栈指针是SP_IRQ,和用户代码运行的SP_SYS不是同一个寄存器。至于只保存R0-R3,R12,是因为程序在正常运行过程中,R4-R11中装载的是局部变量,在中断跳转是ADS编译器会自动将R4-R11入栈保护。

进行了堆栈保护后,要保存进入中断之前的CPSR寄存器,以保证完成中断处理后,使处理器状态和没有发生中断一样。

MRS R3, SPSR ; 保存状态

STMFD SP, {R3, SP, LR}^ ; 保存用户状态的R3,SP,LR,注意不能回写 若STM的寄存器列表中不包含PC,^的作用是保存用户模式下的寄存器。此操作的目的是用于中断嵌套。通过定义IRQ模式的堆栈深度,可以控制中断嵌套的深度。分析整段代码,一次中断响应需要占用8个32bit的堆栈,定义堆栈深度为n*8,可以允许n级中断嵌套。

此处^操作符不允许回写,经过AXD仿真验证,回写操作会写入sp_usr而不是sp_irq,此操作属于不安全的操作,并且编译之后,ADS编译器会给出响应的警告。

MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式 CMP R1, #1

LDREQ SP, =StackUsr

BL $IRQ_Exception_Function ; 调用c语言的中断处理程序 堆栈操作完成之后,切换到系统模式下运行C语言中定义的中断复位函数,才能达到中断嵌套的目的。

当中断服务函数执行完毕之后,退出中断。在uC/OS-II的环境下考虑,因为IRQ中断发生之后,会改变任务就绪列表,因此需要在中断退出时确定是否需要进行任务切换。这点在下面的OS_cpu_a.s中会涉及到。

4.OS_cpu_a.s分析

移植uC/OS-II,需要改写os_cpu.h,OS_cpu_a.s,OS_cpu_a.c,includes.h几个文件。在LPC系列的平台,任务级的任务切换和中断级的任务切换是通过软中断(SWI,Software Interrupt)来完成的。

OS_CPU_A.S中包括SoftwareInterrupt、OSIntCtxSw、OSIntCtxSw_1和__OSStartHighRdy四个函数。SoftwareInterrupt定义了软件中断处理方法,OSIntCtxSw对应于中断级任务切换和任务级任务切换。而__OSStartHighRdy是uC/OS-II启动时调用的,调用新建的优先级最高的任务。这四个函数的编写需要很好的了解ARM7的内核结构和完全了解uC/OS-II内核运行的原理。 在SoftwareInterrupt主要完成寄存器R0-R3,R12,LR在SVC模式(Supervisor)的堆栈中的保存、取得软件中断号并调用相应的处理过程。在OS_CPU.H中可以找到关于几个函数的软件中断定义。 __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); /*开中断*/

第 4 页 共 7 页

嵌入式系统课程论文

由于SWI中断是由指令本身引起的,因此发生中断时,PC值并没有更新,所以计算中断返回地址,并不需要将LR减4,LR指向的就是发生中断指令的下一条指令。SWI中断下堆栈的操作如下指令所示:

LDR SP, StackSvc ; 重新设置堆栈指针 STMFD SP!, {R0-R3, R12, LR} ; 寄存器入栈 ?? ; 软件中断处理 LDMFD SP!, {R0-R3, R12, PC}^ ; 寄存器出栈,返回

OS_TASK_SW是任务级任务切换函数,引起0x00号软中断,并最终会跳转到OSIntCtxSw执行,OSIntCtxSw也是中断级任务切换的函数。

对OSIntCtxSw的理解,实际上是要理解各个任务的堆栈设置。一个任务正常运行,是运行在user模式下,根据ARM7的体系结构,user模式的所有寄存器和system模式下的所有寄存器使用同一个物理地址。其他模式R13,R14不同物理地址,FIQ模式R8-R14不同物理地址。其他模式相比user/sys模式,多一个SPSR寄存器,用于保存发生中断时的CPSR的备份。因此,当发生任务切换,需要保存当前任务的所有寄存器,即R0-R14。 OSIntCtxSw

LDR R2, [SP, #20] ;获取PC (LR_SVC) LDR R12, [SP, #16] ;获取R12 MRS R0, CPSR ;保存当前SVC模式到R0 MSR CPSR_c, #(NoInt | SYS32Mode) ;切换到系统模式 MOV R1, LR ;保存LR_sys

STMFD SP!, {R1-R2} ;保存LR_sys, PC (LR_SVC) STMFD SP!, {R4-R12} ;保存R4-R12 MSR CPSR_c, R0 ;从R0中恢复到SVC模式 LDMFD SP!, {R4-R7} ;获取R0-R3,SVC堆栈 ADD SP, SP, #8 ;出栈R12,PC,计算SP_SVC MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式 STMFD SP!, {R4-R7} ;保存R0-R3 需要注意,执行MSR CPSR_c, #(NoInt | SYS32Mode)之前的SP是指SP_SVC,而模式切换之后,SP对应于SP_user。 注意ADS采用的是满递减堆栈。 此段代码的作用是在系统模式下,依次入栈PC,(即LR_SVC),LR(LR_SYS)R12,R11,??R4。然后切换回SVC模式下出栈R0-R3,在切换回SYSTEM模式下入栈R3,R2,R1,R0。

当以上入栈工作完成之后,再保存OSEnterSum和CPSR即可完成当前任务的上下文环境的保存。

LDR R1, =OsEnterSum ;获取OsEnterSum LDR R2, [R1]

STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum 从上述程序分析可以看出,一个任务的上下文环境入栈如下图所示:

第 5 页 共 7 页

本文来源:https://www.bwwdw.com/article/a4if.html

Top