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

60
www.chinaEDA. cn 第第第第第 第第第第第 ARM ARM 第第第第第第第第 第第第第第第第第 5.1 ARM 第第第第第第第第 第第第第第第第 5.2 ARM 第第第第第第第第 5.3 第第第 C 第第第第第第第第 5.4 第第第 C 第第第第第第第第 5.5 第第第 C 第第第第第第第第 5.6 C 第第第第第第第第第

description

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

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

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

www.chinaEDA.cn

第五章基于第五章基于 ARMARM 的嵌入式程序设计的嵌入式程序设计5.1 ARM 汇编语言的伪操作、宏指令与伪指令5.2 ARM 汇编语言程序设计5.3 嵌入式 C 语言程序设计基础5.4 嵌入式 C 语言程序设计实例5.5 嵌入式 C 语言程序设计技巧5.6 C 与汇编语言混合编程

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

www.chinaEDA.cn

5.1 ARM5.1 ARM 汇编语言的伪操作、汇编语言的伪操作、宏指令与伪指令宏指令与伪指令

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

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

www.chinaEDA.cn

5.1.15.1.1 两种常见的两种常见的 ARMARM 编译开发环境编译开发环境

ADS/SDT IDE 开发环境:它由 ARM 公司开发,使用了 CodeWarrior 公司的编译器;

集成了 GNU 开发工具的 IDE 开发环境::它由 GNU 的汇编器 as 、交叉编译器 gcc 、和链接器 ld 等组成。

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

www.chinaEDA.cn

5.1.2ADS5.1.2ADS 编译环境下的伪操作和宏指令编译环境下的伪操作和宏指令 ADS 编译环境下的伪操作可分为以下几类:

符号定义( Symbol Definition )伪操作 数据定义( Data Definition )伪操作 汇编控制( Assembly Control )伪操作 信息报告( Reporting )伪操作 其他( Miscellaneous )伪操作

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

www.chinaEDA.cn

符号定义伪操作 符号定义伪操作 伪操作 语法格式 作 用

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 浮点寄存器定义名称。

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

www.chinaEDA.cn

数据定义伪操作数据定义伪操作 伪操作 语法格式 作 用

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 用于分配一段半字对齐的半字内存单元。

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

www.chinaEDA.cn

汇编控制伪操作 汇编控制伪操作 伪操作 语法格式 作 用

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 定义的一段代码,称为宏定义体。通过宏名称来调用宏。

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

www.chinaEDA.cn

信息报告伪操作信息报告伪操作 伪操作 语法格式 作 用

ASSERT

ASSERT logical expression

对汇编程序的第二遍扫描中,如果其中 ASSERT中条件不成立, ASSERT 伪操作将报告该错误信息。

INFO INFO numeric-expression , string-expression

在汇编处理过程的第一遍扫描或者第二遍扫描时INFO 伪操作报告诊断信息。

OPT OPT n 通过 OPT 伪操作可以在源程序中设置列表选项。

TTL TTL title 在列表文件的每一页的开头插入一个标题。

SUBT SUBT subtitle 在列表文件的每一页的开头插入一个子标题。

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

www.chinaEDA.cn

伪操作 语法格式 作 用CODE16 CODE16 告诉汇编编译器后面的指令序列为 16 位的 Thumb 指令

CODE32 CODE32 告诉汇编编译器后面的指令序列为 32 位的 ARM 指令。

EQU name EQU expr { , type }

为数字常量、基于寄存器的值和程序中的标号(基于 PC 的值)定义一个字符名称 ,类似于 C 语言中的# define 宏定义。

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 定义局部变量的有效范围。

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

www.chinaEDA.cn

5.1.3 GNU5.1.3 GNU 编译环境下的伪操作和宏指编译环境下的伪操作和宏指令令

GNU 编译环境下的伪操作可分为以下几类:

常量编译控制伪操作汇编程序代码控制伪操作宏及条件编译控制伪操作其他伪操作

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

www.chinaEDA.cn

常量编译控制伪操作常量编译控制伪操作 ::

伪操作 语法格式 作 用.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 将内存单元初始化。

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

www.chinaEDA.cn

汇编程序代码控制伪操作 汇编程序代码控制伪操作 伪操作 语法格式 作 用

.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}

