ARM与嵌入式技术_培训课件_第10章.ppt_第1页
ARM与嵌入式技术_培训课件_第10章.ppt_第2页
ARM与嵌入式技术_培训课件_第10章.ppt_第3页
ARM与嵌入式技术_培训课件_第10章.ppt_第4页
ARM与嵌入式技术_培训课件_第10章.ppt_第5页
已阅读5页,还剩66页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第10章 ARM高级语言程序设计基础,10.1 ARM汇编程序实例,数据块复制 本程序将数据从源程序区src复制到目标数据区dst复制时,以8个字为单位进行.对于最后所剩不足8个字的数据,以字为单位进行复制,这时程序跳转到coyewords处执行.在进行以8个字为单位的数据复制时,保存了所有的8个工作寄存器.程序如下(ADS下)。,AREA Block,CODE,READONLY;设置本段程序的名称及属性 NUM EQU 20 ;设置将要复制的字数 ENTRY ;标识程序入口点 LDR R0,=src ;R0寄存器指向源数据区src LDR R1,=dst ;R1寄存器指向目标数据区dst M

2、OV R2,#NUM ;R2指定将要复制的字数 MOV SP,#设置数据栈指针,用于保存工作寄存器数值,Bcopy MOVS R3,R2,LSR #3 ;需要进行的以8个字为单位的复制次数 BEQ Cword ;对于剩下不足8个字的数据,跳转到Cword以 ;字为单位复制 STMFD SP!,R4-R11 ;保存工作寄存器 Ocopy LDMIA R0!,R4-R11 ;从源数据区读取8个字的数据,放到8个寄存 ;器中,并更新目标数据区指针R0 STMIA R1!,R4-R11 ;将这8个字数据写到目标数据区中,并更新目 ;标数据区指针R1 SUBS R3,R3,#1 ;将块复制次数减1 BN

3、E Ocopy ;循环,直到完成以8个字为单位的块复制 LTMFD SP!R4-R11 ;恢复工作寄存器值 Cword ANDS R2,R2,#7 ;剩下不足8个字的数据的字数 BEQ stop ;数据复制完成 Wcopy LDR R3,R0,#4 ;从源数据区读取18个字的数据,放R3寄存 ;器中,并更新目标数据区指针R0 STR R3,R1,#4 ;将这R3中数据写入到目标数据区中,并更新 ;目标数据区指针R1 SUBS R2,R2,#1 ;将字数减1,BNE Wcopy ;循环,直到完成以字为单位的数据复制 Stop ;程序结束处理 MOV R0,#0 x18 ;本条与下条指令的作用是参

4、数传递 LDR R1,=结束汇编,10.1 ARM汇编程序实例,利用跳转表实现程序跳转 在程序中常常需要根据一定的参数选择执行不同的子程序.本例通过跳转表实现程序跳转.跳转表中存放的是各函数的地址,选择不同子程序的参数是该子程序在跳转表中的偏移量.在本例中R3寄存器中存放的是跳转表的基地址(首地址,其中存放的是第一个子程序的地址). (ADS下),AREA Jump,CODE,READONLY ;设置本段程序的名称及属性 NUM EQU 2 ;跳转表中的子程序个数 ENTRY ;程序执行的入口点 Start MOV R0,#0 ;设置3个参数,R0选择调用哪个子程序 MOV R1,#3 ;R1

5、为子程序要用的参数 MOV R2,#2 ;R2为子程序要用的参数 BL Func ;调用子程序Func ,进行算术运算,Stop MOV R0,#0 x18 ;本条与下条指令的作用是参数传递 LDR R1,=结束汇编,10.2 C语言与汇编的混合编程,简介,在嵌入式程序设计中,C语言编程和ARM汇编语言编程都是 必需的,在某些情况下,还需要C语言与汇编语言的混合编程. 灵活地运用C语言和汇编语言之间的关系进行嵌入式编程有 利于对嵌入式系统以及相关模块的编程开发.在需要C语言和 汇编语言混合编程时,如果汇编代码比较简单,则可直接利用 内嵌汇编来进行混合编程.如果汇编代码比较复杂,则可将汇 编语言

