ARM基础笔记
本文最后更新于:2022年6月6日 晚上
产业链
- CUP核
- SOC芯片 — System On Chip (CPU核 + 外围控制器)
- 产品
ARM核工作模式(USA UFI SM)
八大工作模式
用户模式(USR:User)— 唯一非特权
- 用户程序的工作模式,运行在操作系统的用户态,没有操作其他硬件的权限
- 只有它不是特权模式(privilege mode)
- 只能执行处理自己的数据,不能切换到其他模式下,访问硬件资源或切换到其他模式只能通过软中断或者异常
系统模式(SYS:System)— 特权
- 不受用户模式的限制,用户模式核系统模式共用一套寄存器
- 操作系统的一些特权任务,可以用该模式访问一些受控资源
终止模式(ABT:Abort)— 异常
- 用域支持虚拟内存或存储器保护
- 用户程序访问非法地址、没有权限的地址,进入该模式(如:段错误)
未定义模式(UDF:Undefined)— 异常
- 用于支持硬件协处理器的软件仿真
- CPU在指令的译码阶段不能识别该指令操作时,进入该模式
快速中断模式(FIQ:Fast Interrupt Request)— 异常
- 用于处理对时间要求紧急的中断请求, 主要用于高速数据传输及通道处理
一般中断模式(IRQ:Interrupt Request)— 异常
- 也叫普通中断,用于处理一般中断请求,通常在硬件产生中断信号会自动进入该模式
- 可以自由访问系统硬件资源
管理模式(SVC:Supervisor)— 异常
- CPU上电后的默认模式,主要用作系统初始化(复位异常),软中断处理
- 用户模式下,用户程序请求使用硬件资源时,通过软中断进入该模式
安全监控模式(MON:Monitor)
- TrustZone — 讲SOC的硬件和软件资源划分程安全和非安全两个世界
- 安全世界:所有需要保密的操作 — 指纹识别、密码处理、数据加解密等
- 非安全世界:用户操作系统、各种应用程序
- 两个世界通过Monitor Mode的模式进行转换
总结
- 唯一非特权:User
- 五大异常模式:ABT、UDF、FIQ、IRQ、SVC
- 安全模式:MON
- 中断和异常的区别:异常包含了中断,中断只是异常的一种
寄存器资源
寄存器分类 (Registers)
ARM态通用寄存器和程序计数器 — R0~R15
ARM态程序状态寄存器 — CPSR、SPSR
用途
R0~R10 — 存放用户数据
R11 (fp:frame-pointer)— 栈帧指针 记录一个栈空间的起始地址
R12(ip:The Intra-Procedure-call scratch register)— 临时存储sp(R13)
R13(sp:stack pointer)— 栈指针寄存器, 栈空间的结束地址
R14(lr:link register)— 发送跳转,保存PC寄存器的值
R15(pc:program counter)— 程序计数器,存放CPU需要执行的下一条指令的内存地址
CPSR(Current Program Status Register)— 记录当前CPU状态
SPSR(Saved Program Status Register)— 异常产生时候,保存CPSR的值
总结
ARM寄存器 — 40个,一种模式最多 —18个寄存器
所有模式共享 — R0~R7、R15、CPSR
除FIQ其他模式共享 — R8~R12
5大异常模式私有 — R13~R14、SPSR
MON私有 — R13~R14、SPSR
FIQ — 中断更快的三大原因
- 更多私有寄存器,私有寄存器使用不需要做保护,FIQ恢复现场更快
- FIQ在异常向量表的最高位,省去了跳转的过程,速度更快
- FIQ的处理优先级比IRQ更高,甚至可以打断正在执行的IRQ
User 和System模式没有SPSR,因为它们产生异常时,跳转到其他模式执行,是他们的状态被其他模式保存
常用ARM核指令
指令格式
<opcode> {<cond>} {S} <Rd> ,<Rn>{, <operand2>}
opcode : 操作码
cond :条件
- NE、EQ、GT、LT、GE、LE
S:指令执行结果,影响CPSR的N,Z,C,V
Rd:目标寄存器,存放指令执行结果R0~R15
Rn:操作数1,必须是寄存器
operand2:操作数2
立即数:
#100
,一个常数,一个8位常数通过循环右移偶数位,得到它,则该数字为合法立即数把一个数转换为32bit,16进制
- 除零外,仅有一位数,为合法立即数
- 除零外,仅有两位数,且相邻(包括首尾),为合法立即数
- 除零外,仅有三位数字,且相邻(包括中间有0,首尾相邻),这三位数,最高位只能取1、2、3;最低位只能取4、8、C,这样的组合为合法立即数
寄存器
寄存器移位:只能寄存器移位
- LSL:逻辑左移
- LSR:逻辑右移
- ASR:算数右移
注意:操作数1,只能是寄存器
数据传送指令
MOV:
MOV 目标寄存器,操作数2
将操作数2的值 赋值给目标寄存器
MVN:
MVN 目标寄存器 , 操作数2
将操作数2 取反的值,给目标寄存器
LDR:
LDR 目标寄存器, =数据
任意数据传送到目标寄存器
- 如果后面是一个合法立即数,翻译成MOV指令
- 如果是非法立即数,翻译成 LDR Rn, [PC]
注意: MOV指令 用立即数,效率更高,CPU获取指令时,指令和数据都翻译成机器码,指令和数据同时获取,因此效率更高
但,不是所有数据都能被和指令同时获取,因此,有了合法立即数的概念,一条指令中八位是数据位
数据计算指令
ADD:
ADD 目标寄存器,操作数1,操作数2
SUB:
SUB 目标寄存器,操作数1,操作数2
MUL:
目标寄存器, 操作数1,操作数2
注意:MUL的目标寄存器,和操作数1,编号不能相同
MUL 两个操作数都要是简单寄存器
位运算指令
AND:
AND 目标寄存器,操作数1,操作数2
将操作数1,按位与 ,操作数2,结果存放在目标寄存器
ORR:
ORR 目标寄存器,操作数1,操作数2
操作数1,按位或,操作数2,结果存放在目标寄存器
EOR:
EOR 目标寄存器, 操作数1,操作数2
将操作数1,按位异或,操作数2,结果放在目标寄存器
BIC:
BIC 目标寄存器,操作数1,操作数2
目标寄存器 = 操作数1 & ~操作数2
如果想把data的某些位,变成自己想要的值:
先将对应的位清0,然后再或上对应的值(对应的值左移得到)
清0:先对应位数,几位就几个1,从第几位开始,就左移几位,再取反,再和源数字按位与&
置1:目标数字,左移,从第几位开始就左移几位,再和源数字按位或 |
比较指令
CMP:
CMP 寄存器,操作数2
CMP指令会自动影响CPSR的N、Z、C、V
CMP执行时不关心之前执行了什么指令,只关心CPSR的NZCV表示的条件是否满足条件,满足执行,不满足不执行
跳转指令
B/BL :只能跳转+/- 32M范围,跳转到一个指定标签
B 标签
BL 标签
:BL跳转之前,将跳转前的PC(R15)值保存在LR(R14)给PC赋值:没有范围限制
LDR PC , =标签名
内存访问指令
单个数据访问
LDR(Load Register):将内存的值加载到寄存器(读内存)
STR(Store Register):将寄存器的值写入内存(写内存)
寄存器间接寻址:
- LDR r0, [r1] —-> (r0 = *r1)
- STR r0,**[r1]** —-> (*r1 = r0)
基址变址寻址:将基地址寄存器 + 指令中给出的偏移量 = 数据存放的地址
前索引:
- STR r0, [r1, #4] —-> *(r1 + 4) = r0
- LDR r0, [r1, #4] —-> r0 = *(r1 + 4)
后索引:
- STR r0, [r1], #4 —-> *r1 = r0 ; r1 = r1 + 4
- LDR r0, [r1], #4 —-> r0 = *r1; r1 = r1 + 4
自动索引:
STR r0, [r1, #4] ! —-> *(r1 + 4) = r0; r1 = r1 + 4
LDR r0, [r1, #4] ! —-> r0 = *(r1 + 4); r1 = r1 + 4
多个数据访问
LDM :读内存数据,加载到多个寄存器
LDM {条件}{s} <MODE> 基质寄存器 {!} , {Reglist}^
STM:将多个寄存器的值,存储到一块内存
STM {条件}{s} <MODE> 基质寄存器 {!} , {Reglist}^
MODE:
- IA (increase after)— 后增加地址
- IB (increase before)— 先增加地址
- DA(decrease after)— 后减少地址
- DB(decrease before)— 先减少地址
基址寄存器 :存放内存的起始地址
!:最后更新基址寄存器的值
Reglist :寄存器列表
- 多个寄存器从小到大,中间用“,”隔开{r0, r1, r2} 或 {r0, r7- r10}
- 寄存器编号大的 —- 内存高地址; 寄存器编号小的 —- 内存低地址
^ : 它存在:
- Reglist 没有PC寄存器的时候,操作的寄存器是用户模式下的寄存器
- 在LDM指令中,有PC的时候,数据传输时,会将SPSR的值拷贝到CPSR,用于异常返回
栈操作指令
进(压)栈: stmfd sp! , {寄存器列表}
出栈:ldmfd sp! , {寄存器列表}
进行栈操作前,必须先设置sp的值
进栈和出栈方式一样,ATPCS标准规定满减栈
几种栈操作方式:
CPSR/SPSR操作指令
读操作:
MRS Rn, CPSR/SPSR
将状态寄存器的值,读到通用寄存器
写操作:
MSR CPSR/SPSR, Rn
将通用寄存器的值,写到状态寄存器
指令流水线
(instruction pipeline)—- 以三级流水线为例
- 预取 (fetch)— PC寄存器工作在预取阶段
- 译码 (decode)
- 执行 (execute)
伪指令
**链接地址:**编译器在编译程序的时候,指定的地址,指定这个地址的目的是期望程序在指定的链接地址执行
**运行地址:**程序实际在内存中的运行地址
伪指令:为了方便程序员使用,编译器设计的指令,该指令ARM核无法识别,需要编译器进行翻译
常用的伪指令
LDR r0, =0x999 —-> LDR r0, [PC]
LDR r0, **=Label ** —-> LDR r0, [PC ,# 固定偏移量]
读取Label标签表示的地址,存放到r0中,这个标签最终表示的地址受链接地址的影响
编译器:根据指定的代码开始地址,算出Label标签对应地址,存放在内存中,,通过内存访问指令,根据PC + 固定偏移量,读取内存值。换言之,代码编译结束时候,PC + 固定偏移量表示的内存地址中存放的数据就已经确定死了
LDR r0, Label —-> LDR r0, [PC, # 固定偏移量]
读取Label标签表示的地址的内容
ADR r0, Label —> 根据当前PC的值 +/-偏移量,动态获取当前Label表示的内存地址
问题:如何判别代码在实际内存中运行的地址?
- ADR r0,_start 可以知道,因为它根据PC值,动态获取
- LDR r0, =_start 无法知道,这条指令不论在哪运行,r0的值都是固定的(取决于指定的链接地址)
汇编与C混合编程
汇编调用C语言
ATPCS:
参数传递:函数参数传递的时候,前4个参数通过r0 - r3来传递,超过4个的参数通过栈传递
函数返回:函数返回值通过 r0 带回
注意:!!!调用C语言之前,必须先设置SP
1
2
3
4mov r0, #3
mov r1, #5
ldr sp, =0x40001ff0 @!!! 必须设置sp
bl add //add是.c文件中的一个函数
C语言中内嵌汇编 — GCC编译器为例
1 |
|
1 |
|
C变量的引用, 从输出列表到输入列表开始编号:第一个C变量%0,第二个C变量 %1….
Volatile关键字 (重点)
gcc优化
优化思想
- 如果之前已经把变量的对应的内存数据读到寄存器中,当需要再次读取该变量所对应的内存数据的时候,为了提高效率,编译器会直接使用上一次寄存器中的值,而不再重新从内存读值
优化级别
- O1:一级优化
- O2(speed)/Os(size):二级优化
- O3:三级优化
优化的问题
如果内存中的值,已经被其他的执行单元(比如其他线程、中断)进行了更改,而优化后的代码,每次从寄存器读值,就会造成寄存器中的值和内存中的值不一致的问题
Volatile的作用
volatile修饰一个变量,防止编译器优化(本质),告诉编译器每次使用该变量时,必须从变量所在的内存重新读值
有中断处理函数的代码,使用了全局变量,需要注意什么问题?
定义全局变量的时候,需要加volatile修饰
ARM 编程命令
1 |
|
PWM定时器
PWM 脉冲宽度调制
FS4412开发板中,PWM的时钟频率为 100MHz
三星的计数器是递减计数器
串口通信接口
电平标准 — 外部的硬件电路决定
RS232 和 RS485 (重点)
RS232 vs RS485
- RS232 三个线(RXD,TXD,GND), 全双工,交叉连接
RS485 三个线(A,B,GND), 半双工,A-A;B-B - RS485 传输距离更远
- RS485 速度更快
- RS485 抗干扰更好,RS485采用传输差分信号,A-B线电压差确定传输的一位数据
- RS232 1: -(3
15v) 0: +(315)v , RS485 1: +(26v) 0: -(26v) - RS485支持多点通信,RS232不支持
UART — 芯片内部的异步收发器
- UART— 芯片内部的串口通信标准的数据发送格式
UART (Universal Asynchronous Receiver and Transmitter)—- 通用异步收发器
串口:数据是一位一位的发
并口:数据是多位一起发
UART串口通信参数:
- 波特率 :双方的通信速度
- 数据位:发送数据的位数 —- 先发送低位
- 停止位:表示收发停止
- 校验位:检查数据是否错误,奇偶校验(只能发现一位错误)
- 流量控制:需要硬件支持
串口通信数据错误:
- 电平标准
- 波特率
- 数据位
- 停止位
- 校验位
系统三大总线
数据、地址、控制
DMA模式
Direct Memory Access:设备直接访问内存,不需要经过CPU
异常处理
异常处理流程
ARM核硬件上自动做的事情
CPSR —-拷贝—-> SPSR
设置CPSR对应位:
- 进入ARM态
- 进入对应的异常模式
- 禁止中断
保存PC的值到异常模式的 LR
将PC设置为异常向量表的对应位置:
偏移量 异常 0x1C FIQ 0x18 IRQ 0x14 (Reserved) 0x10 Data Abort 0x0C Prefetch Abort 0x08 Software Interrupt 0x04 Undefined Instruction 0x00 Reset <Vector Table>
异常优先级、对应处理器模式、返回地址:
优先级 异常 模式 返回地址 1 最高 Reset SVC - 2 Data Abort Abort LR-8 3 FIQ FIQ LR-4 4 IRQ IRQ LR-4 5 Prefetch Abort Abort LR-4 6 Software Interrupt SVC LR 7 最低 Undefined Instruction Undefined LR
程序员需要做的事情
设置异常向量表(在异常向量表中,写跳转指令,跳转指定异常处理函数)
告诉ARM核异常向量表的基地址
- cortex-A系列以前,异常向量表可以存放在 0x0000,0000 (低地址)或 0xffff,0000(高地址);cp15(协处理器).c1(寄存器)决定异常向量表存放在高地址还是低地址
- cortex-A系列以后,异常向量表可以在任意位置,cp15.c12保存异常向量表的基地址
编写异常处理函数:
设置SP寄存器
将通用寄存器R0~R12,进行压栈保护
异常处理
异常返回
- 恢复R0~R12 (出栈)
- 恢复CPSR
- 恢复PC
中断处理
CPU主要两种工作模式:轮询和中断
- 轮询:不断询问是否要处理事情,但很多时候不满足条件,浪费了CPU的时间
- 中断方式:当需要CPU处理的时候,产生一个信号,打断CPU正在做的事情,让CPU处理当前的事情,处理完后,回到打断之前的地方继续执行
中断处理注意点:
- 中断打断其他程序的执行,所以中断处理要尽可能的快,不能中断处理耗时过长
- 中断打断程序的执行,所以中断处理的时候,需要先保存现场(CPU的状态和CPU内部寄存器的值:压栈保存),中断处理结束,恢复现场
中断的五大概念(重点)
中断源:产生中断的源头
中断号:SOC芯片厂家对SOC芯片内部中断源的编号
中断处理函数:中断产生后,需要调用执行的函数
中断控制器:控制中断的优先级、中断是否被允许产生
内部中断和外部中断:
内部中断:SOC芯片内部控制器产生的中断
外部中断:SOC芯片外部管脚通过电平触发产生的中断
高电平触发、低电平触发
上升沿触发、下降沿触发
双边触发
学习使用的FS4412开发板,是三星设计的SOC,一共有160个中断源
中断处理过程
- 中断处理过程:
- MPCore 分布式中断控制系统
- GIC: (Generic Interrupt Controller)
A/D转换器
1 |
|
IIC
IIC总线
概念
IIC(Inter-Integrated Circuit,又称 IIC)—- 由PHILIPS公司开发的串口总线,用于连接微控制器及其外围设备
特点
- 只有两条总线线路:一条串行数据线**(SDA),一条串行时钟线(SCL)**
- 每个连接到总线的器件都可以使用软件根据它的唯一的地址来识别
- 传输数据的设备间是简单的主/从关系
- 主机可以用作主机发送器或主机接收器
- 它是一个真正的多主机总线,两个或多个主机同时发起数据传输时,可以通过冲突检测和仲裁来防止数据被破坏
- 串行的8位双向数据传输,先发送高位,位速率在标准模式下可达100kbit/s,在快速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s
IIC总线信号类型
有3种类型信号:开始信号、结束信号、响应信号
- 开始信号(S):SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据
- 结束信号(P):SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据
- 响应信号(ACK):接收器在接收到8位数据后,在第9个时钟周期,拉低 SDA 电平
注意: SDA 上传输的数据必须在 SCL 为高电平期间保持稳定,SDA 上的数据只能在 SCL 为低电平期间变化
IIC 总线的数据传输格式
发送到 SDA 线上的每个字节必须是8位的,每次传输可以发送的字节数量不受限制。首先传输的是数据的最高位(MSB)
启动一个传输时,主机先发送 S 信号,然后发出8位数据。这8位数据中前7位为从机的地址,第8位表示传输的方向(0表示写操作,1表示读操作)。从机收到后会发出一个 ACK 信号
Read Sequence
Write Sequence
I^2^C Terms
Signal | Description |
---|---|
S | Start Condition: SDA goes from high to low while SCL is high |
AD | Slave I^2^C address |
W | Write bit (0) |
R | Read bit (1) |
ACK | Acknowledge: SDA line is low while the SCL line is high at the 9 ^th^ clock cycle |
NACK | Not-Acknowledge: SDA line stays high at the 9th clock cycle |
RA | MPU-60X0 internal register address |
DATA | Transmit or received data |
P | Stop condition: SDA going from low to high while SCL is high |
操作芯片的步骤:
- 通过原理图或手册,确定通信接口
- 确定是IIC或者是其他通信方式之后,找到从机地址(或者其他对应的地址信息)
- 查看手册确定寄存器操作方式(读写)
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!