通过添加填充字节使当前位置满足一定的对齐方式。

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

www.chinaEDA.cn

宏及条件编译控制伪操作宏及条件编译控制伪操作 伪操作 语法格式 作 用

.macro 、 .exitm 及 .endm

.macro acroname { parameter{, parameter …} }….endm

.macro 伪操作标识宏定义的开始, .endm 标识宏定义的结束。用 .macro 及 .endm 定义一段代码,称为宏定义体。 .exitm 伪操作用于提前退出宏。

. ifdef , .else 及 .endif

.ifdef condition….else….endif

当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。其中 else 可以缺省。

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

www.chinaEDA.cn

其他伪操作 其他伪操作 伪操作 语法格式 作 用

.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 编译汇编文件时产生警告。

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

www.chinaEDA.cn

5.1.4ARM5.1.4ARM 汇编语言的伪指令汇编语言的伪指令 伪指令 语法格式 作 用

ADR ADR { cond } register , expr

将基于 PC 或基于寄存器的地址值读取到寄存器中。小范围的地址读取。

ADRL ADRL { cond } register , expr

将基于 PC 或基于寄存器的地址值读取到寄存器中。中等范围的地址读取。

LDR LDR { cond } register , = [ expr | label-expr ]

将一个 32 位的立即数或者一个地址值读取到寄存器中。大范围的地址读取。

NOP NOP 在汇编时将被替换成 ARM 中的空操作。

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

www.chinaEDA.cn

5.2 ARM5.2 ARM 汇编语言程序设计汇编语言程序设计

5.2.1 ARM 汇编中的文件格式5.2.2 ARM 汇编语言语句格式5.2.3 ARM 汇编语言编程的重点5.2.4 ARM 汇编程序实例

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

www.chinaEDA.cn

5.2.1ARM5.2.1ARM 汇编中的文件格式汇编中的文件格式 ARM 源程序文件(可简称为源文件)可以由任意一种文本编辑器来编写程序代码,它一般为文本格式。在 ARM 程序设计中,常用的源文件可简单分为以下几种 :

源程序文件 文件名 说 明

汇编程序文件

*.S 用 ARM 汇编语言编写的 ARM 程序或 Thumb 程序。

C 程序文件 *.C 用 C 语言编写的程序代码。

头文件 *.H 为了简化源程序,把程序中常用到的常量命名、宏定义、数据结构定义等等单独放在一个文件中,一般称为头文件。

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

www.chinaEDA.cn

5.2.2 ARM5.2.2 ARM 汇编语言语句格式汇编语言语句格式 ARM 汇编语言语句格式如下所示:{ symbol } { instruction | directive | pseu

do-instruction } {; comment } 其中: instruction为指令。 directive 为伪操作。 pseudo-instruction为伪指令。 symbol 为符号。 comment为语句的注释。

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

www.chinaEDA.cn

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

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

www.chinaEDA.cn

举例说明举例说明 ARMARM 汇编语言源程序的基本结构汇编语言源程序的基本结构

AREA EXAMPLE , CODE , READONLY

ENTRY

start

MOV r0 , #10

MOV r1 , #3

ADD r0 , r0 , r1

END

本程序的程序体部分实现了一个简单的加法运算。

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

www.chinaEDA.cn

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

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

www.chinaEDA.cn

ARMARM 数据处理操作数据处理操作ARM 中数据的处理有以下三种形式:简单的寄存器操作 立即数操作寄存器移位操作

其中 32 位立即数在 32 位指令中的编码以及 ARM特有的寄存器移位操作是数据处理方面的难点。

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

www.chinaEDA.cn

设置条件码 设置条件码 ARMARM 的任何数据处理指令都能通过增加“的任何数据处理指令都能通过增加“ SS””

操作码来设置条件码(操作码来设置条件码( NN ,, ZZ ,, CC 和和 VV )。)。

条件执行 ARM 指令集不同寻常的特征是每条指令(除

了某些 v5T 指令)都可以是条件执行的。 条件转移

在程序中可以通过条件码的使用让微处理器决定是否进行转移,还可用来控制循环的退出。

转移条件

转移 解释 一般应用

BBAL

无条件的总是

总是执行转移总是执行转移

BEQ 相等 比较的结果为相等或零