6、程序和C语言程序分别以文件的形式加到一个工程里, 通过ATPCS来完成汇编语言程序与C语言程序之间的调用.,10.2 C语言与汇编的混合编程,ATPCS介绍,ATPCS(ARM-Thumb Produce Call Standard)是ARM程序和 Thumb程序中子程序调用的基本规则,目的是为了使单独编译 的C语言和汇编语言程序之间能够相互调用.这些基本规则包括 子程序调用过程中寄存器的使用规则,数据栈的使用规则和参 数的传递规则.,10.2 C语言与汇编的混合编程,寄存器的使用规则,子程序间通过寄存器R0 R3来传递参数。这时,寄存器R0 R3可记作a0 a3。被调用的子程序在返回前无须恢

7、复寄存器R0 R3的内容。 在子程序中,使用寄存器R4 R11来保存局部变量。这时,寄存器R4 R11可以记作v1 v8。如果在子程序中使用了寄存器v1 v8中某些寄存器,则子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值。在Thumb程序中,通常只能使用寄存器R4 R7来保存局部变量。另外R9、R10和R11还有一个特殊的作用,分别记为:静态基址寄存器SB、数据栈限制指针SL和桢指针FP。 寄存器R12用作子程序间的scratch寄存器(用于保存SP,在函数返回时使用该寄存器出栈),记作IP.在子程序间的链接代码段中常有这种使用规则. 寄存器R13用作堆栈指针,记作SP。在

8、子程序中寄存器R13不能用作其他用途。寄存器SP在进入子程序时的值和退出子程序的值必须相等。,10.2 C语言与汇编的混合编程,寄存器的使用规则,寄存器R14称为链接寄存器,记作LR,它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用作其他用途。 寄存器R15为程序计数器,记作PC,它不能用作其他用途。,表:寄存器的名称及使用规则,表续:寄存器的名称及使用规则,10.2 C语言与汇编的混合编程,数据栈的使用规则,根据堆栈指针指向位置的不同,堆栈可分为满栈和空栈2 种.当堆栈指针指向栈顶元素,即指向最后一个入栈的数据元 素时,称为满栈,当堆栈指针指向与栈顶元素相邻的一

9、个可用 数据单元时,称为空栈.根据数据栈增长方向的不同也可分为 递增堆栈和递减堆栈2种.当数据栈向内存地址减少的方向增 长时,称为递减堆栈,当数据栈向内存地址增加的方向增长时, 称为递增堆栈.,10.2 C语言与汇编的混合编程,综合这2种特点则可有以下4种数据栈: FD 满递减 ED 空递减 FA 满递增 EA 空递增 ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐.异常中断的处理程序可使用中断程序的数据栈,但要保证中断程序的数据栈足够大. 因此要求包含外部调用的程序必须满足下列条件: (1)外部接口程序的数据栈必须是8字节对齐的。 (2)本程序生成的数据栈也必须是8字节对齐的

10、。在汇编程序中可以使用PRESERVE8伪指令告诉连接器,本汇编程序数据栈是8字节对齐的。,10.2 C语言与汇编的混合编程,参数传递规则,根据参数个数是否固定可将子程序分为参数个数固定的子程序 和参数个数可变的子程序.这2种子程序的参数传递规则是不同的,1)参数个数固定的子程序参数传递规则 对于参数个数固定的子程序,参数传递与参数个数可变的子程序 参数传递规则不同.如果系统包含浮点运算的硬件部件,浮点参 数将按照下面的规则传递: 各个浮点参数按顺序处理 为每个浮点参数分配FP寄存器.分配的方法是,满足该浮点参数需要的切编号最小的一组连续的FP寄存器.,10.2 C语言与汇编的混合编程,参数传

11、递规则,根据参数个数是否固定可将子程序分为参数个数固定的子程序 和参数个数可变的子程序.这2种子程序的参数传递规则是不同的,2)参数个数可变的子程序参数传递规则 对于参数个数可变的子程序,当参数不超过4个时,可使用寄存 器R0R3来传递参数,当参数超过4个时,还可使用数据栈来传 递参数. 在参数传递时,将所有参数看作是存放在连续的内存字单元中 的字单元中的字数据.然后,依次将各字数据传送到寄存器R0,R1 R2,R3中,如果参数多于4个,将剩余的字数据传送到数据栈中,入 栈的顺序与参数顺序相反,即最后一个字数据先入栈.,10.2 C语言与汇编的混合编程,参数传递规则,根据参数个数是否固定可将子

