【文档说明】第4章-ARM嵌入式系统程序设计及调试基础课件.ppt,共(113)页,2.850 MB,由飞向未来上传
转载请保留链接:https://www.ichengzhen.cn/view-2494.html
以下为本文档部分文字说明:
第4章ARM嵌入式系统程序设计及调试基础1第4章ARM嵌入式系统程序设计及调试基础4.1ARM嵌入式汇编语言程序设计基础4.2ARM嵌入式C语言程序设计基础4.3ARM汇编语言与C/C++的混合编程4.4ARMADS集成开发环境的使用4.5EmbestIDE集成开发环境的使用第4章ARM嵌入式系统
程序设计及调试基础2在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相应的操作码,通常称这些特殊指令助记符为伪指令,他们所完成的操作称为伪操作。伪指令在源程序中的作用是为完成汇编语言程序作各种准备工作的,这些伪指令仅在汇编过程起作用,一旦汇编结
束,伪指令的使命就完成。在ARM得汇编程序中,有符号定义(SymbolDefintion)伪指令,数据定义(DataDefinition)伪指令,地址读取伪指令,汇编控制(AssemblyControl)伪指令,宏指令以及其它伪指令。4.1ARM嵌入式汇编语言
程序设计基础4.1.1ARM汇编器支持的伪指令第4章ARM嵌入式系统程序设计及调试基础31.符号定义伪指令1)GBLA、GBLL和GBLS格式:GBLA(GBLL或GBLS)全局变量名用途:定义一个ARM程序中的全局变量,并将其初始化。其中GBLA伪指令用于定义一个全局的数字变量,并初始化
为0;GBLL伪指令用于定义一个全局的逻辑变量,并初始化为F(假);GBLS伪指令用于定义一个全局的字符串变量,并初始化为空。由于以上3条伪指令用于定义全局变量,因此在整个程序范围内变量名必须唯一。例如:GBLATest1;定义一个全
局的数字变量,变量名为Test1Test1SETA0xAA;将该变量赋值为0xAAGBLLTest2;定义一个全局的逻辑变量,变量名为Test2Test2SETL{TRUE};将该变量赋值为真GBLSTest3
;定义一个全局的字符串变量,变量名为Test3Test3SETS“Testing”;将该变量赋值为“Testing”第4章ARM嵌入式系统程序设计及调试基础42)LCLA、LCLL、LCLS格式:LCLA(LCLL或LCLS)局部变量名用途
:定义一个ARM程序中的局部变量,并将其初始化。其中LCLA伪指令用于定义一个局部的数字变量,并初始化为0;LCLL伪指令用于定义一个局部的逻辑变量,并初始化为F(假);LCLS伪指令用于一个局部的字符串变量,并初始化为空。由于以上3条伪指令用
于声明局部变量,因此在其作用范围内变量名必须唯一。例如:LCLATest4;声明一个局部得数字变量,变量名为Test4Test4SETA0xAA;将该变量赋值为0xAALCLLTest5;声明一个局部的逻辑变量,变量名为Test
5Test5SETL{TURE};将该变量赋值为真LCLSTest6;定义一个局部的字符串变量,变量名为Test6Test6SETS“Testing”;将该变量赋值为“Testing”第4章ARM嵌入式系统程
序设计及调试基础53)SETA、SETL、SETS格式:变量名SETA(SETL或SETS)表达式用途:给一个已经定义的全局变量或局部变量赋值。其中变量名为已经定义过的全局变量或局部变量,表达式为将要赋值给变量的值。SETA伪指令用于给一个数学变量赋值;SETL伪指令用于
给一个逻辑变量赋值;SETS伪指令用于给一个字符串变量赋值。例如:LCLATest3;声明一个局部的数字变量,变量名为Test3Test3SETA0xAA;将该变量赋值为0xAALCLLTest4;声明一个局部的逻辑变量,变量名为Test5Test4SETL{TRUE};将该变量
赋值为真第4章ARM嵌入式系统程序设计及调试基础64)RLIST格式:名称RLIST{寄存器列表}用途:对一个通用寄存器列表定义名称,使用该伪指令定义的名称可在ARM指令LDM/STM中使用。在LDM/STM指令中
,列表中的寄存器访问次序为根据寄存器的标号由低到高,而与列表中得寄存器排列次序无关。例如:RegListRLIST{R0–R5,R8,R10};将寄存器列表名称定义为RegList,可在ARM指令LDM/STM中通过该名称访问寄存器列表。第4章ARM嵌入式系统
程序设计及调试基础72.数据定义伪指令1)DCB格式:标号DCB表达式用途:分配一片连续的字节存储单元并用伪指令中的表达式初始化。其中,表达式可以为0~255的数字或字符串。DCB伪指令也可用“=”代替。例如:StrDCB“Thisisatest!”;分配一
片连续的字节存储单元并初始化2)DCW(或DCWU)格式:标号DCW(或DCWU)伪指令用途:分配一片连续的半字节存储单元,并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。DCW伪指令和DCWU伪指令的区别仅在于用DCW伪指令分配的
字存储单元是半字对齐,而用DCWU伪指令分配的字存储单元并不严格半字对齐。例如:DataTestDCW1,2,3;分配一片连续的半字存储单元并初始化第4章ARM嵌入式系统程序设计及调试基础83)DCD(或DCDU)格式:标号DCD(或DCDU)表达式用途:分配一片连续的字存储单元并
用伪指令指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。DCD伪指令也可用“&”代替。DCD伪指令和DCDU伪指令的区别仅在于用DCD伪指令分配的字存储单元是字对齐的,而用DCDU伪指令分配的字存储单元并不严格字对齐
。例如:FdataTestDCD4,5,6;分配一片连续的字存储单元并初始化4)DCFD(或DCFDU)格式:标号DCFD(或DCFDU)表达式用途:为双精度的浮点数分配一片连续的字存储单元并用伪指令中指定得表达式初始化。
每个双精度的浮点数占据2个字单元。DCFD伪指令和DCFDU伪指令的区别仅在于用DCFD伪指令分配的字存储单元是字对齐的,而用DCFDU伪指令分配的字存储单元并不严格对齐。例如:FdataTestDCFD2E115,-5E7;分配一片连续的字存储单元并初
始化为指定的双精度数第4章ARM嵌入式系统程序设计及调试基础95)DCFS(或DCFSU)格式:标号DCFS(或DCFDU)表达式用途:为单精度得浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个单精度的浮点数占据1个字单元。DCF
S伪指令和DCFSU伪指令的区别仅在于用DCFS伪指令分配的字存储单元是字对齐的,而用DCFSU伪指令分配的字存储单元并不严格字对齐。例如:FDataTESTDCFS2E5,-5E-7;分配一片连续的字存储单元并初
始化为指定的单精度数6)DCQ(或DCQU)格式:标号DCQ(或DCQU)表达式用途:分配一片以8个字节为单位的连续存储区域并用伪指令中指定的表达式初始化。DCQ伪指令和DCQU伪指令的区别仅在于用DCQ伪指令分配的存储单元是字对齐的,而用DCQU伪
指令分配的存储单元并不严格字对齐。例如:DataTestDCQ100;分配一片连续的字存储单元并初始化为指定的值第4章ARM嵌入式系统程序设计及调试基础107)SPACE格式:标号SPACE表达式用途:SPACE伪指令用于分配1片连续的
存储区域并初始化为0。其中,表达式为要分配的字节数。SPACE也可用“%”代替。例如:DataSpaceSPACE100;分配连续100字节的存储单元并初始化为08)MAP格式:MAP表达式{,基址寄存器}用途:定义结构化的内存表的首地址。MAP伪指令也可用“^”代替。表达式可以为程序中的
标号或数学表达式,基址寄存器为可选项,当基址寄存器选项不存在时,表达式的值即是内存表的首地址,当该选项存在时,内存表的首地址为表达式的值与基址寄存器的和。MAP伪指令仅用于定义数据结构,并不实际分配存储单元,通常可与FIELD伪指令配合使用来定义结构化的内存表
。例如:MAP0x100,R0;定义结构化的内存表的首地址的值位0x100+R0第4章ARM嵌入式系统程序设计及调试基础119)FILED格式:标号FIELD表达式用途:定义一个结构化内存表中的数据域,其中,表达式的值为当前数据域在内存表中所占的字节数。FIELD伪指令也可用“#”代替。
与MAP伪指令相同,FIELD伪指令仅用于定义数据结构,并不实际分配存储单元。FIELD伪指令常与MAP伪指令配合使用来定义结构化的内存表。MAP伪指令定义内存表的首地址,FIELD伪指令定义内存表中的各个数据域,并可以为每个数据指定一个标号供其他的指令引用。例如:MAP0x100;
定义结构化内存表首地址的值为0x100AFIELD16;定义A的长度为16字节,位置为0x100BFIELD32;定义B的长度为32字节,位置为0x110SFIELD256;定义S的长度为256字节,位置为0x130第4章ARM嵌入式系统程序设计及调试基础123.地址读取伪指令
第4章ARM嵌入式系统程序设计及调试基础131)ADR伪指令——小范围的地址读取在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现ADR伪指令的功能,若不能用一条指令实现,则产生错误,
编译失败。ADR伪指令中的地址是基于PC或寄存器的,当ADR伪指令中的地址是基于PC时,该地址与ADR伪指令必须在同一个代码段中。地址表达式expression的取值范围如下:当地址值是字节对齐时,其取值范围为-255B~255B;当地址值是字对齐
时,其取值范围为-1020B~1020B。例如:LOOPMOVR0,#10;LOOP为行标,指示某一行代码ADRR4,LOOP;将LOOP地址放入r4(相对地址),因为PC值为当前指令地址值加8字节,替换成ADR伪伪
指令将被编译器译为;SUBR4,PC,0xC;NOP(MOVR0,R0)第4章ARM嵌入式系统程序设计及调试基础142)ADRL伪指令——中等范围的地址读取ADRL比ADR伪指令可以读取更大范围的地址。
在汇编编译器编译源程序时,ADRL伪指令被编译器替换成两条合适的指令。若不能用两条指令实现,则产生错误,编译失败。地址表达式expression的取值范围如下:当地址值是字节对齐时,其取值范围为-64KB~64KB;当地址值是字对齐时,其取值范围为-256KB~256KB。例如:LOOP
MOVR0,#10;LOOP为行标,指示某一行代码ADRLR4,LOOP;将LOOP地址放入R4(相对地址),因为PC值为当前指令地址值加8字节,替换成本ADRL伪伪指令将被编译器译为;SUBR4,PC,#0xC;NOP(MOVR0,R0)第4章ARM嵌入式系
统程序设计及调试基础153)LDR伪指令——大范围的地址读取在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将
常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。例如:LDRR1,=0xFF;将0xFF读取到R1中,编译后得到MOVR1,0xFF例如:LDRR1,=ADDR;将外部地址ADDR读取到R1中,汇编后将得到:;L
DRR1,[PC,OFFSET_TO_LPOOL];…;LPOOLDCDADDR第4章ARM嵌入式系统程序设计及调试基础164.汇编控制伪指令汇编控制伪指令用于控制汇编程序的执行流程。1)IF、ELSE、ENDIF格式:IF逻辑表达式指令序列1ELSE指令序列2ENDIFIF
、ELSE、ENDIF伪指令能根据条件的成立与否决定是否执行某个指令序列。当IF后面的逻辑表达式为真,则执行指令序列1,否则执行指令序列2。其中,ELSE及指令序列2可以没有,此时,当IF后面的逻辑表达式为真,则执行
指令序列1,否则继续执行后面的指令。IF、ELSE、ENDIF伪指令可以嵌套使用。例如:GBLLTest;声明一个全局的逻辑变量,变量名为Test……IFTest=TRUE指令序列1ELSE指令序列2ENDIF第4章ARM嵌入
式系统程序设计及调试基础172)WHILE、WEND格式:WHILE逻辑表达式指令序列WENDWHILE、WEND伪指令能根据条件的成立与否决定是否循环执行某个指令序列。当WHILE后面的逻辑表达式为真,则执行指令序列,该指令序列执行完毕后,再判断逻辑表达式的值,若为真
则继续执行,一直到逻辑表达式的值为假。WHILE、WEND伪指令可以嵌套使用。例如:GBLACounter;声明一个全局的数学变量,变量名为CounterCouterSETA3;由变量Counter控制循环次数……WHILECounter<
10指令序列WEND第4章ARM嵌入式系统程序设计及调试基础185.宏指令1)MACRO、MEND格式:$标号宏名$参数1,$参数2,……指令序列MENDMACRO、MEND伪指令可以将一段代码定义为一个整体
,称为宏指令,然后就可以在程序中通过宏指令多次调用该段代码。其中,$标号在宏指令被展开时,标号会被替换为用户定义的符号,宏指令可以使用一个或多个参数,当宏指令被展开时,这些参数被相应的值替换。宏指令的使用方式和功能与子程序相似,子程序可以提供模块化的程序设计、节省存储空间
并提高运行速度。但在使用子程序结构时需要保护现场,从而增强了系统的开销,因此,在代码较短且需要传递的参数较多时,可以使用宏指令代替子程序。第4章ARM嵌入式系统程序设计及调试基础19包含在MACRO和MEND之间的指令序列称为宏定义体,在宏定义体的第一行应声明宏的原型(包含宏名、所需的参
数),然后就可以在汇编程序中通过宏名来调用该指令序列。在源程序被编译时,汇编器将宏调用展开,用宏定义中的指令序列代替程序中的宏调用,并将实际参数的值传剃给宏定义中的形式参数。MACRO、MEND伪指令可以嵌套使用。2
)MEXIT格式:MEXITMEXIT用于从宏定义中跳转出去。第4章ARM嵌入式系统程序设计及调试基础206.其它常用的伪指令1)AREA格式:AREA段名属性1,属性2,……用途:定义一个代码段或数据段。属性字段表示该代码段(或数据段)的相关属性
,多个属性用逗号分隔。常用的属性如下。(1)属性CODE用于定义代码段,默认为READONLY。(2)属性DATA用于定义数据段,默认为READWRITE。(3)属性READONLY指定本段为只读,代码段默认为READONLY。(4)属性READWRITE指定本段为可读可写,数据段的默认属性
为READWRITE。(5)属性ALIGN表示使用方式为ALIGN表达式。在默认时,ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。(6)属性COMMON定义一个通用的段,不
包括任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元。第4章ARM嵌入式系统程序设计及调试基础21一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。例如:AREAInit,CODE,READONLY指
令序列;该伪指令定义了一个代码段,段名为Init,属性为只读2)ALIGN格式:ALIGN{表达式{,偏移量}}用途:ALIGN伪指令可通过添加填充字节的方式,使当前位置满足一定的对齐方式。其中,表达式的值用于指定对齐方式,可能的取值为2的幕,如1、2、4、8、16等。若未指
定表达式,则将当前位置的对齐方式为:2的表达式次幕加偏移量。例如:AREAInit,CODE,READONLY,ALIGN=3;指定后面的指令为8字节对齐指令序列END第4章ARM嵌入式系统程序设计及
调试基础223)CODE16(或CODE32)格式:CODE16(或CODE32)用途:CODE16伪指令通知编译器,其后的指令序列为16位的Thumb指令;CODE32伪指令通知编译器,其后的指令序列为32位的ARM指令。若在汇编源程序
中同时包括ARM指令和Thumb指令时,可用CODE16伪指令通知编译器其后的指令序列为16位的Thumb指令,CODE32伪指令通知编译器其后的指令序列为32位的ARM指令。因此,在使用ARM指令和Thumb指令混合编程的代
码里,可用这两条伪指令进行切换,但注意他们只通知编译其后指令的类型,并不能处理器进行状态的切换。第4章ARM嵌入式系统程序设计及调试基础23例如:AREAInit,CODE,READONLY……CODE32;通知编译器其后的指令为32位的ARM指令
LDRR0,=NEXT+1;将跳转地址放入寄存器R0BXR0;程序跳转到新的位置执行,并将处理器切换到Thumb工作状态……CODE16;通知编译器其后的指令为16位的Thumb工作状态NEXTLDRR3,=0x3FF……END;程序结束第
4章ARM嵌入式系统程序设计及调试基础244)ENTRY格式:ENTRY用途:指定汇编程序的入口点。在一个完整的汇编程序中至少要有一个ENTRY(也可以有多个,当有多个ENTRY时,程序的真正入口点由链接器指定),例如:AREAInit,CODE,READONLYENTRY;指定应用程序的入
口点……5)END格式:END用途:用于通知编译器已经到了源程序的结尾。例如:AREAInit,CODE,READONLY……END;指定应用程序的结尾第4章ARM嵌入式系统程序设计及调试基础254)ENTRY格式:ENTRY用途:指定汇编程序的入口点。在一个完
整的汇编程序中至少要有一个ENTRY(也可以有多个,当有多个ENTRY时,程序的真正入口点由链接器指定),例如:AREAInit,CODE,READONLYENTRY;指定应用程序的入口点……5)END格式:END用途:用于通知编译器已经到了源程序的结尾
。例如:AREAInit,CODE,READONLY……END;指定应用程序的结尾第4章ARM嵌入式系统程序设计及调试基础268)IMPORT格式:IMPORT标号{[WEAK]}用途:通知编译器要使用的标号在其它的源文件中定义,但要在当前源文件引用,而且无论当前源
文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。标号在程序中区分大小写,[WEAK]选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号值为0,若该标号为B或BL指令引用,则将B或BL
指令值为NOP操作。例如:AREAInit,CODE,READONLYIMPORTMain;通知编译器当前文件要引用标号Main,但Main在其他源文件中定义……END第4章ARM嵌入式系统程序设计及调试基础279)EXTERN格式:EXTERN标号{[WEAK]}用途:通知编译
器要使用的标号在其它的源文件中定义,但要在当前源文件引用,如果当前源文件是否引用该标号,该标号就不会被加入到当前源文件的符号表中。标号在程序中区分大小写,[WEAK]选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数
情况下将该标号值为0,若该标号为B或BL指令引用,则将B或BL指令值为NOP操作。例如:AREAInit,CODE,READONLYEXTERNMain;通知编译器当前文件要引用标号Main,但Main在其他源文件中定义……END第4章ARM嵌入式系统程序设计及调试基础2810)
GET(或INCLUDE)格式:GET文件名用途:将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。可以使用INCLUDE代替GET。汇编程序中常用的方法是在某源文件中定义一些宏指令,用EQU定义常量的符号名称,用MAP和FIELE定
义结构化的数据类型,然后用GET伪指令将这个源文件包含到其他的源文件中。使用方法与C语言中的“include”相似。GET伪指令只能用于包含源文件,包含目标文件使用INCBIN伪指令。例如:AREAInit,CODE,READONLYGETa1,s;通知编译器当前源文件包含源文件a1.s
GETC:\a2.s;通知编译器当前源文件包含源文件C:\a2.s……END第4章ARM嵌入式系统程序设计及调试基础2911)INCBIN格式:INCBIN文件名用途:将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不作任何变动地存放在当前文件中,编译器从其后开始继续处理。例如:A
REAInit,CODE,READONLYINCBINa1.dat;通知编译器当前源文件包含文件a1.datINCBINC:\a2.txt;通知编译器当前源文件包含文件C:\a2.txt……END第4章ARM嵌入式系统程序设计及调试基础3012)RN格式:名称RN表达式用途:给一个寄存
器定义一个别名。采用这种方式可以方便程序员记忆该寄存器的功能。其中,名称为给寄存器定义的别名,表达式为寄存器的编码。例如:TampRNR0;将R0定义一个别名Temp13)ROUT格式:{名称}ROUT用途:给一个局部
变量定义作用范围。在程序中未使用该伪指令时,局部变量的作用范围为所在的AREA,而使用ROUT后,局部变量的作为范围当前ROUT和下一个ROUT之间。第4章ARM嵌入式系统程序设计及调试基础31ARM(Thumb)汇编语言的语句格式为:{标号}{指令或伪指令}{;注释}在汇编语言程序
设计中,每一条指令的助记符可以全部用大写、或全部用小写,但不允许在一条指令中大、小写混用。如果一条语句太长,可将用该语句分为若干行来书写,在行的末尾用“\”表示下一行与本行为同一条语句。1.在汇编语言程序中常用的符号在汇
编语言程序设计中,经常使用各种符号代替地址、变量和常量等,以增加程序的可读性。尽管符号的命名由编程者决定,但并不是任意的,必须遵守以下的约定:①符号区分大小写,同名的大、小写符号会被编译器认为是两个不同的符号;②符号在其作用范
围必须唯一;③自定义的符号名不能与系统的保留字相同;④符号名不应与指令或伪指令同名。4.1.2ARM汇编语言的语句格式第4章ARM嵌入式系统程序设计及调试基础321)程序中的变量程序中的变量是指其值在程序的运行过程中可以改变的量。ARM(或Thumb)汇编
程序所支持的变量有数字变量、逻辑变量和字符串变量。其中数字变量用于在程序的运行中保存数字值,但注意数字值的大小不应超过数字变量所能表示的范围;逻辑变量用于在程序的运行中保存逻辑值,逻辑值只有真或假2种取值
的情况;字符串变量用于在程序的运行中保存一个字符串,但注意字符串的长度不应超过字符串变量所能表示的范围。2)程序中的常量程序中的常量是指其值在程序的运行过程中不能被改变的量。ARM(Thumb)汇编程序所支持的常量有数字常量、逻辑常量和字符串常量。其中数字常
量一般为32位的整数,当作为无符号数时,其取值范围为0~232–1;当作为有符号数时,其取值范围为-232~232–1。逻辑常量只有真或假2种取值情况。字符串常量为一个固定的字符串,一般用于程序运行时的信息提示。第4章ARM嵌入式系统程序设
计及调试基础333)程序中的变量代换程序中的变量可通过代换操作取得一个常量。代换操作符为“$”。如果在数字变量前面有一个代换操作符“$”,编译器会将该数字变量的值转换为十六进制的字符串,并将该十六进制的字符串代换“$”后的数字变量;如果在逻辑变量前面有一
个代换操作符“$”,编译器会将该逻辑变量代换为它的取值(真或假);如果在字符串变量前面有一个代换操作符“$”,编译器会将该字符串变量的值代换“$”后的字符串变量。例如:LCLSS1;定义局部字符串变量S1和S2LCLSS2S1SETS“Test!“S2SE
TS“Thisisa$S1”;字符串变量S2值为“ThisisaTest”第4章ARM嵌入式系统程序设计及调试基础342.汇编语言程序中的表达式和运算符在汇编语言程序设计中,经常使用各种表达式,表达式一般由变量、常量、运算符和括号构成,常用的表达式有数字表达式、逻辑表达式及字符串表达式3种
,其运算顺序遵循如下优先级:①优先级相同的双目运算符的运算顺序从左到右;②相邻的单目运算符的运算顺序为从右到左,且单目运算符的优先级高于其他运算符;③括号运算符的优先级最高。第4章ARM嵌入式系统程序设计及调试基础351)数字表达式及运算符数字表达式一般由数字常量、数
值变量、数字运算符和括号构成。与数字表达式相关的运算符有算术运算符、移位运算符及按位逻辑运算符。(1)算术运算符。数字表达式中的算术运算符有“+”、“-”、“*”、“/”及“MOD”,分别代表加、减、乘、除及取余数运算。以X和Y表示两个
数字表达式,这些算术运算符代表的运算如下:X+Y表示X与Y的和;X-Y表示X与Y的差;X*Y表示X与Y的乘积;X/Y表示X处以Y的余数。(2)移位运算符。数字表达式中的移位运算符有“ROL”、“ROR”、“SHL”及“SHR”,以X和
Y表示两个数字表达式,这些移位运算符代表的运算如下:X:ROL:Y表示将X循环左移Y位;X:ROR:Y表示X循环右移Y位;X:SHL:Y表示将X左移Y位;X:SHR:Y表示将X右移Y位。第4章ARM嵌入式系统程序设计及调试基础36(3)按位逻辑
运算符。数字表达式中的按位逻辑运算符有“AND”、“OR”、“NOT”及“EOR”,以X和Y表示两个数字表达式,以上的按位逻辑运算符代表的运算如下:X:AND:Y表示将X和Y按位作逻辑与的操作;X:OR:Y
表示将X和Y按位作逻辑或的操作;:NOT:Y表示将Y按位作逻辑非的操作;X:EOR:Y表示将X和Y按位作逻辑异或的操作。2)逻辑表达式及运算符逻辑表达式一般由逻辑量、逻辑运算符和括号构成,其表达式的运算结果为真或假。与逻辑表达式相关的运算符如下:(1)“=”、“>”、“<”、“>=”、“<=”、
“/=”、“<>”运算符。以X和Y表示两个逻辑表达式,以上的运算符代表的运算如下:X=Y表示X等于Y;X>Y表示X大于Y;X<Y表示X小于Y;X>=Y表示X大于等于Y;X<=Y表示X小于等于Y;X/=Y表示X不等于Y;X<>Y表示X不等于Y。第4章ARM嵌入式系统程序设计及调试基础37(2
)“LAND”、“LOR“、“LNOT”及“LEOR”运算符。以X和Y表示两个逻辑表达式,以上的逻辑运算符代表的运算如下:X:LAND:Y表示将X和Y作逻辑与的操作;X:LOR:Y表示将X和Y作逻辑或的操作;:LNOT:Y表示将Y作逻辑非的操作;X:LEOR:Y表示将X和Y作逻辑异或的操
作。3)字符串表达式及运算符字符串表达式一般由字符串常量、字符串变量、运算符和括号组成。编译器所支持的字符串的最大长度为512字节。常用的与字符串表达式相关的运算符如下。(1)LEN运算符。LEN运算符返回字符串的长度(字符数),
以X表示字符串表达式,其语法格式如下::LEN:X(2)CHR运算符。CHR运算符将0~255之间的整数转换为一个字符,以M表示某一个整数,其语法格式如下::CHR:M第4章ARM嵌入式系统程序设计及调试基础38(3)STR运算符。STR运
算符将一个数字表达式或逻辑表达式转换为一个字符串。对于数字表达式,STR运算符将其转换为一个以十六进制组成的字符串;对于逻辑表达式,STR运算符将其转换为字符串T或F,其语法格式如下::STR:X其中X为一
个数字表达式或逻辑表达式。(4)LEFT运算符。LEFT运算符返回某个字符串左端的一个字串,其语法格式如下:X:LEFT:Y其中X为源字符串,Y为一个整数,表示要返回的字符个数。(5)RIGHT运算符。与LEFT运算符相对应,RIGHT运算符返回某个
字符串右端的一个字串,其语法格式如下:X:RIGHT:Y其中X为源字符串,Y为一个整数,表示要返回的字符个数。第4章ARM嵌入式系统程序设计及调试基础39(6)CC运算。CC运算符用于将两个字符串连接成一个字符串,其语法格式如下:X:CC:Y其中X为
源字符串1,Y为源字符串2,CC运算符将Y连接到X后面。4)与寄存器和程序计数器(PC)相关的表达式及运算符常用的与寄存器和程序计数器相关的表达式及运算符如下:(1)BASE运算符。BASE运算符返回基于寄存器的表达式中
寄存器的编号,其语法格式如下::BASE:X其中X为与寄存器相关的表达式(2)INDEX运算符。INDEX运算符返回基于寄存器的表达式中相对于其基址寄存器的偏移量,其语法格式如下::INDEX:X其中,X为与寄存器相关的表达式5)其他常用运算符(1)?运算符。?运算符返回某代码行
所生成的可执行代码的长度,如“?X”表示返回定义符号X的代码行所生成的可执行代码的字节数。(2)DEF运算符。DEF运算符判断是否定义某个符号,如:DEF:X:表示当符号X已经定义,结果为真;否则为假。第4章ARM嵌入式系统程序设计及调试基础401.汇编语言的
程序结构在ARM(Thumb)汇编语言程序中,以程序段位单位组织代码。段是相对独立的指令或数据序列,具有特定的名称。段可以分为代码段和数据段,代码段的内容为执行代码,数据段存放运行时需要用到的数据。一个汇编程序至少要有一个代码段,当程序较长时,可以分割为多个代码段和数据段,多个段在程序编译
链接时最终形成一个可执行的映象文件。可执行映象文件通常由以下几部分构成。(1)一个或多个代码段,代码段的属性为只读。(2)零个或多个包含初始化数据的数据段,其属性为可读写。(3)零个或多个不包含初始化数据的数据段,其属性为可读
写。链接器根据系统默认或用户设定的规则,将各个段安排在存储器中的相应位置。因此源程序中段之间的相对位置与可执行的映象文件中段的相对位置一般不会相同。4.1.3ARM汇编语言的程序结构第4章ARM嵌入式系统程序设计及调试基础41以下是一个汇编语言的程序基本结构:AREAInit,CODE,REA
DONLYENTRYSTARTLDRR0,=0x3FF5000LDRR1,#0xFFSTRR1,[R0]LDRR0,=0x3FF5008LDRR1,0x01STRR1,[R0]…………END。在汇编语言程序中,用AREA伪指令
定义一个段,并说明所定义段的相关属性,以上程序段中定义了一个名为Init的代码段,属性为只读。ENTRY伪指令标识程序的入口点,接下来为指令序列,程序的末尾为END伪指令。第4章ARM嵌入式系统程序设计及调试基础422.汇编语言的子程序调用
在ARM汇编语言程序中,子程序的调用一般是通过BL指令来实现的,其格式如下:BL子程序名该指令在执行时完成如下操作:将子程序的返回地址存放在连接寄存器(LR)中,同时将程序计数器(PC)指向子程序的入口点,当子程序执行完毕需要返回调用处时,
只需要将存放在(LR)中的返回地址重新拷贝给PC即可。在调用子程序的同时,也可以完成参数的传递和从子程序返回运算的结果,通常可以使用寄存器R0至R3完成。第4章ARM嵌入式系统程序设计及调试基础43C语言是一种结构化的
程序设计语言,它的优点是运行速度快、编译效率高、移植性好和可读性强。C语言具有简单的语法结构和强大的处理功能,并可方便的实现对硬件的直接操作。C语言支持模块化程序设计结构,支持自顶向下的结构化程序设计方法。因此C语言编写的应用软件,可大大提高软件的可读性,缩短开发
周期,便于系统的改进和扩充,这为开发大规模、高性能和高可靠性的应用系统提供了基本保证。嵌入式C语言程序设计是利用基本的C语言知识,面向嵌入式工程实际应用进行程序设计。嵌入式C语言程序设计首先是C语言程序设计,必须符合C语言基本语法。嵌入式C语言程序设计又是面向嵌入式的应用,因此就
要利用C语言基本知识开发出面向嵌入式的应用程序。如何能够在嵌入式系统开发中熟练、正确的运用C语言开发出高质量的应用程序,是学习嵌入式程序设计的基础。4.2ARM嵌入式C语言程序设计基础第4章ARM嵌入式系统程序设计及调试基础441.C语言的“预处理伪指
令”在嵌入式程序设计中的应用在C语言源程序中常常加入一些“预处理命令”,可以改进程序设计环境,提高编程效率,它虽然写在源程序中,但不产生程序代码,因此称为预处理伪指令。C语言所有预处理伪指令都是以#号开头,以区别于源文件中的语句行与说明行。预处理伪指令有以下三种:文件包含
、宏定义和条件编译。预处理伪指令有以下特点:把文件的正文替换进来,如标准头文件和自定义头文件;对宏定义进行宏扩展,减少了编程量,改进源程序的可读性;条件编译改善了编程的灵活性,也改善了可移植性。4.2.1嵌入式C语言程序设计基础第4章ARM嵌入
式系统程序设计及调试基础451)文件包含伪指令文件包含伪指令可将头文件包含到程序中,头文件中定义的内容包括符号常量、复合变量原型、用户定义的变量类型原型和函数的原型说明等。指令格式如下:#include<头文件名.h>;标准头文件#include“头文件名.h”;用户自定义头文件#i
nclude宏标识符文件包含伪指令举例如下:#include<string.h>;标准头文件#include<stdio.h>;标准头文件本例中string.h和stdio.h是标准头文件,按环境变量include指定的目录顺序搜索string.h和
stdio.h。第4章ARM嵌入式系统程序设计及调试基础462)宏定义伪指令(1)简单宏。定义格式为:#define宏标识符宏体。在定义宏时应尽量避免使用C语言的关键字和预处理器的预定义宏。(2)参数宏。定义格式为:#define宏标识符(形式参数表)宏体。使用参数宏
时,形式参数表应换为同样个十的实参数表,这一点类似函数的调用。(3)条件宏定义。先测试是否定义过某个宏标识符,然后决定如何处理。其定义格式如表4.2所示。(4)宏释放。用于释放原先定义的宏标识符。经释放后的宏标示符可再次用于定义其它宏体。其定义格式为:#undef宏标识符。第4章ARM嵌入
式系统程序设计及调试基础47第4章ARM嵌入式系统程序设计及调试基础483)条件编译伪指令条件编译伪指令是写给编译器的,指示编译器在满足某一条件时仅编译源文件中与之相应的比分。其格式如下:#if(条件表达式1)...#elif(条件表达式2)...#elif(条件
表达式n)...#else...#endif第4章ARM嵌入式系统程序设计及调试基础492.嵌入式程序设计中的函数及函数库1)函数的定义格式[存储格式说明符]类型说明符[修饰符]标识符(参数表){函数体}其中:存储格式说明
符有:static—静态存储类型,extern—外部存储类型(全局)两种。类型说明符有:char,unsignedchar、int、unsigned、long、unsignedlong、float、double、lon
gdouble、struct、union、void等几种。若函数返回的是指针,在函数名前加“*”。标识符有:函数名、*函数名—返回值是指针、(*函数名)—函数指针、*(*函数名))—标识符是指针,返回值是指针等几种。修饰符有:interrupt—为中断函数,其返回类型和参数均必须为void、n
ear—近调用(16位段内地址)、far—远调用(32位段间地址)、huge—规范化调用(32位段间规范地址)等几种。第4章ARM嵌入式系统程序设计及调试基础504.处理器寄存器到协处理器寄存器的数据
传送指令格式:MCR{条件}协处理器编号,操作码1,源寄存器,目的寄存器1,目的寄存器2,操作码2用途:将ARM处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和
协处理器操作码2为协处理器将要执行的操作,源寄存器为ARM处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄存器。例如:MCRP3,3,R0,C4,C5,6;该指令将ARM处理器寄存器R0中的数据传送到协处理器P3的寄存器C4和C
5中。第4章ARM嵌入式系统程序设计及调试基础512)函数库介绍以前面已提到的用户自定义头文件“44blib.h”为例进行说明。该头文件对程序开发中所用到的函数进行了声明,这些函数构成了一个基本函数库44blib.C,用于用户的程序开发,它不属于标
准的C语言。图4.1是44blib.c文件的代码结构图。从该图可以看出,用户如何根据自己开发板的硬件及功能模块来编写自定义函数库。第4章ARM嵌入式系统程序设计及调试基础52图4.144blib.c文件的代码结构图第4章ARM嵌入式系
统程序设计及调试基础533.嵌入式程序设计中常用的C语言语句1)if条件语句(1)两重选择if(条件表达式)语句1;else语句2;(2)多重选择if(条件表达式1)语句1;elseif(条件表达式2)语句2;elseif(条件表达式3)语句3;...elseif
(条件表达式n)语句n;第4章ARM嵌入式系统程序设计及调试基础542)switch/case语句switch(开关表达式){case常量表达式1:[语句1;]case常量表达式2:[语句2;]......cas
e常量表达式n:[语句n;]default:[语句n+1;]3)循环语句(1)for循环语句。for(表达式1;表达式2;表达式3)语句;(2)while语句。whlie(条件表达式)语句;(3)dowh
ile语句。do语句;whlie(条件表达式);第4章ARM嵌入式系统程序设计及调试基础554.嵌入式程序设计中C语言的变量、数组、结构和联合1)变量定义格式为:[存储类型]类型说明符[修饰符]标识符[=初值][,标识符[=初值]]…;其中:类型说明符:对于数字与字符,其类型共有9种:ch
ar,unsignedchar、int、unsigned、long、unsignedlong、float、double、longdouble。通过typedef来定义类型的别名。标识符:变量,是不带“*”的标识符。指针,是带有“*”的标识符。存储类型:auto—自动储存
的类型,是局部变量。register—寄存器储存类型。extern—外部储存类型。static—静态储存类型第4章ARM嵌入式系统程序设计及调试基础56赋初值部分:若初值缺省,则auto储存类型和register储存类型的变量为随机值;static储存类型的变量被编译器自动清0;
对于指针,无论什么存储类型,一律为空指针(Null)修饰符:const—常量修饰符,指示被修饰的变量或变量指针是常量。volatile—易失性修饰符,说明所定义的变量或指针,是可以被多种原因修改的。防止丢失任何一次这种修改,因此要把它修饰位易失性的变量。near、far—近、远修饰
符,用于说明访问内存中变量在位置上的远近。2)数组说明[存储类类型]类型说明符[修饰符]标识符[=初始值][,标识符[=初始值]]...;第4章ARM嵌入式系统程序设计及调试基础57(1)一维数组。定义格式为:类型说明符标识符[常量表达式][={初值,初值,…}];char标识符[]=“字符串
”;(2)一维指针数组和一维数组指针。定义格式为:一维指针数组:类型说明符*标识符[常量表达式][={地址,地址…}];一维数组指针:类型说明符(*标识符)[][=数组标识符];(3)二维数组。定义格式为:类型说明符标识符[m][n][={{初值表},{初值表}…}
];(4)二维指针数组。定义格式为:类型说明符*标识符[m][n][={{地址表}},{地址表}}];(5)二维数组指针。定义格式为:类型说明符(*标识符)[][n][=数组标识符];(6)多重指针。定义格式为:类型标识符**标识符[=&指针];第4章ARM嵌入式系统
程序设计及调试基础583)结构说明(1)原型法方法一:声明结构类型的同时定义变量名,其定义格式如下:[存储类说明符]struct[结构原型名]{类型说明符[,标识符…];类型说明符[,标识符…];…}标识
符[={初值表}][,标识符{={初值表}…}方法二:先声明结构类型再定义变量名,其定义格式如下:struct结构原型名{类型说明标识符[,标识符…];…}[存储类说明符]结构原型名标识符[={初值表}[,标识
符{={初值表}…};第4章ARM嵌入式系统程序设计及调试基础59(2)类型别名法。先为结构原型名起别名,再用别名做定义说明,其格式如下:typedefstruct[结构原型名]{类型说明符标识符[,标识符…];类型说明符标识符[,标识
符…];…}结构别名[存储类说明符]结构别名标识符[={初值表}[,标识符{={初值表}…};第4章ARM嵌入式系统程序设计及调试基础604)联合说明联合是在内村中定义的一段多种类型数据所共享的空间,空间的大小以最长的类型为准。(1)声明联合类型的同时定义变量名,其定义格式如下:[存储类
说明符]union[联合原型名]{类型说明符标识符[,标识符…];类型说明符标识符[,标识符…];…}标识符={初值表}[,标识符[={初值表}]…](2)先声明联合类型再定义变量名,其定义格式如下:union结构原型名{类型说明标识符[,标识符…];…}[存
储类说明符]union结构原型名标识符[={初值表}[,标识符{={初值表}…};第4章ARM嵌入式系统程序设计及调试基础61下面以S3VCE40开发板各功能模块的整个测试程序为例,介绍基于ARM的嵌入式C语言程序设
计结构。图4.2是S3VCE40开发板各功能模块的测试主程序组成结构图。从图4.3可以看出:一个完整的成功的主文件包括以下一个组成部分:①包含头文件:用#include指令将本文件所用到的头文件包含到
该程序中;②函数声明:将本文件中定义的函数进行函数声明;③各种类型的变量、数组定义:定义本文件中用到的各种类型的外部变量及数组;④本文件中各个函数代码的定义,其中必须包括一个主函数main()。4.2.2嵌入式C语言程序设计结构第4章ARM嵌入式系统程序设计及调试基础62图4.2S3VCE4
0开发板各功能模块的测试主程序组成结构图第4章ARM嵌入式系统程序设计及调试基础631.变量定义在变量声明时,最好把所有相同类型的变量放在一块定义,这样可以优化存储器布局。变量定义中,为了精简程序,程序员总是竭力避免使用冗余变量。2.参数传递为了使单独编译的C语言程序和汇编程序能够相互调用,定义了
统一的函数过程调用标准ATPCS。ATPCS定义了寄存器组中的{R0~R3}作为参数传递和结果返回寄存器。如果参数数目超过4个,则使用堆栈进行传递。由于内部寄存器的访问速度远远大于存储器,所以要尽量使参数传递在寄存器里面进行。3.循环条件在
C语言中,常用下面累加计数的循环形式:for(loop=1;loop<=limit;loop++),而下面这种递减计数的方法很少使用:for(loop=limit;loop!=0;loop--),但在ARM的体系结构下编程,
建议采用递减至0的方法来设置循环条件。4.2.3嵌入式C语言程序设计技巧第4章ARM嵌入式系统程序设计及调试基础64ATPCS即ARM-ThumbProcedureCallStandard。ATPCS规定了一些子程序之间调用的基本原则,这些基本规则包括子程序调用过程中寄存器的使用规则
,数据栈的使用规则,参数的传递规则。几种特定的子程序调用规则:支持数据栈限制检查的ATPCS,支持只读段位置无关的ATPCS,支持可读写段位置无关的ATPCS,支持ARM程序Thumb程序混合使用的ATPCS,处理浮点运算的ATPCS。有调用关系的所有子程序必须遵守同一种ATPCS
。编译器或者汇编器在ELF格式的目标文件中设置相应的属性,标识用户选定的ATPCS类型。对应不同类型的ATPCS规则,有相应的C语言库,连接器根据用户指定的ATPCS类型链接相应的C语言库。4.3ARM汇
编语言与C/C++的混合编程4.3.1ATPCS过程调用规范概述第4章ARM嵌入式系统程序设计及调试基础65使用ADS的C语言编译器编译的C语言子程序满足用户指定的ATPCS类型。而对于汇编语言程序来说,完
全要依赖用户来保证各子程序满足选定的ATPCS类型。具体来说,汇编语言子程序必须满足下面3个条件:在子程序编写时必须遵守相应的ATPCS规则;数据栈的使用要遵守ATPCS规则;在汇编编译器中使用-apcs选项。1.基本ATP
CS基本ATPCS规定了在子程序调用时的一些基本规则,包括以下三个方面的内容:各寄存器的使用规则及其相应的名字,数据栈的使用规则,参数传递的规则。相对于其它类型的ATPCS,满足基本ATPCS的程序的执行速度更快,所占用
的内存更少。但是它不能提供以下的支持:ARM程序和Thumb程序的相互调用,数据以及代码的位置无关的支持,子程序的可重入性,数据栈检查的支持。而派生的其它几种特定的ATPCS就是在基本ATPCS的基础上再添加其它的规则而形成的,其目的就是提供上述的功
能。第4章ARM嵌入式系统程序设计及调试基础662)数据栈的使用规则栈指针通常可以指向不同的位置。当栈指针指向栈顶元素时,称为FULL栈。当栈指针指向与栈顶元素相邻的一个元素时,成为Empty栈。数据栈的增
长方向也可以不同,当数据栈向内存减小的地址方向增长时,称为Descending栈;当数据栈向着内存地址增加的方向增长是,成为Ascending栈。综合这两种特点可以有以下4种数据栈,即FD、ED、FA、EA。ATPCS规定数据栈为FD类型,并对
数据栈的操作是8字节对齐的。1)寄存器的使用规则第4章ARM嵌入式系统程序设计及调试基础673)参数的传递规则根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变的子程序。这两种子程序的参数传递规则是不同的。(1)参数个数可变的子
程序参数传递规则。对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递;当参数超过4个时,还可以使用数据栈来传递参数。在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。
然后,依次将各字数据传送到寄存器R0,R1,R2,R3;如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后的一个字数据先入栈。按照上面的规则,一个浮点型参数可以通过寄存器传递,也可以通过数据栈传递,也可以一半通过寄存器传递,
另一半通过数据栈传递。第4章ARM嵌入式系统程序设计及调试基础68(2)参数个数固定的子程序参数传递规则。对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则不同,如果系统包含浮点运算的硬
件部件,浮点参数将按照下面的规则传递:各个浮点参数按顺序处理;为每个浮点参数分配FP寄存器;分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器。第1个整数参数通过寄存器R0~R3来传递,其它参数通过数据栈传递。(3)子程序结果返回规则当结果为一个32
位的整数时,可以通过寄存器R0返回。当结果为一个64位的整数时,可以通过R0~R1返回,以此类推。当结果为一个浮点数时,可以通过浮点运算部件的寄存器F0、D0或者S0来返回。当结果为一个复合的浮点数时,可以通过寄存器F0~F
n或者D0~Dn来返回。对于位数更多的结果,需要通过调用内存来处传递。第4章ARM嵌入式系统程序设计及调试基础692.支持ARM程序和Thumb程序混合使用的ATPCS在编译或汇编时,使用/intework告诉编译器或汇编器生成的目标
代码遵守支持ARM程序和Thumb程序混合使用的ATPCS,它用在以下场合:程序中存在ARM程序调用Thumb程序的情况;程序中存在Thumb程序调用ARM程序的情况;需要链接器来进行ARM状态和Thumb状态切换的情况;在下述情况下
使用选项nointerwork:程序中不包含Thumb程序;用户自己进行ARM程序和Thumb程序切换。需要注意的是:在同一个C/C++程序中不能同时有ARM指令和Thumb指令。第4章ARM嵌入式系统程序设计及调试基础70在应用系统的程序设计中,若所有的编程任务都用汇编语言
来完成,其工作量是可想而知的,同时,不利于系统升级或应用软件移植,事实上,ARM体系结构支持C/C+与汇编语言的混合编程,在一个完整的程序设计当中,除了初始化部分用ARM汇编语言完成以外,其主要的编程任
务一般都用C/C++完成。ARM汇编语言与C/C++的混合编程通常用以下几种方式:(1)在C/C++代码中嵌入汇编指令。(2)在汇编程序和C/C++的程序之间进行变量的互访。(3)汇编程序、C/C++程序间的相互调用。在实际的编程设计中,使用较多的方式是:程序的初始化
部分用汇编语言完成,然后用C/C++完成主要的编程任务,程序在执行时首先完成初始化过程,然后跳转到C/C++程序代码中,汇编程序和C/C++程序之间一般没有参数的传递,也没有频繁的相互调用,因此,整个程序的结构显得相对简单,容易理解。4.3.2汇编语言与C/C++的混合编程第4章ARM嵌
入式系统程序设计及调试基础711.从汇编程序访问C全局变量在使用嵌入式汇编时,有可能涉及汇编程序和C/C++的程序之间的数据传递问题,怎样从嵌入式汇编程序中获取访问C/C++程序中的全局变量?在通常情况下,可以访问全局变量的地址单元。要访问一个全局变量,应该先使用IMPO
RT伪指令引入这个全局变量,并利用LDR和STR指令根据全局变量的地址访问它们。对于不同类型的变量,需要采用不同选项的LDR和STR指令。下面例子是一个汇编代码的函数,它读取全局变量globval,将其
加1后写回。AREAglobals,CODE,READONLY;声明代码段globals,只读属性EXPORTasmsubroutine;可以被外部引用IMPORTglobval;声明引用外部变量globvalasmsubr
outineLDRR1,=globval;加载变量地址LDRR0,[R1];读出数据ADDR0,R0,#1;加1操作STRR0,[R1];保存变量MOVPC,LR;返回调用处END第4章ARM嵌入式系统程序设计及调试基础722.汇编程
序调用C程序汇编程序的设计要遵守基本的ATPCS规则,一边正确使用数据栈和预定义的寄存器。在汇编语言程序中应该使用IMPORT伪操作来声明要引用的C语言程序,并通过BL指令来调用子程序。下面是汇编程序调用C程序的例子。以下是汇编语言程序调用C程序的示例,汇编语言调用C语言程序所写的函数g,以
求出5个特定整数的和。//在C语言程序中,g()返回5个整数a,b,c,d,e的和intg(inta,intb,intc,intd,inte){returna+b+c+d+e;}第4章ARM嵌入式系统程序设计及调试基础73在下面的汇编语言程序中,调用C程序计算5个整数R0,2R0,3R0,4R0
,5R0的和EXPORTf;声明f可以被外部引用AREAf,CODE,READONLY;声明代码段f,只读属性IMPORTg;声明引用函数g()STRLR,[SP,#-4]!;保存返回地址ADDR1,R0,R0;R1=2R0ADDR2,R1,R0;R2=3R0ADDR3,R1,R2;R3=5R
0STRR3,[SP,#-4]!;把R3=5R0存储在数据栈中ADDR3,R1,R1;R3=4R0BLg;调用C程序g()ADDSP,SP,#4;使数据栈指针指向返回地址,准备返回LDRPC,[SP],#4;返回END第4章AR
M嵌入式系统程序设计及调试基础743.C程序调用汇编程序在C语言程序中调用用汇编语言程序的方法是使用EXTERN关键词,以声明该汇编程序。在汇编程序中使用EXPORT伪指令声明该程序段可以被其它程序引用。下面是一个C程序调用汇编程序的例子,其中汇编程序str
copy把R1指向的数据复制到R0指向的地址中去,这个数据的最后一个数据时0,C程序调用strcopy完成数据块复制工作。;汇编语言程序AREAScopy,CODE,READONLYEXPORTstrcopy;声明本汇编程序可以被引用strcopyLDRBR2,
[R1],#1;把R1指向的数据读到R2STRBR2,[R0],#1;把R2中的数据存储到R0指向的地址单元CMPR2,0;检测是否是最后一个数据BNEstrcopy;不是最后一个数据,继续MOVPC
,LR;否则,返回END第4章ARM嵌入式系统程序设计及调试基础75//C程序#include<stdio.h>EXTERNvoidstrcopy(char*d,constchar*s)intmain(){constchar*srcstr=“Firststring-sou
rce”;//定义指向字符串的指针chardststr[]=“Secondstring-destination”;//定义字符串常量printf(“Beforecopying:\n”);//打印双引号中的字符串并换行pr
intf(“%s\n%s\n”,srcstr,dststr);//输出字符串strcopy(dststr,srcstr);//将源串和目标串地址传strcopyprintf(“Aftercopying:\n”);printf(“%s\n%s\n”,srcstr,
dststr);return(0);}第4章ARM嵌入式系统程序设计及调试基础76ARMADS全称为ARMDeveloperSuite,是ARM公司推出的新一代ARM集成开发工具,用于无操作系统的ARM系统开发,是对裸机(可理解成一个高级单片机)的开发。ADS的最新版本是1.2。它除了可以
安装在WindowsNT4,Windows2000,Windows98和Windows95操作系统下,还支持WindowsXP和WindowsMe操作系统。ADS由命令行开发工具,ARM运行时库,GUI开发环境、实
用程序和支持软件组成,而GUI开发环境包含CodeWarrior和AXD两种,其中CodeWarrior是集成开发工具,AXD是调试工具。有了这些部件,用户就可以为ARM系列的RISC处理器编写和调试自己的开发应用程序了。4.4ARMADS集成开发环境的基本使用4
.4.1ADS集成开发环境简介第4章ARM嵌入式系统程序设计及调试基础77图4.3ARMADS的CodeWarrior和AXD运行时的主界面及有关信息分布图第4章ARM嵌入式系统程序设计及调试基础78Co
deWarriorIDE提供了一个简单通用的图形化用户界面用于管理软件开发项目。可以以ARM和Thumb处理器为对象,利用CodeWarriorIDE开发C、C++和ARM汇编代码。下面通过【例3.1】所示的数
据块的复制程序的设计与调试来介绍CodeWarriorIDE工具的使用。1.创建工程或打开工程创建项目工程是嵌入式开发的第一步,因为工程将所有的源代码文件组织在一起,并能够决定最终生成文件存放的路径,输出的格式等。若是对已经存在的工程进行设计调试,则直接打开已建的工程
即可。运行ADS1.2开发环境,打开CodeWarrior集成开发环境。在CodeWarrior中新建一个工程的方法有两种,可以在工具栏中单击“New”按钮,也可以在“File”菜单中选择“New…”菜单,如图4.4所示
。这样就会打开一个如图4.5所示的工程类型选择对话框,它为用户提供了七种可选择的工程类型:4.4.2CodeWarrior的基本使用方法第4章ARM嵌入式系统程序设计及调试基础79图4.4新建工程或文件子菜单操作图第4章ARM嵌入式系统程序设计及调试基础80图4.5新
建工程类型选择对话框第4章ARM嵌入式系统程序设计及调试基础81七种可选择的工程类型:ARMExecutablImage:用于将ARM指令代码生成一个ELF格式的可执行映像文件;ARMObjectLibrary:用于将ARM指令的代码生成一个armar格式的目标文件库;Em
ptyProject:用于创建一个不包含任何库或源文件的工程;MakefileImporterWizard:用于将VisualC的nmake或GNUmake文件转入到CodeWarriorIDE工程文件;ThumbARM
ExecutableImage:用于将ARM指令和Thumb指令的混和代码生成一个可执行的ELF格式的映像文件;ThumbExecutableimage:用于将Thumb指令创建一个可执行的ELF格式的映像文件;ThumbObjectLibrary:用于将Th
umb指令的代码生成一个armar格式的目标文件库。第4章ARM嵌入式系统程序设计及调试基础82本实例选择ARMExecutableImage,在“Projectname:”中输入工程文件名“blocks”,点击“
Location:”文本框的“Set…”按钮,将该工程保存的路径为“F:\ARMEXAMPLE\blocks”,将这些设置好后,点击“确定”按钮,即可建立一个工程名为blocks的新工程。此时会出现blocks.
mcp的窗口。blocks.mcp工程窗口有三个标签页,分别为files、linkorder和target,默认显示的是第一个标签页files。在该标签页点击鼠标右键,选中“AddFiles…”可以把要用到的源程序添加到工程中。在建立好一个工程时,默认的target是Debug
Rel,在本例中也使用默认的DebugRel目标。target有三种类型,三种target的类型及含义如下:(1)DebugRel:使用该目标,在生成目标的时候,会为每一个源文件生成调试信息;(2)Debug:使用该目标为每一个源文件生成最完全的调试信息;(3)R
elease:使用该目标不会生成任何调试信息。第4章ARM嵌入式系统程序设计及调试基础832.建立源程序并添加到工程在“File”菜单中选择“New”,在打开的如图4.6所示的对话框中,选择标签页File,
在Filename中输入要创建的文件名,输入“blocks.s”,点击“确定”关闭窗口。在新建源程序文本框中输入源程序并存盘,如图4.7所示。对于新建的源程序存盘时,注意添加文件扩展名,其中汇编文件的扩展名为S,C语言的扩展名为C,C++的
扩展名为CPP。工程源程序建好后,需要将源程序添加到工程项目中。将源程序添加到工程的常用方法有两种:一种方法是在工程空白处用鼠标右点,在出现的对话框中选择“AddFiles…”,如图4.8所示;另一种是在“
Project”菜单项中,选择“AddFiles…”。这两种方法都会打开文件浏览框,用户可以把已经存在的文件添加到工程中来。当选中要添加的文件时,会出现一个如图4.9所示的对话框,询问用户把文件添加到何类目标中,根据实际情况进行选择。在这里,我们选择DebugRel目标。第4章ARM嵌入式系
统程序设计及调试基础84图4.6新建源程序对话框第4章ARM嵌入式系统程序设计及调试基础85图4.7新建好的源程序blocks.s第4章ARM嵌入式系统程序设计及调试基础86图4.8添加工程文件对话框图4.9添加文件到指定目标选项对话框图4.10DebugRel设置对话框第4章ARM嵌入
式系统程序设计及调试基础87图4.10DebugRel设置对话框第4章ARM嵌入式系统程序设计及调试基础883.工程项目的有关选择设置在进行编译和链接前,首先要进行工程生成目标选项的设置,这些选项包括编译器选项、汇编选项、链接器选项等,它们将决定C
odeWarriorIDE如何处理工程项目,并生成特定的输出文件。点击Edit菜单,选择“DebugRelSettings…”,出现如图4.10所示的对话框。这个对话框中的设置很多,在这里只介绍一些最为常用的设置选项,读者若对其它未涉及到的选项感兴趣,可以
查看相应的帮助文件。第4章ARM嵌入式系统程序设计及调试基础891)target设置选项TargetName:该文本框显示了当前的目标设置。Linker:该选项供用户选择要使用的链接器。在这里默认选择的是ARMLinker,使用该链接器,将使用Armlink链接编
译器和汇编器生成的工程中的文件相应的目标文件。若选择None,则表示不用任何链接器,亦即工程中的所有文件都不会被编译器或汇编器处理。若选择ARMLibrarian,则表示将编译或汇编得到的目标文件转换为ARM库文件。Pre-linker:目前CodeWarrio
rIDE不支持该选项。Post-Linker:选择在链接完成后,还要对输出文件进行的操作。若希望生成一个可以烧写到Flash中去的二进制代码,则选择ARMfromELF,表示在链接生成映像文件后,再调用FromELF命令将含有调试信息的ELF格式的映像文件转换成其他格
式的文件。第4章ARM嵌入式系统程序设计及调试基础902)Language设置选项图4.11是编程语言设置对话框。ARMAssembler、ARMCCompiler、ARMC++Compiler、ThumbCCompiler、ThumbC++Compiler分别用于对ARM汇编语言、
C语言、C++语言、ThumbC、ThumbC++的支持选项进行设置。同时选择其中的TARGET可以对目标体系结构或处理器进行设置。图4.11编程语言设置对话框第4章ARM嵌入式系统程序设计及调试基础913)Linker设置鼠标选中ARMLinker,出现如
图4.12所示对话框。下面详细介绍该对话框的主要的标签页选项,因为这些选项对最终生成的文件有着直接的影响。图4.12链接器Output选项设置对话框第4章ARM嵌入式系统程序设计及调试基础92图4.13链接器Options选项设置对话框
第4章ARM嵌入式系统程序设计及调试基础934.编译和链接工程在对工程进行相关的设置后,如图4.14所示,点击CodeWarriorIDE的菜单Project下的make子菜单项,就可以对工程进行编译和链接了,其结果如图4.15所示。图4.14编译链接
操作子菜单Make操作图第4章ARM嵌入式系统程序设计及调试基础94图4.15执行编译和链接Make后生成的结果第4章ARM嵌入式系统程序设计及调试基础95(a)工程Blocks下的blocks_Data(b)blocks_Data下的Debugrel图4.16生成
的目标文件目录DebugRel第4章ARM嵌入式系统程序设计及调试基础96AXD(ARMeXtendedDebugger)是ADS软件中独立于CodeWarriorIDE的图形软件,打开AXD软件,默认打开的目标是ARMulator。ARMulato
r也是调试时最常用的一种调试工具,下面结合ARMulator介绍在AXD中进行代码调试的方法和过程。要使用AXD必须首先要生成包含有调试信息的程序。在4.4.2节已经生成的blocks.axf(若是C语言,则为main.axf)就是含有调试信息的可执行
ELF格式的映像文件。1.在AXD中打开调试文件在CodeWarriorIDE主菜中执行Project——>Debug或单击工程窗口中的Debug快捷键,可自动打开AXD界面,并把映像文件装载到目标内存中,在所打开的映像文件中会有一个蓝色的箭头指示当前执行的位置,如图4.17所示。或者单独运行A
XD,在AXD主菜单File中选择“Loadimage…”选项,打开LoadImage对话框,找到要装载的.axf映像文件,点击“打开”按钮,就把映像文件装载到目标内存中了。4.4.3用AXD调试器进行代码调试第4章ARM嵌入式系统程序设计及调试基础97图4
.17在AXD下打开的映像文件第4章ARM嵌入式系统程序设计及调试基础98在AXD主菜单Execute中选择“Go”子菜单项,将全速运行代码。要想进行单步的代码调试,在Execute菜单中选择“Step”
选项,或用F10即可以单步执行代码,窗口中蓝色箭头会发生相应的移动。图4.18是blocks工程调试运行窗口。图4.18blocks工程调试运行窗口第4章ARM嵌入式系统程序设计及调试基础992.设置断
点调试时,用户往往希望在程序执行到某处时查看所关心的变量值,此时可以通过设置断点达到要求。将光标移动到要进行断点设置的代码处,在Execute菜单中,选择ToggleBreakpoint命令或按F9键,就会在光标的起始位置出现一个
红色的实心圆点,表明该处已设为断点,如图4.19所示。按F5键,程序将运行到断点处。图4.19设置断点操作结果图第4章ARM嵌入式系统程序设计及调试基础1003.查看寄存器和存储器的内容查看寄存器或存储器的值,在实际开发调试中经常使用。从AXD主菜单的ProcessorV
iews菜单中选择“Registers”选项,可观察寄存器的内容。选择“Memory”选项,可观察存储器的内容,如图4.20所示。图4.20查看寄存器和存储器内容操作示意图第4章ARM嵌入式系统程序设计及调试基础1014.查看变量值假设是进行C/C++程序的调试,经常需要查看某个变量的值。查看
变量值的方法是:在AXD主菜单下的ProcessorViews菜单中选择“Watch”,会出现一个Watch窗口,然后用鼠标选中需观察的变量,点击鼠标右键,在快捷菜单中选中“AddtoWatch”,这样需观察的变量就添加到Watch窗口的列表中。图4.2
1是查看变量的设置操作示意图。程序运行过程中,用户可以看到需观察的各变量的值在不断的变化。默认显示变量数值是以十六进制格式显示的,用户也可以通过在watch窗口点击鼠标右键,在弹出的快捷菜单中选择“Format”选项,如图4.22所示,选择所查看的变量显示数据的格
式。第4章ARM嵌入式系统程序设计及调试基础102图4.21查看变量的设置操作示意图第4章ARM嵌入式系统程序设计及调试基础103图4.22查看变量的值及设置变量的格式第4章ARM嵌入式系统程序设计及调试基础104EmbestIDE英文全称是EmbestIntegra
tedDevelopmentEnvironment,是深圳英蓓特信息技术有限公司推出的一套应用于嵌入式软件开发的新一代集成开发环境。EmbestIDE是一个高度集成的图形界面操作环境,包含编辑器、编译汇编链接器、调试器、工程管理和Flash编
程等工具,其界面风格同MicrosoftVisualStudio,用户可以很方便地在该集成开发环境中创建和打开工程,建立、打开和编辑文件,编译、链接、运行、调试各种嵌入式应用程序。EmbestIDEforARM支持ARM7和ARM9等ARM内核的处理器,其运行的主机环境为Win
dows98/NT/2000/XP,支持的开发语言为标准C和汇编语言。图4.23是EmbestIDE运行时的主界面及有关信息分布图。4.5EmbestIDE集成开发环境的基本使用4.5.1EmbestIDE集成开发环境简介第4章ARM嵌入式系统程序设计
及调试基础105图4.23EmbestIDEEmbestIDE运行时的主界面及有关信息分布图第4章ARM嵌入式系统程序设计及调试基础106EmbestIDE的使用方法与ADS在许多方面类似,为了节约篇幅,下面只
简要地介绍EmbestIDE的使用方法。详细的使用可参看软件中的使用说明。1.新建工程或打开工程在EmbestIDE主菜单下执行File→NewWorkspace,新建工程。若工程及有关文件已建好,则只要直接
打开已建工程即可。2.新建源程序并添加到工程在EmbestIDE主菜单下执行File→New建立源文件,并执行Project→AddToProject→Files添加源文件。3.工程项目的有关选择设置在主菜单下选择Projec
t—→Settings,弹出工程设置对话框,如图4.24所示,先选择Processor属性页,对目标版所用处理器进行设置。再依次选择仿真器设置、调试设置、目录设置、编译设置、汇编设置、连接设置等项目,进行相应的设置。4.5.2EmbestIDE的基本使用方
法第4章ARM嵌入式系统程序设计及调试基础107图4.24工程基本设置操作图第4章ARM嵌入式系统程序设计及调试基础1084.工程的编译、链接完成工程设置后,即可对工程进行编译、链接。用户可以通过选择主窗口Build菜单项或Build工具栏按钮,执行Build—→Bu
ild源文件名,编译相应的文件或工程并生成目标代码,如图4.25所示。若有错,则编译、链接终止,需进行错误修改。图4.25工程的编译和链接操作图第4章ARM嵌入式系统程序设计及调试基础1095.模拟调试
EmbestIDE包含ARM模拟器,支持脱离目标板的ARM应用模拟调试,是开发人员进行在线调试前的开发辅助工具。(1)调试设置:选择Project—→Settings,弹出工程设置对话框,选择remote属性页,对调试设备模块进行设置。选择debu
g页面,进行调试模块设置。(2)选择Debug—→RemoteConnect连接软件仿真器,执行Download命令下载程序,并打开寄存器窗口。图4.26是Debug调试操作子菜单及操作图。(3)打开存储器窗口,并设置观察存储器的地址。(4)单步执行程序并观察和记录寄
存器与存储器值的变化。(5)结合调试内容和相关内容,观察程序运行,记录有关结果。第4章ARM嵌入式系统程序设计及调试基础110图4.26Debug调试操作子菜单及操作图第4章ARM嵌入式系统程序设计及调试基础1116.加载调试在线调试时,首先将集成环
境与JTAG仿真器连接,选择Debug—→RemoteConnect选项可激活连接,然后单击Download菜单将目标文件下载到目标系统的指定存储区中。文件下载后即可进行在线仿真调试。调试时可打开有关窗口进行有关调试信息的观察。7.FLASH编程EmbestIDEForARM提供了
Flash编程工具,可以在板擦除Flash,或将文件烧写到Flash中。第4章ARM嵌入式系统程序设计及调试基础1121.为什么通常使用C语言与汇编语言混合编程,它有什么优点?2.设计一个实现1+2+3+…+N的ARM汇编程序。3.设计一段程序完成数据块的复制,数据从源数据区snum复制到目标
数据区dnum。复制时,以4个字为单位进行。对于最后所剩不足4个字的数据,以字为单位进行复制。4.利用跳转表的思想编写一个汇编程序,寄存器R3中存放的是跳转表的基地址寄存器R0的值用于选择不同的子程序,实现当R0
分别为0,1,2时完成跳转到3个不同的子程序。习题4第4章ARM嵌入式系统程序设计及调试基础1135.利用C语言和汇编的混合编程,完成两个字符串的比较,并返回比较结果,分别用C语言和汇编语言完成比较程序。6.C语言与汇编语言混合编程时的参数传递规则有哪些
?7.用汇编语言设计程序实现10!,并用调用子程序的方法实现1!+2!+3!+……+10!。8.ADS可用于完成哪些开发?它由几部分组成?各部分具有哪些功能?9.用ADS进行代码生成时,如何设置编译、汇编、链接等目标选项?10.按照4.4所讲解的AD
S的使用方法和4.5所讲解的EmbestIDE的使用方法,分别将本章的数据块的复制程序实际操作调试一遍,并记录和分析有关结果。