BNE 不等 比较的结果为不等或非零

BPL 正 结果为正数或零

BMIBCC

负无进位

结果为负数算术操作未得到进位

BLO 低于 无符号数比较,结果为低于

BCSBHS

有进位高于或相等

算术操作得到了进位无符号数比较,结果为高于或相等

BVC 无溢出 有符号整数操作,未出现溢出

BVS 有溢出 有符号整数操作,出现溢出

BGT 大于 有符号整数比较,结果为大于

BGE 大于或相等 有符号整数比较,结果为大于或相等

BLT 小于 有符号整数比较,结果为小于

BLE 小于或相等 有符号整数比较,结果为小于或相等

BHI 高于 无符号数比较,结果为高于

BLS 低于或相等 无符号数比较,结果为低于或相等

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

www.chinaEDA.cn

汇编语言子程序调用及返回汇编语言子程序调用及返回 子程序的调用

在 ARM 汇编语言中,子程序调用是通过 BL 指令来完成的。 BL 指令的语法格式如下:

BL subname

其中, subname是被调用的子程序的名称。 子程序的返回

在返回调用子程序时,转移链接指令保存到 LR 寄存器( r14 )中的值需要拷贝回程序寄存器 PC( r15 )。

子程序返回的方法

对于最简单的子程序,一条MOV指令就可以完成子程序的返回,如下所示:

SUB2 …

MOV pc,r14 ;把r14拷贝到r15来返回

对于在子程序中出现嵌套调用时,链接寄存器LR中的返回地址可能会在第二次调用时被覆盖,所以需要将返回地址压入堆栈来进行保存

SUB1 STMFD r13!,{r0 – r2, r14} ;保存工作寄存器和链接

BL SUB2

LDMFD r13!,{r0 – r2,PC} ;恢复工作寄存器并返回

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

www.chinaEDA.cn

跳转表思想跳转表思想在程序设计中,有时为使程序完成一定

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

BL JUMPTAB

JUMPTAB ADR r1,SUBTAB ;r1:=SUBTAB

CMP r0,#SUBMAX ;检查超限