12、程序分为参数个数固定的子程序 和参数个数可变的子程序.这2种子程序的参数传递规则是不同的,3)子程序结果返回规则 结果为一个32位的整数时,可通过寄存器R0返回; 结果为一个64位整数时,可通过寄存器R0和R1返回,依次类推; 结果为一个浮点数,可通过浮点运算部件的寄存器F0,D0或者 S0来返回; 结果为复合型的浮点数(如复数)时,可通过寄存器F0Fn或者 D0Dn; 对于位数更多的结果,需要通过内存来传递.,10.2 C语言与汇编的混合编程,注意,在同一个C源程序中不能同时包含ARM指令和Thumb指令,但汇 编可以.,如果程序遵守支持ARM程序和Thumb程序混合使用的ATPCS,则 程

13、序中ARM子程序和Thumb子程序可相互调用.对于C源程序, 只要在编译时指定apcs/interwork选项,编译器生成的代码会自 动遵守支持ARM程序和Thumb程序混合使用的ATPCS.而对于汇 编源程序,必须保证编写的代码遵守支持ARM程序和Thumb程 序混合使用的ATPCS.,10.2 C语言与汇编的混合编程,内嵌汇编,在语言中嵌入汇编语言程序可实现一些高级语言没有的功 能,并可提高执行效率armcc和armcpp内嵌汇编器支持完整的 ARM指令集;tcc和tcpp用于Thumb指令集,但是内嵌汇编器并不支 持诸如直接修改PC实现跳转的底层功能.内嵌的汇编指令包括大部 分ARM指令

14、和Thumb指令,但是不能直接引用C语言的变量定义,数 据交换必须通过ATPCS进行.嵌入式汇编在形式上表现为独立定义 的函数体.,10.2 C语言与汇编的混合编程,内嵌汇编指令的语法格式,_ _asm 指令;指令 . 指令 各指令用”;”分隔,如果一条指令占据多行,除最后一行外都要使用 连字符”.在汇编指令段中可使用C语言的注释语句.需要特别注意 的是_ _asm是两个下划线.,10.2 C语言与汇编的混合编程,内嵌汇编指令的特点,1)操作数 在内嵌的汇编指令中,操作数可以是寄存器,常量或C语言表 式.它们可以是char,short或int类型,而且都是作为无符号数进行 操作,如果需要有符号

15、数,用户需要自己处理与符号有关的操 作。编译器将计算这些表达式的值,并为其分配寄存器。当汇编 指令中同时用到了物理寄存器和C语言的表达式,要注意使用的表 达式不要过于复杂。,内嵌汇编指令的特点,2)物理寄存器 在内嵌的汇编指令中,使用物理寄存器有以下限制: 不能直接想PC寄存器赋值,程序的跳转只能通过B指令和BL指令实现。 在使用物理寄存器的内嵌汇编指令中,不要使用过于复杂的C语言表达式。因为当表达式过于复杂时,将会需要较多的物理寄存器,这些寄存器可能与指令中的物理寄存器的使用冲突。当编译器发现了寄存器的分配冲突时,会产生相应的错误信息,报告寄存器分配冲突。 编译器可能会使用R12寄存器或R1

16、3寄存器存放编译的中间结果,在计算表达式值时可能会将寄存器R0R3,R12以及R14用于子程序调用。因此在内嵌的汇编指令中,不要将这些寄存器同时指定为指令中的物理寄存器.,内嵌汇编指令的特点,2)物理寄存器 在内嵌的汇编指令中,使用物理寄存器有以下限制: 在内嵌的汇编指令中使用物理寄存器时,如果有C语言变量使用了该物理寄存器,则编译器将在合适的时候保存并恢复该变量的值。需要注意的是,当寄存器SP,SI,FP以及SB用作特定的用途时,编译器不能恢复这些寄存器的值。 通常在内嵌的汇编指令中不要指定物理寄存器,因为这可能会影响编译器分配寄存器,进而可能影响代码的效率。 3)常量 在内嵌的汇编指令中,

17、常量前的符号#可以省略。如果在一个表达式中使用符号“#”,则该表达式必须是一个常量。,内嵌汇编指令的特点,4) 标号 C语言程序中的标号可被内嵌的汇编指令使用。但是只有指令B可使用C语言程序中的标号,指令BL不能C语言程序中的标号。指令B使用C语言程序中的标号时,语法格式如下: Bcondlabel 5) 内存单元的分配 内嵌汇编器不支持汇编语言中用于内存分配的伪操作。所用的内存单元的分配都是通过C语言程序完成的,分配的内存单元通过变量供内嵌的汇编器使用.,内嵌汇编指令的特点,6)指令展开 内嵌的汇编指令中如果包含常量操作数,则该指令可能会被汇编器展开成几条指令。例如,指令“ADD R0,R0

