计算机原理笔记
本页面主要记录计算机原理的部分考点。
冯·诺伊曼结构
主要特征:
-
由运算器、存储器、控制器、输入和输出设备组成
-
数据和程序以二进制代码形式表示, 存放在同一存储器中。
-
按存储程序原理工作。计算机工作时由控制器自动地依次从存储器中取出指令序列(即程序) 并加以执行。
哈佛架构
主要特征:
-
独立的存储器模块分别存储指令和数据,实现并行处理。
-
两套独立的地址和数据总线,分别作为CPU与每个存储器之间的专用传输路径。
微机系统总线
-
地址总线(Address Bus): 传送地址信息的信号线。地址总线的位数决定了CPU可以直接寻址的内存空间的大小。 因为地址通常是从CPU发出的,所以地址总线是单向的、三态总线。 单向指信息只能沿一个方向传送,三态指除了输出高、低电平状态外,还可以处于高阻抗状态(浮空状态)。
-
数据总线(Data Bus): 传送数据信息的信号线(双向、三态)。 数据总线是双向三态总线,即数据既可以从CPU送到其它部件,也可以从其它部件传送给CPU,数据总线的位数和处理器的位数相对应。
-
控制总线(Control Bus): 传送控制信号的一组总线。 这组信号线比较复杂,由它来实现CPU对外部功能部件(包括存储器和I/O接口)的控制及接收外部传送给CPU的状态信号,不同的微处理器采用不同的控制信号。控制总线的信号线,有的为单向,有的为双向或三态,有的为非三态,取决于具体的信号线。
微处理器定义
微处理器:利用超大规模集成电路技术把运算器和控制器集成在一片硅片上形成微处理器, 即CPU。 一般由算术逻辑单元、 累加器和通用寄存器组、 程序计数器、 数据地址锁存器/缓冲器、时序和控制逻辑部件及内部总线等组成。
其中包含:
-
算术逻辑单元 ALU:进行各种算术运算和逻辑运算。
-
累加器和通用寄存器组 :保存参加运算的数据和运算的中间结果。累加器是特殊的寄存器,它既向ALU提供操作数,又接收ALU的运算结果。
-
CPU中有一些专用寄存器(如程序计数器 PC、堆栈指针 SP 和标志寄存器 FR 等)。
-
程序计数器用来存放下一条要执行的指令地址。
-
堆栈指针SP:用来存放栈顶地址。堆栈是一种特殊的存贮区域,按照“ 先进后出” 的原则工作。
-
标志寄存器:存放指令执行结果的特征和处理器的状态。
-
指令译码器:对指令进行译码,产生相应的控制信号送至时序和控制逻辑电路,组合成外部电路工作所需要的时序和控制信号。
微处理器工作过程
取指令 → 分析指令(指令译码) → 执行指令
细节
-
取指令阶段:程序计数器 (PC) 内容传送到地址寄存器,向地址总线放置地址,读取对应指令到缓冲寄存器,CPU 移动缓存寄存器内容到指令寄存器,输入到指令译码器产生操作时序序列。
-
取源操作数:操作控制器依据译码得到的时序序列向地址寄存器传送地址,读取对应储存器位置的内容到缓冲寄存器。
-
送目的操作数:将缓冲寄存器数据放置到累加器,操作 ALU 进行处理。
指令流水线
为提高CPU的工作效率,采用指令流水线技术, 各个部件并行工作, 减少了指令的平均执行时间。
8086/ 8088CPU 使用二级流水线:取指令、 执行指令。
ARM7 TDMI 核采用了 3 级流水线:取指令、指令译码、执行指令。
总线接口单元
总线接口单元 BIU(Bus Interface Unit)负责与 存储器、 I/O 端口进行数据传送。
具体功能:
取指令:总线接口部件从内存中取出指令后送到指令队列。预取指令。配合EU执行指令,存取操作数和运算结果。
顺序指令执行:指令队列存放紧接在执行指令后面的那一条指令。
执行转移指令: BIU 清除指令队列中的内容,从新的地址取入指令,立即送往执行单元,然后再从新单元开始重新填满队列。
执行单元
EU (Execution Unit)执行单元
功能:负责指令执行。
EU 控制系统:
接受从总线接口单元的指令队列中取来的指令代码,对其译码和向 EU 内各有关部分发出时序命令信号,协调执行指令规定的操作。
处理器分类
CISC处理器:体现CISC计算机主要特点。 偏重由硬件执行指令, 不断增强指令集的复杂度。
RISC处理器:体现RISC 计算机主要特点。 简化指令实现的硬件设计, 复杂指令由简单指令合成。
ARM 处理器特点
-
属于RISC型处理器结构
-
多种处理器工作模式
-
ARM和Thumb两种处理器工作状态,兼容 8 位 /16 位器件
-
大量寄存器的使用,大多数操作都在寄存器中进行。
-
具有灵活方便的接口。具有协处理器接口,允许接16个协处理器
-
处理器芯片内嵌入了在线仿真ICE-RT逻辑,便于通过JTAG来仿真调试
-
低功耗设计
ARM 指令集
-
采用固定的指令长度
-
访问内存使用 LOAD/STORE 模式
Cortex M3/M4
-
32位微处理器: 32位数据、寄存器组、存储器接口。
-
哈佛架构:独立的指令总线和数据总线。
-
流水线:三级流水线技术。
-
存储空间: 32 位寻址,可支持 4GB 存储器空间。
-
寄存器: 寄存器组和某些编程模式与经典 ARM 处理器有所不同。
-
中断和异常:内置嵌套向量中断控制器;支持 11 种系统异常外加 240 种外部 IRQ。
-
MPU:一个可选的存储器保护单元允许对特权访问和用户程序访问制定访问规则。
-
指令集: Thumb-2 指令集;允许 32位指令和16位指令被同时使用。
Cortex-M4处理器具有:
-
支持单指令多数据操作。
-
支持饱和运算指令,可以避免出现溢出时,计算结果产生大的畸变。
-
支持可选择的浮点指令。
Thumb-2
- Thumb-2 支持16位和32位指令集,是16位Thumb指令集的一个超集。
+---------------------------+
| Thumb-2 Instruction |
| Set |
| (32-bit and 16-bit) |
| |
| +-------------------+ |
| | Cortex-M3/M4 | |
| | | |
| | +-------------+ | |
| | | Thumb | | |
| | | Instructions| | |
| | | (16-bit) | | |
| | +-------------+ | |
| +-------------------+ |
| |
+---------------------------+
- Thumb-2 中 16 位 Thumb 指令和 32 位 ARM 指令并存,处理器执行程序没有切换的额外开销,可节省执行时间和指令空间;不需要将源代码分成 ARM 和 Thumb 来编译,无需反复求证和测试状态何时切换,程序效率高,易开发。
功能框图
其中含有处理器内核、嵌套向量中断控制器、 systick定时器、总线矩阵等
可选模块: 浮点单元、存储器保护单元、支持软件调试操作的部件等。
总线接口
I-CODE总线: 用于将内核的指令总线通过总线矩阵访问程序存储器来获取指令。
D-CODE总线: 用于将内核的数据总线通过总线矩阵访问程序存储器来读取立即数, 以及进行调试器访问操作。
系统总线: 用于将内核的系统总线通过总线矩阵访问RAM和外设, 以进行读写数据。
私有外设总线(PPB总线) :通过使用一种先进微控制器总线结构协议来访问外设。
数据类型
处理器支持字节(8位)、半字(16位)、字(32位)数据类型。
对存储器进行数据读写操作时,可以按字节,也可以按半字、 字进行。
所有的数据操作(如ADD)都是以字为单位的。
注意
为保证数据读写的正确进行,要求:
-
半字存储单元地址最低位为0;或者说,半字的存储地址必须是2字节对齐的
-
例如,合法的半字地址可以是 0x0000、0x0002、0x0004 等。
-
非法的半字地址是 0x0001、0x0003 等(最低位为1)。
-
-
字存储单元地址最低两位为 0;或者说,字的存储地址必须是4字节对齐的
-
例如,合法的字地址可以是 0x0000、0x0004、0x0008 等。
-
非法的字地址是 0x0001、0x0002、0x0003 等(最低两位不全为0)。
-
N位无符号数: 二进制格式表示范围为 0 ~ 2N-1 的非负整数;
N位有符号数时, N位数据值使用2的补码格式表示范围为 -2N-1 ~ +2N-1-1 的整数
数据储存
-
大端存储:最高字节位于低地址。
0x12345678
-> 低地址 0x12 0x34 0x56 0x78 高地址 -
小端存储:最高字节位于高地址。
0x12345678
-> 低地址 0x78 0x56 0x34 0x12 高地址
ARM 对于这两种储存形式都支持。
I/O 编址方式
-
独立编制方式:也称 I/O 映射方式,I/O 口地址与内存单元地址分开独立编址, I/O 端口地址不占用内存空间的地址范围。 如 X86 专门的输入/输出(I/O) 指令和控制逻辑。
-
统一编制方式:也称内存映射方式,I/O 端口与内存单元同样对待,每个端口占用一个存储单元地址, 将内存的一部分划出来用作I/O地址空间。 如ARM、 PowerPC等
用对存储器的访问指令来实现对I/ O端口的读/写。
存储器的读/写指令的寻址方式多, 功能强, 编制程序方便灵活。
I/O端口占用内存地址空间, 内存容量减小。
特权级别
CM4处理器具有存储器访问的保护机制,它使得普通用户程序代码不能意外地或恶意地执行涉及要害的操作。
特权级别,提供一种存储器访问的保护机制
程序执行设置2种权限: 特权级和非特权级(用户级)
-
特权级:程序可以访问所有存储器空间, 执行所有命令。
-
非特权(用户级):资源访问受限, 或不允许访问。
工作状态和工作模式
CM4 的工作状态有两种:
-
调试状态: 由于某种原因使处理器被暂停后, 就进入调试状态,并停止指令的执行。 原因可以是调试事件或触发断点等。
-
Thumb状态:当处理器执行程序代码(Thumb指令)时, 就处于 Thumb状态。
调试状态仅用于调试操作,可以通过两种方式进入调试状态:调试器发起的暂停请求,或处理器中的调试部件产生的调试事件。在此状态下,调试器可以访问或修改处理器寄存器的数值。
CM4 的工作状态和工作模式转换示意如下:
CM4 的工作模式分为两种:
-
线程模式(Thread Mode) :在复位或异常返回时进入该模式。处于线程模式时既能使用特权级,也可以使用用户级代码。
-
处理模式(Handler Mode) : 执行中断服务程序等异常处理。在处理模式下,处理器具有特权访问等级
特权级 | 用户级 | |
---|---|---|
异常handler的代码 | handler模式 | 错误的用法 |
主应用程序的代码 | 线程模式 | 线程模式 |
寄存器组
Cortex-M4处理器在处理器内核中有多个执行数据处理和控制的寄存器,这些寄存器包括由16个32位寄存器组成的通用寄存器组以及几个特殊功能寄存器:
通用寄存器组包含:
-
R0 ~ R7 为通用寄存器(低组)
-
R8 ~ R12 为通用寄存器(高组)
-
R13 为主堆栈指针(MSP)和进程堆栈指针(PSP),有两个物理寄存器。
-
R14 为连接寄存器(LR)
-
R15 为程序寄存器(PC)
特殊寄存器包含:
-
xPSR 程序状态寄存器,共三个:应用PSR(APSR),执行PSR(EPSR),中断PSR(IPSR)
-
中断屏蔽寄存器,共三个:PRIMASK,FAULTMASK,BASEPRI
-
CONTROL 控制寄存器
通用寄存器组
通用寄存器(低组)可以被大多数 16-bit Thumb 指令和所有 32-bit Thumb-2 指令访问。
通用寄存器(高组)可以被所有 Thumb-2 指令和少多数 16-bit Thumb访问。
上述低、高组通用寄存器复位后的初始值是未定义的。
R13 是堆栈指针。有两个物理寄存器,分别对应于主堆栈指针(MSP)和进程堆栈指针(PSP),因此系统可以同时支持两个堆栈。
选择堆栈指针
两个堆栈指针在同一时间只有一个可见。
堆栈指针的最低两位总是0, 这意味着他们总是字对齐。
-
主堆栈指针(MSP) : 默认的堆栈指针,复位后或处理器处于处理模式时使用。
初始值需要在复位流程中从存储器的第一个字中取出。
-
进程堆栈指针(PSP) : 只能用于线程模式。
初始值未定义。
堆栈指针的选择由特殊寄存器 CONTROL 决定
R14 是连接寄存器 (LR)。 当一个子程序或函数被调用时, LR用来保存返回的地址。也用于异常返回。
Main ; Main program
…
BL func1 ; 带返回的跳转指令
Call function1
; PC = function1
; LR = next instruction
… ; program code
func1
…
BX LR ; Return
R15 是程序计数器PC。 指向“正在取指”的指令,是可读写的。
读操作返回的是当前指令地址+4;写PC会引起程序的跳转操作。
可以在汇编语言中通过R15或PC访问。
特殊功能寄存器组
在Cortex-M4处理器中的特殊功能寄存器表示处理器状态、定义了操作状态和中断/异常屏蔽。包括:
-
程序状态寄存器 (PSR): 表示处理器的状态
-
中断屏蔽寄存器(PRIMASK, FAULTMASK, and BASEPRI): 用于控制异常或中断的使能和禁止。
-
控制寄存器 (CONTROL): 定义线程模式的访问级别和选择堆栈指针
其中程序状态寄存器 (PSRs) 可以分为三个状态寄存器:
-
应用状态寄存器PSR (APSR): 保存当前指令运算结果的状态
-
中断状态寄存器PSR (IPSR): 保存当前中断的向量号。
-
执行状态寄存器PSR (EPSR) [ICI/IT: ICI指令/IT指令状态位; T总为1]
APSR 位分配
N(31位处) 溢出标志:1 为溢出,0 为没有溢出
Z(30位处) 进位/借位标志:1 表示进位或未借位,0 表示未进位或借位
C(29位处) 零标志:1 表示结果为零,0 表示结果非零
V(28位处) 负标志:1 表示结果为负数,0 表示结果非负。
例如如下运算得到的 APSR 中结果为
0x70000000 + 0x70000000 = 0xE0000000
N=1, Z=0, C=0, V=1
0x90000000 + 0x90000000 = 0x20000000
N=0, Z=0, C=1, V=1
0x80000000 + 0x80000000 = 0x00000000
N=0, Z=1, C=1, V=1
0xA0000000 - 0xA0000000 = 0x00000000
N=0, Z=1, C=1, V=0
异常向量表
当异常产生并且被处理器内核接受后, 处理器将执行异常处理程序, 这时就需要知道异常处理程序的起始地址, 解决该问题的方法是使用异常向量表。
异常向量表是系统RAM或者系统ROM的一个区域。
向量表的大小取决于CPU支持的异常类型和数量。
Cortex-M4处理器向量表中的每个元素代表一个异常类型处理程序的起始地址, 每个起始地址占一个字, 即4个字节。
向量表的存储位置是可以设置的, 通过嵌套向量中断控制器(NVIC)中的一个重定位寄存器来指出向量表的地址。
复位后, 重定位寄存器的值默认为0, 向量表则位于地址0x0处, 用于初始时的异常分配。
Cortex‐M4 支持大量异常,包括 11 个系统异常,和最多 240 个外部中断,简称IRQ。具体使用这 240 个中断源中的多少个,则由芯片制造商决定。
由外设产生的中断信号,除了SysTick 之外,全都连接到NVIC 的中断输入信号线。
当一个发生的异常被CM4 内核接受,对应的异常handler 就会执行。为了决定handler 的入口地址, CM4 使用“向量表查表机制”。
复位流程
复位种类: 上电复位、掉电复位、复位引脚复位、 看门狗复位、软件复位等。
嵌入式系统: 有内核复位和系统复位,属于软件复位。
-
内核复位: 只复位处理器,而不复位如GPIO、 TIM、 USART、 SPI等的外设寄存器。
-
系统复位: 既复位处理器,又复位外设寄存器
Cortex-M4处理器具有的复位类型:
-
上电复位。复位微控制器中的所有部分,包括 处理器、调试支持部件和外设等。
-
系统复位。只会复位处理器和外设,不包括处 理器的调试支持部件。
-
处理器复位。只复位处理器。
整体复位流程如下:
处理器复位后,它会从存储器中读取两个字:
-
从地址 0x0000 0000 处读取MSP的初始值。
因M4中的栈操作是基于满递减的,所以堆栈指针MSP的初始值应该设置在栈顶的位置。即MSP的初始值必须是堆栈内存的末地址加1。
-
从地址 0x0000 0004 处读取程序计数器PC的初始值——这个值是复位向量, 最低位 (LSB) 必须是 1。然后从这个值所对应的地址处取指令。
ARM 指令系统
表示操作性质的操作码和表示操作对象的操作数两部分
ARM指令系统的指令长度固定,这是RISC指令系统的基本特点
指令基本格式:
<opcode>{<cond>}{S} <Rd>, <Rn> {, <shifter_operand>}
opcode: 指令操作码
cond: 执行条件, 表示在何种条件下执行该指令,该参数可省略
S: 该指令的执行是否影响APSR(标志)寄存器的值(若写上则影响CPSR) ,该参数可省略
Rd: 目的操作数(寄存器)
Rn: 第一源操作数(寄存器) , 必须是寄存器
shifter_operand : 第二源操作数
“<>”符号内的项是必需的,“{}”符号内的项是可选的
条件码
ARM指令都包含4位的条件码(cond),位于指令的最高4位,条件码共有16种, 在APSR中条件码对应的标志满足指定条件时,带条件码的指令才被执行,否则指令被忽略。
其中条件码的分类记录如下:
寻址方式
立即寻址
直接使用立即数进行寻址,例如 immed_16, immed_12, immed_8 等
寄存器直接寻址
操作数的值位于CPU的内部寄存器中,是各类处理器中通常采用的一种执行效率较高的寻址方式
MOV R1,R2 ; R1 ← R2,寄存器R2的内容传送到寄存器R1
ADD R0,R1,R2 ; R0 ← R1 + R2,寄存器R1和R2的内容相加,结果传送到R0
寄存器移位寻址
但 ARM 不同于Intel X86 CPU的寻址方式,寻址的操作数可以由寄存器中的数值进行相应移位得到,移位的方式以助记符形式给出(例如ASR、LSL等)
例如:
AND R0,R1,R2,LSL #2 ; R0 ← R1 & (R2 逻辑左移2位)
MOV R0,R1,ASR R2 ; R0 ← (R1算术右移R2规定的位数)
寄存器间接寻址
寄存器中存放的内容为操作数的内存地址, 寄存器是内存操作数的地址指针, 在指令中须用中括号“[ ]”括起来
STR R0,[R1]
; R0中的值传送到以R1的值作为地址的存储器中
; 指令执行完成后R1的值不变
LDR R0,[R1]
;R1中的值作为地址,将内存中该地址单元的数据传送到R0
;指令执行完成后R1的值不变
基址变址寻址
某个寄存器(一般作为基址寄存器)提供一个基准地址,该基地址将与指令中给出的称为“地址偏移量”(变址,或者称索引)的数据相加,形成操作数的有效地址
常用于访问基地址附近的地址单元,如查表、数组操作、功能部件寄存器访问等
寄存器间接寻址方式也可以看作是偏移量为0的基址变址寻址
有以下几种常见的形式
-
立即数变址(前索引)。数据的存储器地址为寄存器中的值和立即数(偏移)的和。
LDR R0,[R1,#4] ;R0←[R1+4],立即数前索引变址寻址
-
立即数变址(前索引),带更新。数据的存储器地址为寄存器中的值和立即数(偏移)的和,将数据的存储器地址写回到存放地址的寄存器中。
LDR R0,[R1,#4]! ;R0←[R1+4],R1←R1+4,立即数前索引变址寻址,带更新
-
立即数后索引变址。数据的存储器地址为寄存器中的值,寄存器中的值和立即数(偏移)的和写回到存放地址的寄存器中。
LDR R0,[R1],#4 ;R0←[R1],R1←R1+4,立即数后索引变址寻址,带更新
-
寄存器变址(前索引)。数据的存储器地址为基地址寄存器中的值和索引值寄存器中的值求和。
LDR R0,[R1,R2] ;R0←[R1+R2] ,寄存器前索引变址寻址
ARM 核心指令
书写语法
ARM 汇编器引了一个“统一汇编语言(UAL)”语法机制。对于 16 位指令和 32 位指令,汇编器允许开发者统一使用 32 位 Thumb-2 指令的语法格式书写(很多 Thumb-2 指令的用法也与 32 位 ARM 指令相同),并且由汇编器来决定是使用 16 位指令,还是使用 32 位指令。
以前, Thumb 的语法和 ARM 的语法不同,在有了 UAL 之后,两者的书写格式就统一了。
虽然引入了 UAL,但是仍然允许使用传统的 Thumb 语法。如果使用传统的 Thumb 语法,有些指令会默认地更新 APSR,即使没有加上 S 后缀。如果使用 UAL 语法,则必须指定 S 后缀才会更新。
绝大多数 16位指令只能访问 R0-R7; 32位 Thumb-2指令则可以随意访问 R0-R15。
在 Thumb-2 指令集中,有些操作既可以由 16 位指令完成,也可以由 32 位指令完成。在 UAL 下,汇编器能主动决定用哪个,也可以手工指定是用 16 位的还是 32 位的。
例子
ADDS R0, #1 ; 汇编器将为了节省空间而使用16位指令
ADDS.N R0, #1 ;指定使用16位指令(N=Narrow)
ADDS.W R0, #1 ;指定使用32位指令(W=Wide)
.W 后缀指定 32 位指令。如果没有给出后缀,汇编器会先试着用16 位指令以给代码瘦身,如果不行再使用 32 位指令。
.N 后缀指定 16 位指令,其实是多此一举,不过汇编器可能仍然允许这样的语法。
指令可选后缀
-
S后缀:指令中使用S后缀时,指令执行后程序状态寄存器的条件标志位将被刷新,不使用S后缀时,指令执行后程序状态寄存器的条件标志将不会发生变化。
S后缀通常用于对条件进行测试,例如是否有溢出,是否进位等;根据这些变化,就可以进行一些判断,如是否大于,是否相等;从而可能影响指令执行的顺序。
例子
假设R0=0x1,R3=0x3,指令执行之前APSR=nzcv ,分别执行如下,则指令APSR的值有何变化?
SUB R1,R0,R3 ; R0的值减去R3的值,结果存入R1
SUBS R1,R0,R3 ; R0的值减去R3的值,结果存入R1,影响标志位。
分析:执行第1条指令对于标志寄存器的值没有任何影响,因此 APSR的值不变。
执行第2条指令后,因为R0的值减去R3值,结果变成了一个负数,故而N被置位了。
-
! 后缀:如果指令地址表达式中不含 !后缀,则基址寄存器中的地址值不会发生变化。
指令中的地址表达式中含有 ! 后缀时,指令执行后,基址寄存器中的地址值将发生变化,变化的结果如下:
基址寄存器中的值(指令执行后)=指令执行前的值+地址偏移量
例子
分别执行下面两条指令有何区别?
LDR R3,[R0,#4]
LDR R3,[R0,#4]!
分析:在上述指令中,第1条指令没有后缀!,指令的结果是把R0加4作为地址指针,把这个指针所指向的地址单元所存储的数据读入R3, R0的值不变。第2条指令除了实现以上操作外,还把R0+4的结果送到R0中。
注意
(1) ! 后缀必须紧跟在地址表达式后面,而地址表达式要有明确的地址偏移量;
(2) ! 后缀不能用于R15(PC)的后面;
(3) 当用在单个地址寄存器后面时,必须确信这个寄存器有隐性的偏移量,例如
STMDB R1!{R3,R5,R7}
此时地址基址寄存器R1的隐性偏移量是4。 -
EQ 条件执行后缀:当 APSR 中的 Z 标志置位时该指令执行,否则不执行。
S 后缀和 EQ 后缀的关系为:
-
如果既有条件后缀又有S后缀,则书写时S排在后面。
-
条件后缀是要测试条件标志位,而S后缀是要刷新条件标志位。
-
条件后缀要测试的是执行前的标志位,而S后缀是依据指令的结果改变条件标志。
数据传送指令
Cortex-M4中的数据传送类型包括:
-
两个寄存器间传送数据
-
寄存器与特殊功能寄存器间传送数据
-
把一个立即数加载到寄存器
指令 | 功能描述 |
---|---|
MOV <Rd>, #<immed_8> |
将8位立即数传送到目标寄存器 |
MOV <Rd>, <Rn> |
将低寄存器值传送给低目标寄存器 |
MOV <Rd>, <Rm> |
将高或低寄存器值传送给高或低目标寄存器 |
MVN <Rd>, <Rm> |
将寄存器值取反后传送给目标寄存器 |
MOV{S} <Rd>, #<immed_12> |
将12位立即数传送到寄存器中 |
MOV{S} <Rd>, <Rm>{, <shift>} |
将移位后的寄存器值传送到寄存器中 |
MOVT <Rd>, #<immed_16> |
将16位立即数传送到寄存器的高半字[31:16]中 |
MOVW <Rd>, #<immed_16> |
将16位立即数传送到寄存器的低半字[15:0]中,并将高半字[31:16]清零 |
MRS <Rn>, <SReg> |
加载特殊功能寄存器的值到Rn |
MSR <SReg>, <Rn> |
存储Rn的值到特殊功能寄存器 |
例子
MOV R4, R0 ; 将 R0 值传送到 R4
MOVS R4, R0 ; 将 R0 值传送到 R4,并更新 APSR (标志位)
MRS R7, PRIMASK ; 将 PRIMASK (特殊功能寄存器) 值传送到R7
MSR CONTROL, R2 ; 将 R2 值传送到CONTROL (特殊功能寄存器)
MOV R3, #0x34 ; 给R3 赋值 0x34
MOVS R3, #0x34 ; 给R3 赋值 0x34,并更新 APSR(标志位)
MOVW R6, #0x1234 ; 将16位立即数0x1234传送到 R6的低半字 [15:0]
中,并将高半字 [31:16]
清零
MOVT R6, #0x8765 ; 将16位立即数0x8765传送到R6的高半字节,低半字节不变
MVN R3, R7 ; 将R7值取反后传送给R3
储存器访问指令
Cortex-M4处理器对存储器的访问只能通过加载LDR和存储STR指令来实现。
有两种类型: 单寄存器加载与存储指令;多寄存器加载与存储指令
LDR 是把存储器中的内容加载到寄存器中, STR 则是把寄存器内容存储至存储器中,数据类型:字节、半字、字和双字;
LDM/STM 多寄存器加载和存储指令可以实现一条指令加载和存储多个寄存器的内容,且大大高数据操作效率。
内核对存储器空间及I/O映射空间采用统一编址, 存储器访问指令除了对存储器操作以外,对外围I/O和程序数据的访问都可通过加载/存储指令实现。
单个寄存器加载和储存指令示例如下:
指令 | 功能描述 |
---|---|
LDRB Rd, [Rn, #offset] |
从地址 Rn + offset 处读取一个字节到 Rd |
LDRH Rd, [Rn, #offset] |
从地址 Rn + offset 处读取一个半字到 Rd |
LDR Rd, [Rn, #offset] |
从地址 Rn + offset 处读取一个字到 Rd |
LDRD Rd1, Rd2, [Rn, #offset] |
从地址 Rn + offset 处读取一个双字(64 位整数)到 Rd1 (低 32 位)和 Rd2 (高 32 位) |
STRB Rd, [Rn, #offset] |
把 Rd 中的低字节存储到地址 Rn + offset 处 |
STRH Rd, [Rn, #offset] |
把 Rd 中的低半字存储到地址 Rn + offset 处 |
STR Rd, [Rn, #offset] |
把 Rd 中的字存储到地址 Rn + offset 处 |
STRD Rd1, Rd2, [Rn, #offset] |
把 Rd1 (低 32 位)和 Rd2 (高 32 位)表达的双字存储到地址 Rn + offset 处 |
多寄存器加载和储存 LDM / STM
Rd 后面的 “!” 表示在每次访问前(Before)或访问后 (After),要自增(Increment)或自减(Decrement)基址寄存器 Rd 的值,增/减单位: 1个字(4 字节)。 共有四种组合方式:
-
IA ; (Increment+ After),每次读/写后地址加4
-
IB ; (Increment+ Before),每次读/写前地址加4
-
DA ; (Decrement + After),每次读/写后地址减4
-
DB ; (Decrement + Before),每次读/写前地址减4
例子
若 R8=0x8000, 则下面两条指令:
STMIA R8!, {R0-R3}; R8 值变为0x8010, 每存一次增一次, 先存储后自增
STMDB R8, {R0-R3}; R8 值的“一个内部复本”先自减后存储, 但是R8 的值不变
四种方式的储存顺序如下图所示
LDR 同理。
自动索引
LOAD/STORE 指令中的自动索引(Auto-indexing)功能是为利用ARM流水线延迟周期而设计的。当流水线处于延迟周期时, 处理器的执行单元被占用, 算术逻辑单元ARM( ALU )和桶形移位器却可能处于空闲状态,此时可以利用它们来完成往基址寄存器上加一个偏移量的操作,供后面的指令使用。
自动索引又分为前索引(Pre-indexing)和后索引(Postindexing)。
前索引 (Pre‐indexing) :
LDR R0, [R1, #20]! ; 前索引,有"!"
该指令先把地址R1+offset 处的值加载到R0, 然后, R1 = R1+ 20; 这里的 “!” 就是指更新基址寄存器R1 的值。
步骤 ① : R0 ← [R1+ 20]
步骤 ② : R1 = R1+ 20
LDR R0, [R1, #20] ; 前索引,没有"!"
该指令把地址R1+offset 处的值加载到R0。不更新R1。
步骤① : R0← [R1+ 20]
后索引 (Post‐indexing):
STR R0, [R1], #-12 ;后索引
该指令是把 R0 的值存储到地址R1 处,然后, R1 = R1+(‐12)。
注意, [R1]后面是没有 “!” 的。
PUSH / POP
PUSH/POP是另外一种形式的多存储和多加载指令。
利用当前选定的栈指针来生成地址。
栈指针可以是主栈指针( MSP),也可以是进程栈指针(PSP)
由处理器的当前模式和control特殊寄存器的数值决定。
PUSH/POP 作为堆栈专用操作,也属于数据传送指令类。
PUSH{cond} reglist
POP{cond} reglist
Reglist为非空寄存器列表
算数运算指令
指令 | 功能描述 |
---|---|
ADD Rd, Rn, Rm |
Rd = Rn + Rm |
ADD Rd, Rm |
Rd += Rm |
ADD Rd, #imm |
Rd += imm (常规加法,imm 的范围是 im8 (16 位指令)或 im12 (32 位指令)) |
ADC Rd, Rn, Rm |
Rd = Rn + Rm + C |
ADC Rd, Rm |
Rd += Rm + C |
ADC Rd, #imm |
Rd += imm + C (带进位的加法,imm 的范围是 im8 (16 位指令)或 im12 (32 位指令)) |
ADDW Rd, #imm12 |
Rd += imm12 (寄存器和 12 位立即数相加) |
SUB Rd, Rn |
Rd -= Rn |
SUB Rd, Rn, #imm3 |
Rd = Rn - imm3 |
SUB Rd, #imm8 |
Rd -= imm8 |
SUB Rd, Rn, Rm |
Rd = Rn - Rm (常规减法) |
SBC Rd, Rm |
Rd -= Rm - 借位 |
SBC Rd, Rn, #imm12 |
Rd = Rn - imm12 - 借位 |
SBC Rd, Rn, Rm |
Rd = Rn - Rm - 借位 (带借位的减法) |
RSB Rd, Rn, #imm12 |
Rd = imm12 - Rn |
RSB Rd, Rn, Rm |
Rd = Rm - Rn (反向减法) |
MUL Rd, Rm |
Rd *= Rm |
MUL Rd, Rn, Rm |
Rd = Rn * Rm (常规乘法) |
MLA Rd, Rm, Rn, Ra |
Rd = Ra + Rm * Rn |
MLS Rd, Rm, Rn, Ra |
Rd = Ra - Rm * Rn (乘加与乘减) |
UDIV Rd, Rn, Rm |
Rd = Rn / Rm (无符号除法,余数被丢弃) |
SDIV Rd, Rn, Rm |
Rd = Rn / Rm (带符号除法,余数被丢弃) |
SMULL RL, RH, Rm, Rn |
[RH:RL] = Rm * Rn |
SMLAL RL, RH, Rm, Rn |
[RH:RL] += Rm * Rn (带符号的 64 位乘法) |
UMULL RL, RH, Rm, Rn |
[RH:RL] = Rm * Rn |
UMLAL RL, RH, Rm, Rn |
[RH:RL] += Rm * Rn (无符号的 64 位乘法) |
例: 以加法为例说明16bit、 32bit的算术四则运算指令:
ADD R0, R1 ; R0 += R1
ADD R0, #0x12 ; R0 += 12
ADD.W R0, R1, R2 ; R0 = R1+R2
➢ 虽然助记符都是ADD,但是二进制机器码是不同的。
➢ 当使用 16 位加法时, 会自动更新 APSR 中的标志位。
➢ 然而,在使用了 “.W” 显式指定了 32 位指令后,就可以通过 “S” 后缀控制对 APSR 的更新:
ADD.W R0, R1, R2 ; 不更新标志位
ADDS.W R0, R1, R2 ; 更新标志位
:指令的 16 位版本会更新APSR中的标志
ADD R0, R1, R2, LSL#2 ; R0 = R1 + (R2左移2位)
在这个表达式中,对R2中的数据逻辑左移2位, 然后和R1相加,其结果放入寄存器R0中。
ADD R0, R1, R2,LSL R3
假设R3的值为3,则在这个表达式中表示对R2中的数据逻辑左移3位,然后和R1相加,其结果放入寄存器R0中。
对于各类加减法指令,其具有相似的格式,不在赘述。
MUL 32位乘法指令
汇编格式: MUL{<cond>}{S} Rd,Rm,Rs
功能: MUL指令完成将操作数Rm与操作数Rs 的乘法运算,并把结果放置到目的寄存器Rd中,即Rd=Rm× Rs。
S选项决定指令的操作是否影响APSR中条件标志位的值
例 :
MUL R0, R1, R2 ; R0 = R1 × R2
MULS R0, R1, R2 ; R0 = R1 × R2,同时设置APSR中的相关条件标志位
MLA 32位乘加指令
汇编格式: MLA{<cond>}{S} Rd, Rm, Rs, Rn
功能: MLA指令完成将操作数Rm与操作数Rs的乘法运算,再将乘积加上Rn,并把结果放置到目的寄存器Rd中,即Rd=( Rm × Rs) +Rn。
如果书写了S,同时根据运算结果设置APSR中相应的条件标志位。
例如:
MLA R0, R1, R2, R3 ; R0 = R1× R2+ R3
MLAS R0, R1, R2, R3 ; R0 = R1× R2+ R3,同时
设置APSR中的相关条件标志位
SMULL 64位有符号数乘法指令
汇编格式: SMULL{<cond>}{S} Rdlo, Rdhi, Rm, Rs,
功能: SMULL指令实现32位有符号数相乘,得到64位结果。
32操作数Rm与32操作数Rs作乘法运算,并把结果的低32位放置到目的寄存器Rdlo中,结果的高32位放置到目的寄存器Rdhi中,即 [Rdhi Rdlo]= Rm × Rs。
如果书写了S,同时根据运算结果设置APSR中相应的条件标志位。
操作数Rm和操作数Rs均为32位的有符号数。
例如
SMULL R0, R1, R2, R3 ; R0 = (R2× R3)的低 32位 Rdlo, R1 = (R2× R3)的高32位 Rdhi
对于乘法,64位有符号数乘加指令 SMLAL、64位无符号数乘法指令 UMULL、64位无符号数乘加指令 UMLAL 具有一致的格式,不再赘述。
乘法指令注意事项
-
在操作中使用寄存器,但R15不可用。
-
在寄存器的使用中,注意Rd不能同时作为Rm,其它无限制。
-
有符号运算和无符号运算的低32位是没有区别的。
-
Rdlo、 Rdhi和Rm必须使用不同的寄存器
除法指令
UDIV Rd, Rn, Rm ; Rd = Rn/Rm (无符号除法)
SDIV Rd, Rn, Rm ; Rd = Rn/Rm (带符号除法)
-
为了捕捉被零除的非法操作, 可以在NVIC 的配置控制寄存器中置位DIVBZERO位。如果出现了被零除的情况,将会引发一个用法fault异常。
-
如果没有任何措施,Rd将在除数为零时被清零。
逻辑运算指令
指令 | 功能描述 |
---|---|
AND Rd, Rn |
Rd &= Rn |
AND Rd, Rn, #imm12 |
Rd = Rn & imm12 |
AND Rd, Rm, Rn |
Rd = Rm & Rn (按位与) |
ORR Rd, Rn |
Rd |= Rn |
ORR Rd, Rn, #imm12 |
Rd = Rn | imm12 |
ORR Rd, Rm, Rn |
Rd = Rm | Rn (按位或) |
BIC Rd, Rn |
Rd &= ~Rn |
BIC Rd, Rn, #imm12 |
Rd = Rn & ~imm12 |
BIC Rd, Rm, Rn |
Rd = Rm & ~Rn (位清零) |
ORN Rd, Rn, #imm12 |
Rd = Rn | ~imm12 |
ORN Rd, Rm, Rn |
Rd = Rm | ~Rn (按位或反码) |
EOR Rd, Rn |
Rd ^= Rn |
EOR Rd, Rn, #imm12 |
Rd = Rn ^ imm12 |
EOR Rd, Rm, Rn |
Rd = Rm ^ Rn (按位异或) |
BIC 位清除指令
汇编格式: BIC{<cond>}{S} Rd, Rn, operand2
功能: BIC 指令将 Rn 的值与 operand2 的反码做逻辑与操作,并把结果放置到目的寄存器 Rd 中。其中 Rn 为操作数 1,是一个寄存器, operand2 为操作数 2,可以是一个寄存器,被移位的寄存器,或一个立即数。
例:若要将R0的第0位和第3位清0,其余位不变:
BIC R0, R0, #9
总的来看:
-
AND: 常用于需要将某数的特定位清零的场合。
-
ORR: 常用于需要将某数的特定位置位的场合。
-
EOR: 常用于将某数的特定位取反的场合。
-
BIC: 常用于清除某操作数中的相应位,其余位保持不变。
移位和循环指令
指令 | 功能描述 |
---|---|
LSL Rd, Rn, #imm5 |
Rd = Rn << imm5 |
LSL Rd, Rn |
Rd <<= Rn |
LSL Rd, Rm, Rn |
Rd = Rm << Rn (逻辑左移) |
LSR Rd, Rn, #imm5 |
Rd = Rn >> imm5 |
LSR Rd, Rn |
Rd >>= Rn |
LSR Rd, Rm, Rn |
Rd = Rm >> Rn (逻辑右移) |
ASR Rd, Rn, #imm5 |
Rd = Rn ·>> imm5 |
ASR Rd, Rn |
Rd ·>>= Rn |
ASR Rd, Rm, Rn |
Rd = Rm ·>> Rn (算术右移) |
ROR Rd, Rn |
Rd >>= Rn |
ROR Rd, Rm, Rn |
Rd = Rm >> Rn (循环右移) |
RRX Rd, Rn |
Rd = (Rn >> 1) + (C << 31) |
RRXS Rd, Rn |
Rd = (Rn >> 1) + (C << 31), C = Rn & 1 (带进位的右移一格) |
如果在移位和循环指令上加上“S”后缀,这些指令会更新进位标识 C。
如果是16 位Thumb-2指令,则总是更新进位标识 C 的。
桶形移位器
ARM微处理器一个显著的特征是:在操作数进入ALU之前,对操作数进行预处理。如:指定位数的左移或右移,这种功能明显增强了数据处理操作的灵活性。这种预处理是通过ARM微处理器内嵌的桶形移位器(Barrel Shifter)来实现的。
桶形移位器支持数据的各种移位操作,移位操作在 ARM 指令集中不作为单独的指令使用,它只能作为指令格式中的一个字段,在汇编语言中作为指令中的选项。
例如,数据处理指令的第二个操作数为寄存器时,就可以加入移位操作选项对它进行各种移位操作。
符号拓展指令
二进制补码表示法中,最高位是符号位。
把一个8 位或16 位数扩展成32 位时:
① 对于负数, 必须把所有高位全填1, 其数值不变;
② 至于正数或无符号数,则只需简单地把高位清0。
指令 | 功能描述 |
---|---|
SXTB Rd, Rm |
Rd = Rm 的带符号扩展,把带符号字节整数扩展到 32 位 |
SXTH Rd, Rm |
Rd = Rm 的带符号扩展,把带符号半字整数扩展到 32 位 |
例: R0=0x55aa8765
SXTB R1, R0 ;R1=0x00000065
SXTH R1, R0 ;R1=0xffff8765
UXTB R1, R0 ;R1=0x00000065
UXTH R1, R0 ;R1=0x00008765
字节调序指令
REV Rd, Rn 在字中调整字节序
REV16 Rd, Rn 在高低半字中调整字节序
REVSH 在低半字中调整字节序,并带符号扩展
例1: R0=0x12345678
REV R1, R0;
REVSH R2, R0;
REV16 R3, R0;
则 R1=0x78563412,
R2=0x00007856,
R3=0x34127856。
例2: R0=0x33448899
REVSH R1, R0;
后 R1=0xFFFF9988。
位域处理指令
指令 | 功能描述 |
---|---|
BFC Rd, #<lsb>, #<width> |
将 Rd 指定位域清零:Rd 中第 lsb 位开始的 width 宽度,lsb 为最低有效位,width 为位域宽度 |
BFI Rd, Rn, #<lsb>, #<width> |
将 Rn 内容插入 Rd 指定位域 |
CLZ Rd, Rn |
计算 Rn 中前导 0 的数目 |
RBIT Rd, Rn |
将 Rn 的位按位旋转 180 度 |
SBFX Rd, Rn, #<lsb>, #<width> |
将 Rn 指定位域拷贝到 Rd,并带符号扩展到 32 位 |
UBFX Rd, Rn, #<lsb>, #<width> |
将 Rn 指定位域拷贝到 Rd,并无符号扩展到 32 位 |
例:(1) BFC(位域清零) 指令
LDR R0, =0x1234FFFF
BFC R0, #4, #10
执行完后, R0= 0x1234C00F
(2) BFI(位域插入指令)
LDR R0, =0x12345678
LDR R1, =0xAABBCCDD
BFI R1, R0, #8, #16
则执行后, R1= 0xAA5678DD
( 3 ) RBIT 指令
记 R1=0xB4E10C23 (二进制数值为 1011,0100,1110,0001,0000,1100,0010,0011) , 指令
RBIT R0, R1
执行后,则 R0=0xC430872D (二进制数值为1100,0100,0011,0000,1000,0111,0010,1101)。
(4) UBFX/SBFX
LDR R0, =0x5678ABCD
UBFX R1, R0, #12,#16 ; R1=0x0000678A。
类似地, SBFX 也抽取任意的位域, 但是以带符号的方式进行扩展。
LDR R0, =0x5678ABCD
SBFX R1, R0, #8,#4 ; R1=0xFFFFFFFB
子程序调用与无条件转移指令
B, BL, BLX, BX 跳转 实现程序转移
MOV 、 LDR、 POP、 LDM 直接修改 PC 值, 实现程序转移
主要指令格式有:
B Label ;转移到Label 处对应的地址。(跳转地址的地址标号,也可以是一个绝对地址表达式, B 0x123456)
BX reg ;转移到由寄存器reg 给出的地址(带转态切换的跳转指令)
BL Label ;转移到Label 处对应的地址,并且把转移前的下条指令地址保存到LR(带链接的跳转指令)
BLX reg ;转移到由寄存器reg 给出的地址,并且把转移前的下条指令地址保存到LR
MOV PC, R0 ;转移地址由R0 给出
LDR PC, [R0] ;转移地址存储在R0 所指向的存储器中
POP {…, PC} ;把返回地址以弹出堆栈的风格送给PC,从而实现转移
LDMIA SP!, {…, PC} ; POP 的另一种等效写法
比较和测试指令
比较指令通常用于把一个寄存器与一个32位的值进行比较或测试, 根据结果更新APSR中的标志位。在设置标志位后,其它指令可以通过条件执行来改变程序的执行顺序。比较指令不需要使用S后缀。
比较指令包括:
CMP 比较指令:CMP 指令在内部做两个数的减法,并根据差来设置标志位,但是不把差写回。
CMP<Rn>,<Rm>
CMP<Rn>,#<immed>
CMP R0, R1 ; 计算R0-R1的差, 根据结果更新标志位
CMP R0, #0x12 ; 计算R0-0x12的差,根据结果更新标志位
CMN 反值比较指令:CMN 指令在内部做两个数的加法(相当于减去减数的相反数)。
CMN<Rn>,<Rm>
CMN<Rn>,#<immed>
CMN R0, R1 ; 计算R0+R1的和, 并根据结果更新标志位
CMN R0, #0x12 ; 计算R0+0x12的和,并根据结果更新标志位
TST 位测试指令:TST 指令的内部执行 AND 指令,只是不写回运算结果。
TST<Rn>,<Rm>
TST<Rn>,#<immed>
TST R0, R1 ; 计算R0 & R1, 并根据结果更新标志位
TST R0, #0x12 ; 计算R0 & 0x12, 并根据结果更新标志位
TEQ 相等测试指令:TEQ 指令的内部执行EOR 指令,只是不写回运算结果。
TEQ<Rn>,<Rm>
TEQ<Rn>,#<immed>
TEQ R0, R1 ; 计算R0 ^ R1, 并根据结果更新标志位
TEQ R0, #0x12 ; 计算R0 ^ 0x12,并根据结果更新标志位
其他指令
NOP指令
Cortex-M4支持 NOP 指令,用于产生指令对齐或延时。 NOP指令什么也不做,仅消耗一条指令的时间。指令格式如下:
NOP ;什么也不做
也可以用C语言调用:
_NOP(); //什么也不做
BKPT指令
指令格式为: BKPT
其中immed_8为数字表达式,取值为0~255范围内的整数。 BKRT指令产生软件断点中断,主要用于程序的调试。
也可以用C语言调用:
_BKPT(immed_8); //断点
伪指令
ADR伪指令
ADR是小范围的地址读取伪指令,将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到目标寄存器中。
在汇编器汇编源程序时, ADR伪指令被汇编器替换成一条合适的指令。通常, 汇编器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误, 汇编失败。
ADR伪指令格式: ADR{cond} register, expr
其中, register是目标寄存器; expr是相对偏移地址表达式,通常是一个地址标号。 expr的取值范围:
-
当地址值是字节对齐时,其取指范围为: -255B~255B
-
当地址值是字对齐时,其取指范围为: -1020B~1020B
示例:
Next MOV R1, #0XF0 ; Next是标号
ADR R2, Next ; 读取Next的地址
注:不是绝对地址,是装载地址和PC寄存器的差值,及相对地址
ADRL伪指令
ADRL是中等范围的地址读取伪指令,与ADR功能相似,也是将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到目标寄存器中,但是可以读取更大范围的地址。
在汇编器汇编源程序时, ADRL伪指令被汇编器替换成两条合适的指令。若不能用两条指令实现,则产生错误,编译失败。
ADRL伪指令格式: ADRL{cond} register, expr
expr的取值范围:
-
当地址值是字节对齐时,其取指范围为: -64K~64K
-
当地址值是字对齐时,其取指范围为: -256K~256K
示例:
Next MOV R1, #0XF0 ; Next是标号
ADRL R2, Next ; 读取Next的地址
LDR伪指令
LDR是大范围的地址读取伪指令,用于加载32位的立即数或一个地址值到指定寄存器。
LDR伪指令格式: LDR register, =const-expr const-expr是常量表达式,可以包含标号。
示例:
LDR R0, =Next ; Next是标号
LDR R1, =(Next+10) ; Next是标号
LDR R2, =0x0F ; 表达式是一个常量
LDR R3, =0x5678ABCD ; 表达式是一个常量
在汇编器汇编源程序时, LDR伪指令被汇编器替换成一条合适的指令。若加载的常量未超出MOV或MVN所能加载的数值范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池(Literal pools),并使用一条程序相对偏移的LDR指令从文字池读出常量。
文字池其实就是一个存储常量数据的存储空间,汇编器会使用文字池来在代码段中存储常量数据,汇编器通常会在每个段的末尾放置文字池。
在前面的示例中, LDR R2, =0x0F 被替换为一条 MOV R2, 0x0F 指令。而 LDR R3, =0x5678ABCD 中的常量0x5678ABCD被汇编器放入文字池(位于代码段的末尾),然后汇编器使用 LDR R3, [PC, #offset] 指令从文字池读出常量 0x5678ABCD 到 R3。其中, #offset是常量的存储地址与PC值之间的相对偏移量。
ARM 汇编指示命令
汇编指示命令种类较多,这里仅简单介绍常用的常量定义 EQU 命令
常量定义
数据常量定义汇编指示命令
数据常量定义伪指令EQU用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言中的#define。
EQU语法格式:
名称 EQU 表达式{,类型 } ;
其中EQU可用“*”代替 EQU 伪指令定义的字符名称,当表达式为32位常量时,可指定表达式的数据类型,有三种类型: CODE16、 CODE32和DATA 。
示例:
Data_in EQU 100 ;
定义标号Data_in的值为100
Addr EQU 0xFF, CODE32 ;
定义Addr值为0xFF, 32位的ARM指令
ARM 可执行映像构成
映像文件一般由域(Region)组成,域最多由三个输出段组成(RO,RW,ZI)组成, 输出段又由输入段对应生成。
输出端组成详解
ARM汇编语言程序源文件中一般有代码段和数据段等多个段, 即输入段, 输入段经过汇编处理链接后就变成了映像文件中的RO段和RW段,还有ZI段,也就是三个输出段(RO,RW,ZI)。
属性为READONLY的段(例4-6中的Reset段,Init段)经过汇编处理链接后生成映像文件中的RO段;属性为READWRITE且包含初始化变量的数据段经过汇编处理链接后生成映像文件中的RW段;属性为READWRITE且未包含初始化变量(NOINIT属性)的段(例4-6中的MyStack段)经过汇编处理链接后生成ZI段。
域, 是指整个映像文件所处在的区域,它 又分为加载域(Load Region)和运行域(Execution Region)。
运行时,程序中的RW段和ZI段必须装载到可读写的RAM中。即涉及程序的加载域和运行域。
加载域:是指程序烧入ROM中的状态。 一般来说ROM里的整个映像文件所在的地址空间就是加载域;
运行域:是指程序运行时的状态。 在运行时,程序中的RW段和ZI段必须装载到可读写的RAM中。
映像文件一般包含RO和RW数据。 映像文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。
各个段在储存中的位置:
加载域中输出段的地址位置: RO段后面紧跟着RW段,通常是地址连续的。
运行域中输出段的地址位置: RO段在Flash地址空间,RW和ZI段在RAM地址空间, RO段与RW/ZI段地址不连续,但RW和ZI一定是连续的。
AAPCS 标准
ARM公司推出的 AAPCS (ARM ArchtectureProcedure Call Standard) 标准,规定了一些函数间调用的规则,这些规则包括函数调用过程中:
⮚ 寄存器的使用规则,
⮚ 数据栈的使用规则,
⮚ 参数的传递规则。
主流工具链的C语言编译器编译的C语言函数均满足AAPCS的规则,
对于汇编语言:需要开发者来保证汇编语言函数(子程序)满足AAPCS的规则
AAPCS 标准的内容如下:
-
父函数与子函数间的入口参数依次通过R0~R3寄存器传递。
父函数在调用子函数前先将参数存入到R0~R3中。 当超过4个参数时, 其它参数通过栈传递。当子函数运行时,根据自身参数个数自动从R0~R3或者栈中读取参数。
-
子函数通过R0寄存器将返回值传递给父函数。
子函数返回时,将返回值存入R0,当返回到父函数时父函数读取R0获得返回值。
-
发生函数调用时, R0~R3 是传递参数的寄存器,即使是父函数没有参数需要传递,子函数也可以任意更改 R0~R3 寄存器,无需考虑会破坏它们在父函数中保存的数值,返回父函数前无需恢复其值。
AAPCS规定,发生函数调用前, 由父函数将 R0~R3 中有用的数据压栈,然后才能调用子函数,以防止父函数 R0~R3 中的有用数据被子函数破坏。
-
R4~R11为普通的通用寄存器,若子函数需要使用这些寄存器,则需要将这些寄存器先压栈然后再使用,以免破坏了这些寄存器中保存的父函数的数值。 子函数返回父函数前需要先出栈恢复其数值,然后再返回父函数。
AAPCS规定,发生函数调用时,父函数无需对这些寄存器进行压栈处理, 若子函数需要使用R4~R11 ,则由子函数负责压栈,以防止父函数R4~R11中的数据被破坏。
-
编译器在编译时就确定了函数间的调用关系,它会使函数间的调用遵守3、 4条规定。但编译器无法预知中断函数的调用,被中断的函数无法提前对R0~R3进行压栈处理,因此需要在中断函数里对它所使用的R0~R11压栈。 对于中断函数,不遵守第3条规定,遵守第5条规定。
-
R12寄存器在某些版本的编译器下另有它用,用户程序不能使用,因此编写汇编函数时也必须对它进行压栈处理,确保它的数值不能被破坏。
-
R13寄存器是堆栈寄存器(SP),用来保存堆栈的当前指针。
-
R14寄存器是链接寄存器(LR),用来保存函数的返回地址。
-
R15寄存器是程序寄存器(PC),指向程序当前的地址。
堆栈寻址
堆栈寻址可以分为四类,分别是 FA、FD、EA 和 ED。这些类别描述了在访问堆栈时,堆栈指针(SP)的变化方式以及数据访问的顺序。具体来说,这些类别是根据以下两个维度来区分的:
-
堆栈增长方向:
-
Full Stack (F): 堆栈指针(SP)指向堆栈中最后一个被压入的数据项(即堆栈的顶部)。
-
Empty Stack (E): 堆栈指针(SP)指向堆栈中下一个将要被压入数据项的位置(即堆栈顶部的下一个空闲位置)。
-
堆栈操作顺序:
-
Ascending (A): 堆栈向内存地址增加的方向增长(即堆栈从低地址向高地址增长)。
-
Descending (D): 堆栈向内存地址减少的方向增长(即堆栈从高地址向低地址增长)。
FA (Full Ascending)
-
堆栈增长方向: Full Stack (F)
-
堆栈操作顺序: Ascending (A)
-
描述: 堆栈指针(SP)指向堆栈中最后一个被压入的数据项,堆栈向内存地址增加的方向增长。
-
操作:
-
压栈(Push): 先将 SP 增加(因为堆栈是 Ascending),然后将数据存入 SP 指向的位置。
-
弹栈(Pop): 先从 SP 指向的位置取出数据,然后将 SP 减少。
FD (Full Descending)
-
堆栈增长方向: Full Stack (F)
-
堆栈操作顺序: Descending (D)
-
描述: 堆栈指针(SP)指向堆栈中最后一个被压入的数据项,堆栈向内存地址减少的方向增长。
-
操作:
-
压栈(Push): 先将 SP 减少(因为堆栈是 Descending),然后将数据存入 SP 指向的位置。
-
弹栈(Pop): 先从 SP 指向的位置取出数据,然后将 SP 增加。
不同的处理器架构可能采用不同的堆栈寻址方式。例如,ARM 架构通常使用 FD 类型的堆栈寻址,而 x86 架构则使用 ED 类型的堆栈寻址。
EA (Empty Ascending)
- 堆栈增长方向: Empty Stack (E)
- 堆栈操作顺序: Ascending (A)
- 描述: 堆栈指针(SP)指向堆栈中下一个将要被压入数据项的位置,堆栈向内存地址增加的方向增长。
- 操作:
- 压栈(Push): 先将数据存入 SP 指向的位置,然后将 SP 增加(因为堆栈是 Ascending)。
- 弹栈(Pop): 先将 SP 减少,然后从 SP 指向的位置取出数据。
ED (Empty Descending)
- 堆栈增长方向: Empty Stack (E)
- 堆栈操作顺序: Descending (D)
- 描述: 堆栈指针(SP)指向堆栈中下一个将要被压入数据项的位置,堆栈向内存地址减少的方向增长。
- 操作:
- 压栈(Push): 先将数据存入 SP 指向的位置,然后将 SP 减少(因为堆栈是 Descending)。
- 弹栈(Pop): 先将 SP 增加,然后从 SP 指向的位置取出数据。
不同的处理器架构可能采用不同的堆栈寻址方式。例如,ARM 架构通常使用 FD 类型的堆栈寻址,而 x86 架构则使用 ED 类型的堆栈寻址。
储存器容量拓展
-
位扩展: 适用于单个芯片的地址范围(字数)能满足要求,而位数不够的情况。
-
字扩展: 单片存储器芯片的地址范围达不到要求,就需要进行字扩展。字扩展就是对存储地址空间(字数上)的扩充,而位数不变。
位绑定
位绑定操作: 指把一个地址单元的 32 位变量中的每一位,通过一个简单的地址变换算法,映射到另一个地址空间,使得每一位占用1个地址,当对此地址空间进行操作时,只有数据的最低位是有效的。
位带别名区:
别名区中的每个字与对应位绑定区的相应位的对应关系为:
\(bit_{wordaddr} = bit_{Dandbase} + (byte_{ offset} \times 32 ) + ( b i t _ { n u m b e r} \times 4 )\)
bitwordaddr 是位带区目标位映射到别名存储器区中字的起始地址
bitbandbase 是别名区的起始地址
对于SRAM位绑定区为0x22000000
对于片上外设位绑定区为0x42000000
byteoffset 是包含目标位的字节在位段里的序号
bitnumber是目标位所在位置(0~7)
SRAM 与 DRAM
静态SRAM: 集成度较高;不需刷新;速度快,价格贵.
动态DRAM: 需刷新;集成度高;速度较低,价格便宜.
三级储存体系
存储系统的层次结构为 CPU、缓存CACHE、主存(内存)、辅存(外存)。
把各种不同存储容量、不同存取速度及价格的存储器,按照一定的体系结构组织起来,使所放的程序和数据按 照一定的层次分布在各种存储器中。
• 主存:用来存放运行期间的大量程序和数据。一般插入等待状态。
• 辅存:一般由磁表面存储器构成,用来存放系统程序。大型文件,数据库等。
三种存储器构成三级存储器管理,各级职能和要求不同。快存追求速度,以和CPU速度匹配;辅存追求容量大;主存介于两者之间,对容量,速度都有一定要求。
总线
总线带宽
总线宽度: 总线的数据线的数目。早期只有8位或16位,现在都是32位或64位。总线宽度越宽,同时传输的数据就越多,吞吐量就越大。
总线时钟频率:总线传输需要在时钟控制下进行。时钟频率越高,每秒在每条数据线上传输的信息就越多。时钟频率的单位为MHz。(有时以总线传输率作为指标,单位是Mbit/s,是指每秒在一条数据线上传输的数据量,数值上和时钟频率相同)。
总线带宽: 总线所有数据线每秒可以传输的数据量,也就是最大传输率。单位为MB/s。前三个指标之间的关系:
总线带宽= 总线宽度×总线时钟频率/8
单位是MB/s, 或 w/T(字节/秒)
总线时序
总线数据传输周期: 系统总线上的数据是在主模块(主设备)的控制下进行的,主模块有控制总线的能力,总线完成一次数据传输分为4个阶段:
✓ 总线请求和仲裁阶段(Request and Arbitration):欲使用总线的主模块提出申请,总线仲裁器确定把下一个传输周期的总线使用权指配给提出申请的模块。
✓ 寻址阶段(Addressing):获得总线使用权的主模块发出存储器地址或I/O端口地址,使从模块启动。
✓ 数据传输阶段(Data Transferring):源模块和目的模块之间进行数据传输。
✓ 结束阶段(Ending):主从模块的有关信息均从系统总线上撤除,让出总线。
时序: 总线通过规定明确的操作间的时间关系来保证传输的可靠性, 这种时间关系称为时序。
总线仲裁
当总线上存在多个主设备时,例如多个CPU或DMA控制器,需要保证一个时刻只有一个主设备操作总线,以避免冲突。
通常,可以在计算机系统中设置一个独立的总线仲裁器来决定下一时刻由哪个主设备使用总线。
集中仲裁
总线主设备在操作总线之前,需要首先向仲裁器提出申请(request),并在收到许可(grant)后使用总线。
仲裁器在同时收到多个申请时,按照某种优先级原则,向申请者发出许可。
分布仲裁
不使用独立的集中仲裁器, 每个主设备需 要附带一个实现仲裁功能的组件。
在需要使用总线时, 由各自的仲裁组件交 互信息, 使用选举或其它原则决定总线的 使用权。
DMA
DMA(Direct Memory Access)是一种不需要CPU参与的在系统其它设备之间(主要是I/O设备和主存储器)传送数据的技术。
• DMA传输特点:
✓ 在存储器和外设或外设和外设之间建立直接传输通路,传输数据无需CPU中转。
✓ 需专门的DMA控制器(DMAC), DMA传输期间由DMAC控制总线。
✓ 适合高速大批量数据传送的地方。
✓ 硬件复杂、成本较高。
在简单总线(如ISA)上, DMA由具有总线控制能力的DMA控制器完成。
DMA控制器是独立的总线主设备,由CPU通过指令或I/O设备通过专用的信号线启动,使用请求/允许信号与CPU协商后使用系统总线,控制数据的传送。
DMA控制器的基本功能:
① 能接受外设的DMA请求信号DMAREQ,并能向外部设备发出DMA响应信号DMAACK;
② 能向CPU发出总线请求信号(HOLD),当CPU发出总线响应信号(HLDA)后能接管对总线的控制权,进入DMA方式;
③ 能发出地址信息,对存储器寻址并修改地址指针;
④ 能向存储器和外部设备发出读写控制信号;
⑤ 能决定传送的字节数,并能判断DMA传送是否结束;
⑥ 能发出DMA结束信号,释放总线,让CPU重新获得总线控制权
中断
中断: 中断是当CPU正常运行程序时,用户通过某种方式向CPU请求为自己服务, CPU接收请求暂时中断正在运行的程序, 转去执行直接为用户服务的服务程序,执行完毕后再返回被中断的程序。这一过程被称为中断
异常:是程序执行过程中如果发生了意外事件,例如ARM体系中的软件中断、未定义指令、调试事件及系统复位功能等, 正常执行的程序被暂停,转到相应的处理程序执行。
中断可看成是异常的一种。中断对内核来说是意外突发事件, 请求信号来自外部;异常是内核产生的,即是在执行指令或存取中产生的。
中断源
➢ 硬中断:由外部电路在CPU引脚上产生的中断请求如监测对象、打印机、键盘
➢ 软中断: CPU执行程序过程中产生的中断请求如一条软中断指令、程序运行出现某种问题
中断源,导致CPU产生中断的来源,有硬中断源和软中断源,常见中断源如下:
-
常见的外部输入输出设备 (IRQ) 键盘,打印机,鼠标等
-
数据通道中断源 (IRQ) 软盘,硬盘等
-
实时过程产生的中断 (IRQ) 例如实时设备的采样中断
-
定时或者计数信息 (IRQ)
• 定时、计数中断可以是外部,也可以是内部中断。例如实时时钟
-
故障源 一般是不可屏蔽中断(NMI)或者系统异常
(a)外设故障: 电源掉电,运行超限、 监控诊断程序产生的诊断错误等。
(b)计算机故障:处理器出错(运算溢出、除数为0、 非法数据格式),内存出错(非法地址、主存储器页面失效、数据或地址校验错), 控制器出错(非法指令、 未定义的操作码、堆栈溢出)等。
-
程序调试设置的中断源
硬件断点:硬件调试器
软件断点:调试程序应支持单步运行和断点运行
中断源又可以分为条件中断和无条件中断
• 对于无条件中断的中断源的申请, CPU是一定要响 应的。如: 不可屏蔽中断。
• 对于条件中断,要求CPU必须处于某种条件下,才 可以响应中断。
✓ 如果CPU处于可以响应条件中断的状态,称为开 中断状态。
✓ 如果CPU处于不可以响应条件中断的状态, 称为 关中断状态。
-
控制CPU处于开中断或者关中断状态的方法,与CPU有关。
-
Cortex-M处理器内核中有中断屏蔽寄存器PRIMASK、FAULTMASK和BASEPRI,用于异常或者中断的屏蔽管理。
注: 进行条件中断处理时,注意CPU是处于开中断 还是关中断状态。 有时候, CPU根据自己的工作状态 ,也会切换系统的开/关中断状态。
断点: 指CPU执行的现行程序被中断时的下一条指令的地址,又称断点地址
中断现场: 指CPU转去执行中断服务程序前的运行状态, 包括CPU内部各寄存器、 断点地址等。
中断向量表
调用异常服务程序必须将处理器的指令指针转移到异常服务程序的入口, 方法是使用中断向量表。
中断向量表有两种类型。
-
指令型的中断向量表
这种中断向量表一般位于系统的ROM区域, ROM单元中 存放的是转移指令。
-
向量中断的中断向量表
在系统的ROM或者RAM区域存放的是中断向量(使用比较灵活)。中断向量是异常服务程序的入口地址。
Cortex-M处理器采用向量中断表
中断过程
分为中断请求、中断判优、中断响应、中断服务和中断返回几个阶段
初始化
在中断过程发生之前,需要确认系统是否能响应该中断源,如果是有条件的外部中断,一定要检查它的屏蔽位、优先级和触发方式,为该中断准备对应的中断服务程序(ISR)。
-
中断请求
由中断源向CPU提出中断请求(系统异常和软中断除外 )信号(电平信号或脉冲信号)
注:
外部设备发出的请求信号和CPU的要求是否一致。
-
中断判优
对中断源的优先级进行判别。若符合中断条件和优先权, CPU接受中断请求,进入中断响应阶段
-
中断响应
保存断点地址(一般采用堆栈,有的采用 寄存器)
利用中断向量表将指令指针指向中断服务程序入口
在中断响应里,要完成断点保存、标志寄存器保存、关中断等。
部分操作由CPU自动完成,用户也需要完成一些操作。
-
中断服务
中断服务就是执行中断服务程序
-
中断返回
中断服务程序结束后,返回到正常程序的断点,继续执行原来的程序。
若断点地址保存在堆栈中,中断返回时,将断点地址从堆栈中弹出到指令指针。
利用堆栈保存断点地址,容易实现中断嵌套。
Cortex 中断源
硬中断源: 除了内核的系统异常(包括复位、硬件错误、存储器管理错误和总线错误等)外,主要是外部中断源,由嵌套向量中断控制器NVIC(Nested Vectored Interrupt Controller)管理
◆ 外部中断请求IRQ
✓ 定时器、 I/O端口和通信接口等外设产生
✓ 软件设置寄存器产生软中断
◆ 不可屏蔽中断NMI
✓ 看门狗定时器或者掉电检测产生
◆ SysTick系统节拍中断
✓ 来自处理器内核
◆ 内核系统异常
软中断源
• 系统异常,如使用错误和调试监控等;
• 一条中断指令,例如执行SVC指令触发的SVC异常;
SVC 系统服务异常
SVC(系统服务异常,亦简称系统服务调用):用户级想操作一些特权级的操作,是不可行的。假如修改底层寄存器的值是特权级操作,用户想修改底层的寄存器,这时可以利用SVC 异常。 OS会提供一些系统服务函数,用户调用这些服务函数,OS会发出SVC异常,从而进入异常服务函数里面, 再调用相关函数对寄存器进行修改。
• 修改内核的软件触发中断寄存器(NVIC-STIRx)产生;
• 修改内核的设置中断挂起寄存器(NVIC-ISPRx)产生;
• 外部中断控制器EXTI的软件中断事件寄存器(EXTI_SWIERx)设置相应的请求位
Cortex 中断特性
• Cortex-M处理器支持的异常源通过异常类型 编号标识,具有优先级特性,多数异常和中 断的优先级还可以编程设置
• 异常类型编号1~15的为系统异常,类型16 及以上的则为外部中断输入
• 实际编程中可采用CMSIS-Core的统一函数 调用。 CMSIS编号值位于厂商提供的头文件, 在一个名为IRQn的typedef段中
ARM Cortex-M4 处理器共支持240个外部中断,外部中断输入 #0~#239 对应到NVIC的中断输入。
Cortex-M处理器内核中的中断屏蔽寄存器PRIMASK、 FAULTMASK和BASEPRI,用于异常或者中断的屏蔽管理
• PRIMASK寄存器只能在特权等级下访问,可以禁止除了NMI和HardFault外的所有异常,常应用于需要暂时禁止所有中断以执行一些实时关键的任务
• PRIMASK寄存器实质上是将当前的优先等级改为0(可编程最高等级)
Cortex-M0+处理器的NVIC_IPR只有8个寄存器,最多支持32个IRQ中断源,加上16个内核异常,共有48个中断源。
Cortex 中断优先级
Cortex-M4处理器的8位优先级寄存器进一步划分为两个部分: 分组优先级(也称抢占优先级, pre-emption priority)和子优先级(subpriority)。
分组优先级决定了是否能嵌套,高的组优先级中断(数值低)可以抢占低的组优先级(数值高)中断。如组优先级相同, 但即使子优先级比正在执行的中断的子优先级高也是不能抢占的。
在组优先级一致的情况下, 如多个中断请求同时发生, 则子优先级高的可以先执行,而子优先级低的则只能暂时挂起(pending)
Cortex-M4处理器的应用中断和复位控制寄存器SCB->AIRCR,其中 PRIGROUP 域的值决定了8种不同的优先级分组方式,每种方式下分组优先级域和子优先级域分配数量互补的位。
优先级分组 | 抢占优先级域 | 子优先级域 |
---|---|---|
0(默认) | Bit[7 : 1] | Bit[0] |
1 | Bit[7 : 2] | Bit[1 : 0] |
2 | Bit[7 : 3] | Bit[2 : 0] |
3 | Bit[7 : 4] | Bit[3 : 0] |
4 | Bit[7 : 5] | Bit[4 : 0] |
5 | Bit[7 : 6] | Bit[5 : 0] |
6 | Bit[7] | Bit[6 : 0] |
7 | 无 | Bit[7 : 0] |
确定实际的分组优先级和子优先级时,还需要考虑实际可用的配置寄存器的宽度
优先级配置确定可用的优先等级后,可以选择一个有效的优先等级值。每个外部中断都有对应的中断优先级,设置在中断优先级寄存器NVIC_IPRx内。该寄存器最大宽度为8位,最小为3位,其数量取决于芯片中实际存在的外部中断数。
Cortex 向量表重定位
“向量表重定位”方式提供一个可编程的向量表偏移寄存器SCB->VTOR, SCB->VTOR寄存器配置新向量表的基地址。
一般是将新向量表的位置重定位为RAM存储器的首地址(0x20000000)。由于最小的中断向量表要求对齐为128字节,所以SCB->VTOR寄存器的最低7位保留且被强制置为0。中断向量表的大小一般要被扩展为下一个2的整数次方。
例 1
Cortex-M微控制器有32个或者75个中断源的情况下,请给出向量表的大小以及可能设置的向量表的基地址。
32个中断源的向量表大小为:
(32(用于中断) +16(系统异常))× 4(每个向量的字节数) =192
扩展为下一个2的整数次方得到256字节,因此向量表的基地址可被设置为0x00000000、 0x00000100、 0x00000200等。
75个中断源的向量表大小为:
(75(用于中断) +16(系统异常))× 4(每个向量的字节数) =364
扩展为下一个2的整数次方得到512字节,因此向量表的基地址可被设置为0x00000000、 0x00000200、 0x00000400等。
IO 数据传送方式
无条件传送方式
无条件传送方式
– 无条件是指CPU不管外设的状态,在需要和外设交换信息的时候,就用输入或输出指令和外设交换信息
– 在这种方式下, CPU和外设之间只有数据信息的传送,没有状态信息的传送
– 但无条件传送,不等于不需要接口电路,只是接口电路可以比较简单,因为只有数据的通道,一般只有输出锁存器和输入缓冲器
特点:
-
适用于外设动作时间已知, 在CPU与外设进行数据传送时, 外设保证已准备好的情况
-
软硬件十分简单。
查询传送方式
查询传送方式
– 又称为异步传送方式。 CPU要遵循“先查询,后传送”的原则,保证只有在外设已经是在“准备好”状态,才开始传送数据
– 查询式传送的一般流程:
① 先从状态端口读入状态字;
② 如果状态是“就绪”,开始传送;
③ 如果状态是“没有就绪”,则继续查询,直到状态是“就绪”,才开始传送
– 查询传送方式优点: CPU和外部设备之间可以很好地配合工作,且工作可靠;控制程序编写容易;适应于大多数应用场合
– 查询传送方式的缺点: CPU要不断地查询外部设备的状态,查询等待会影响CPU的工作效率
– 对CPU负担不重,所配外部设备对象不多,实时性要求不太高的情况下可使用这种传送方式
中断传送方式
中断传送方式
– 中断方式是由外设向CPU发出要求交换数据的请求,即中断请求。CPU收到中断请求后,中断当前的工作,为外设服务。服务结束(输入或输出)后,继续原来的工作
– 采用中断传送方式时, CPU在外部设备还未就绪的这段时间,一直在执行主程序,而不是处于等待状态,实现了主机和外部设备的并行工作
– 中断方式不要求CPU等待或查询,可以大大地提高CPU工作效率。但接口电路比较复杂
串口
接收端时钟采用高于波特率的更高频率的时钟
作用:
•提高采样的分辨率和抗干扰能力,更准确地检测到起始位
•每一位的采样在码元的中心进行,可最大限度避免收、发时钟偏差的影响
GPIO
STM32F407 的常用的 GPIO 寄存器
GPIOx_MODER
GPIO 端口模式寄存器位分配图 (GPIOx_MODER) (x = A..I)
偏移地址: 0x00
位 2y:2y+1 MODERy[1:0]:端口 x 配置位(y = 0..15)
00:输入(复位状态) 01:通用输出模式
10:复用功能模式 11:模拟模式
GPIOx_OTYPER
GPIO端口输出类型寄存器位分配图 (GPIOx_OTYPER) (x = A..I)
偏移地址: 0x04
位 15:0 OTy:端口 x 配置位(y = 0..15)
0:输出推挽 1:输出开漏
GPIOx_OSPEEDR
GPIO 端口输出速度寄存器位分配图 (GPIOx_OSPEEDR) (x = A..I)
偏移地址: 0x08
位 2y:2y+1 OSPEEDRy[1:0]:端口 x 配置位(y = 0..15)
00: 2 MHz(低速) 01: 25 MHz(中速)
10: 50 MHz(快速) 11: 100 MHz(高速)
GPIOx_PUPDR
GPIO 端口上拉/下拉寄存器位分配图 (GPIOx_PUPDR) (x = A..I)
偏移地址: 0x0C
位 2y:2y+1 PUPDRy[1:0] :端口 x 配置位(y = 0..15)
00: 无上拉或下拉 01: 上拉
10: 下拉 11: 保留
GPIOx_IDR
GPIO 端口输入数据寄存器位分配图 (GPIOx_IDR) (x = A..I)
偏移地址: 0x10
位 15:0 IDRy[15:0]:端口x输入数据 (y = 0..15)
GPIOx_ODR
GPIO 端口输出数据寄存器位分配图 (GPIOx_ODR) (x = A..I)
偏移地址: 0x14
位 15:0 ODRy[15:0]:端口x输出数据 (y = 0..15)
GPIOx_BSRR
GPIO 端口置位/复位寄存器位分配图 (GPIOx_BSRR) (x = A..I)
偏移地址: 0x18
位 31:16 BRy:端口 x 复位位y (y = 0..15)
1:对相应的 ODRx 位进行复位 0:不会对相应的 ODRx 位执行任何操作
位 15:0 BSy:端口 x 置位位y (y = 0..15)
1:对相应的 ODRx 位进行置位 0:不会对相应的 ODRx 位执行任何操作
常用寄存器地址
定义寄存器的符号地址