LDRLS PC,[ r1, r0, LSL #2 ]

;如果OK,跳转到表中

B ERROR ;否则,发出错误信息

SUBTAB DCD SUB0 ;子程序表入口

DCD SUB1

DCD SUB2

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

www.chinaEDA.cn

ARMARM 与与 ThumbThumb间的状态转换及函数的相调用间的状态转换及函数的相调用

状态切换的实现 ARM/Thumb 之间的状态切换是通过一条专用的

转移交换指令 BX 来实现的。 BX利用 Rn 寄存器中目的地址值的最后一位来判断跳转后的状态。当最后一位为 0 时,表示转移到 ARM状态;当最后一位为 1 时,表示转移到 Thumb状态,如下图所示。

ARM和Thumb状态转换图

Rn

PC

BX Rn

BX

BX{Cond.} Rn

ARM/Thumb选择位:0-ARM1-ThumbBX

当前状态是ARM时当前状态是Thumb时

031

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

www.chinaEDA.cn

ARMARM 与与 ThumbThumb间的状态转换及函数的相调用间的状态转换及函数的相调用

ARM/Thumb 之间的函数调用 在同一状态下的子程序调用,通常只需要一条指令实现

调用: BL function 实现返回也只需要从 LR恢复 PC即可:

MOV PC , LR 在不同状态下的子程序调用中,就需要进行状态之间的

切换,需要考虑到以下几点: 需要由 BX来切换状态,因为 BL不能完成状态切换。 需要在 BX 之前先保存好 LR , BX 不能自动保存返回

地址到 LR 。 需要 用“ BX LR” 来返回,不能使用“ MOV PC ,

LR” ,返回时要仔细考虑保存在 LR 中最低位的内容是否正确。

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

www.chinaEDA.cn

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

ADS下的伪操作符 GNU下的伪操作符INCLUDE .include

TCLK2 EQU PB25 .equ TCLK2 ,PB25

EXPORT .global

IMPORT .extern

DCD .long

IF:DEF : .ifdef

ELSE .else

ENDIF .endif

:OR : |

:SHL <<

RN .req

GBLA .global

BUSWIDTH SETA 16 .equ BUSWIDTH,16

MACRO .macro

MEND .endm

END .end

AREA Word,CODE,READONLY .text

AREA Block,DATA,READWRITE .data

CODE32 .arm或.CODE[32]

CODE16 .thumb或.CODE[16]

LTORG .ltorg

% .fill

Entry Entry:

ldr pc, [pc, #&18] ldr pc, [pc, #+0x18]

ldr pc, [pc, #-&18] ldr pc, [pc, #-0x18]

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

www.chinaEDA.cn

5.3 5.3 嵌入式嵌入式 CC 语言程序设计基础语言程序设计基础

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

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

www.chinaEDA.cn

5.3.1 C5.3.1 C 语言语言“预处理伪指令”在嵌入式程序“预处理伪指令”在嵌入式程序设计中的应用设计中的应用

“预处理命令”可以改进程序设计的环境,提高编程效率,一般以 #号打头 ,可分为以下三种 :

文件包含 宏定义条件编译

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

www.chinaEDA.cn

文件包含文件包含文件包含伪指令可将头文件包含到程序中,头

文件中定义的内容包括符号常量、复合变量原型、用户定义的变量类型原型和函数的原型说明等。编译器编译预处理时用文件包含的正文内容替换到实际程序中。

文件包含伪指令的格式#include<头文件名 .h> ;标准头文件#include“头文件名 .h” ;自定义头文件#include 宏标识符

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

www.chinaEDA.cn

宏定义宏定义宏定义伪指令分为:简单宏、参数宏、条件

宏、预定义宏及宏释放。 简单宏: # define 宏标识符 宏体 参数宏: # define 宏标识符(形式参数表) 宏体条件宏定义:

#ifdef 宏标识符 #ifndef 宏标识符#undef 宏标识符 #define 宏标识符 宏体#define 宏标识符 宏体 #else

#else #undef 宏标识符#define 宏标识符 宏体 #define 宏标识符 宏体#endif #endif

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

www.chinaEDA.cn

条件编译条件编译条件编译伪指令是写给编译器的,指示编译器在满足某一条件时仅编译源文件中与之相应的部分。其格式如右框中所示:

#if(条件表达式 1)

#elif(条件表达式 2)

#elif(条件表达式 n)

#else

#endif

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

www.chinaEDA.cn

5.3.25.3.2 嵌入式程序设计中的函数及函数库嵌入式程序设计中的函数及函数库 函数是 C 语言程序设计的核心。一个较大的 C

语言程序一般是由一个主函数和若干个子函数组成,每个函数完成一个特定的功能。函数之间也可以相互调用。

函数的格式 :

定义性说明格式 :[存储类说明符 ] 类型说明符 [修饰符] 标识符 (参数

表) { 函数体 } 原型说明格式 :extern 类型说明符 [ 修饰符 ] 标识符(参数表){函

数体}

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

www.chinaEDA.cn

嵌入式程序设计中的函数及函数库嵌入式程序设计中的函数及函数库

函数库是为了减少编程工作量,将一些常用的功能的函数放在函数库中供公共使用 .

它包括 C 的标准库函数,也包括一些用户自己编写非标准库。

例如, 44blib.h 是根据基于 S3C44B0X处理器的开发板及其功能模块编写的一个 C语言函数库。它不属于 C 语言的标准库。

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

www.chinaEDA.cn

5.3.35.3.3 嵌入式程序设计中常用的嵌入式程序设计中常用的 CC 语言语句语言语句 C 语言语句格式为: [ 标号: ] 语句 [ ; ]

C 语言语句很多,常用到的有以下几种:条件语句 swith 语句

循环语句

条件语句的格式

两重选择:i f(条件表达式)语句1;el se语句2;

多重选择 :i f(条件表达式1)语句2;el se i f(条件表达式2)语句3;…

el se i f(条件表达式n)语句n;

switch语句的格式

switch(开关表达式){ case常量表达式1: [语句1;]

case常量表达式2: [语句2;]

case常量表达式n: [语句n;]

default: [语句n+1;]

}

循环语句的格式

for循环语句格式:for(表达式1;表达式2;表达式3)语句;

whi l e循环语句格式:whi l e(条件表达式)语句;

do whi l e循环语句格式:do语句;

whi l e(条件表达式);

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

www.chinaEDA.cn

5.3.4 5.3.4 嵌入式程序设计中嵌入式程序设计中 CC 语言的变量、语言的变量、数组、结构、联合数组、结构、联合

变量 [存储类型 ] 类型说明符 [ 修饰符 ] 标识符 [ =初值 ] [,标识符 [ =初值 ]]… ;

数组 一维数组:

类型说明符标识符 [常量表达式 ][= { …初值,初值, }];char 标识符 [ ] =“ 字符串”;

二维数组:类型说明符 标识符 [m][n] [ = {{初值表 },{初值表 }…}];

指针数组和数组指针 类型说明符 * 标志符 [常量表达式 ] [={ …地址,地址, }];

类型说明符 ( *标志符) [ ][=数组标识符 ];

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

www.chinaEDA.cn

结构说明 [存储类说明符 ] struct [结构原型名 ]

{ 类型说明标识符 [ …,标识符 ];类型说明标识符 [ …,标识符 ];…

} 标识符 [={ 初值表 } [ ,标识符 [={ 初值表 }]…] ;

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

www.chinaEDA.cn

联合说明

[存储类说明符 ] union[联合原型名] {类型说明符 标识符 [ …,标识符 ];

类型说明符 标识符 [ …,标识符 ];…

}标识符 = {初值表} [ ,标识符 [= { 初值表 }]…] ;

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

www.chinaEDA.cn

5.4 5.4 嵌入式嵌入式 CC 语言程序设计实例语言程序设计实例

5.4.1 嵌入式 C 语言程序编写的简单构架5.4.2 Flash测试代码介绍

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

www.chinaEDA.cn

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

main.c文件

包含头文件

各种类型的变量、数组定义 char * info[][2]

函数声明,这儿只声明本文件中定义的函数,其他用到的库函数已在包含头文件中声明

void Main(void)

void user_input_action(int value)

char User_Getch(void)

void FuncTest_view(int stdio)

void LCD_menu(void)

44blib.h

44b.h

rtc.h

…/LCD_Test/bmp.h

本文件中各个函数代码的定义

void Main(void)本文件主函数,它又调用其他函数,包括库函数中的函数

void user_input_action(int value)在本函数中,运用switch()语句根据不同的参数去调用不同的测试函数来完成不同的功能模块测试。

char User_Getch(void)

void FuncTest_view(int stdio)

void LCD_menu(void)

sys_init()Lcd_Init()Lcd_Clr()Lcd_Active_Clr()Lcd_Dma_Trans()Delay(100)init_keyboard()FuncTest_view(UART)

TS_Test()Digit_Led_Test()Lcd_Test()Test_Keyboard()Test_Iis()Test_Timer()Dhcp_Test()Test_Flash()Test_Iic()Tftp_Test()

BootLoader

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

www.chinaEDA.cn

5.4.25.4.2 嵌入式嵌入式 CC 语言程序编写的简单构架语言程序编写的简单构架 #include预编译指令

个 C 语言代码,一般要用 #include 编译指令将所需要的头文件加到该程序中,这是很有必要的,尤其是对编写较大的程序代码时。随后是定义一些外部变量,并对程序中的函数进行声明。

主函数main()的编写; 在 每 一 个 C 语 言 代 码 中 , 一 定要有一 个 main

()函数,在该函数中完成该程序文件所要完成的各个功能,一般是通过调用各个子函数来完成。当然,它也可以调用其他文件中的函数。

完成相应功能的各个功能函数的编写。各个函数之间可以相互调用。

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

www.chinaEDA.cn

5.4.3 Flash5.4.3 Flash 测试代码介绍测试代码介绍 下面给出功能测试程序中 Flash测试程序

的代码结构图:

flash.c文件

各种类型的变量、数组定义

函数声明 int Identify(int base_addr)

int erase_flash(int base_addr)

int erase_sector(int base_addr)

int wait_flash_ready(int address, pB0SIZE data)

包含头文件 flash.h

各个函数代码的定义 void Test_Flash(void)

int Program(int addr_base, char* pData, int data_size)

int Identify(int base_addr)

int erase_sector(int base_addr)

int erase_flash(int base_addr)

int wait_flash_ready(int address, pB0SIZE data)

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

www.chinaEDA.cn

5.5 5.5 嵌入式嵌入式 CC 语言程序设计技巧语言程序设计技巧

5.5.1 变量定义5.5.2 参数传递 5.5.3 循环条件

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

www.chinaEDA.cn

5.5.15.5.1 变量定义变量定义 在变量声明的时候,最好把所有相同类型的变量放在一起定义,这样可以优化存储器布局。由下例可以看出:

对于局部变量类型的定义,使用 short 或 char 来定义变量并不是总能节省存储空间。有时使用 32位 int 或 unsinged int 局部变量更有效率一些,如下图所示:

变量定义中,为了精简程序,程序员总是竭力避免使用冗余变量。但有时使用冗余变量可以减少存储器访问的次数这可以提高系统性能。

例子:

在数据区里的布局:

char a; char a;short b; char c;char c; short b;int d; int d;

a pad b

c pad

d

a c b

d

不同类型局部变量的编译结果

int wordinc (int a){return a+1}

short shortinc(short a ){return a+1}

short charinc(short a ){return a+1}

wordincADD a1,a1,#1

shortincADD a1,a1,#1MOV a1,a1.LSL #16MOV a1,a1,ASR #16MOV PC,LR

charincADD a1,a1,#1AND a1,a1,#&ffMOV PC,LR

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

www.chinaEDA.cn

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

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

www.chinaEDA.cn

5.6 C5.6 C 与汇编语言混合编程与汇编语言混合编程

5.6.1 ATPCS介绍5.6.2 内嵌汇编5.6.3 C 和 ARM 汇编程序间相互调用

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

www.chinaEDA.cn

5.6.1ATPCS5.6.1ATPCS 介绍介绍 ATPCS ( ARM-Thumb Produce Call St

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

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

www.chinaEDA.cn

寄存器的使用规则寄存器的使用规则 ::寄存器 别名 特殊名 使用规则

R0 a1   参数 / 结果/ scratch 寄存器 1R1 a2 参数 / 结果/ scratch 寄存器 2R2 a3   参数 / 结果/ scratch 寄存器 3R3 a4   参数 / 结果/ scratch 寄存器 4R4 v1  

ARM 状态局部变量寄存器 1R5 v2  

ARM 状态局部变量寄存器 2R6 v3  

ARM 状态局部变量寄存器 3R7 v4 wr ARM 状态局部变量寄存器 4

Thumb 状态工作寄存器R8 v5  

ARM 状态局部变量寄存器 5R9 v6 sb ARM 状态局部变量寄存器 6 ,

在支持 RWPI 的 ATPCS 中为静态基址寄存器R10 v7 sl ARM 状态局部变量寄存器 7 ,

在支持数据栈检查的 ATPCS 中为数据栈限制指针R11 v8 fp ARM 状态局部变量寄存器 8/ 帧指针R12   ip 子程序内部调用的 scratch 寄存器R13   sp 数据栈指针R14   lr 连接寄存器R15   pc 程序计数器

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

www.chinaEDA.cn

数据栈的使用规则数据栈的使用规则根据堆栈指针指向位置的不同 和增长方向的不同可以分为以下 4 种数据栈 :

FD ( Full Descending ) 满递减 ED ( Empty Descending )空递减 FA ( Full Ascending ) 满递增 EA ( Empty Ascending ) 空递增

ATPCS规定数据栈为 FD (满递减)类型,并且对数据栈的操作是 8 字节对齐的。

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

www.chinaEDA.cn

参数的传递规则参数的传递规则参数个数固定的子程序参数传递规则:

第一个整数参数,通过寄存器 R0~ R3 来传递。其他参数通过数据栈传递。 参数个数可变的子程序参数传递规则:

当参数不超过 4个时,可以使用寄存器 R0~R3来传递参数;当参数超过 4个时,还可以使用数据栈来传递参数 子程序结果返回规则

结果为一个 32位的整数时,可以通过寄存器 R0返回;结果为一个 64位整数时,可以通过寄存器 R0和 R1返回,依次类推。

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

www.chinaEDA.cn

5.6.25.6.2 内嵌汇编内嵌汇编 在 C 程序中嵌入汇编程序可以实现一些高级语言没有的功能,并可以提高执行效率。 armcc 和 armcpp内嵌汇编器支持完整的 ARM 指令集; tcc 和 tcpp用于 Thumb指集。 内嵌的汇编指令包括大部分的 ARM 指令和Thumb指令,但是不能直接引用 C 的变量定义,数据交换必须通过 ATPCS进行。嵌入式汇编在形式上表现为独立定义的函数体。

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

www.chinaEDA.cn

内嵌汇编指令的语法格式内嵌汇编指令的语法格式 __asm (“指令 [ ;指令 ]” );ARM C 汇编器使用关键字“ __asm" 。如果有多条汇编指令需要嵌入,可以用“{}”将它们归为一条语句。如:__asm{指令[;指令]…[ 指令 ]}需要特别注意的是 __asm是两个下划线。

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

www.chinaEDA.cn

内嵌的汇编指令的特点内嵌的汇编指令的特点 操作数可以是寄存器、常量或 C 表达式。它们可以是 char 、 short 或者 int 类型,而且是作为无符号数进行操作 。内嵌的汇编指令中使用物理寄存器有一些限制。常量前的符号“ #” 可以省略 只有指令 B可以使用 C 程序中的标号,指令 BL不能使用 C 程序中的标号。 不支持汇编语言中用于内存分配的伪操作。指令中如果包含常量操作数,该指令可能会被汇编器展开成几条指令。

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

www.chinaEDA.cn

内嵌汇编器与内嵌汇编器与 armasmarmasm 汇编器的区别汇编器的区别 内嵌汇编器不支持通过“ ·” 指示符或 PC获取当前指令地址; 不支持 LDR Rn , = expression 伪指令,而使用 MOV Rn , expression 指令向寄存器赋值; 不支持标号表达式;不支持 ADR 和 ADRL伪指令; 不支持BX和 BLX指令; 不可以向PC 赋值; 使用 0x前缀替代“&”表示十六进制数。

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

www.chinaEDA.cn

内嵌汇编注意事项内嵌汇编注意事项 必须小心使用物理寄存器,如 R0~R3 , LR和 PC 。 不要使用寄存器寻址变量。 使用内嵌汇编时,编译器自己会保存和恢复它可能用到的寄存器,用户无须保存和恢复寄存器。 LDM 和 STM 指令的寄存器列表只允许物理寄存器。 汇编语言用“,”作为操作数分隔符

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

www.chinaEDA.cn

5.6.3 C5.6.3 C 和和 ARMARM 汇编程序间相互调用汇编程序间相互调用 在 C 和 ARM 汇编程序之间相互调用

必须遵守 ATPCS ( ARM-Thumb Procedure Call Standard )规则。

C 和汇编之间的相互调用可以从以下这三方面来介绍:

汇编程序对 C 全局变量的访问在 C 语言程序中调用汇编程序在汇编程序中调用 C 语言程序

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

www.chinaEDA.cn

汇编程序访问全局汇编程序访问全局 CC 变量变量 汇编程序可以通过地址间接访问在 C 语言程序中声明的全局变量。通过使用 IMPORT关键词引人全局变量,并利用 LDR 和 STR 指令根据全局变量的地址可以访问它们。 对于不同类型的变量,需要采用不同选项的 LDR和 STR 指令,如下所示:

unsigned char LDRB/STRBunsigned short LDRH/STRHunsigned int LDR/STRchar LDRSB/STRSBshort LDRSH/STRSH

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

www.chinaEDA.cn

在在 CC 语言程序中调用汇编程序语言程序中调用汇编程序 为了保证程序调用时参数的正确传递,汇编程序的设计要遵守 ATPCS 。在汇编程序中需要使用 EXPORT 伪操作来声明,使得本程序可以被其它程序调用。同时,在 C 程序调用该汇编程序之前需要在 C 语言程序中使用 extern关键词来声明该汇编程序。

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

www.chinaEDA.cn

在汇编程序中调用在汇编程序中调用 CC 语言程序语言程序为了保证程序调用时参数的正确传递,

汇编程序的设计要遵守 ATPCS 。在 C 程序中不需要使用任何关键字来声明将被汇编语言调用的 C 程序,但是在汇编程序调用该 C程序之前需要在汇编语言程序中使用 IMPORT 伪操作来声明该 C 程序。在汇编程序中通过 BL 指令来调用子程序。