18、,#1023”可能会被展开成下面的指令序列: ADD R0,R0,#1024 SUB R0,R0,#01 乘法指令MUL可能会被展开成一系列的加法操作和移位操作。事实上,除了与协处理器相关的指令外,大部分ARM和Thumb指令中包含常量操作数都可能被展开成多条指令。,内嵌汇编指令的特点,7) SWI和BL指令的使用 在内嵌的SWI和BL指令中,除了正常的操作数域外,还必须增加下面3个可选的寄存器列表: 第1个寄存器列表中的寄存器用于存放输入的参数 第2个寄存器列表中的寄存器用于存放返回的结果 第3个寄存器列表中的寄存器供被调用的子程序作为工作寄存器,这些寄存器的内容可能被调用的子程序破坏。,内

19、嵌汇编指令的特点,8)内嵌汇编器与armasm汇编器的区别 内嵌汇编器与armasm汇编器的区别如下: 内嵌汇编器不支持通过“.”指示符或PC获取当前指令地址 不支持LDR Rn,=expression伪指令,而使用MOV Rn,= expression 指令向寄存器赋值。 不支持标号表达式 不支持ADR和ADRL伪指令 不支持BX和BLX指令 不可以向PC赋值 使用0 x前缀替代 /*x存放在R0中*/ ,10.2 C语言与汇编的混合编程,内嵌汇编注意事项,(2)不要使用寄存器寻址变量 尽管根据编译器规则似乎可确定R0对应x,但这样的代码会使汇编器认为发生了寄存器冲突,用其他寄存器代替R0存

20、放参数x,则使得该函数将x原封不动地返回。 int bad_f(int x) _ _asm ADD x, x,#1 return x ; ,10.2 C语言与汇编的混合编程,内嵌汇编注意事项,(3)使用内嵌汇编时,编译器自己会保存和恢复它可能用到的寄存器,用户无须保存和恢复寄存器。事实上,除了CPSR和SPSR寄存器,对物理寄存器没写就读都会引起汇编器报错。 int f(int x) _ _asm STMFD SP,R0 ;对R0的保存是非法的,因为发生了写之前读 ADD R0,x,#1 EOR x,R0,x LDMFD SP!R0 ;对R0的恢复是不需要的 return x; ,10.2 C

21、语言与汇编的混合编程,内嵌汇编注意事项,(4) LDM和STM指令的寄存器列表只允许物理寄存器 内嵌汇编可以修改处理器模式,协处理器状态和FP,SL及SB等ATPCS寄存器。但是编译器在编译时并不了解这些变化,所以必须保证在执行C语言代码前恢复相应被修改的处理器模式. (5)汇编语言用“,”作为操作数分隔符 如果有带“,”的C语言表达式作为操作数,必须用“()”将其归为一个汇编操作数。例如: _ _asmADD x,y,(f(),z),其中(f(),z)为C、C+语言表达式。,10.3 C语言与ARM汇编语言间相互调用,汇编程序访问C语言全局变量,全局变量只能通过地址间接调用。为了访问C中的全

22、局变量,首先通过 .extern伪指令引入全局变量,然后将其地址装入寄存器中。根据变量的类 型,可以通过下面的装载和存储指令访问。 对unsigned char类型变量,使用LDRB/STRB访问 对unsigned short类型变量,使用LDRH/STRH访问(对体系结构3使用LDRB/STRB访问) 对unsigned int类型变量,使用LDR/STR访问 对char类型变量,使用LDRSB/STRSB访问 对short类型变量,使用LDRSH/STRSH访问 小于8个字的结构体可以通过LDM/STM指令来访问整个变量,结构体中的变量 也可以通过相应类型的装载和存储指令来访问,此时必须

23、知道此成员与结构体 起始地址的偏移量。,10.3 C语言与ARM汇编语言间相互调用,汇编程序访问C语言全局变量,例子:汇编程序访问C语言全局变量(ADS下) AREA globals,CODE,READONLY EXPORT asmsubroutine;用EXPORT伪操作声明该变量可被其他文件引用, ;相当声明了一个全局变量 IMPORT globvar ;用IMPORT伪操作声明该变量是在其他文件中定 ;义的,在本文件中可能要用到该变量 asmsubroutine LDR R1, =globvar ;读取globvar的地址到R1中 LDR R0, R1 ;将其值读入到寄存器R0中 ADD

