计算机硬件基础
Linux层次结构
Linux子系统
- 进程管理:管理进程的创建、调度、销毁等
- 内存管理:管理内存的申请、释放、映射等
- 文件系统:管理和访问磁盘中的文件
- 设备管理:硬件设备及驱动的管理
- 网络协议:通过网络协议栈(TCP、IP…)进行通信
计算机的组成
输入设备
- 把其他信号转换成计算机能识别和处理的信号并送入计算机中
- 如键盘、鼠标、摄像头等
输出设备
- 运算结果以人或其他设备所能接受的形式送出计算机外
- 如显示器、音响、打印机等
存储器
- 存储器是用来存储程序和数据的部件,是实现"存储程序控制"的基础
- 如内存、硬盘等
运算器
- CPU中负责进行算数运算和逻辑运算的部件,其核心是算术逻辑单元ALU
控制器
- 控制器是CPU的指挥中心,其控制着整个CPU执行程序的逻辑过程
运算器和控制器共同组成了CPU
总线
总线是计算机中各个部件之间传送信息的公共通信干线, 在物理上就是一束导线按照其传递信息的类型可以分为数据总线、地址总线、控制总线
DMA总线
DMA(Direct Memory Access)即直接存储器访问,使用DMA总线可以不通过CPU直接在存储器之间进行数据传递
三级存储结构
Cache
- 速度最快、价格最贵、容量最小、断电数据丢失、cpu可直接访问
- 存储当前正在执行的程序中的活跃部分,以便快速地向CPU提供指令和数据
主存储器
- 速度、价格、容量介于Cache与辅存之间、断电数据丢失、cpu可直接访问
- 存储当前正在执行的程序和数据
辅助存储器
- 速度最慢、价格最低、容量最大、断电数据不丢失、cpu不可直接访问
- 存储暂时不运行的程序和数据,需要时再传送到主存
地址空间
一个处理器能够访问(读写)的存储空间是有限的,我们称这个空间为它的地址空间(寻址空间),一般来说N位地址总线的处理器的地址空间是2的N次方
CPU工作原理
一条指令的执行分为三个阶段
-
取址
- CPU将PC寄存器中的地址发送给内存,内存将其地址中对应的指令返回到CPU中的指令寄存器(IR)
-
译码
- 译码器对IR中的指令进行识别,将指令(机器码)解析成具体的运算
-
执行
- 控制器控制运算器中对应的运算单元进行运算,运算结果写入寄存器
ARM体系结构
处理器
RISC处理器
- 只保留常用的的简单指令,硬件结构简单,复杂操作一般通过简单指令的组合实现,一般指令长度固定,且多为单周期指令
- RISC处理器在功耗、体积、价格等方面有很大优势,所以在嵌入式移动终端领域应用极为广泛
CISC处理器
- 不仅包含了常用指令,还包含了很多不常用的特殊指令,硬件结构复杂,指令条数较多,一般指令长度和周期都不固定
- CISC处理器在性能上有很大优势,多用于PC及服务器等领域
SOC
即片上系统,将一个系统中所需要的全部部件集成在一个芯片中在体积、功耗、价格上有很大优势
指令集
指令
- 能够指示处理器执行某种运算的命令称为指令(如加、减、乘 …)
- 指令在内存中以机器码(二进制)的方式存在
- 每一条指令都对应一条汇编
- 程序是指令的有序集合
指令集
- 处理器能识别的指令的集合称为指令集
- 不同架构的处理器指令集不同
- 指令集是处理器对开发者提供的接口
ARM指令集
ARM指令集
- 所有指令(机器码)都占用32bit存储空间
- 代码灵活度高、简化了解码复杂度
- 执行ARM指令集时PC值每次自增4
Thumb指令集
- 所有指令(机器码)都占用16bit存储空间
- 代码密度高、节省存储空间
- 执行Thumb指令集时PC值每次自增2
编译原理
- 机器码(二进制)是处理器能直接识别的语言,不同的机器码代表不同的运算指令,处理器能够识别哪些机器码是由处理器的硬件设计所决定的,不同的处理器机器码不同,所以机器码不可移植
- 汇编语言是机器码的符号化,即汇编就是用一个符号来代替一条机器码,所以不同的处理器汇编也不一样,即汇编语言也不可移植
- C语言在编译时我们可以使用不同的编译器将C源码编译成不同架构处理器的汇编,所以C语言可以移植
ARM数据类型
ARM采用32位架构,基本数据类型有以下三种
- Byte 8bits
- Halfword 16bits
- Word 32bits
数据存储
- Word型数据在内存的起始地址必须是4的整数
- Halfword型数据在内存的起始地址必须是2的整数倍
即数据本身是多少位在内存存储时就应该多少位对齐
字节序
- 大端对齐
- 低地址存放高位,高地址存放低位
- 小端对齐
- 低地址存放低位,高地址存放高位
ARM一般使用小端对齐
ARM指令存储
- 处理器处于ARM状态时
- 所有指令在内存的起始地址必须是4的整数倍
- PC值由其[31:2]决定,[1:0]位未定义
- 处理器处于Thumb状态时
- 所有指令在内存的起始地址必须是2的整数倍
- PC值由其[31:1]决定,[0]位未定义
- 注:
即指令本身是多少位在内存存储时就应该多少位对齐
ARM工作模式
模式 | 释义 |
---|---|
User | 非特权模式,一般在执行上层的应用程序时ARM处于该模式 |
FIQ | 当一个高优先级中断产生后ARM将进入这种模式 |
IRQ | 当一个低优先级中断产生后ARM将进入这种模式 |
SVC | 当复位或执行软中断指令后ARM将进入这种模式 |
Abort | 当产生存取异常时ARM将进入这种模式 |
Undef | 当执行未定义的指令时ARM将进入这种模式 |
System | 使用和User模式相同寄存器集的特权模式 |
Monitor | 为了安全而扩展出的用于执行安全监控代码的模式 |
- 不同模式拥有不同权限
- 不同模式执行不同代码
- 不同模式完成不同的功能
ARM工作模式分类
按照权限
User
为非特权模式(权限较低),其余模式均为特权模式(权限较高)
按照状态
FIQ、IRQ、SVC、Abort、Undef
属于异常模式,即当处理器遇到异常后会进入对应的模式
ARM寄存器组织
概念
寄存器是处理器内部的存储器,没有地址
ARM寄存器
ARM-9寄存器一共有40个
在某个特定模式下只能使用当前模式下的寄存器,一个模式下特有的寄存器其他模式下不可使用
专用寄存器
- R15(PC,Program Counter)
- 程序计数器,用于存储当前取址指令的地址
- R14(LR,Link Register)
- 链接寄存器,一般有以下两种用途:
- 执行跳转指令(BL/BLX)时,LR会自动保存跳转指令下一条指令的地址,程序需要返回时将LR的值复制到PC即可实现
- 产生异常时,对应异常模式下的LR会自动保存被异常打断的指令的下.一条指令的地址,异常处理结束后将LR的值复制到PC可实现程序返回
- 链接寄存器,一般有以下两种用途:
- R13(SP,Stack Pointer)
- 栈指针,用于存储当前模式下的栈顶地址
CPSR寄存器
CPSR(Current Program Status Register),当前程序状态寄存器
CPSR寄存器分为四个域
-
[31:24]
为条件域用F表示 -
[23:16]
为状态域用S表示 -
[15:8]
为预留域用X表示 -
[8:0]
为控制域用C表示 -
Bit[4:0]
-
[10000]User [10001]FIQ [10010]IRQ [10011]SVC
-
[10111]Abort [11011]Undef [11111]System [10110]Monitor
-
Bit[5]
- [0]ARM状态 [1]Thumb状态
-
Bit[6]
- [0]开启FIQ [1]禁止FIQ
-
Bit[7]
- [0]开启IRQ [1]禁止IRQ
-
Bit[28]
- 当运算器中进行加法运算且产生符号位进位时该位自动置1,否则为0
- 当运算器中进行减法运算且产生符号位借位时该位自动置0,否则为1
-
Bit[29]
- 当运算器中进行加法运算且产生进位时该位自动置1,否则为0
- 当运算器中进行减法运算且产生借位时该位自动置0,否则为1
-
Bit[30]
- 当运算器中产生了0的结果该位自动置1,否则为0
-
Bit[31]
- 当运算器中产生了负数的结果该位自动置1,否则为0
ARM异常处理
概念
处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件异常事件处理完成之后再返回到被异常打断的点继续执行程序
异常处理机制
不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制
ARM异常源
导致异常产生的事件称为异常源
ARM异常源· | 释义 |
---|---|
FIQ | 快速中断请求引脚有效 |
IRQ | 外部中断请求引脚有效 |
Reset | 复位电平有效 |
Software Interrupt | 执行swi指令 |
Data Abort | 数据终止 |
Prefetch Abort | 指令预取终止 |
Undefined Instruction | 遇到不能处理的指令 |
ARM异常模式
在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切换成对应的异常模式
异常源 | FIQ | IRQ | Reset SWI | Data Abort Prefetch Abort | Undef Instruction |
---|---|---|---|---|---|
异常模式 | FIQ | IRQ | SVC | Abort | Undef |
ARM异常响应
ARM产生异常后的动作(自动完成)
- 拷贝CPSR中的内容到对应异常模式下的SPSR_
- 修改CPSR的值
- 修改中断禁止位禁止相应的中断
- 修改模式位进入相应的异常模式
- 修改状态位进入ARM状态
- 保存返回地址到对应异常模式下的LR__
- 设置PC为相应的异常向量(异常向量表对应的地址)
异常向量表
-
异常向量表的本质是内存中的一段代码
-
表中为每个异常源分配了四个字节的存储空间
-
遇到异常后处理器自动将PC修改为对应的地址
-
因为异常向量表空间有限一般我们不会再这里
-
写异常处理程序,而是在对应的位置写一条跳
-
转指令使其跳转到指定的异常处理程序的入口
注:ARM的异常向量表的基地址默认在0x00地址但可以通过配置协处理器来修改其地址
异常返回
ARM异常返回的动作(自己编写)
- 将SPSR_的值复制给CPSR使处理器恢复之前的状态
- 将LR__的值复制给PC使程序跳转回被打断的地址继续执行
IRQ异常举例
异常优先级
FIQ和IRQ的区别
ARM微架构
指令流水线
ARM指令流水线
- ARM7采用3级流水线
- ARM9采用5级流水线
- Cortex-A9采用8级流水线
注1:虽然流水线级数越来越多,但都是在三级流水线的基础上进行了细分
PC的作用(取指)
不管几级流水线,PC指向的永远是当前正在取指的指令,而当前正在执行的指令的地址为PC-8
指令流水线意义
指令流水线机制的引入确实能够大大的提升指令执行的速度但在实际执行程序的过程中很多情况下流
水线时是无法形成的比如芯片刚上电的前两个周期、执行跳转指令后的两个周期等所以指令流水线的
引入以及优化只能使平均指令周期不断的接近1而不可能真正的达到1,且流水线级数越多芯片设计的
复杂程度就越高,芯片的功耗就越高
多核处理器
即一个SOC中集成了多个CPU核
作用
:不同的线程可以运行在不同的核心中做到真正的并发
资源
:多核处理器共用外设与接口资源
ARM指令
汇编的本质
汇编:
- 每条汇编都会唯一对应一条机器码,且CPU能直接识别和执行即汇编中所有的指令都是CPU能够识别和执行的
- 汇编中寄存器的使用、栈的分配与使用、程序的调用、参数的传递等都需要自己维护
C语言:
- 每条C语句都要被编译器编译成若干条汇编指令才能被CPU识别和执行即C语句中的指令CPU不一定能直接识别,需要编译器进行“翻译”
- C中寄存器的使用、栈的分配与使用、程序的调用、参数的传递等 都是编译器来分配和维护
汇编和C语言的区别
-
汇编语言是一种用文字助记符来表示机器指令的符号语言,是最接近机器码的一种语言。其主要优点是占用资源少、程序执行效率高。但是不同的CPU,汇编语言可能有所差异,所以不易移植
-
C语言是一种结构化的高级语言。其优点是可读性好,移植容易,是普遍使用的一种计算机语言。缺点是占用资源较多,执行效率没有汇编高。
ARM指令
常用指令
数据转移指令
- MOV 数据转移指令
- MVN 数据转移取反指令
数据运算指令的格式
<操作码> <目标寄存器> <第一操作寄存器> <第二操作数>
- 操作码 表示执行那种操作
- 目标寄存器 用于存储运算的结果
- 第一操作寄存器 存储第一个参与运算的数据(
只能写寄存器
) - 第二操作数 第二个参加运算的数据(可以是
寄存器
也可以是立即数
)
数据运算指令
- 加法:ADD R1,R2,R3 R1=R2+R3
- 减法:SUB R1,R2,R3 R1=R2-R3
- 逆向减法指令:RSB R1,R2,#3 R1=3-R2
- 乘法指令:MUL R1,R2,R3 R1=R2*R3 乘法不可以使用立即数 只能是两个寄存器相乘
- 除法:
ARM精简指令集不支持除法
- 按位与指令:AND R1,R2,R3 R1=R2&R3
- 按位或指令:ORR R1,R2,R3 R1=R2|R3
- 按位异或指令:EOR R1,R2,R3 R1=R2^R3
- 左移指令:LSL R1,R2,R3 R1=(R2<<R3)
- 右移指令:LSR R1,R2,R3 R1=(R2>>R3)
- 位清零指令:BIC R1,R2,#0XF 第二操作数中的哪一位为1,就将第一操作寄存器中的哪一位清零,然后将结果放入目标寄存器
- 数据运算指令的格式扩展
- MOV R1, R2, LSL #1 R1=(R2<<1)
- 数据运算指令对条件位(N、Z、C、V)的影响
- 默认情况下数据运算不会对条件位产生影响
在指令后加后缀”S"后可以影响
SUBS R2,R1,#3
- 32位的cpu怎么计算64位的数据
- 将64位的数据拆为高32位和低32位
- ADDS R5,R1,R3
- ADC R6,R2,R4
带进位的加法运算指令
- 例如:
- MOV R1,#OXFFFFFFFF
- MOV R2,#0X00000001
- MOV R3,#OXOOOOOOO5
- MOV R4,#OXOOOOOOO2
- ADDS R5,R1,R3
- ADC R6,R2,R4
本质:R6 = R2+R4+'C'
- 例如:
- MOV R1,#OX00000001
- MOV R2,#0X00000002
- MOV R3,#OXOOOOOOO5
- MOV R4,#OXOOOOOOO1
- SUBS R5,R1,R3
- SBC R6,R2,R4
本质:R6 = R2-R4-'!C'
立即数
- #123 立即数(0-255)立即数就是包含在指令当中的数,属于指令的一部分
- 优点:
- 取指的时候就可以将其读取到cpu,不用单独去内存读取,速度快
- 缺点
- 不能时任意的32位的数字,有局限性
跳转指令
- B 标号
跳转到标号的地方但是不返回
- 本质:将PC寄存器的值修改成标号下第一条指令的地址
- BL 标号 跳转到标号的地方并返回
- 本质:将PC寄存器的值修改成跳转标号下第一条指令的地址,同时将跳转指令的地址存储到LR
返回时,需要手动将LR的值赋给PC
- 修改PC值,地址需要自己计算
ARM指令的条件码
比较指令
CMP R1,R2
- 本质:就是一条减法指令,只是没有将运算的结果存入寄存器
比较跳转指令
BEQ FUNC
- 本质:if(Z==1){B FUNC}
ARM指令中大多数指令都可以带条件码后缀
访问读写内存Load/Srore指令
写内存
- STR R1,[R2]
- 将R1寄存器中的数据存储到R2指向的内存空间
- STRB R1,[R2]
- 一次写入一个字节的数据到R1
- 后缀B 一个字节
- 后缀H 半个字
- STR 默认一个字
读内存
- LDR R3,[R2]
- 将内存中R2的数据读到R3
LDR指令同样支持以上后缀
寻址方式
就是CPU去寻找一个操作数的方式
-
立即寻址
- ADD R1,R2,#1
-
寄存器寻址
-
ADD R1,R2,R3
-
寄存器移位寻址
- MOV R1,R2,LSL,#1
-
-
寄存器间接寻址
- STR R1,[R2]
-
基址加变址寻址
- STR R1,[R2,R3] 本质:将R1寄存器中的数据写入到R2+R3指向的内存空间
- STR R1,[R2,R3,LSL #1] 本质:将R1寄存器中的数据写入到R2+(R3<<1)指向的内存空间
-
基址加变址寻址的索引方式
- MOV R1,#0XFFFFFFFF
- MOV R2,#0X40000000
前索引
STR R1,[R2,#8] 本质:将R1寄存器中的数据写入到R2+8指向的内存空间后索引
STR R1,[R2],#8 本质:将R1寄存器中的数据写入到R2指向的内存空间,然后R2自增8自动索引
STR R1,[R2,#8]! 本质:将R1寄存器中的数据写入到R2+8指向的内存空间,然后R2自增8
以上寻址和索引方式同样适用于LDR
多寄存器内存访问指令
- STM R11,{R1-R4} 本质:将R1-R4寄存器中的数据存储到内存以R11为起始的内存中
- LDM R11,{R6-R11} 本质:将内存中R11为起始地址的数据读取到R6-R11的内存中
当寄存器不连续时,使用逗号分隔
不管寄存器列表中的顺序如何,存储时永远是低地址存储小编号的寄存器
自动索引也适用于都寄存器内存访问指令
- STM R11!,{R1-R4}
- SRMIA R11!,{R1-R4} 默认后缀为IA选项(包括R11的地址)
- SRMIB R11!,{R1-R4} 向自增4个字节,然后再存储(不包括R11的地址)
- SRMDA R11!,{R1-R4} 减少4字节,方向和SRMIA相反,向低地址递减(包括R11的地址)
- SRMDB R11!,{R1-R4} 减少4字节,方向和SRMIA相反,向低地址递减(不包括R11的地址)
栈的种类
栈的本质就是一段内存,程序运行时用于保存一些临时数据
如局部变量,函数的参数,返回值,以及跳转时需要保护的寄存器
栈的分类
- 增栈:压栈时栈指针越来越大,出栈时栈指针越来越小
- 减栈:压栈时栈指针越来越大,出栈时栈指针越来越小
- 满栈:栈指针指向最后一次压入到栈中的数据,压栈时需要先移动栈指针到相邻位置然后再压栈
- 空栈:栈指针指向最后一次压入到栈中的数据的相邻位置,压栈时可直接压栈,之后需要将栈指针移动到相邻位置
栈分为空增(EA)、空减(ED)、满增(FA)、满减(FD)四种
ARM处理器一般使用满减栈
- STMFD R11!,{R1-R4} 入栈
- LDMFD R11!.{R6-R9} 出栈
例子
叶子函数:不需要记录LR
MOV SP, #0x40000020
MIAN:
MOV R1, #3
MOV R2, #5
@调用FUNC子程序
BL FUNC
ADD R3, R1, R2
B STOP
FUNC:
@压栈保护现场
STMFD PC!,{R1,R2}
MOV R1, #10
MOV R2, #20
SUB R3, R2, R1
@出栈恢复现场
LDMFD PC!.{R1,R2}
MOV PC, LR
STOP:
B STOP
非叶子函数:需要记录LR
MOV SP, #0x40000020
MIAN:
MOV R1, #3
MOV R2, #5
BL FUNC1
ADD R3, R1, R2
B STOP
FUNC1:
STMFD SP!, {R1,R2,LR}
MOV R1, #10
MOV R2, #20
BL FUNC2
SUB R3, R2, R1
LDMFD SP!, {R1,R2,LR}
MOV PC, LR
FUNC2:
STMFD SP!, {R1,R2}
MOV R1, #7
MOV R2, #8
MUL R3, R1, R2
LDMFD SP!, {R1,R2}
MOV PC, LR
专用指令
状态寄存器传送指令
读CPSR
- MSR R1,CPSR
写CPSE
- MSR CPSR,#0X10
软中断指令
SWI #1
- 软中断指令
@ 异常向量表
B MAIN
B .
B SWI_HANDLER
B .
B .
B .
B .
B .
@ 应用程序
MAIN:
MOV SP, #0x40000020
@ 初始化SVC模式下的栈指针
MSR CPSR, #0x10
@ 切换成USER模式,开启FIQ、IRQ
MOV R1, #1
MOV R2, #2
SWI #1
@ 触发软中断异常
ADD R3, R2, R1
B STOP
@ 异常处理程序
SWI_HANDLER:
SRMFD SP!,{R1,R2,LR}
@ 压栈保护现场
MOV R1, #10
MOV R2, #20
SUB R3, R2, R1
LDMFD SP!,{R1,R2,PC}^
出栈恢复现场
@ 将压入到栈中的LR(返回地址)出栈给PC,实现程序的返回
@ ‘^’表示出栈的同时将SPSR的值传递给CPSR,实现CPU状态的恢复
协处理器指令
操控制协处理器的指令
- 协处理器数据运算指令
- CDP
- 协处理器存储器访问指令
- STC
- 将协处理器中的数据存储到存储器
- LDC
- 将存储器中的数据读取到协处理器中
- 协处理器寄存器传送指令
- MRC
- 将协处理器中寄存器的数据传送到ARM处理器
- MCR
- 将ARM处理器中的寄存器数据传送到协处理器中的寄存器
伪指令
本身不是指令,编译器可以将其替换成若干条指令
-
NOP
- 空指令
-
LDR
- 可以将任意32位的数据放到寄存器中
- 可以是指令也可以是伪指令
- LDR R1,=0X12345678 @ 此时为伪指令
- LDR R1,[R2] @ 此时为指令
- LDR R1,=stop @ 此时为伪指令
- 将stop的地址写入R1寄存器
- LDR R1,stop @ 此时为伪指令
- 将stop地址中的内容写入寄存器
伪操作
不会生成代码,只是在编译阶段告诉编译器怎么编译
.global symbol
- 将symbol 声明为全局
.equ DATA,0XFF
MOV R1,#DATA
- 作用类似于#define
.macor FUNC
MOV R1,#1
MOV R2,#2
.endm
FUNC
- 调用FUNC函数时,相当于MOV R1,#1;MOV R2,#2两句
.if 1
MOV R1,#1
MOV R2,#2
.endif
- 条件成立MOV R1,#1;MOV R2,#2两句编译
- 否则不编译
.rept 3
MOV R1,#1
MOV R2,#2
.endr
- 将代码编译三遍
.weak symbol
B symbol
-
弱化一个符号,告诉编译器使用了symbol 但是symbol没有定义 不要报错
-
会把该语句编译成为一个空指令NOP
MOV R1,#1
.word 0xffffffff
MOV R2,#2
-
在当前地址申请一个字(4字节)的空间,并做初始化
-
在R1地址之后不放R2,放0xffffffff,空出一个字的内存,之后在放R2
MOV R1,#1
.byte 0xff
.align n
MOV R2,#2
- 在当前地址申请一个字节的空间,并做初始化
- .align 告诉编译器之后的代码一定要2的n次方对齐
MOV R1,#1
.space 12,0x12
MOV R2,#2
- 在当前地址申请任意个字节的空间,并做初始化
.arm
.thumb
- 告诉编译器后续的代码为arm代码
- 告诉编译器后续的代码为thumb代码
.end
- 汇编的结束
简述伪操作,伪指令和指令的区别
C和汇编的混合编程
C和汇编的混合编程的原则:
- 在那种语言环境下符合那种语言的语法规则
- 在汇编中将c语言中的函数当作标号来处理
- 在c语言中将汇编的标号当作函数来处理
汇编语言跳转C语言
MOV R1,#1;
MOV R2,#2
BL fun_c
MOV R3,#3
- fun_c c语言函数
C语言调用汇编语言
.global FUNC_ASM @将FUNC_ASM声明为全局
FUNC_ASM:
MOV R4,#4
MOV R5,#5
void func_c(void)
{
int a;
a++;
FUNC_ASM();
a--;
}
C内联汇编
void func_c(void)
{
int a;
a++;
asm
{
"MOV R6,#6\n"
"MOV R7,#7\n"
};
FUNC_ASM();
a--;
}
ATPCS协议
主要内容
介绍
ATPCS(ARM-Thumb Produce Call Standard)是ARM程序和Thumb程序中子程序调用的基本规则,目的是为了使单独编译的C语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。
栈的种类
-
使用满减栈
-
寄存器的使用
-
R15 PC程序计数器 只能用于存储程序的指针 不能做其他用途
-
R14 LR链接寄存器 只能用于存储返回地址,必能用作其他用途
-
R13 SP栈指针 只能用于存储栈指针,不能做其他用途
-
R0-R3 当函数的参数少于四个的时候,使用R0-R3传参,多出的部分用栈传递
- 函数的返回值使用R0寄存器传递
-
其余寄存器主要用于存储局部变量
-
FS4412开发环境搭建
安装交叉编译工具链
-
安装交叉编译工具链
-
在 ubuntu 下打开一个终端并进入到家目录
$ cd ~
- 在家目录下创建“Linux_4412/toolchain”目录用于存放交叉编译工具链
$ mkdir -p Linux_4412/toolchain
- 进入到该目录下
$ cd Linux_4412/toolchain/
- 将资料中“1.交叉编译工具链”下的 gcc-4.6.4.tar 拷贝到该目录下
- 解压“gcc-4.6.4.tar.xz”
$ tar xvf gcc-4.6.4.tar.xz
-
将交叉编译工具链添加到全局环境变量使其全局可用
-
回到家目录
$ cd ~
- 打开家目录下的配置文件“
- .bashrc”
$ vi .bashrc
-
在配置文件的最后一行添加如下内容,将交叉编译工具链的安装路径添加到了全局环境变量中,然后保存退出
-
重启配置文件使配置生效
$ source ~/.bashrc
- 执行以下命令
$ arm-none-linux-gnueabi-gcc -v
-
有以下信息打印则表示安装成功
-
注:
若使用的是 ubuntu 16.04 或更高的版本(64 位 ubuntu),需要安装 32 位库
$ sudo apt-get install lib32z1 lib32ncurses5
硬件控制原理
硬件控制原理:
-
CPU本身是不能直接控制硬件的,硬件一般是由其对应的控制器来控制,SOC中将各个硬件控制器的寄存器映射到了CPU地址空间中的一段范围,这样CPU就可以通过读写寄存器来间接控制硬件
-
注:这里的寄存器在S0C中但在CPU之外,有地址,访问方式与内存一样,常用于控制硬件
地址映射表:
- 在一个处理器中,一般会将Flash、RAM、寄存器等存储设备分别映射到寻址空间中的不同地址段,我们将这个映射关系成为这个处理器的地址映射表
GPIO
编程步骤
- 通过电路原理图分析LED的控制逻辑 - 高电平点亮、低电平熄灭
- 通过电路原理图查找LED与Exynos4412的连接关系 - GPX2_7
- 通过数据手册分析GPIO中哪些寄存器可以控制LED - GPX2CON、GPX2DAT
- 通过程序去操控对应的寄存器完成对LED的控制
编写汇编代码
.text
_start:
#打开led2
LED_CONFIG:
LDR R2, =0x11000c40
LDR R1, =0x10000000
STR R1, [R2]
LED_ON:
LDR R2, =0x11000c44
LDR R1, =0x00000080
STR R1, [R2]
STOP:
B STOP
.end
转bin格式
TARGET = led-asm
CROSS_COMPILE = arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
all:
$(CC) -c $(TARGET).s -o $(TARGET).o
$(LD) $(TARGET).o -Ttext 0x40008000 -o $(TARGET).elf
$(OBJCOPY) -O binary -S $(TARGET).elf $(TARGET).bin
clean:
rm $(TARGET).o $(TARGET).elf $(TARGET).bin
led实验
led闪烁
.text
_start:
MAIN:
BL LED_CONFIG
LOOP:
BL LED_ON
BL DELAY
BL LED_OFF
BL DELAY
B LOOP
LED_CONFIG:
LDR R2, =0x11000c40
LDR R1, =0x10000000
STR R1, [R2]
MOV PC, LR
LED_ON:
LDR R2, =0x11000c44
LDR R1, =0x00000080
STR R1, [R2]
MOV PC, LR
LED_OFF:
LDR R2, =0x11000c44
LDR R1, =0x00000000
STR R1, [R2]
MOV PC, LR
DELAY:
LDR R1, =100000000
L:
SUB R1, R1, #1
CMP R1, #0
BNE L
MOV PC, LR
STOP:
B STOP
.end
C工程与寄存器封装
1.16进制地址
/*
* 一.汇编语言访问存储器
* 1.读存储器
* LDR R1,[R2]
* 2.写存储器
* STR R1,[R2]
*
* 二.C语言访问存储器
* 1.读存储器
* data = *ADDR
* 2.写存储器
* *ADDR = data
*/
void Delay(unsigned int Time)
{
while(Time --);
}
#if 0
int main()
{
/*通过设置GPX2CON寄存器来将GPX2_7引脚设置成输出功能*/
*(unsigned int *)0x11000c40 = 0x10000000;
while(1)
{
/*点亮LED2*/
*(unsigned int *)0x11000c44 = 0x00000080;
/*延时*/
Delay(1000000);
/*熄灭LED2*/
*(unsigned int *)0x11000c44 = 0x00000000;
/*延时*/
Delay(1000000);
}
return 0;
}
#endif
2.define宏定义 寄存器地址
#if 0
#define GPX2CON (*(unsigned int *)0x11000c40)
#define GPX2DAT (*(unsigned int *)0x11000c44)
int main()
{
GPX2CON = 0x10000000;
while(1)
{
/*点亮LED2*/
GPX2DAT = 0x00000080;
/*延时*/
Delay(1000000);
/*熄灭LED2*/
GPX2DAT = 0x00000000;
/*延时*/
Delay(1000000);
}
return 0;
}
#endif
3.结构体宏定义地址
#if 0
typedef struct
{
unsigned int CON;
unsigned int DAT;
unsigned int PUD;
unsigned int DRV;
}gpx2;
#define GPX2 (*(gpx2 *)0x11000c40)
int main()
{
GPX2.CON = 0x10000000;
while(1)
{
/*点亮LED2*/
GPX2.DAT = 0x00000080;
/*延时*/
Delay(1000000);
/*熄灭LED2*/
GPX2.DAT = 0x00000000;
/*延时*/
Delay(1000000);
}
return 0;
}
#endif
4.头文件定义
#if 0
#include "exynos_4412.h"
int main()
{
GPX2.CON = 0x10000000;
while(1)
{
/*点亮LED2*/
GPX2.DAT = 0x00000080;
/*延时*/
Delay(1000000);
/*熄灭LED2*/
GPX2.DAT = 0x00000000;
/*延时*/
Delay(1000000);
}
return 0;
}
#endif
5.头文件定义 设置寄存器不干扰其他比特位