1 / 71

第五章基于 ARM 的嵌入式程序设计

第五章基于 ARM 的嵌入式程序设计. 5.1 ARM 汇编语言的伪操作、宏指令与伪指令 5.2 ARM 汇编语言程序设计 5.3 嵌入式 C 语言程序设计基础 5.4 嵌入式 C 语言程序设计实例 5.5 嵌入式 C 语言程序设计技巧 5.6 C 与汇编语言混合编程 5.7 基于 Embest IDE for ARM 环境的软件开发实例. 5.1 ARM 汇编语言的伪操作、宏指令与伪指令. 5.1.1 两种常见的 ARM 编译开发环境 5.1.2 ADS 编译环境下的伪操作和宏指令 5.1.3 GNU 编译环境下的伪操作和宏指令

Download Presentation

第五章基于 ARM 的嵌入式程序设计

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. 第五章基于ARM的嵌入式程序设计 • 5.1 ARM汇编语言的伪操作、宏指令与伪指令 • 5.2 ARM汇编语言程序设计 • 5.3嵌入式C语言程序设计基础 • 5.4嵌入式C语言程序设计实例 • 5.5 嵌入式C语言程序设计技巧 • 5.6 C与汇编语言混合编程 • 5.7基于Embest IDE for ARM 环境的软件开发实例

  2. 5.1 ARM汇编语言的伪操作、宏指令与伪指令 • 5.1.1 两种常见的ARM编译开发环境 • 5.1.2 ADS编译环境下的伪操作和宏指令 • 5.1.3 GNU编译环境下的伪操作和宏指令 • 5.1.4 ARM汇编语言的伪指令

  3. 5.1.1两种常见的ARM编译开发环境 • ADS/SDT IDE开发环境:它由ARM公司开发,使用了CodeWarrior公司的编译器; • 集成了GNU开发工具的IDE开发环境::它由GNU的汇编器as、交叉编译器gcc、和链接器ld等组成。

  4. 5.1.2ADS编译环境下的伪操作和宏指令 ADS编译环境下的伪操作可分为以下几类: • 符号定义(Symbol Definition)伪操作 • 数据定义(Data Definition)伪操作 • 汇编控制(Assembly Control)伪操作 • 信息报告(Reporting)伪操作 • 其他(Miscellaneous)伪操作

  5. 伪操作 语法格式 作 用 GBLA GBLA Variable 声明一个全局的算术变量,并将其初始化成0。 GBLL GBLL Variable 声明一个全局的逻辑变量,并将其初始化成{FALSE}。 GBLS GBLS Variable 声明一个全局的字符串变量,并将其初始化成空串“”。 LCLA LCLA Variable 声明一个局部的算术变量,并将其初始化成0。 LCLL LCLL Variable 声明一个局部的逻辑变量,并将其初始化成{FALSE}。 LCLS LCLS Variable 声明一个局部的串变量,并将其初始化成空串“”。 SETA SETA Variable expr 给一个全局或局部算术变量赋值。 SETL SETL Variable expr 给一个全局或局部逻辑变量赋值。 SETS SETS Variable expr 给一个全局或局部字符串变量赋值。 RLIST name LIST{list of registers} 为一个通用寄存器列表定义名称。 CN name CN expr 为一个协处理器的寄存器定义名称。 CP name CP expr 为一个协处理器定义名称。 DN/SN name DN/SN expr DN/SN为一个双精度/单精度的VFP寄存器定义名称。 FN name FN expr 为一个FPA浮点寄存器定义名称。 符号定义伪操作

  6. 伪操作 语法格式 作 用 LTORG LTORG 声明一个数据缓冲池(也称为文字池)的开始。 MAP MAP expr{,base-register} 定义一个结构化的内存表(Storage Map)的首地址。 FIELD {label} FIELD expr 定义一个结构化内存表中的数据域。 SPACE {label} SPACE expr 分配一块连续内存单元,并用0初始化。 DCB {label} DCB expr{,expr} 分配一段字节内存单元,并用expr初始化。 DCD/ DCDU {label} DCD expr {,expr}… 分配一段字内存单元。 DCDO {label} DCDO expr{,expr}… 分配一段字对齐的字内存单元。 DCFD/ DCFDU {label} DCFD {U}fpliteral{,fpliteral}… 为双精度的浮点数分配字对齐的内存单元。 DCFS/ DCFSU {label} DCFS {U} fpliteral {,fpliteral}… 为单精度的浮点数分配字对齐的内存单元。 DCI {label} DCI expr{,expr}… 在ARM代码中分配一段字对齐的内存单元; 在Thumb代码中,分配一段半字对齐的半字内存单元。 DCQ/ DCQU {label} DCQ{U}{﹣}literal{,{﹣}literal}… 分配一段以双字(8个字节)为单位的内存 DCW/ DCWU {label} DCW{U}expr{,expr}… DCW用于分配一段半字对齐的半字内存单元。 数据定义伪操作

  7. 伪操作 语法格式 作 用 IF,ELSE及ENDIF IF logical expression … {ELSE …} ENDIF 能够根据条件把一段源代码包括在汇编语言程序内或者将其排除在程序之外。 WHILE及WEND WHILE logical expression … WEND 能够根据条件重复汇编相同的一段源代码。 MACRO 、MEND及MEXIT MACRO {$label} macroname {$parameter{,$parameter}…} … ;宏代码 MEND MACRO标识宏定义的开始,MEND标识宏定义的结束。MERIT用于从宏中跳转出去。用MACRO和MEND定义的一段代码,称为宏定义体。通过宏名称来调用宏。 汇编控制伪操作

  8. 伪操作 语法格式 作 用 ASSERT ASSERT logical expression 对汇编程序的第二遍扫描中,如果其中ASSERT中条件不成立,ASSERT伪操作将报告该错误信息。 INFO INFO numeric-expression,string-expression 在汇编处理过程的第一遍扫描或者第二遍扫描时INFO伪操作报告诊断信息。 OPT OPT n 通过OPT伪操作可以在源程序中设置列表选项。 TTL TTL title 在列表文件的每一页的开头插入一个标题。 SUBT SUBT subtitle 在列表文件的每一页的开头插入一个子标题。 信息报告伪操作

  9. 伪操作 语法格式 作 用 CODE16 CODE16 告诉汇编编译器后面的指令序列为16位的Thumb指令 CODE32 CODE32 告诉汇编编译器后面的指令序列为32位的ARM指令。 EQU name EQU expr{,type} 为数字常量、基于寄存器的值和程序中的标号(基于PC的值)定义一个字符名称。 AREA AREA sectionname{,attr}{,attr}… 定义一个代码段或者数据段。 ENTRY ENTRY 指定程序的入口点。 END END 告诉编译器已经到了源程序结尾。 ALIGN ALIGN {expr{,offset}} 通过添加补丁字节使当前位置满足一定的对齐方式。 EXPORT/ GLOBAL EXPORT symbol {[WEAK]} 声明一个符号可以被其他文件引用,相当于声明了一个全局变量。 IMPORT IMPORT symbol {[WEAK]} 告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号。 EXTERN EXTERN symbol {〔WEAK〕} 告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号。 GET/ INCLUDE GET filename 将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理。 INCBIN INCBIN filename 将一个文件包含到当前源文件中,被包含的文件不进行汇编处理。 KEEP KEEP{symbol} 告诉编译器将局部符号包含在目标文件的符号表中。 NOFP NOFP 禁止源程序中包含浮点运算指令。 REQUIRE REQUIRE lable 指定段之间的相互依赖关系。 RN name RN expr 为一个特定的寄存器定义名称。 ROUT {name} ROUT 定义局部变量的有效范围。 其他伪操作

  10. 5.1.3 GNU编译环境下的伪操作和宏指令 • GNU编译环境下的伪操作可分为以下几类: • 常量编译控制伪操作 • 汇编程序代码控制伪操作 • 宏及条件编译控制伪操作 • 其他伪操作

  11. 伪操作 语法格式 作 用 .byte .byte expr {,expr} … 分配一段字节内存单元,并用expr初始化。 .hword/.short .hword expr {,expr} … 分配一段半字内存单元,并用expr初始化。 .ascii .ascii expr {,expr} … 定义字符串expr(非零结束符)。 .asciz /.string .asciz expr {,expr} … 定义字符串expr(以/0为结束符)。 .float/.single .float expr {,expr} … 定义一个32bit IEEE 浮点数expr。 .double .double expr {,expr} … 定义64bit IEEE浮点数expr。 word/.long /.int .word expr {,expr} … 分配一段字内存单元,并用expr初始化。 .fill .fill repeat {,size}{,value} 分配一段字节内存单元,用size长度value填充repeat次。 .zero .zero size 分配一段字节内存单元,并用0填充内存。 .space/.skip .space size {, value} 分配一段内存单元,用value将内存单元初始化。 常量编译控制伪操作

  12. 伪操作 语法格式 作 用 .section .section expr 定义域中包含的段。 .text .text {subsection} 将操作符开始的代码编译到代码段或代码段子段。 .data .data {subsection} 将操作符开始的数据编译到数据段或数据段子段。 .bss .bss {subsection} 将变量存放到.bss段或.bss段的子段。 .code 16/.thumb .code 16 .thumb 表明当前汇编指令的指令集选择Thumb指令集。 .code 32/.arm .code 32 .arm 表明当前汇编指令的指令集选择ARM指令集。 .end .end 标记汇编文件的结束行,即标号后的代码不作处理。 .include .include “filename” 将一个源文件包含到当前源文件中。 .align/.balign .align {alignment} {,fill} {,max} 通过添加填充字节使当前位置满足一定的对齐方式。 汇编程序代码控制伪操作

  13. 伪操作 语法格式 作 用 .macro、.exitm及.endm .macro acroname{parameter{,parameter}…} … .endm .macro伪操作标识宏定义的开始,.endm标识宏定义的结束。用.macro及.endm定义一段代码,称为宏定义体。.exitm伪操作用于提前退出宏。 . ifdef,.else及.endif .ifdef condition … .else … .endif 当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。其中else可以缺省。 宏及条件编译控制伪操作

  14. 伪操作 语法格式 作 用 .eject .eject 在汇编符号列表文件中插入一分页符。 .list .list 产生汇编列表(从 .list 到 .nolist)。 .nolist .nolist 表示汇编列表结束处。 .title .title “heading” 使用“heading ”作为标题。 .sbttl .sbttl “heading” 使用“heading”作为子标题。 .ltorg .ltorg 在当前段的当前地址(字对齐)产生一个文字池。 .req .req name,expr 为一个特定的寄存器定义名称。 .err .err 使编译时产生错误报告。 .print .print string 打印信息到标准输出。 .fail .fail expr 编译汇编文件时产生警告。 其他伪操作

  15. 伪指令 语法格式 作 用 ADR ADR {cond}register,expr 将基于PC或基于寄存器的地址值读取到寄存器中。小范围的地址读取。 ADRL ADRL {cond} register,expr 将基于PC或基于寄存器的地址值读取到寄存器中。中等范围的地址读取。 LDR LDR {cond} register,=[expr | label-expr] 将一个32位的立即数或者一个地址值读取到寄存器中。大范围的地址读取。 NOP NOP 在汇编时将被替换成ARM中的空操作。 5.1.4ARM汇编语言的伪指令

  16. 5.2 ARM汇编语言程序设计 • 5.2.1 ARM汇编中的文件格式 • 5.2.2 ARM汇编语言语句格式 • 5.2.3 ARM汇编语言编程的重点 • 5.2.4 ARM汇编程序实例

  17. 源程序文件 文件名 说明 汇编程序文件 *.S 用ARM汇编语言编写的ARM程序或Thumb程序。 C程序文件 *.C 用C语言编写的程序代码。 头文件 *.H 为了简化源程序,把程序中常用到的常量命名、宏定义、数据结构定义等等单独放在一个文件中,一般称为头文件。 5.2.1ARM汇编中的文件格式 ARM源程序文件(可简称为源文件)可以由任意一种文本编辑器来编写程序代码,它一般为文本格式。在ARM程序设计中,常用的源文件可简单分为以下几种:

  18. 5.2.2ARM汇编语言语句格式 ARM汇编语言语句格式如下所示: {symbol}{instruction | directive | pseudo-instruction}{;comment} 其中: • instruction为指令。 • directive为伪操作。 • pseudo-instruction为伪指令。 • symbol为符号。 • comment为语句的注释。

  19. ARM汇编语言程序格式 ARM汇编语言是以段(section)为单位来组织源文件的。段是相对独立的、具有特定名称的、不可分割的指令或者数据序列。段又可以分为代码段和数据段,代码段存放执行代码,数据段存放代码运行时需要用到的数据。一个ARM源程序至少需要一个代码段,大的程序可以包含多个代码段和数据段。

  20. 举例说明ARM汇编语言源程序的基本结构 • AREA EXAMPLE,CODE,READONLY • ENTRY • start • MOV r0,#10 • MOV r1,#3 • ADD r0,r0,r1 • END • 本程序的程序体部分实现了一个简单的加法运算。

  21. 5.2.3ARM汇编语言编程的重点 • ARM数据处理操作 • 设置条件码 • 汇编语言子程序调用及返回 • 跳转表思想 • ARM与Thumb之间的状态转换及函数的相调用

  22. ARM数据处理操作 • ARM中数据的处理有以下三种形式: • 简单的寄存器操作 • 立即数操作 • 寄存器移位操作 其中32位立即数在32位指令中的编码以及ARM特有的寄存器移位操作是数据处理方面的难点。

  23. 设置条件码 • ARM的任何数据处理指令都能通过增加“S”操作码来设置条件码(N,Z,C和V)。 • 条件执行 • ARM指令集不同寻常的特征是每条指令(除了某些v5T指令)都可以是条件执行的。 • 条件转移 • 在程序中可以通过条件码的使用让微处理器决定是否进行转移,还可用来控制循环的退出。

  24. 汇编语言子程序调用及返回 • 子程序的调用 • 在ARM汇编语言中,子程序调用是通过BL指令来完成的。BL指令的语法格式如下: • BL subname • 其中,subname是被调用的子程序的名称。 • 子程序的返回 • 在返回调用子程序时,转移链接指令保存到LR寄存器(r14)中的值需要拷贝回程序寄存器PC(r15)。

  25. 跳转表思想 • 在程序设计中,有时为使程序完成一定的功能,需要调用一系列子程序中的一个,而决定究竟调用哪一个由程序的计算值确定。跳转表是解决该问题的有效方案。跳转表是利用程序计数器PC在通用寄存器文件中的可见性来实现的,如下例所示:

  26. ARM与Thumb间的状态转换及函数的相调用 • 状态切换的实现 • ARM/Thumb之间的状态切换是通过一条专用的转移交换指令BX来实现的。BX利用Rn寄存器中目的地址值的最后一位来判断跳转后的状态。当最后一位为0时,表示转移到ARM状态;当最后一位为1时,表示转移到Thumb状态,如下图所示。

  27. ARM与Thumb间的状态转换及函数的相调用 • ARM/Thumb之间的函数调用 在同一状态下的子程序调用,通常只需要一条指令实现调用:BL function • 实现返回也只需要从LR恢复PC即可:MOV PC,LR • 在不同状态下的子程序调用中,就需要进行状态之间的切换,需要考虑到以下几点: • 需要由BX来切换状态,因为BL不能完成状态切换。 • 需要在BX之前先保存好LR,BX不能自动保存返回地址到LR。 • 需要用“BX LR”来返回,不能使用“MOV PC,LR”,返回时要仔细考虑保存在LR中最低位的内容是否正确。

  28. 5.2.4ARM汇编程序实例 • 简单的ARM指令程序 • 数据块复制 • 利用跳转表实现程序跳转 • ADS编译环境下的汇编代码与GNU编译环境下有较多不同点,主要是符号及伪操作的不同。

  29. 5.3 嵌入式C语言程序设计基础 • 5.3.1 C语言“预处理伪指令”在嵌入式程序 设计中的应用 • 5.3.2 嵌入式程序设计中的函数及函数库 • 5.3.3 嵌入式程序设计中常用的C语言语句 • 5.3.4 嵌入式程序设计中C语言的变量、数 组、结构、联合

  30. 5.3.1C语言“预处理伪指令”在嵌入式程序设计中的应用5.3.1C语言“预处理伪指令”在嵌入式程序设计中的应用 “预处理命令”可以改进程序设计的环境,提高编程效率,一般以#号打头 ,可分为以下三种 : 文件包含 宏定义 条件编译

  31. 文件包含 文件包含伪指令可将头文件包含到程序中,头文件中定义的内容包括符号常量、复合变量原型、用户定义的变量类型原型和函数的原型说明等。编译器编译预处理时用文件包含的正文内容替换到实际程序中。 文件包含伪指令的格式 #include<头文件名.h> ;标准头文件 #include“头文件名.h”;自定义头文件 #include 宏标识符

  32. 宏定义 宏定义伪指令分为:简单宏、参数宏、条件宏、预定义宏及宏释放。 简单宏: # define宏标识符 宏体 参数宏:# define宏标识符(形式参数表)宏体 条件宏定义: #ifdef 宏标识符 #ifndef 宏标识符 #undef 宏标识符 #define 宏标识符 宏体 #define 宏标识符 宏体 #else #else #undef 宏标识符 #define 宏标识符 宏体 #define 宏标识符 宏体 #endif #endif

  33. 条件编译 • 条件编译伪指令是写给编译器的,指示编译器在满足某一条件时仅编译源文件中与之相应的部分。其格式如右框中所示: #if(条件表达式1) … #elif(条件表达式2) … #elif(条件表达式n) … #else … #endif

  34. 5.3.2嵌入式程序设计中的函数及函数库 函数是C语言程序设计的核心。一个较大的C语言程序一般是由一个主函数和若干个子函数组成,每个函数完成一个特定的功能。函数之间也可以相互调用。 函数的格式: 定义性说明格式 : [存储类说明符] 类型说明符 [修饰符] 标识符 (参数表) {函数体} 原型说明格式 : extern 类型说明符[修饰符] 标识符(参数表){函数体}

  35. 嵌入式程序设计中的函数及函数库 • 函数库是为了减少编程工作量,将一些常用的功能的函数放在函数库中供公共使用. • 它包括C的标准库函数,也包括一些用户自己编写非标准库。 • 例如,44blib.h是根据基于S3C44B0X处理器的开发板及其功能模块编写的一个C语言函数库。它不属于C语言的标准库。

  36. 5.3.3嵌入式程序设计中常用的C语言语句 C语言语句格式为: [标号:] 语句[;] C语言语句很多,常用到的有以下几种: • 条件语句 • swith语句 • 循环语句

  37. 5.3.4嵌入式程序设计中C语言的变量、数组、结构、联合5.3.4嵌入式程序设计中C语言的变量、数组、结构、联合 • 变量 • [存储类型] 类型说明符 [修饰符] 标识符 [=初值] [,标识符[=初值]]…; • 数组 • 一维数组: • 类型说明符 标识符 [常量表达式][={初值,初值,…}]; • char 标识符[ ] =“字符串”; • 二维数组: • 类型说明符 标识符[m][n] [={{初值表},{初值表}…}]; • 指针数组和数组指针 • 类型说明符 *标志符 [常量表达式] [={地址,地址,…}]; • 类型说明符 (*标志符)[ ][=数组标识符];

  38. 嵌入式程序设计中C语言的变量、数组、结构、联合嵌入式程序设计中C语言的变量、数组、结构、联合 • 结构说明 • [存储类说明符] struct [结构原型名] • { 类型说明标识符[,标识符…]; • 类型说明标识符[,标识符…]; • … • }标识符[={初值表} [,标识符[={初值表}]…];

  39. 嵌入式程序设计中C语言的变量、数组、结构、联合嵌入式程序设计中C语言的变量、数组、结构、联合 • 联合说明 • [存储类说明符] union[联合原型名] • {类型说明符 标识符[,标识符…]; • 类型说明符 标识符[,标识符…]; • … • }标识符={初值表}[,标识符[={初值表}]…];

  40. 5.4 嵌入式C语言程序设计实例 • 5.4.1 S3VCE40开发板测试程序实例 • 5.4.2 嵌入式C语言程序编写的简单构架 • 5.4.3 Flash测试代码介绍

  41. 5.4.1S3VCE40开发板的测试程序实例 我们以S3VCE40开发板上的各个功能模块的整个测试程序为例,介绍如何运用C语言进行基于ARM的嵌入式程序设计。该程序完成的功能如下所示: • 实验板加电时数码管八段全亮;LED1、LED2轮流闪烁(频率近1Hz);使用PC键盘操作;串口终端输出信息如图: • 然后使用开发板上的PC键盘选择各部分功能测试操作,如下图:

  42. 程序源代码介绍 整个测试程序主文件main.c的代码构成图如下图所示,由BootLoader启动程序进入C语言主函数main()入口。

  43. 5.4.2嵌入式C语言程序编写的简单构架 • #include预编译指令 个C语言代码,一般要用#include编译指令将所需要的头文件加到该程序中,这是很有必要的,尤其是对编写较大的程序代码时。随后是定义一些外部变量,并对程序中的函数进行声明。 • 主函数main()的编写; 在每一个C语言代码中,一定要有一个main()函数,在该函数中完成该程序文件所要完成的各个功能,一般是通过调用各个子函数来完成。当然,它也可以调用其他文件中的函数。 • 完成相应功能的各个功能函数的编写。 各个函数之间可以相互调用。

  44. 5.4.3Flash测试代码介绍 下面给出功能测试程序中Flash测试程序的代码结构图:

  45. 5.5 嵌入式C语言程序设计技巧 • 5.5.1 变量定义 • 5.5.2 参数传递 • 5.5.3 循环条件

  46. 5.5.1变量定义 • 在变量声明的时候,最好把所有相同类型的变量放在一起定义,这样可以优化存储器布局。由下例可以看出: • 对于局部变量类型的定义,使用short或char来定义变量并不是总能节省存储空间。有时使用32位int或unsinged int局部变量更有效率一些,如下图所示: • 变量定义中,为了精简程序,程序员总是竭力避免使用冗余变量。但有时使用冗余变量可以减少存储器访问的次数这可以提高系统性能。

  47. 5.5.2参数传递 为了使单独编译的C语言程序和汇编程序能够互相调用,定义了统一的函数过程调用标准ATPCS。ATPCS定义了寄存器组中的{R0~R3}作为参数传递和结果返回寄存器,如果参数数目超过四个,则使用堆栈进行传递。 内部寄存器的访问速度是远远大于存储器的,所以要尽量使参数传递在寄存器里面进行,即应尽量把函数的参数控制在四个以下。

  48. 5.5.3循环条件 计数循环是程序中十分常用的流程控制结构,一般有以下两种形式: for (loop=1;loop<=limit;loop++) for (loop=limit;loop!=0;loop--) 这两种循环形式在逻辑上并没有效率差异,但是映射到具体的体系结构中时,就产生了很大的不同,如下图所示。

  49. 5.6 C与汇编语言混合编程 • 5.6.1 ATPCS介绍 • 5.6.2 内嵌汇编 • 5.6.3 C和ARM汇编程序间相互调用

  50. 5.6.1ATPCS介绍 ATPCS(ARM-Thumb Produce Call Standard)是ARM程序和Thumb程序中子程序调用的基本规则,目的是为了使单独编译的C语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。

More Related