24、 R0, R0, #2 STR R0, R1 ;修改后再将寄存器R0的值赋予变量globvar MOV PC, LR END,10.3 C语言与ARM汇编语言间相互调用,C程序调用汇编程序的例子,C程序调用汇编程序应首先通过extern声明要调用的汇编程序 模块,声明中形参个数要与汇编模块中需要的变量个数一致, 且参数传递要满足ATPCS规则;然后再在C程序正文中调用。 例子:一个C程序调用汇编程序的串拷贝例子。,例子:一个C程序调用汇编程序的串拷贝例子。 C程序为: #include extern void strcopy(char *d, char *s);用extern声明一个函 数为外

25、部函数,可被其 他文件中的函数调用 int main() char *srcstr = First string - source ; char *dststr = Second string - destination ; printf(Before copying:n); printf( %sn %sn,srcstr,dststr); strcopy(dststr,srcstr) ; 调用汇编函数strcopy() printf(After copying:n); printf( %sn %sn,srcstr,dststr); return (0); ,例子:一个C程序调用汇编程序的串拷贝

26、例子。 ARM汇编语言模块:(ADS下) AREA SCopy,CODE,READONLY EXPORT strcopy ;用EXPORT伪操作声明该变量可被其他文件引用,相; ;当于声明了一个全局变量 Strcopy ;R0指向目的数据串,R1指向源数据串 LDRB R2, R1,#1 ;字节加载,并更新地址 STRB R2, R0,#1 ;字节保存,并更新地址 CMP R2, #0 ;检查R2是否等于0 BNE strcopy ;若条件不成立,则继续执行 MOV PC,LR ;从子程序返回 END 根据ATPCS,函数的前4个参数在R0-R3中,C语言代码源程序可保存为strtest.c,

27、汇编语言程序是scopy.s,10.3 C语言与ARM汇编语言间相互调用,汇编程序调用C程序的例子,汇编程序调用C语言模块,在调用之前首先必须根据C语言模块 中需要的参数个数以及ATPCS参数传递规则,完成参数的传递, 即前4个参数通过R0R3传递,后面的参数通过堆栈传递;然后 再利用B/BL指令进行调用。 例:定义汇编语言将要调用的C函数功能为返回5个参数之和, 其函数原型为: int g(int a, int b, int c, int d, int e) return a + b + c + d +e; 汇编语言要完成的功能是求取i+2i+3i+4i+5i的结果.,程序如下:(ADS下)

28、 EXPORT f AREA f,CODE,READONLY IMPORT g ;i在R0中 STR LR, sp, #-4! ;预先保存LR ADD R1, R0, R0 ;R1=2*i (第2个参数) ADD R2, R1, R0 ;R2=3*i (第3个参数) ADD R3, R1, R2 ;R3=5*I (第5个参数) STR R3, sp, #-4! ;第5个参数入栈 ADD R3, R1, R1 ;R3=4*i (第4个参数) BL g ;调用C语言函数g() ADD sp, sp, #4 ;第5个参数出栈 LDR PC, sp, #4 ;返回 END 程序执行结束,结果保存于寄存

29、器R0中。,10.4 基本IO程序(含启动代码),下面我们通过基本IO程序来介绍完整的ARM程序的编写过程。 启动代码,启动代码是用汇编语言编写的一段程序,一般完成堆栈初始化、 系统变量初始化、中断系统初始化、地址重映射、I/O初始化以及 外围初始化等操作,并引导程序进入C语言编写的主程序。设计时 可以根据需要进行适当的删减. 通常来说,启动代码的大致流程如下: 系统变量的初始化 中断向量重映射 中断向量表 转到C入口地址 初始化各模式栈指针,10.4 基本IO程序(含启动代码),由于ARM7处理器的中断向量位于地址0 x000000000 x0000001C处,因此应 将中断向量表置于此处。

30、当程序工作于用户RAM模式,或用户外部存储器模式 时,需要进行中断向量的重映射。 中断向量表的程序如下: Vectors: LDR PC, Reset_Addr LDR PC, Undef_Addr LDR PC, SWI_Addr LDR PC, PAbt_Addr LDR PC, DAbt_Addr DCD 0 xb9205f80 保留向量 LDR PC, PC, #-0 xff0 LDR PC, FIQ_Addr,中断向量表,10.4 基本IO程序(含启动代码),其中DCD 0 xb9205f80为保留向量,这个向量在ARM文件中标识 为保留,该位置被Boot装载程序用作有效的用户程序关

