描述
本书可以作为高等学校计算机、电子、电信类专业的教材,也可以作为嵌入式开发相关人员的参考用书。
目录
第1章嵌入式系统概述
1.1嵌入式系统简介
1.1.1嵌入式系统的产生
1.1.2嵌入式系统的定义、特点和分类
1.1.3嵌入式系统的两种应用模式
1.1.4嵌入式系统的典型组成
1.2嵌入式微处理器
1.2.1嵌入式微处理器简介
1.2.2主流嵌入式微处理器
1.3嵌入式操作系统
1.3.1嵌入式Linux
1.3.2Windows CE
1.3.3Symbian
1.3.4Android
1.3.5μC/OSⅡ
1.3.6VxWorks
1.4嵌入式系统的应用领域和发展趋势
1.4.1嵌入式系统的应用领域
1.4.2嵌入式系统的发展趋势
1.5本章小结
习题
第2章ARM处理器体系结构
2.1ARM处理器概述
2.1.1ARM处理器简介
2.1.2ARM体系结构发展
2.1.3ARM处理器系列主要产品
2.1.4ARM开发工具简介
2.2CortexA8处理器架构
2.3CortexA8处理器工作模式和状态
2.3.1CortexA8处理器工作模式
2.3.2CortexA8处理器状态
嵌入式系统设计与应用——基于ARM CortexA8和Linux
2.4CortexA8存储器管理
2.4.1ARM的基本数据类型
2.4.2浮点数据类型
2.4.3大/小端存储模式
2.4.4寄存器组
2.4.5CortexA8存储系统
2.5CortexA8异常处理
2.5.1异常向量和优先级
2.5.2异常响应过程
2.5.3异常返回过程
2.5.4CortexA8处理器S5PC100中断机制
2.6本章小结
习题
第3章ARM指令集
3.1ARM指令集概述
3.1.1指令格式
3.1.2指令的条件码
3.2ARM指令的寻址方式
3.2.1立即寻址
3.2.2寄存器寻址
3.2.3寄存器间接寻址
3.2.4寄存器移位寻址
3.2.5变址寻址
3.2.6多寄存器寻址
3.2.7相对寻址
3.2.8堆栈寻址
3.2.9块复制寻址
3.3ARM指令简介
3.3.1跳转指令
3.3.2数据处理指令
3.3.3程序状态寄存器处理指令
3.3.4加载/存储指令
3.3.5协处理器指令
3.3.6异常产生指令
3.4Thumb指令简介
3.5ARM汇编语言编程简介
3.5.1伪操作
3.5.2伪指令
3.5.3汇编语句格式
3.5.4汇编语言的程序结构
3.6C语言与汇编语言的混合编程
3.6.1C程序中内嵌汇编
3.6.2汇编中访问C语言程序变量
3.6.3ARM中的汇编和C语言相互调用
3.7本章小结
习题
第4章S5PV210微处理器与接口
4.1基于S5PV210微处理器的硬件平台体系结构
4.1.1S5PV210处理器简介
4.1.2S5PV210内部各模块介绍
4.2存储系统
4.2.1S5PV210的地址空间
4.2.2S5PV210启动流程
4.3时钟系统
4.3.1S5PV210时钟概述
4.3.2S5PV210的时钟结构
4.4GPIO接口
4.4.1GPIO概述
4.4.2GPIO寄存器
4.4.3GPIO操作步骤
4.4.4一个LED灯的例子
4.5串行通信接口
4.5.1串行通信方式
4.5.2RS232C串行接口
4.5.3S5PV210的异步串行通信
4.5.4S5PV210的UART寄存器
4.5.5UART通信示例
4.6A/D转换器
4.7本章小结
习题
第5章ARMLinux内核
5.1ARMLinux概述
5.1.1GNU/Linux操作系统的基本体系结构
5.1.2ARMLinux内核版本及特点
5.1.3ARMLinux内核的主要架构及功能
5.1.4Linux内核源码目录结构
5.2ARMLinux进程管理
5.2.1进程的表示和切换
5.2.2进程、线程和内核线程
5.2.3进程描述符task_struct的几个特殊字段
5.2.4do_fork()函数
5.2.5进程的创建
5.2.6线程和内核线程的创建
5.2.7进程的执行——exec函数族
5.2.8进程的终止
5.2.9进程的调度
5.3ARMLinux内存管理
5.3.1ARMLinux内存管理概述
5.3.2ARMLinux虚拟存储空间及分布
5.3.3进程空间描述
5.3.4物理内存管理
5.3.5基于slab分配器的管理技术
5.3.6内核非连续内存分配(vmalloc)
5.3.7页面回收简述
5.4ARM_Linux模块
5.4.1LKM的编写和编译
5.4.2LKM版本差异比较
5.4.3模块的加载与卸载
5.4.4工具集moduleinittools
5.5ARMLinux中断管理
5.5.1ARM_Linux中断的一些基本概念
5.5.2内核异常向量表的初始化
5.5.3Linux中断处理
5.5.4内核版本2.6.38后的中断处理系统的一些改变——通用中断
子系统
5.6ARMLinux系统调用
5.7本章小结
习题
第6章Linux文件系统
6.1Linux文件系统概述
6.2ext2文件系统格式
6.2.1ext2文件系统
6.2.2目录结构
6.3ext3和ext4文件系统
6.3.1ext3文件系统
6.3.2ex4文件系统
6.4嵌入式文件系统JFFS2
6.4.1嵌入式文件系统
6.4.2JFFS2嵌入式文件系统
6.5YAFFS与YAFFS2文件系统简介
6.5.1YAFFS文件系统
6.5.2YAFFS2文件系统简介
6.6根文件系统
6.6.1根文件系统概述
6.6.2根文件系统的制作工具——BusyBox
6.6.3YAFFS2文件系统的创建
6.7本章小结
习题
第7章嵌入式Linux系统移植及调试
7.1BootLoader基本概念与典型结构
7.1.1BootLoader基本概念
7.1.2BootLoader的操作模式
7.1.3BootLoader的典型结构
7.1.4常见的BootLoader
7.2UBoot
7.2.1UBoot概述
7.2.2UBoot启动的一般流程
7.2.3UBoot环境变量
7.2.4UBoot命令
7.3交叉开发环境的建立
7.4交叉编译工具链
7.4.1交叉编译工具链概述
7.4.2工具链的构建方法
7.4.3交叉编译工具链的主要工具
7.4.4Makefile
7.5嵌入式Linux系统移植过程
7.5.1UBoot移植
7.5.2内核的配置、编译和移植
7.6GDB调试器
7.7远程调试
7.7.1远程调试工具的构成
7.7.2通信协议——RSP
7.7.3远程调试的实现方法及设置
7.7.4远程调试应用实例方法
7.8内核调试
7.8.1printk()
7.8.2KDB
7.8.3Kprobes
7.8.4KGDB
7.9本章小结
习题
第8章设备驱动程序设计
8.1设备驱动程序开发概述
8.1.1Linux设备驱动程序分类
8.1.2驱动程序的处理过程
8.1.3设备驱动程序框架
8.1.4驱动程序的加载
8.2内核设备模型
8.2.1设备模型功能
8.2.2sysfs
8.2.3sysfs的实现机制kobject
8.2.4设备模型的组织——platform总线
8.3字符设备驱动设计框架
8.3.1字符设备的重要数据结构
8.3.2字符设备驱动框架
8.4GPIO驱动概述
8.4.1gpiolib关键数据结构
8.4.2GPIO的申请和注册
8.5I2C总线驱动设计
8.5.1I2C总线概述
8.5.2I2C驱动程序框架
8.5.3关键数据结构
8.5.4I2C核心接口函数
8.5.5I2C设备驱动的通用方法
8.6块设备驱动程序设计概述
8.6.1块设备驱动整体框架
8.6.2关键数据结构
8.6.3块设备的请求队列操作
8.7嵌入式网络设备驱动设计
8.7.1网络设备驱动程序框架
8.7.2网络设备驱动程序关键数据结构
8.7.3网络设备驱动程序设计方法概述
8.8网络设备驱动程序示例——网卡DM9000驱动程序分析
8.9本章小结
习题
第9章Qt图形界面应用程序开发基础
9.1Qt简介
9.2Qt 5概述
9.2.1Qt 5简介
9.2.2通过“帮助”菜单了解Qt 5的组成——模块
9.2.3Linux下Qt开发环境的安装与集成
9.2.3Qt Creator功能和特性
9.3信号和插槽机制
9.4Qt程序设计
9.4.1helloworld程序
9.4.2多窗口应用程序
9.5Qt数据库应用
9.5.1数据库驱动
9.5.2Qt与SQLite数据库的连接
9.5.3SQL模型
9.6本章小结
习题
第10章SQLite数据库
10.1SQLite数据库概述
10.1.1基于Linux平台的嵌入式数据库概述
10.1.2SQLite的特点
10.1.3SQLite的体系结构
10.2SQLite安装
10.3SQLite的常用命令
10.4SQLite的数据类型
10.5SQLite的API函数
10.5.1核心C API函数
10.5.2扩充C API函数
10.6本章小结
习题
第11章嵌入式系统的开发设计案例
11.1嵌入式系统设计方法介绍
11.1.1传统的嵌入式系统设计方法
11.1.2“协同设计”概念的嵌入式系统设计方法
11.2基于ARM的嵌入式Web服务器设计实例
11.2.1系统环境搭建
11.2.2Web服务器原理
11.2.3嵌入式Web服务器设计
11.3物联网网关设计实例
11.3.1背景介绍——环境监测系统平台整体架构
11.3.2网关节点硬件设计方案
11.3.3系统软件设计
11.3.4数据库建设
11.4智能无人值守实验室监控系统设计实例
11.4.1系统总体框架
11.4.2学生选课预约
11.4.3门禁系统
11.4.4ZigBee网络的网络拓扑及路由协议
11.4.5Qt的使用
11.5本章小结
参考文献
前言
嵌入式计算机技术是21世纪计算机技术重要发展方向之一,应用领域十分广泛且增长迅速。据估计,未来十年95%的微处理器和65%的软件都将被应用于各种嵌入式系统中。技术的发展和生产力的提高离不开人才的培养。目前业界对嵌入式技术人才的需求十分巨大,尤其在迅速发展的电子、通信、计算机等领域,这种需求更为显著。另外,企业对嵌入式系统开发从业者的工程实践能力、经验要求也越来越重视。因此目前国内外很多专业协会和高校都在致力于嵌入式相关课程体系的建设,结合嵌入式系统的特点,在课程内容设计、师资队伍建设、教学方法探索、教学条件和实验体系建设等方面都取得了较好成效。从国外嵌入式课程建设来看,2004年,ACM(美国计算机协会)和IEEE联合制订了新版的计算机学科的课程体系(2004版),其中一个主要改革就是将Embedded System课程列为本科生的专业基础课,并且给出了基本课程体系。同时,美国卡内基梅隆大学、加州大学伯克利分校等国外高校也不断在完善他们的嵌入式教育体系,欧盟也推出了面向欧盟高校和企业的嵌入式研究计划,这些信息为编写适合计算机专业使用的“嵌入式系统”教材提供了指导和参考。从技术更新角度来看,近年来嵌入式系统技术得到了广泛应用和爆发性增长,普适计算、无线传感器、可重构计算、物联网、云计算等新兴技术的出现又为嵌入式系统技术的研究与应用注入了新的活力。这也对“嵌入式系统”课程教材的设计提出了更新更高的要求。从国内嵌入式系统教材来看,一是有部分嵌入式硬件系统仍然采用ARM9体系结构作为核心处理器架构,甚至还有ARM7体系结构,这不仅和市场脱节极其严重,也和高校与时俱进的教学理念产生冲突;二是目前国内高校配合嵌入式系统理论教材的嵌入式系统实验实训平台普遍已经进入更新换代周期,根据调研发现,国内许多高校嵌入式实验平台多在2006年至2009年购入,普遍采用ARM7、ARM9架构的核心处理器,至今已工作六年以上,在未来两年内将迎来实验设备普遍更换的潮流,而目前的教材仍然以老平台作为实践依据,这也形成了理论和实践的脱节,也要求嵌入式理论教材能够跟上实验实训要求的步伐;三是从应用角度来看,操作系统和应用软件近年来都取得较大发展,开源OS和APP的迅猛发展以及物联网与嵌入式系统的紧密结合也要求嵌入式教材尽可能保持技术的敏感度。在此前提下,本书对基于CortexA8处理器架构和嵌入式Linux的嵌入式系统进行了阐述。本书的特点如下。① 参考了ACM&IEEE联合制订的计算机学科的课程体系(2004版)关于EmbeddedSystem的课程要求,并结合了国内高校计算机学科课程大纲的要求。参考资料主要来自近几年国内外出版的嵌入式相关刊物、ARM官网、嵌入式专业网站、著名嵌入式设备公司相关资料和编写小组近年来的科研项目与指导学生创新实践活动资料,具有较好的时效性和实用性。② 采用ARMv7版本的CortexA8处理器架构作为系统核心处理器架构,取代原有ARM7/ARM9处理器架构。ARM9架构作为国内嵌入式系统教学主要选择架构已经近十年,一方面市场上主流芯片已经难觅其踪,另一方面国内高校ARM9实验平台也已经普遍超期服役,在未来两年内将迎来实验设备普遍更换的高峰。在更新设备选择资源中,以CortexA8处理器架构的实验实训平台具有极高的性价比(良好的扩展性和众多嵌入式设备厂家支持)。CortexA8处理器是ARM的款超标量处理器,具有提高代码密度和性能的技术,用于多媒体和信号处理的NEON技术,以及用于高效支持预编译和即时编译Java及运行时编译目标(RCT)技术。同时CortexA8处理器架构目前属于技术上稳定的处理器架构,有较多相关的嵌入式实验平台可供选择。嵌入式系统设计与应用——基于ARM CortexA8和Linux
③ 增加Qt和SQLite数据库相关知识,以满足日益增长的嵌入式系统UI设计与数据处理需求,更符合计算机学科特点和满足智能移动平台需要。④ 从编写小组自身从事的科研项目和实践活动出发,选择具有一定实用价值,包含交叉学科知识,反映嵌入式系统与物联网技术结合的三个项目实例。这些实例不仅从理论上深化拓展嵌入式系统设计方法和理念,也从实践角度提出“碰到问题如何运用所学知识解决问题”的观点,促进学生学以致用思想的升华。本书受国家自然科学基金面上项目“随钻测量井下网络化光纤传感器及信息传输关键技术研究”(编号41372155)的支持。编写过程中,王剑负责第1、11章的编写和全书的统稿; 刘鹏负责第2、3、4、9章的编写工作; 胡杰负责第5、6章的编写工作; 文汉云负责第10章的编写工作; 孟真玮负责第7、8章的编写工作。叶玲对本书进行了审校工作。同时本书的编写也得到了朱文霞和深圳博嵌科教仪器有限公司的大力支持和帮助,在此表示衷心的感谢。本书参考了国内外的许多的技术资料,书末有具体的参考文献,有兴趣的读者可以查阅相关信息。本书配有电子课件,需要的读者可以登录清华大学出版社本书页面下载。限于编者水平有限,书中不妥之处在所难免,敬请广大读者批评指正并提出宝贵意见。作者2016年12月
在嵌入式系统开发中,目前常用的编程语言是汇编语言和C语言。在较复杂的嵌入式软件中,由于C语言编写程序较方便,结构清晰,而且有大量支持库,所以大部分代码采用C语言编写,特别是基于操作系统的应用程序设计。但是在系统初始化、BootLoader、中断处理等,对时间和效率要求较严格的地方仍旧要使用汇编语言来编写相应代码块。本章将介绍ARM指令集指令及汇编语言的相关知识。3.1ARM指令集概述ARM处理器的指令集主要有: ARM指令集,是ARM处理器的原生32位指令集,所有指令长度都是32位,以字对齐(4字节边界对齐)方式存储; 该指令集效率高,但是代码密度较低。 Thumb指令集是16位指令集,2字节边界对齐,是ARM指令集的子集; 在具有较高代码密度的同时,仍然保持ARM的大多数性能优势。 Thumb2指令集是对Thumb指令集的扩展,提供了几乎与ARM指令集完全相同的功能,同时具有16位和32位指令,既继承了Thumb指令集的高代码密度,又能实现ARM指令集的高性能; 2字节边界对齐,16位和32位指令可自由混合。 Thumb2EE指令集是Thumb2指令集的一个变体,用于动态产生的代码; 不能与ARM指令集和Thumb指令集交织在一起。除了上面介绍的指令集外,ARM处理器还有针对协处理器的扩展指令集,如普通协处理器指令、NEON和VFP扩展指令集、无线MMX技术扩展指令集等。3.1.1指令格式ARM指令集的指令基本格式如下:
< opcode > {< cond >} { S } < Rd >, < Rn >, < shift_operand >
指令中“< >”内的项是必需的,“{ }”内的项是可选的。各个项目的具体含义如表31所示。
表31ARM指令格式
符号说明
opcode操作码,即指令助记符,如MOV、SUB、LDR等cond条件码,描述指令执行的条件S可选后缀,指令后加上S,指令执行成功完成后自动更新CPSR寄存器中的条件标志位Rd目的寄存器Rn存放第1个操作数的寄存器shift_operand第2个操作数,可以是寄存器、立即数等3.1.2指令的条件码ARM指令集中几乎每条指令都可以是条件执行的,由cond可选条件码来决定,位于ARM指令的4位[31∶28],可以使用的条件码如表32所示。每种条件码的助记符由两个英文符号表示,在指令助记符的后面和指令同时执行。根据程序状态寄存器CPSR中的条件标志位[31∶28]判断当前条件是否满足,若满足则执行指令。若指令中有后缀S,则根据执行结果更新程序状态寄存器CPSR中的条件标志位[31∶28]。
表32ARM指令条件码
指令条件码助记符CPSR条件标志位值含义
0000EQZ=1相等0001NEZ=0不相等0010CS/HSC=1无符号数大于或等于0011CC/LOC=0无符号数小于0100MIN=1负数0101PLN=0正数或零0110VSV=1溢出0111VCV=0没有溢出1000HIC=1,Z=0无符号数大于1001LSC=0,Z=1无符号数小于或等于1010GEN=V有符号数大于或等于1011LTN!=V有符号数小于1100GTZ=0,N=V有符号数大于1101LEZ=1,N!=V有符号数小于或等于1110AL任何无条件执行(指令默认条件) 1111NV任何从不执行(不要执行)3.2ARM指令的寻址方式寻址方式是指处理器根据指令中给出的地址信息,找出操作数所存放的物理地址,实现对操作数的访问。根据指令中给出的操作数的不同形式,ARM指令系统支持的寻址方式有: 立即寻址、寄存器寻址、寄存器间接寻址、寄存器移位寻址、变址寻址、多寄存器寻址、堆栈寻址、块复制寻址和相对寻址等。3.2.1立即寻址立即寻址也叫立即数寻址,指令的操作码字段后面的地址码部分不是操作数地址而是操作数本身,包含在指令的32位编码中。立即数前要加前缀“#”。示例:
ADD R0,R0,#1; R0 ← R0 1
MOVR0,#0x00ff; R0 ← 0x00ff
3.2.2寄存器寻址寄存器寻址是指将操作数放在寄存器中,指令中地址码部分给出寄存器编号。这是各类微处理器常用的一种有较高执行效率的寻址方式。示例:
ADD R0,R1,R2; R0 ← R1 R2
MOVR0,R1; R0 ← R1
3.2.3寄存器间接寻址操作数存放在存储器中,并将所存放的存储单元地址放入某一通用寄存器中,在指令中的地址码部分给出该通用寄存器的编号。示例:
LDRR0,[R1]; R0 ← [R1]
该指令将寄存器R1中存放的值0xA0000008作为存储器地址,将该存储单元中的数据0x00000003传送到寄存器R0中,寻址示意图如图31所示。
图31寄存器间接寻址方式示意图
3.2.4寄存器移位寻址该指令中,寄存器的值在被送到ALU之前,先进行移位操作。移位的方式由助记符给出,移位的位数可由立即数或寄存器直接寻址方式表示。可以采用的移位操作有: LSL: 逻辑左移,寄存器值低端空出的位补0。 LSR: 逻辑右移,寄存器值高端空出的位补0。 ASR: 算术右移,算术移位操作对象是有符号数,位移过程中要保证操作数的符号不变,若操作数是正数,高端空出位补0; 若操作数是负数,高端空出位补1。 ROR: 循环右移,从低端移出的位填入高端空出的位中。 RRX: 带扩展的循环右移,操作数右移1位,高端空出的位用C标志位填充。示例:
MOV R0,R1, LSL #2; R0 ← R1中的数左移2位
ADDR0, R1, R2, LSR #3; R0 ←R1 R2中的数右移3位
3.2.5变址寻址变址寻址方式是将某个寄存器(基址寄存器)的值与指令中给出的偏移量相加,形成操作数的有效地址,再根据该有效地址访问存储器。该寻址方式常用于访问在基址附近的存储单元。示例:
LDRR0,[R1, #2]; R0 ← [R1 2]
该指令将R1寄存器的值0xA0000008加上位移量2,形成操作数的有效地址,将该有效地址单元中的数据传送到寄存器R0中。3.2.6多寄存器寻址多寄存器寻址方式可以在一条指令中传送多个寄存器的值,一条指令多可以传送16个通用寄存器的值。连续的寄存器之间用“”连接,不连续的中间用“,”分隔。示例:
LDMIAR0!,{R1R3, R5}; R1 ← [R0]
; R2 ← [R0 4]
; R3 ← [R0 8]
; R5 ← [R0 12]
该指令将R0寄存器的值0xA0000004作为操作数地址,将存储器中该地址开始的连续单元中的数据传送到寄存器R1、R2、R3、R5中,寻址示意图如图32所示。
图32多寄存器寻址方式示意图
3.2.7相对寻址相对寻址方式就是以PC寄存器为基址寄存器,以指令中的地址标号为偏移量,两者相加形成操作数的有效地址。偏移量指出的是当前指令和地址标号之间的相对位置。子程序调用指令即是相对寻址方式。示例:
BLADDR1; 跳转到子程序ADDR1处执行
…
ADDR1:
…
MOVPC,LR; 从子程序返回
3.2.8堆栈寻址堆栈是按“先进后出”或“后进先出”方式进行存取的存储区。堆栈寻址是隐含的,使用一个叫做堆栈指针的专门寄存器,指示当前堆栈的栈顶。根据堆栈的生成方式不同,分为递增堆栈和递减堆栈。当堆栈向高地址方向生长时,叫做递增堆栈(向上生长); 当堆栈向低地址方向生长时,叫做递减堆栈(向下生长)。堆栈指针指向后压入堆栈的数据时,称为满堆栈; 堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈。这样就有四种类型的堆栈工作方式: 满递增堆栈(FA)、满递减堆栈(FD)、空递增堆栈(EA)、空递减堆栈(ED)。示例:
STMFD SP!,{R1R3, LR}; 将寄存器R1~R3和LR压入堆栈,满递减堆栈
LDMFDSP!,{R1R3, LR}; 将堆栈数据出栈,放入寄存器R1~R3和LR
3.2.9块复制寻址块复制寻址方式是多寄存器传送指令LDM/STM的寻址方式。LDM/STM指令可以将存储器中的一个数据块复制到多个寄存器中,或将多个寄存器中的值复制到存储器中。寻址操作中使用的寄存器可以是R0~R15这16个寄存器的所有或子集。根据基地址的增长方向是向上还是向下,以及地址的增减与指令操作的先后顺序(操作先进行还是地址先增减)的关系,有四种寻址方式: IB(Increment Before): 地址先增加再完成操作,如STMIB、LDMIB。 IA(Increment After): 先完成操作再地址增加,如STMIA、LDMIA。 DB(Decrement Before): 地址先减少再完成操作,如STMDB、LDMDB。 DA(Decrement After): 先完成操作再地址减少,如STMDA、LDMDA。3.3ARM指令简介ARM指令集主要有: 跳转指令、数据处理指令、程序状态寄存器处理指令、加载/存储指令、协处理器指令和异常产生指令六大类。ARM指令集是加载/存储型的,指令的操作数都存储在寄存器中,处理结果直接放入到目的寄存器中。采用专门的加载/存储指令来访问系统存储器。本节介绍ARM指令集中常用指令的用法和使用要点。3.3.1跳转指令跳转指令用于实现程序流程的跳转。在ARM程序中有两种方式可以实现程序流程的跳转: 直接向程序计数器PC中写入跳转地址,可以实现4GB地址空间内的任意跳转。例如:
LDRPC, [PC, # 0x00FF]; PC ← [PC 8 0x00FF]
使用专门的跳转指令。ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB地址空间的跳转。跳转指令有: 1. B指令
B{条件}目标地址
跳转指令B是简单的跳转指令,跳转到给定的目标地址,从那里继续执行。示例:
BWAITA; 无条件跳转到标号WAITA处执行
B0x1234; 跳转到地址0x1234处
2. BL指令
BL{条件}目标地址
用于子程序调用,在跳转之前,将下一条指令的地址复制到链接寄存器R14(LR)中,然后跳转到指定地址执行。示例:
BLFUNC1; 将当前PC值保存到R14中,然后跳转到标号FUNC1处执行
3. BLX指令
BLX{条件}目标地址
BLX指令从ARM指令集跳转到指定地址执行,并将处理器的工作状态由ARM状态切换到Thumb状态,同时将PC值保存到链接寄存器R14中。示例:
BLX FUNC1; 将当前PC值保存到R14中,然后跳转到标号FUNC1处执行,
; 并切换到Thumb状态
BLX R0; 将当前PC值保存到R14中,然后跳转R0中的地址处执行,
; 并切换到Thumb状态
4. BX指令
BX{条件}目标地址
带状态切换的跳转指令,跳转到指定地址执行。若目标地址寄存器的位[0]为1,处理器的工作状态切换为Thumb状态,同时将CPSR中的T标志位置1,目标地址寄存器的位[31∶1]复制到PC中; 若目标地址寄存器的位[0]为0,处理器的工作状态切换为ARM状态,同时将CPSR中的T标志位清0,目标地址寄存器的位[31∶1]复制到PC中。示例:
BXR0; 跳转R0中的地址处执行,如果R0[0]=1,切换到Thumb状态
3.3.2数据处理指令数据处理指令主要完成寄存器中数据的各种运算操作。数据处理指令的使用原则: 所有操作数都是32位,可以是寄存器或立即数。 如果数据操作有结果,结果也为32位,放在目的寄存器中。 指令使用“两操作数”或“三操作数”方式,即每一个操作数寄存器和目的寄存器分别指定。 数据处理指令只能对寄存器的内容进行操作。指令后都可以选择S后缀来影响标志位。比较指令不需要后缀S,这些指令执行后都会影响标志位。1. MOV指令
MOV{条件}{S}目的寄存器,源操作数
MOV指令将一个立即数、一个寄存器或被移位的寄存器传送到目的寄存器中。后缀S表示指令的操作是否影响标志位。如果目的寄存器是寄存器PC可以实现程序流程的跳转,寄存器PC作为目的寄存器且后缀S被设置,则在跳转的同时,将当前处理器工作模式下的SPSR值复制到CPSR中。示例:
MOVR0, #0x01; 将立即数0x01装入到R0
MOVR0, R1; 将寄存器R1的值传送到R0
MOVSR0, R1, LSL #3; 将寄存器R1的值左移3位后传送到R0,并影响标志位
MOVPC, LR; 将链接寄存器LR的值传送到PC中,用于子程序返回
2. MVN指令
MVN{条件}{S}目的寄存器,源操作数
MVN指令将一个立即数、一个寄存器或被移位的寄存器的值先按位求反,再传送到目的寄存器中。后缀S表示是否影响标志位。示例:
MVNR0, #0x0FF; 将立即数0xFF按位求反后装入R0,操作后R0=0xFFFFFF00
MVNR0, R1; 将寄存器R1的值按位求反后传送到R0
3. ADD指令
ADD{条件}{S}目的寄存器,操作数1,操作数2
ADD指令将两个操作数相加后,结果放入目的寄存器中。同时根据操作的结果影响标志位。示例:
ADDR0, R0, #1; R0 = R0 1
ADDR0, R1, R2; R0 = R1 R2
ADDR0, R1, R2, LSL #3; R0 = R1 (R2 << 3)
4. SUB指令
SUB{条件}{S}目的寄存器,操作数1,操作数2
SUB指令用于把操作数1减去操作数2,将结果放入目的寄存器中。同时根据操作的结果影响标志位。示例:
SUBR0, R0, #1; R0 = R0 – 1
SUBR0, R1, R2; R0 = R1- R2
SUBR0, R1, R2, LSL #3; R0 = R1 – (R2 << 3)
5. RSB指令
RSB{条件}{S}目的寄存器,操作数1,操作数2
RSB指令称为逆向减法指令,用于把操作数2减去操作数1,将结果放入目的寄存器中。同时根据操作的结果影响标志位。示例:
RSBR0, R0, #0xFFFF; R0 = 0xFFFF- R0
RSBR0, R1, R2; R0 = R2 – R1
6. ADC指令
ADC{条件}{S}目的寄存器,操作数1,操作数2
ADC指令将两个操作数相加后,再加上CPSR中的C标志位的值,将结果放入目的寄存器中。同时根据操作的结果影响标志位。示例:
ADDSR0, R0, R2
ADCR1, R1, R3; 用于64位数据加法,(R1,R0)=(R1,R0) (R3,R2)
7. SBC指令
SBC{条件}{S}目的寄存器,操作数1,操作数2
SBC指令用于操作数1减去操作数2,再减去CPSR中的C标志位值的反码,将结果放入目的寄存器中。同时根据操作的结果影响标志位。示例:
SUBSR0, R0, R2
SBCR1, R1, R3; 用于64位数据减法,(R1,R0)=(R1,R0)-(R3,R2)
8. RSC指令
RSC{条件}{S}目的寄存器,操作数1,操作数2
RSC指令用于操作数2减去操作数1,再减去CPSR中的C标志位值的反码,将结果放入目的寄存器中。同时根据操作的结果影响标志位。示例:
RSBSR2, R0, #0
RSCR3, R1, #0; 用于求64位数据的负数
RSCR0, R1, R2; R0 = R2 – R1-!C
9. AND指令
AND{条件}{S}目的寄存器,操作数1,操作数2
AND指令实现两个操作数的逻辑与操作,将结果放入目的寄存器中。同时根据操作的结果影响标志位。常用于将操作数某些位清0。示例:
ANDR0, R1, R2; R0 = R1 & R2
ANDR0, R0, #3; R0的位0和位1不变,其余位清0
10. ORR指令
ORR{条件}{S}目的寄存器,操作数1,操作数2
ORR指令实现两个操作数的逻辑或操作,将结果放入目的寄存器中。同时根据操作的结果影响标志位。常用于将操作数某些位置1。示例:
ORRR0, R0, #3; R0的位0和位1置1,其余位不变
11. EOR指令
EOR{条件}{S}目的寄存器,操作数1,操作数2
EOR指令实现两个操作数的逻辑异或操作,将结果放入目的寄存器中。同时根据操作的结果影响标志位。常用于将操作数某些位置取反。示例:
EORR0, R0, #0F; R0的低4位取反
12. BIC指令
BIC{条件}{S}目的寄存器,操作数1,操作数2
BIC指令用于清除操作数1的某些位,将结果放入目的寄存器中。同时根据操作的结果影响标志位。操作数2为32位掩码,掩码中设置了哪些位则清除操作数1中这些位。示例:
BICR0, R0, #0F; 将R1的低4位清0,其他位不变
13. CMP指令
CMP{条件}操作数1,操作数2
CMP指令用于把一个寄存器的值减去另一个寄存器的值或立即数,根据结果设置CPSR中的标志位,但不保存结果。示例:
CMPR1, R0; 将R1的值减去R0的值,并根据结果设置CPSR的标志位
CMPR1, #0x200; 将R1的值减去0x200,并根据结果设置CPSR的标志位
14. CMN指令
CMN{条件}操作数1,操作数2
CMN指令用于把一个寄存器的值减去另一个寄存器或立即数取反的值,根据结果设置CPSR中的标志位,但不保存结果。该指令实际完成两个操作数的加法。示例:
CMNR1, R0; 将R1的值和R0的值相加,并根据结果设置CPSR的标志位
CMNR1, #0x200; 将R1的值和立即数0x200相加,并根据结果设置CPSR的标志位
15. TST指令TST{条件}操作数1,操作数2
TST指令用于把一个寄存器的值和另一个寄存器的值或立即数进行按位与运算,根据结果设置CPSR中的标志位,但不保存结果。该指令常用于检测特定位的值。示例:
TSTR1, #0x0F; 检测R1的低4为是否为0
16. TEQ指令
TEQ{条件}操作数1,操作数2
TST指令用于把一个寄存器的值和另一个寄存器的值或立即数进行按位异或运算,根据结果设置CPSR中的标志位,但不保存结果。该指令常用于检测两个操作数是否相等。示例:
TEQR1, R2; 将R1的值和R2的值进行异或运算,并根据结果设置CPSR的标志位
3.3.3程序状态寄存器处理指令MRS指令和MSR指令用于在状态寄存器和通用寄存器间传输数据。状态寄存器的值要通过“读取→修改→写回”三个步骤操作来实现,可先用MRS指令将状态寄存器的值复制到通用寄存器中,修改后再通过MSR指令把通用寄存器的值写回状态寄存器。示例:
MRSR0, CPSR; 将CPSR的值复制到R0中
ORRR0, R0, #C0; R0的位6和位7置1,即屏蔽外部中断和快速中断
MSRCPSR, R0; 将R0值写回到CPSR中
MRS指令和MSR指令的格式如下:
MRS{条件}通用寄存器, 程序状态寄存器(CPSR或SPSR)
MSR{条件}程序状态寄存器(CPSR或SPSR)_, 操作数
其中,MSR指令中的可用于设置程序状态寄存器中需要操作的位: 位[31∶24]为条件标志位域,用f表示。 位[23∶16]为状态位域,用s表示。 位[15∶8]为扩展位域,用x表示。 位[7∶0]为控制位域,用c表示。示例:
MSRCPSR_cxsf, R3
3.3.4加载/存储指令加载/存储指令用于在寄存器和存储器之间传输数据,Load指令用于将存储器中的数据传输到寄存器中,Store指令用于将寄存器中的数据保存到存储器中。1. LDR指令
LDR{条件}目的寄存器,
LDR指令将一个32位字数据传输到目的寄存器中。如果目的寄存器是PC,从存储器中读出的数据将作为目的地址,以实现程序流程的跳转。示例:
LDRR1, [R0, #0x12]; 将存储器地址为R0 0x12的字数据写入R1
LDRR1, [R0, R2]; 将存储器地址为(R0 R2)的字数据写入R1
2. STR指令
STR{条件}源寄存器,
STR指令用于从源寄存器中将一个32位字数据写入存储器中。示例:
STRR1, [R0, #0x12]; 将R1中的字数据写入以R0 0x12为地址的存储器中
STRR1, [R0], #0x12; 将R1中的字数据写入以R0 0x12为地址的存储器中,
; 并将新地址R0 0x12写入R0
3. LDM和STM指令
LDM(或STM){条件}{类型}基址寄存器{!},寄存器列表{∧}
LDM指令和STM指令实现一组寄存器和一片连续存储空间之间的数据传输。LDM指令加载多个寄存器,STM指令存储多个寄存器,它们常用于现场保护、数据复制、参数传输等,有8种模式: IA: 每次传送后地址加4。 IB: 每次传送前地址加4。 DA: 每次传送后地址减4。 DB: 每次传送前地址减4。 FD: 满递减堆栈。 ED: 空递减堆栈。 FA: 满递增堆栈。 EA: 空递增堆栈。可选后缀{!},选用该后缀,当数据传输完成后,将后地址写入基址寄存器中,否则基址寄存器值不变; 基址寄存器不能为R15(PC),寄存器列表可以是R0~R15的任意组合。可选后缀{∧},当指令为LDM且寄存器列表中有R15(PC),选用该后缀表示除了完成数据传输以外,还将SPSR复制到CPSR。示例:
LDMIAR0, {R3R9}; R0指向的存储器单元的数据,保存到R3~R9中,R0值不更新
STMIAR1!, {R3R9}; 将R3~R9数据存储到R1指向的存储器单元中,R1的值更新
4. SWP指令
SWP{条件}目的寄存器,源寄存器1, [源寄存器2]
SWP指令用于将源寄存器2所指向的存储器中的字数据传输到目的寄存器中,同时将源寄存器1中的字数据传输到源寄存器2所指向的存储器中。当源寄存器1和目的寄存器为同一个寄存器时,该指令完成该寄存器和存储器内容的交换。示例:
SWPR1, R1, [R0]; 将R1的内容与R0指向的存储单元的内容进行交换
SWPR1, R2, [R0]; 将R0指向的存储单元的内容写入到R1中,并将R2的内容写入
; 到该内存单元中
3.3.5协处理器指令ARM体系结构允许通过增加协处理器来扩展指令集。ARM协处理器具有自己专用的寄存器组,它们的状态由控制ARM状态的指令的镜像指令来控制。程序的控制流指令由ARM处理器来处理,所有的协处理器指令只能同数据处理和数据传送有关。ARM协处理器指令可完成下面三类操作: ARM协处理器的数据处理操作。 ARM处理器和协处理器的寄存器之间数据传输。 ARM协处理器的寄存器和存储器之间数据传输。ARM协处理器指令主要包括5条,它们的格式和功能如表33所示。
表33ARM协处理器指令
助记符说明功能
CDP coproc,opcodel,CRd,CRn,CRm{,opcode2}协处理器数据操作指令用于ARM处理器通知协处理器执行特定的操作LDC{L} coproc,CRd 协处理器数据读取指令从某一连续的存储单元将数据读取到协处理器的寄存器中STC{L} coproc,CRd 协处理器数据写入指令将协处理器的寄存器数据写入到某一连续的存储单元中MCR coproc,opcodel,Rd,CRn{,opcode2}ARM寄存器到协处理器寄存器的数据传输指令将ARM处理器的寄存器中的数据传输到协处理器的寄存器中MRC coproc,opcodel,Rd,CRn{,opcode2}协处理器寄存器到ARM寄存器的数据传输指令将协处理器的寄存器中的数据传输到ARM处理器的寄存器中3.3.6异常产生指令ARM处理器有两条异常产生指令,软中断指令(SWI)和断点中断指令(BKPT)。1. SWI指令
SWI{条件}24位立即数
SWI指令用于产生SWI异常中断,实现从用户模式切换到管理模式,CPSR保存到管理模式下的SPSR中,执行转移到SWI向量。其他模式下也可使用SWI指令,同样切换到管理模式。该指令不影响条件码标志。示例:
SWI0x02; 软中断,调用操作系统编号为0x02的系统例程
2. BKPT指令
BKPT16位立即数
BKPT指令产生软件断点中断,软件调试程序可以使用该中断。立即数会被ARM硬件忽视,但能被调试工具利用来得到有用的信息。示例:
BKPT0xFF32
3.4Thumb指令简介为了兼容存储系统总线宽度为16位的应用系统,ARM体系结构中提供了16位Thumb指令集,它可以看作是ARM指令压缩形式的子集,是针对代码密度的问题而提出的,它具有16位的代码密度,这对于嵌入式系统来说至关重要。Thumb不是一个完整的体系结构,不能指望处理器只执行Thumb指令而不支持ARM指令集。因此,Thumb指令只需要支持通用功能,必要时可以借助完善的ARM指令集。只要遵循一定的调用规则,Thumb子程序和ARM子程序可以互相调用。当处理器在执行ARM程序段时,称ARM处理器处于ARM工作状态; 当处理器在执行Thumb程序段时,称ARM处理器处于Thumb工作状态。Thumb指令集没有协处理器指令、信号量指令以及访问CPSR或SPSR的指令,没有乘加指令及64位乘法指令等,并且指令的第二操作数受到限制; 除了分支指令B有条件执行功能外,其他指令均为无条件执行; 大多数Thumb数据处理指令采用2地址格式。Thumb指令集与ARM指令集的区别一般有如下4点: ① 跳转指令。程序相对转移,特别是条件跳转与ARM代码下的跳转相比,在范围上有更多的限制,转向子程序是无条件的转移。② 数据处理指令。Thumb数据处理指令是对通用寄存器进行操作,在大多数情况下,操作的结果须放入其中一个操作数寄存器中,而不是第3个寄存器中。Thumb数据处理操作比ARM状态的更少。访问R8~R15受到一定限制: 除MOV和ADD指令访问寄存器R8~R15外,其他数据处理指令总是更新CPSR中ALU状态标志; 访问寄存器R8~R15的Thumb数据处理指令不能更新CPSR中的ALU状态标志。③ 单寄存器加载和存储指令。在Thumb状态下,单寄存器加载和存储指令只能访问寄存器R0~R7。④ 多寄存器加载和多寄存器存储指令。LDM和STM指令可以将任何范围为R0~R7的寄存器子集加载或存储。PUSH和POP指令使用堆栈指针R13作为基址实现满递减堆栈。除R0~R7外,PUSH指令还可以存储链接寄存器R14,并且POP指令可以加载程序计数器PC。3.5ARM汇编语言编程简介3.5.1伪操作
ARM汇编语言程序是由机器指令、伪指令和伪操作组成的。伪操作是ARM汇编语言程序里的一些特殊的指令助记符,和指令系统中的助记符不同,这些助记符没有相应的操作码。伪操作主要是为完成汇编程序做一些准备工作,在源程序汇编过程中起作用,一旦汇编完成,伪操作的使命就完成。
宏是一段独立的程序代码,通过伪操作定义,在程序中使用宏指令即可调用宏。当程序被汇编时,汇编程序校对每个宏调用进行展开,用宏定义代替源程序中的宏指令。1. 符号定义伪操作符号定义伪操作用于定义ARM汇编程序中的变量、对变量赋值及定义寄存器名称等。常用伪操作有: GBLA、GBLL和GBLS: 定义全局变量。 LCLA、LCLL和LCLS: 定义局部变量。 SETA、SETL和SETS: 为变量赋值。 RLIST: 为通用寄存器列表定义名称。 CN: 为协处理器的寄存器定义名称。 CP: 为协处理器定义名称。 DN和SN: 为VFP的寄存器定义名称。 FN: 为FPA的浮点寄存器定义名称。2. 数据定义伪操作数据定义伪操作用于数据表定义、文字池定义、数据空间分配等。常用伪操作有: LTORG: 声明一个数据缓冲池的开始。 MAP: 定义一个结构化的内存表的首地址。 FIELD: 定义结构化内存表的一个数据域 SPACE: 分配一块内存空间,并用0初始化。 DCB: 分配一段字节的内存单元,并用指定的数据初始化。 DCD和DCDU: 分配一段字的内存单元,并用指定的数据初始化。 DCFD和DCFDU: 分配一段双字的内存单元,并用双精度的浮点数据初始化。 DCFS和DCFSU: 分配一段字的内存单元,并用单精度的浮点数据初始化。 DCQ和DCQU: 分配一段双字的内存单元,并用64位整型数据初始化。 DCW和DCWU: 分配一段半字的内存单元,并用指定的数据初始化。3. 汇编控制伪操作汇编控制伪操作用于条件汇编、宏定义、重复汇编控制等,常用伪操作有: IF、ELSE和ENDIF: 根据条件把一段源程序代码包括在汇编程序内或排除在程序之外。 WHILE和WEND: 根据条件重复汇编相同的源程序代码段。 MACRO和MEND: MACRO标识宏定义的开始,MEND标识宏定义结束。用MACRO和MEND定义一段代码,称为宏定义体,在程序中可以通过宏指令多次调用该代码段。 MEXIT: 用于从宏中跳转出去。4. 其他伪操作其他伪操作常用的有段定义伪操作、入口点设置伪操作、包含文件伪操作、标号导出或引入声明等。
ALIGN: 边界对齐。 AREA: 段定义。 CODE16和CODE32: 指令集定义。 END: 汇编结束。 ENTRY: 程序入口。 EQU: 常量定义。 EXPORT和GLORBAL: 声明一个符号可以被其他文件引用。 IMPORT和EXTERN: 声明一个外部符号。 GET和INCLUDE: 包含文件。 INCBIN: 包含不被汇编的文件。 RN: 给特定的寄存器命名。 ROUT: 标记局部标号使用范围的界限。3.5.2伪指令ARM中的伪指令并不是真正的ARM或Thumb指令,这些伪指令在汇编编译器对源程序进行汇编处理时被替换成对应ARM或Thumb指令(序列)。常用的伪指令如下。(1) ADRADR为小范围的地址读取伪指令,该指令将基于PC的相对偏移地址或基于寄存器的相对偏移地址读取到寄存器中。格式为
ADR {cond} register, expr
cond是可选的指令执行条件。 register是目的寄存器。 expr是基于PC或基于寄存器的地址表达式,当地址值是字节对齐时,取值范围为-255~255B; 当地址值是字对齐时,取值范围为-1020~1020B; 当地址值是16字节对齐时,取值范围更大。(2) ADRLADRL为中等范围的地址读取伪指令,该指令比ADR的取值范围更大。格式为
ADRL {cond} register, expr
cond是可选的指令执行条件。 register是目的寄存器。 expr是基于PC或基于寄存器的地址表达式,当地址值是字节对齐时,取值范围为-64~64KB; 当地址值是字对齐时,取值范围为-256~256KB; 当地址值是16字节对齐时,取值范围更大; 在32位的Thumb2指令中,取值范围可达-1~1MB。(3) LDRLDR为大范围的地址读取伪指令,将一个32位的常数或者一个地址值读取到寄存器中。格式为
LDR {cond} register, = [expr|labelexpr]
cond是可选的指令执行条件。 register是目的寄存器。 expr是32位常量。(4) NOPNOP是空操作伪指令,在汇编时被替换成ARM中的空操作。3.5.3汇编语句格式ARM(Thumb)汇编语言的语句格式为
[标号] [; 注释]
在ARM汇编程序中,ARM指令、伪操作、伪指令、伪操作的助记符全部用大写字母,或者全部用小写字母,不能既有大写也有小写字母。 所有标号在一行的顶格书写,后面不要添加“: ”,所有指令不能顶格书写。 注释内容以“;”开头到本行结束。 源程序中允许有空行,如果单行太长,可以用字符“\”将其分开,“\”后不能有任何字符,包括空格和制表符等。 变量的设置,常量的定义,其标识符必须在一行顶格书写。3.5.4汇编语言的程序结构段(section)是ARM汇编语言组织源文件的基本单位,是独立的、具有特定名称的、不可分割的指令或数据序列。段分为代码段和数据段,代码段存放执行代码,数据段存放代码执行时需要的数据。一个ARM汇编程序至少需要一个代码段,较大的程序可以包含多个代码段和数据段。ARM汇编语言源程序经过汇编后生成可执行的映像文件,格式有axm、bin、elf、hex等。可执行的映像文件通常包括三个部分: 一个或多个代码段,代码段的属性为只读。 0个或多个包含初始化数据的数据段,属性为可读可写。 0个或多个不包含初始化数据的数据段,属性为可读可写。链接器根据一定的规则将各个段安排到内存的相应位置。源程序中段之间的相对位置与可执行的映像文件中段的相对位置不一定相同。下面的程序说明了ARM汇编语言程序的基本结构:
AREABUF, DATA, READWRITE; 声明数据段BUF
countDCB30; 定义一个字节单元count
AREAEXAMPLE1, CODE, READONLY; 声明代码段EXAMPLE1
ENTRY; 程序入口
CODE32; 声明32位ARM指令
START
LDRBR0, count; R0 = count
MOVR1, #10; R1 = 8
ADDR0, R0, R1; R0 = R0 R1
BSTART
END
3.6C语言与汇编语言的混合编程在嵌入式开发中,C语言是一种常见的程序设计语言。C语言程序可读性强、易维护、可移植性和可靠性高。ARM体系结构不仅支持汇编语言也支持C语言,在一些情况下需要采用汇编语言和C语言混合编程。3.6.1C程序中内嵌汇编在C语言程序中嵌入汇编可以完成一些C语言不能完成的操作,同时代码效率也比较高。在ARM C语言程序中使用关键词__asm来标识一段汇编指令代码,格式为:
__asm//asm前2个下画线
{
instruction [; instruction]
…
[instruction]
}
如果一行有多条汇编指令,指令间用“;”隔开; 如果一条指令占多行,要使用续行符号“\”。在ARM C语言程序中也可以使用关键词asm来标识一段汇编指令代码,格式为:
asm(“instruction [; instruction]”);
3.6.2汇编中访问C语言程序变量在C语言中声明的全局变量可以被汇编语言通过地址间接访问。例如,在C语言程序中已经声明了一个全局变量glovbvar,通过IMPORT伪指令声明外部变量的方式访问:
AREAEXAMPLE2, CODE, REDAONLY
IMPORTglovbvar; 声明外部变量glovbvar
START
LDRR1, =glovbvar; 装载外部变量地址
LDR R0, [R1]; 读出全局变量glovbvar数据
ADDR0, R0, #1
STRR0,[R1]; 保存变量值
MOVPC, LR
END
3.6.3ARM中的汇编和C语言相互调用为了使单独编译的C语言程序和汇编语言程序之间能够相互调用,必须遵守ATPCS规则。ATPCS即ARMThumb Procedure Call Standard(ARMThumb过程调用标准)的简称。ATPCS规定了应用程序的函数可以如何分开地写,分开地编译,后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。基本ATPCS规定了在子程序调用时的一些基本规则,包括3个方面的内容: 各寄存器的使用规则及其相应的名称。 数据栈的使用规则。 参数传递的规则。1. C程序调用汇编程序C程序调用汇编程序首先通过extern声明要调用的汇编程序模块,声明中形参个数要与汇编程序模块中需要的变量个数一致,且参数传递要满足ATPCS规则,然后在C程序中调用。示例:
#include
extern void strcopy(char *d, char *s); //使用关键词声明
int main()
{
char *srcstr = “first”;
char *dststr = “second”;
strcopy(dststr,srcstr); //汇编模块调用
}
被调用的汇编程序:
AREAScopy, CODE, REDAONLY
EXPORT strcopy ; 使用EXPORT伪操作声明本汇编程序
strcopy
LDRB R2, [R1], #1
STRB R2, [R0], #1
CMPR2, #0
BNE strcopy
MOV PC, LR
END
2. 汇编程序调用C程序在调用之前必须根据C语言模块中需要的参数个数,以及ATPCS参数规则,完成参数传递,即前四个参数通过R0~R3传递,后面的参数通过堆栈传递,然后再利用B、BL指令调用。示例:
int g(int a,int b,int c,int d,int e)//C语言函数原型
{
return (a b c d e);
}
//汇编程序调用C程序g()计算i 2*i 3*i 4*i 5*i的结果
EXPORT f
AREAf, CODE, REDAONLY
IMPORT g //声明C程序函数g()
STR LR, {SP, #4}!//保存PC
ADD R1 ,R0, R0
ADD R2, R1, R0
ADD R3, R1, R2
STR R3, {SP, #4}!
ADD R3, R1, R1
BL g //调用C程序函数g()
ADD SP, SP, #4
LDR PC, [SP], #4
END
3.7本章小结本章首先对ARM处理器指令的九种寻址方式进行了说明,并详细介绍了ARM指令集中各种指令的格式、功能和使用方法,简单介绍了16位的Thumb指令。随后介绍了ARM汇编语言的伪操作、伪指令和汇编语句格式,通过示例讲述了汇编语言程序的结构。后讲述了C语言和汇编语言混合编程的规则和方法。通过本章,读者了解到ARM程序设计的基本知识,为基于ARM处理器的嵌入式软硬件开发奠定基础。习题1. 请写出对应C代码的ARM指令C代码:
If(a > b)
a ;
Else
b ;
2. 请写出下列ARM指令的功能
MOVR1,#0x10;
MOVR0,R1;
MOVSR3,R1,LSL #2;
MOVPC,LR
3. 在前文提到了实现Thumb状态和ThumbEE状态之间切换的指令为ENTERX指令和LEAVEX指令,请查阅相关资料对这两个命令进行比较。4. 请查阅资料,试比较ARM、Thumb、Thumb2、Thumb2EE指令集的区别。
评论
还没有评论。