31、键字。 LPC2000芯片规定,当向量表中所有的数据累加和为0,且ISP 外部硬件条件不满足时,Boot装载程序将执行用户程序。 可以注意到IRQ向量使用的指令(LDR PC, PC, #-0 xff0) 与其他向量不同,正常情况下,这条指令所在的地址为 0 x00000018,当CPU执行这条指令但还没有跳转时,PC的值为 0 x00000020,0 x00000020-0 x00000ff0为0 xfffff030,这 正是向量中断控制器VIC的向量中断地址寄存器VICVectAddr, 这个寄存器保存当前将要服务的IRQ中断服务程序的入口地址, 这样就可直接跳转到需要的中断服务程序中。,

32、中断向量表,10.4 基本IO程序(含启动代码),调试时,通常使程序运行于RAM空间(LPC2200芯片RAM的起 始地址为0 x40000000),此时需要进行异常向量的重映射, 使从0 x00000000开始的32个字节的异常向量及额外保留的32 个字节,共64字节映射到0 x40000000处,同时将存储映射模 式配置为用户RAM模式。,中断向量的重映射,10.4 基本IO程序(含启动代码),中断向量重影射的程序如下: RemapSRAM: STMFD SP!, R0-R1,LR REMAPS: MOV R0, #0 x40000000 LDR R1, =Vectors LDMIA R1

33、!, R2-R9 STMIA R0!, R2-R9 LDMIA R1!, R2-R9 STMIA R0!, R2-R9 LDR R0, =MEMMAP MOV R1, #0 x02 STR R1, R0 LDMFD SP!, R0-R1,PC,中断向量的重映射,10.4 基本IO程序(含启动代码),编程前,需明确ARM使用的堆栈为满降序栈。要定义各工作模式 的栈指针,首先需要使处理器处于相应的工作模式下,因此程序 中需要进行工作模式的切换;另外,要注意只有当处理器处于特 权模式(非用户模式)下的时候,才能进行工作模式的切换。当 系统复位或软件中断响应时,处理器进入的是管理模式,此时, 可以进行

34、各种模式的切换,由此可以设置各种工作模式下的栈指 针。 初始化各模块栈指针的程序如下: InitStack: MOV R1, LR LDR R0, =Top_Stack 为各种工 作模式设置堆栈,初始化各模栈指针,#进入未定义指令工作模式并设栈指针 MSR CPSR_c, #Mode_UND|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #UND_Stack_Size #进入中止工作模式并设置栈指针 MSR CPSR_c, #Mode_ABT|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #ABT_Stack_Size #进入FIQ工作模式并设置

35、栈指针 MSR CPSR_c, #Mode_FIQ|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #FIQ_Stack_Size #进入IRQ工作模式并设置栈指针 MSR CPSR_c, #Mode_IRQ|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #IRQ_Stack_Size,#进入管理工作模式并设置栈指针 MSR CPSR_c, #Mode_SVC|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #SVC_Stack_Size #进入用户工作模式并设置栈指针 MSR CPSR_c, #Mode_USR MOV SP

36、, R0 MOV PC, R1 .equ Top_Stack, (0 x40004000 - 0 x20) .equ Top_Stack, 0 x00000004 4个字节 .equ Top_Stack, 0 x00000020 32个字节 .equ Top_Stack, 0 x00000004 .equ Top_Stack, 0 x00000004 .equ Top_Stack, 0 x00000100 256个字节 .equ Top_Stack, 0 x00000200 512个字节,10.4 基本IO程序(含启动代码),中断向量重影射的程序如下: 启动代码的部分内容可以放到C程序中完成,

37、诸如初始化中断向量 控制器、初始化C程序变量等。汇编程序到C程序的入口非常简 单,如下: .extern Main B Main 注意C程序的名称:Main,这里不要使用main。因为若使用 main,则GCC编译器会先调用GCC运行时库函数_gccmain,然 后再执行main函数,编译器若找不到_gccmain函数,则会提示 “undefined reference to _gccmain”错误。,转到C入口地址,10.4 基本IO程序(含启动代码),完整的启动代码,下面是完整的启动代码Startup.s程序。(GNU下) #7种工作模式 .EQUMode_USR,0 x01标准工作模式以

38、及中断允 许位I和F的定义 .EQUMode_FIQ,0 x11 .EQUMode_IRQ,0 x12 .EQUMode_SVC,0 x13 .EQU Mode_ABT,0 x17 .EQUMode_UND,0 x1B .EQUMode_SYS,0 x1F,#中断屏蔽位及状态位 .EQUI_Bit,0 x80 I=1,则禁止IRQ中断 .EQUF_Bit,0 x40 F=1,则禁止FIQ中断 .EQUT_bit,0 x20 T=1,为Thumb状 态,=0为ARM状态 .EQU MEMMAP 0 xE01FC040 .EQUTop_Stack,(0 x40004000 - 0 x20) 栈顶地

39、址,保留32个字节 .EQU UND_Stack_Size,0 x00000004 .EQU SVC_Stack_Size,0 x00000020 .EQU ABT_Stack_Size,0 x00000004 .EQU FIQ_Stack_Size,0 x00000004 .EQU IRQ_Stack_Size,0 x00000100 .EQU USR_Stack_Size,0 x00000200,.global _start .code 32 .text _start:程序入口 Vectors: LDR PC, Reset_Addr LDR PC, Undef_Addr LDR PC, S

40、WI_Addr LDR PC, PAbt_Addr LDR PC, DAbt_Addr DCD 0 xb9205f80 保留向量 LDR PC, PC, #-0 xff0 LDR PC, FIQ_Addr Reset_Addr: .longReset_Handler Undef_Addr: .long Undef_Handler SWI_Addr: .long SWI_Handler PAbt_Addr: .long PAbt_Handler DAbt_Addr: .long DAbt_Handler .long 0 保留地址 FIQ_Addr: .long FIQ_Handler,Undef

41、_Handler:BUndef_Handler PAbt_Handler: B PAbt_Handler DAbt_Handler: BDAbt_Handler SWI_Handler: B SWI_Handler ;li FIQ_Handler: B FIQ_Handler Reset_Handler:BL InitStack BL RemapSRAM .extern Main B Main #* InitStack: MOV R1, LR LDR R0, =Top_Stack MSR CPSR_c, #Mode_UND|I_Bit|F_Bit MOV SP, R0 SUB R0, R0,

42、#UND_Stack_Size,MSR CPSR_c, #Mode_ABT|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #ABT_Stack_Size MSR CPSR_c, #Mode_FIQ|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #FIQ_Stack_Size MSR CPSR_c, #Mode_IRQ|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #IRQ_Stack_Size MSR CPSR_c, #Mode_SVC|I_Bit|F_Bit MOV SP, R0 SUB R0, R0, #SVC_Sta

43、ck_Size MSR CPSR_c, #Mode_USR MOV SP, R0 MOV PC, R1,#* RemapSRAM: STMFD SP!, R0-R1,LR REMAPS: MOV R0, #0 x40000000 LDR R1, =Vectors LDMIA R1!, R2-R9 STMIA R0!, R2-R9 LDMIA R1!, R2-R9 STMIA R0!, R2-R9 LDR R0, =MEMMAP MOV R1, #0 x02 STR R1, R0 LDMFD SP!, R0-R1,PC .end,10.4 基本IO程序(含启动代码),基本I/O主程序,由于在上述

44、的启动代码中,只是进行了基本的初始化操作,还 有一些系统初始化的操作没有进行,诸如初始化中断向量控制 器以及初始化C程序变量等,因此这些工作需要在C程序中完 成。sys_init()为系统初始化子程序,其中包含使能锁相环PLL、 设置存储器加速模块和初始化中断向量控制器。 程序开头的头文件target.h中含有程序中用到的所有符号的定义, 144 脚封装的LPC2200最多可以有112个GPIO口,在使用前应遵 循如下规则:首先将管脚通过管脚功能选择寄存器设置为GPIO 功能;设定GPIO管脚的输入/输出功能;进行相应的输出管脚 置位、清零,输入管脚数据采集操作。,C语言程序如下: #incl

45、ude target.h void time_dly(uint32 dly) uint32 i; for(; dly0; dly-) for(i=0; i500; i+); void sys_init() PLLCON = 1;/PLL使能 VPBDIV = PLL_VPBDIV;/设置VPB分频值 PLLCFG = PLL_CFG;/设置PLL倍频值和分频值,决定PLL频率Fcclk PLLFEED = 0 xaa; /使PLLCON和PLLCFG更改生效 PLLFEED = 0 x55;,while(PLLSTAT ,sys_init(); / 初始化I/O口:P0.7 IODIR0 =

46、0 x80; /设置P0.7为输出口 IOSET0 = 0 x80; /P0.7置1 do /使P0.7的电平高低循环变化 time_dly(1000); IOCLR0 = 0 x80; time_dly(1000); IOSET0 = 0 x80; while(1); ,10.5 SPI通信程序,当LPC2200芯片的SPI接口设置为主机时,其操作过程如下:,1、设置I/O口的连接; 2、将SPI时钟计数寄存器SPCCR设置为所需要的值,得到相应的 SPI时钟; 3、设置SPI控制寄存器SPCR为主机模式,并确定时钟极性; 4、片选从机,将要发送的数据写入SPI数据寄存器SPDR,此写 操作

47、将启动SPI数据传输; 5、等待SPI状态寄存器SPSR中的SPIF置位,若SPIF置位则数 据发送完成; 6、读取SPI状态寄存器; 7、从SPI数据寄存器中读出接收到的数据(可选)。 注:读或写SPI数据寄存器用来清零SPIF状态位。因此,如果不执行可选的 SPI数据寄存器读操作,则需要执行该寄存器的写操作以清零SPIF状态位。,10.5 SPI通信程序,当SPI接口设置为从机时,其操作过程如下: 要求驱动SPI 逻辑的系统时钟速度至少8倍于SPI。,1、设置I/O口的连接; 2、设置SPI控制寄存器SPCR为从机模式,并确定时钟极性; 3、将要发送的数据写入SPI数据寄存器SPDR(可选

48、),注意这 只能在从SPI传输没有进行时执行; 4、等待SPI状态寄存器SPSR中的SPIF位置位,若SPIF置位则 接收到主机发送的数据; 5、读取SPI状态寄存器; 6、从SPI数据寄存器中读出接收到的数据(可选)。 注:读或写SPI 数据寄存器用来清零SPIF状态位,因此至少需要执行一个该寄存器的读或写操作来清零SPIF状态位。,10.5 SPI通信程序,SPI初始化,在使用SPI总线前,必须对SPI接口进行初始化。SPI总线频率由 VPB频率(fpclk)决定:SPI频率=fpclk/fdiv,其中fdiv为 SPI总线的分频值。设置SPI为主机工作模式,禁止SPI中断,传 输数据时M

49、SB位在先,时钟极性为SCK低有效,且数据在SCK的第 一个时钟沿采样。则SPI的初始化程序为: void SPI_Init (void) SPCCR = 0 x52;/设置SPI时钟分频 SPCR = 0 x30;/设置SPI接口模式,MSTR=1,CPOL=1,CPHA=0,LSBF=0 ,10.5 SPI通信程序,发送数据,假设使用P0.0作为SPI从机的选择信号SLAVE_SPI_CS,则SPI 发送数据子程序如下: #define SLAVE_SPI_CS 0 x00000001 uint8 SPI_send(uint8 data) IO0CLR = SLAVE_SPI_CS;/片选

50、 SPDR = data;/数据入SPI数据寄存器 while(0= SPSR ,10.5 SPI通信程序,主程序,int Main(void) uchar i; PINSEL0 = 0 x00005500;/设置SPI管脚连接,且 P0.0为IO管脚 IO0DIR = SLAVE_SPI_CS;/设置P0.0方向为输出 SPI_Init( ); for(i=0;i=9;i+)/发送09 10个数据 SPI_send( i ); delay(10); ,10.6 串口通信程序,串口通信通常包括如下过程:,1、将I/O脚设置为串行通信管脚; 2、设置通信波特率(U0DLM/U1DLM、U0DLL

51、/U1DLL); 3、设置串口工作模式(U0LCR/U1LCR、U0FCR/U1FCR); 4、发送或接收数据(U0THR/U1THR、U0RBR/U1RBR); 5、检查串口状态字或等待串口中断(U0LSR/U1LSR)。 下面以UART0为例,介绍串口操作的程序。,10.6 串口通信程序,串口初始化程序,若fosc为10MHz,PLL倍频值为4,VPB分频值为4,要求串口通信波特率为 9600bps,则串口初始化程序如下: void UART0_init(void) PINSEL0=0 x05;/设置P0.0、P0.1分别为TxD、RxD U0LCR=0 x80;/DLAB=1,可设置波特率 bauddiv= (100000004)/9

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论