计算机基础
计算机的组成
输入设备、输出设备、存储器、运算器、控制器
- 输入设备:将其他信号转换为计算机可以识别的信号(电信号)。
- 输出设备:将电信号(0、1)转为人或其他设备能理解的信号。
- 运算器:CPU对信息处理和运算的部件,常进行算术运算和逻辑运算,其核心是算术逻辑单元ALU,CPU中用各种各样的数字电路搭配成各种各样的运算电路,如:加、减法等。
- 控制器:整个计算机的指挥中心
- 存储器:存放程序和数据的部件,也是计算机能够实现"存储程序控制"的基础。
程序:指令的有序集合
ROM: flash (EMMC)、磁盘空间 、掉电不丢失数据
RAM: 内存、掉电丢失数据
IO逻辑(输入/输出)
计算机系统中的高低电平逻辑1和0,数据在计算机中的存储、传输、运算都是以二进制形式进行的。
数据的传输通过总线真正传递的是电信号,高低电平(0、1)。运算在电路中进行,集成电路中运算。
电压:电压差 电势差
模拟信号:模拟信号是连续的,模拟信号可以是任意数值状态;
数字信号:数字信号是离散(不连续)的,数字信号只有"0"和"1"两种状态
三级存储结构
cache: 速度快、价格贵、容量小、断电丢失、CPU可以直接访问。存储当前正在执行的程序中的活跃部分,以便快速地向CPU提供指令和数据
基本原理:
高速缓存Cache详解(西电考研向)_多路组相联-CSDN博客
主存储器:速度、价格、容量介于CACHE和辅助存储器之间、断电丢失、CPU可以直接访问。存储当前正在执行的程序和数据
辅助存储器:速度慢、价格低、容量大、断电不丢失、cpu不可以直接访问。存储暂时不运行的程序和数据,需要时再传送到主存
Cache对程序员来说一般会有透明性,也就是程序员其实是看不到Cache的,因此不能对它进行操作。
总线
总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号(系统总线)。
数据总线
(1)是CPU与内存或其他器件之间的数据传送的通道。
(2)数据总线的宽度决定了CPU和外界的数据传送速度。
(3)每条传输线一次只能传输1位二进制数据。如: 8根数据线一次可传送一个8位二进制数据(即一个字节)。
(4)数据总线是数据线数量之和。
地址总线
(1)CPU是通过地址总线来指定存储单元的。
(2)地址总线决定了cpu所能访问的最大内存空间的大小。如: 10根地址线能访问的最大的内存为1024位二进制数据(1024个内存单元)
(3)地址总线是地址线数量之和。
控制总线
(1)CPU通过控制总线对外部器件进行控制。
(2)控制总线的宽度决定了CPU对外部器件的控制能力。
(3)控制总线是控制线数量之和。
总结:
数据总线的宽度决定CPU与其他元器件一次最大传送的数据量;
地址总线的宽度决定CPU的寻址能力;
控制总线决定CPU对其他元器件的控制能力。
例子
DMA总线
DMA(Direct Memory Access)即直接存储器访问,使用DMA总线可以不通过CPU直接在存储器及外设之间进行数据传递。(不做控制功能)
单片机基础
单片机简介
单片机(Single-Chip Microcomputer)是一种集成电路芯片。
微控制单元(Microcontroller Unit;MCU) ,又称单片微型计算机(Single Chip Microcomputer )或者单片机,其采用集成电路技术将具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、定时器/计时器、多种I/O口和中断系统等功能集成到一块硅片上。可以说单片机就是一个小而完善的微型计算机系统。
SoC(System on Chip),片上系统SoC的定义多种多样,由于其内涵丰富、应用范围广,很难给出准确定义。一般说来, SoC称为系统级芯片,也有称片上系统,意指它是一个产品,是一个有专用目标的集成电路,其中包含完整系统并有嵌入软件的全部内容。同时它又是一种技术,用以实现从确定系统功能开始,到软/硬件划分,并完成设计的整个过程。SoC就是定制功能版本的MCU
单片机型号
51单片机(8051)
STC89C51 宏晶科技 STC AT89C51 ATMEL
宏晶科技公司
ATMEL公司
32单片机
STM32 意法半导体ST GD32 兆易创新GD
32位处理器 - 处理数据宽度是32位的。
处理器位数:CPU单次运算最大处理的数据位数
意法半导体
兆易创新
开发板/最小系统板
开发板通常是学习用途,功能比较全,接口丰富,是用于研发、研究、学习的一块板子。
最小系统板是个核心板,集成了核心的通用功能,可以根据需求定制各种不同的底板,通用性较好。再者核心板作为一块独立的模块被分离出来,也降低了开发的难度,增加了系统的稳定性和可维护性通常用于做项目,也可以作为模块在产品里在直接用。
STM32
简介
https://www.st.com.cn/zh/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html
STM32 | 产品 | STM32 | MCU单片机 | 意法半导体STM | STMCU中文官网
STM32是意法半导体公司生成一款32位的微控制器。
STM32功能强大、性能优异、片上资源丰富、功耗低、是一款经典的嵌入式微控制器。
命名规范
|------|-----------------------------------------------------------------------------------------------------------|
| ST | 意法半导体 |
| M | 微控制器 |
| 32 | 32位处理器 |
| 类型 | F 通用/基础型 foundation G 多用途型 general-purpose L 低功耗 low power H 高性能 High performance S 简单/标准型 Standard U超低功耗 |
| 系列 | 0 精简系列 1/2/3 增强系列 4/7 高性能系列 |
| 子型号 | 00/01/02/03/05/07 |
| 引脚数量 | K/6-32脚 C/8-48脚 R-64脚 V-100脚 Z-144脚 A-168脚 I-176脚 B-208脚 N-216脚 |
| 存储量 | 6 : 32KB 8 : 64KB B :128KB C :256KB D : 384KB E : 512KB G : 1MB I : 2MB |
| 封装 | U - UQFN封装 T - TQFP封装 |
| 工作温度 | 6 - -40 ~ 85度 |
STM32的优势
STM32 | 产品 | STM32 | MCU单片机 | 意法半导体STM | STMCU中文官网
产品型号丰富,可选择性强;
运算速度快,功耗低;
处理器外设接口丰富;
库函数开发体系学习资料多,应用广泛。
ARM体系结构
STM32G030采用ARM Cortex-M0+内核架构
STM32U575采用ARM Cortex-M33内核架构
M33内核详情:
https://www.st.com.cn/content/st_com/zh/arm-32-bit-microcontrollers/arm-cortex-m33.html
想了解其他架构请点击下方链接
冯·诺依曼架构&哈佛架构(嵌入式学习)_冯诺依曼架构-CSDN博客
认识ARM
- ARM代表一个公司
ARM是一家公司,ARM公司是一家芯片知识产权(IP)供应商,它与一般的半导体公司最大的不同就是不制造芯片且不向终端用户出售芯片,而是通过转让设计方案,由合作伙伴生产出各具特色的芯片。
- ARM可以表示一些处理器的统称
早期经典处理器:ARM7 ARM9 ARM11
后续处理器开始以cortex命名
Cortex-X系列
超高性能
Cortex-A系列
针对开放式操作系统的高性能处理器
应用于智能手机、数字电视、智能平板等高端运用
Cortex-R系列
提供非常高的性能和吞吐量,同时保持精准的时序属性和可预测的中断延时,通常用于时序关键的应用中
针对实时系统、满足实时性的控制需求
应于汽车制动系统、动力系统等
Cortex-M系列
为单片机驱动的系统提供了低成本优化方案
应用于传统的微控制器市场、智能传感器、汽车周边、物联网设备等
- ARM表示一种指令集
指令:能够指示处理器执行命令称为指令 + - << >>
指令集:处理器能够识别的指令的集合称为指令集
ARM指令集:所有指令(机器码),都专用32bit存储空间,代码灵活,指令简洁,执行ARM指令PC每次自加4
Thumb指令集:所有指令(机器码),都专用16bit存储空间,代码灵活,指令简洁,执行ARM指令PC每次自加2
ARM的命名有指令集架构、 处理器架构、 处理器型号三类命名规则
架构:
arm-v4,arm-v5,arm-v6,arm-v7(32Bits),arm-v8(64Bits)
架构指支持的汇编指令集(不同架构,汇编指令集不同)
问:目前主流处理器架构?
ARM架构、Intel X86/X64架构、MIPS架构、RISC-V(开源)
指令集(RISC和CISC)
精简指令集(RISC)-->微处理器
只保留常用的的简单指令,硬件结构简单,复杂操作一般通过简单指令的组合实现,一般指令长度固定,且多为单周期指令。
RISC处理器在功耗、体积、价格等方面有很大优势,所以在嵌入式移动终端领域应用极为广泛
举例:如有加法运算器 ,没有乘法运算器 3*3 ---》3+3+3
复杂指令集(CISC)-->电脑CPU
不仅包含了常用指令,还包含了很多不常用的特殊指令,硬件结构复杂,指令条数较多,一般指令长度和周期都不固定
CISC处理器在性能上有很大优势,多用于PC及服务器等领域
Cortex-M33的寄存器
(1)通用寄存器
R0-R12 :13个通用寄存器。其中 R0-R7为低端寄存器,可作为16位或32 位指令操作数,R8-R12为高端寄存器,只能用作32位操作数
R13 :栈指针寄存器 SP(the stark pointer),它用于访问堆栈内存(例如,堆栈PUSH或POP操作)。
R14 :链接寄存器LR(the link register),用于存储子程序或者函数调用的返回地址
R15 :程序计数器PC(the program counter register)存储下一条将要执行的指令的地址。
(2)特殊寄存器
xPSR :组合程序状态寄存器,该寄存器由三个程序状态寄存器组成
应用程序状态寄存器 (APSR):保存程序计算结果的状态标志 N负数标志 Z零标志 C进位借位标志 V溢出标志
中断程序状态寄存器 (IPSR):包含当前ISR(中断服务程序)的异常编号
执行程序状态寄存器 (EPSR):包含Thumb状态位
CONTROL :控制寄存器
控制处理器处于线程模式时,使用哪个堆栈
=0,使用MSP 处理器模式时,固定使用MSP
=1,使用PSP
CPU运行原理
根据下载的程序运行,程序是指令的有序集合
一条指令(机器码)的执行通常分为三个阶段:
1)取指:控制器将PC寄存器中的值发送给内存,内存将对应地址中的指令(机器码)传送回CPU的指令寄存器IR中
2)译码:指令译码器对IR中的指令进行识别,将指令(机器码)解析(翻译)成具体的运算操作(+/-/*...)
3)执行:控制器控制运算器中对应的运算单元进行运算,运算结果写入寄存器
注意:PC每取地址一次,自加一次。PC的值自动增加使PC指向内存中的下一条指令
思考:
1.运算器不同,处理指令不同。不同的处理器上如何运行同一个c语言程序?
- 假设指令集有乘法指令,结果并没有乘法运算器,怎么办?
指令流水线
指令的执行是按照流水线
取指--》取指器 根据PC值取指令
译码--》译码器
执行--》执行器
以上三个器件,都是单周期的器件,三个器件的工作是独立
指令1 指令2 指令3 指令4 指令5
1 取指
2 译码 取指
3 执行 译码 取指
4 执行 译码 取指
5 执行 译码 取指
6 执行 译码
7 执行
PC永远指向当前取指指令的地址,一旦取到指令,pc后移4byte,保存下一条指令地址。
指令流水线机制的引入确实能够大大的提升指令执行的速度,但在实际执行程序的过程中很多情况下流水线时是无法形成的,比如芯片刚上电的前两个周期、执行跳转指令后的两个周期等。
所以指令流水线的引入以及优化只能使平均指令周期不断的接近1而不可能真正的达到1,且流水线级数越多芯片设计的复杂,程度就越高,芯片的功耗就越高。
编译原理
gcc编译流程分为四个步骤:
1:预编译处理:主要是进行宏替换和拷贝包含的头文件到本文件
2:编译:检查代码的规范性,是否有语法错误等,没错的话将代码编译成汇编语言
3:汇编:将汇编文件转换成二进制目标文件
4:链接:链接库函数生成可执行文件
机器码(二进制)是处理器能直接识别的语言,不同的机器码代表不同的运算指令,处理器能够识别哪些机器码是由处理器的硬件设计所决定的,不同的处理器机器码不同,所以机器码不可移植
汇编语言是机器码的符号化,即汇编就是用一个符号来代替一条机器码,所以不同的处理器汇编也不一样,即汇编语言也不可移植
C语言在编译时我们可以使用不同的编译器将C源码编译成不同架构处理器的汇编,所以C语言可以移植
ARM的数据类型
char(字节):8位
halfword(半字):16位
word(全字):32位
doubleword:64位(cortex-a)
quadword:128位(ARM-v8)
ARM-v7架构:32bit处理器
ARM-v8架构:64bit处理器
处理器的32位和64位什么含义?
32位:一条指令可以进行32位数据的运算
64位:一条指令可以进行64位数据的运算
字节序
大端对齐
低地址存到高地址,高地址存放到低地址
小端对齐
低地址存到低地址,高地址存放到高地址
注:ARM一般使用的是小端对齐
ARMv8-M的指令集
ARM处理器支持两种指令集:ARM 和 Thumb。
ARM指令集 32位精简指令集; 指令长度固定;
降低编码数量产生的耗费,减轻解码和流水线的负担;
Thumb指令集指令宽度16位;
Thumb指令集是ARM指令集的一个子集;
与32位指令集相比,大大节省了系统的存储空间;(密度高)
Thumb指令集不完整,所以必须配合ARM指令集一同使用。
•Arm Cortex-M处理器中使用的指令集称为Thumb指令集。这个指令集包含一系列扩展。Cortex-M33的指令集是Cortex-M23处理器指令集的超集。为了便于项目迁移,以前的Cortex-M处理器中可用的所有指令在Armv8-M架构中也可用。一般来说,Cortex-M处理器中的指令集提供了向上兼容的特性。例如:Cortex-M23的指令集是Cortex-M0/M0+处理器中指令集的超集。
•Cortex-M33的指令集是Cortex-M3和Cortex-M4处理器指令集的超集。除了双精度浮点指令和缓存预加载指令外,Cortex-M7处理器支持的所有指令都可以在Cortex-M33中使用。(注:Cortex-M33没有双精度FPU选项或缓存存储器控制器功能。)向上兼容性是Cortex-M处理器家族的一个重要特征,因为它提供了软件的可重用性和可移植性。Armv8-M基线指令集中使用的许多指令的大小都是16位。这使得高代码密度成为可能。对于一般的数据处理和控制任务,程序代码可以主要由16位指令(而不是32位)组成,以减少程序内存的大小。
问:Thumb指令集和ARM指令集的区别?
系统架构
系统主要由以下几个模块组成 :
● 二个主模块 :
-- Cortex-M0+ 内核及先进高性能总线 (AHB bus)
-- 通用 DMA (通用的直接存储器存取)
● 三个从模块 :
-- 内部 FLASH
-- 内部SRAM
-- AHB和AHB到APB的连接桥,所有的外设都挂在APB总线上
问:flash和SRAM的区别?
Flash存储器是一种非易失性存储器,可以在掉电之后保存数据,通常用于存储程序代码。Flash存储器的可写入次数有限,且需要执行擦除操作才能写入新的数据,因此,在使用过程中需要注意擦写周期和数据备份问题。
SRAM存储器则是一种易失性存储器,具有相对较快的读写速度和无限的读写次数,但掉电时将会丢失所有内容。SRAM存储器主要用于暂存数据和临时变量,读写操作由CPU直接完成,访问速度较快。
单片机的Flash存储器和SRAM存储器通常都嵌入在单片机芯片内部,能够方便地实现对程序和数据的读写操作。通常,编译器会将程序代码烧录到Flash存储器中,并使用SRAM存储器来存储变量、函数堆栈和其他临时数据。
问:什么是外设?如何理解片上外设 (片上资源) ?
与传统的外设不同,片上外设通常具有以下优点:
高效性:片上外设能够与主处理器实现高速的数据传输,响应时间短,执行效率高。
集成度高:片上外设多个模块都嵌入到处理器芯片内部,极大地降低了PCB面积和电路复杂度。
低功耗:处理器和片上外设采用相同的工艺,能够满足高密度和低功耗的需求。
可靠性高:提高了整体系统的可靠性和稳定性,也降低了电磁干扰的可能。
问:AHB和APB的区别?
AHB(高级高性能总线)是高速总线,是一种系统总线,它主要负责连接处理器、DMA等一些内部接口。AHB系统由主模块、从模块和基础结构3部分组成,整个AHB总线上的传输都由主模块发出,由从模块负责回应。
APB(高级外设总线)是低速总线,它主要负责连接外围设备,它又分为APB1和APB2,它的总线架构不像 AHB支持多个主模块,在APB里面唯一的主模块就是APB桥。
如何开发
开发环境搭建
STM32CubeMX
ST公司出品 工具链接
工具链接 https://www.st.com/zh/development-tools/stm32cubemx.html
STM32CubeMX 是一种图形工具,通过分步过程可以非常轻松地配置STM32微控制器和微处理器,以及为Arm® Cortex®-M内核或面向Arm® Cortex®-A内核的特定Linux®设备树生成相应的初始化C代码。
作用及功能:
(1)工程项目搭建和配置
(2)直观选择微控制器
(3)图形化引脚功能配置、引脚冲突提示
(4)动态配置时钟树
(5)动态设置参数和初始化
Keil uVision5
Keil μVision 5 也称MDK-ARM、Realview MDK
MDK ------》Microcontroller Development Kit
MDK包含以下几个部分:
μVision5:一种集成开发环境,提供了多种不同的功能,如编辑器、编译器、调试器等。
ARM编译器:一种嵌入式ARM C / C++编译器,可在多种不同的微控制器平台上运行。
Device Family Pack:一种特定于属于不同微控制器平台/系列/型号的软件包,包括库文件、设备描述文件等。
Debugger:一款高级调试器,支持多种不同的调试功能,如单步调试、断点调试、内存映射等。
具体安装步骤见以下文件
软件介绍
【Keil5教程及技巧】耗时一周精心整理万字全网最全Keil5(MDK-ARM)功能详细介绍【建议收藏-细细品尝】-CSDN博客
汇编语言
c语言中哪些代码可以生成汇编指令?
1》带#号预处理,辅助编译器怎么编译,编译什么内容
预处理器是C语言编译器的一个组成部分,它在编译代码之前对代码进行处理。预处理器指令以#号开头,告诉编译器在编译代码之前执行一些操作。其中,#include指令用于将头文件包含到源代码中,#define指令用于定义宏。预处理器的主要作用是辅助编译器编译代码。 例如在编译时将头文件中的函数声明插入到源代码中,或者将宏替换为实际的值。预处理器处理完代码后,编译器将生成目标代码,最终生成可执行文件。
2》带;号的语句,可以编译生成指令
在编译器中,分号是语句结束的标志,编译器会将分号之前的语句编译成指令并添加到指令序列中。
汇编整体分类
1》指令:编译完生成一条机器码存储在内存单元当中,CPU执行时能完成对应的操作(类似于C中的语句)
2》伪操作(相当于c中的#的内容,告诉编译器怎么编译):不会生成机器码也不会占用内存,其作用是告诉编译器怎样编译(类似于C中的预处理指令)
3》伪指令:不是指令,编译器在编译时将其替换成等效的指令 (如:cpu中没有乘法器,对应没有乘法指令,3*3 ---》用加法器实现3+3+3,替换实现)
汇编中注释代码用@或;注释一行 ,/* */注释一段代码
指令分类
1.数据处理指令: 对数据进行逻辑、算数运算
2.跳转指令: 实现程序的跳转,实质是修改PC
3.Load/Store指令: 对内存的读写操作
4.状态寄存器传送指令: 对状态寄存器进行读写操作
5.异常中断产生指令: 触发软中断,常用于内核的系统调用 //SWI:软中断
6.协处理器指令: 操作协处理器的指令
//如3*3 ---》用加法器实现3+3+3,比较慢。我们可以外接一个协处理器(乘法器)(每个协处理器的功能比较单一),协处理器指令就是操作这个协处理器的,用的比较多的cp15协处理器。
程序编写
` `AREA` `STACK,` `NOINIT,` `READWRITE`
`__initial_sp`
`AREA` `RESET,` `DATA,` `READONLY`
`__Vectors DCD __initial_sp `
`DCD` `main`
`AREA |.text|,` `CODE,` `READONLY`
`ENTRY`
`main`
`loop`
`B` `loop`
`END`
`
解释:
-
AREA STACK, NOINIT, READWRITE: 这一行指定了一个名为STACK的内存区域,它用于存储堆栈。NOINIT表示这个内存区域在程序启动时不需要初始化,READWRITE表示这个内存区域可以被读取和写入。
-
__initial_sp: 这一行定义了一个名为`__initial_sp`的符号,它表示堆栈指针的初始值。在这里,它被定义为堆栈的起始地址。通常,这个符号会在链接脚本中进一步定义为实际的堆栈内存区域的起始地址。
-
AREA RESET, DATA, READONLY: 这一行指定了一个名为RESET的内存区域,用于存储重置向量表。DATA表示这个区域包含数据,READONLY表示这个区域只能被读取。
-
__Vectors DCD __initial_sp: 这一行定义了一个重置向量表,用于指示程序启动时应该执行的操作。`DCD`表示存储一个双字(32位)的数据。在这里,第一个双字存储的是堆栈指针的初始值,即__initial_sp。
-
DCD main: 这一行将程序的入口地址(main函数)添加到重置向量表中。这意味着程序在启动时将跳转到main函数开始执行。
-
AREA |.text|, CODE, READONLY: 这一行指定了一个名为.text的内存区域,用于存储代码。CODE表示这个区域包含代码,READONLY表示这个区域只能被读取。
-
ENTRY main: 这一行指定了程序的入口点为main函数。这意味着程序将从`main`函数开始执行。
-
loop B loop: 这一行是一个无限循环,它会不断地跳转到`loop`标签所在的位置,导致程序永远循环执行这条指令。
-
END: 这一行表示程序的结束。
数据搬移指令
MOV R0,#1 ;MOV搬移指令 相当于R0=1`
`MOV R1,R0 ; R1=R0`
`
` `AREA` `STACK,` `NOINIT,` `READWRITE`
`__initial_sp`
`AREA` `RESET,` `DATA,` `READONLY`
`__Vectors DCD __initial_sp `
`DCD` `main`
`AREA |.text|,` `CODE,` `READONLY`
`ENTRY`
`main`
`MOV` `R0,#1`
`MOV` `R1,R0`
`loop`
`B` `loop`
`END`
`
验证
注意:需要连接上LINK,记得先装驱动
如果是立即数,前边必须加#
什么是立即数?
立即数的概念
立即数(Immediate Value)是在计算机编程和汇编语言中常见的一种操作数类型。它是指在指令中直接编码的数值,而不是存储在内存或寄存器中的值。立即数通常是常量或者由程序员明确指定的值,可以直接参与到算术运算、逻辑运算或者数据处理中,无需从其他地方读取。
寻址方式:
立即数是保存在指令中的数,取指令的同时将值取过去,和普通变量的区别是,变量保存在内存中的数据,需要单独取值运算。
立即数的本质:立即数是包含在指令当中的数据(即属于指令的一部分)
立即数的优点:读取指令的同时也将立即数读取到了内存中,速度快
立即数的缺点:数量有限
注:使用mov 给寄存器里面存放值的时候,#号后面需是有效数(1:立即数,2:取反之后是立即数),如果不是立即数需要用ldr指令进行存放。
LDR R0,=0X12345678`
`
算术运算指令
MOV R0,#1`
` MOV R1,#2`
` ADD R2,R1,R0;加法---->R2=R1+R0;`
` SUB R2,R1,R0;减法---->R2=R1-R0;`
` MUL R2,R1,R0;乘法---->R2=R1*R0;`
`
逻辑运算指令
MOV R0,#1`
` MOV R1,#2`
` MOV R2,#3`
` AND R3,R1,R0 ;与---->R3=R1&R0`
` ORR R3,R1,R0 ;或---->R3=R1|R0`
` EOR R3,R1,R0 ;异或---->R3=R1^R0`
` LSL R3,R2,R1 ;左移---->R3=R2<<R1`
` LSR R3,R2,R1 ;右移---->R3=R2>>R1`
`
xPSR状态寄存器
N (Negative) - 负标志:如果最近的算术操作的结果是负数,此位被置位。
Z (Zero) - 零标志:如果最近的算术或逻辑操作的结果是零,此位被置位。
C (Carry) - 进位标志:如果最近的加法或乘法操作产生了进位,或者减法操作产生了借位,此位被置位。
V (Overflow) - 溢出标志:如果最近的有符号整数运算产生了溢出,此位被置位。
Q (Saturation) - 饱和标志:如果一个饱和运算发生了饱和,此位被置位(仅在支持饱和运算的指令集架构中有效)。
GE (Greater Equal) - 大于等于标志:这是四个位(GE[3:0]),用于比较两个操作数,分别对应无符号和有符号的比较结果。
除了这些条件码标志,APSR还包含一些控制位,如:
I (Interrupt Disable) - 中断禁止标志:如果被置位,硬件中断被屏蔽。
F (Fast Interrupt Disable) - 快速中断禁止标志:如果被置位,快速中断(FIQ)被屏蔽。
T (Thumb) - Thumb标志:如果被置位,处理器处于Thumb状态;否则,它处于ARM状态。
MOV R0,#1 ;指令`
` MOV R1,#2 ;指令`
` ;默认情况下数据运算不会对条件位(NZCV)产生影响,我们可以在指令后添加后缀'S'`
` SUBS R2,R0,R1 ;加S并使用减法指令产生负数,验证N位,发现被置位`
` ;测试Z和减法C位`
` MOV R0,#1 ;指令`
` MOV R1,#1 ;指令`
` SUBS R2,R0,R1 ;加S并使用减法指令产生0,验证Z位,发现Z和C都被置1,因为减法时产生借位C会被置0,结果0没有借位`
` ;测试加法C位`
` MOV R0,#0XFFFFFFFE ;0XFFFFFFFE不是立即数,但是编译没有报错,看一下仿真里编译窗口里,MOV被替换为MVN了,数也变了`
` MOV R1,#3`
` ADDS R2,R1,R0 ;加法指令产生了进位(注意这里是32位),C位被置1`
` ;验证V`
` LDR R0,=0X7FFFFFFE ;0X7FFFFFFE加了3之后产生溢出`
` MOV R1,#3 ;0X7FFFFFFE加了3之后产生溢出`
` ADDS R2,R1,R0 ;加法指令产生了进位(注意这里是32位),V位被置1`
`
分支/跳转指令
B (Branch):这是一个基本的无条件跳转指令,用于将程序计数器(PC)设置为指令中指定的地址,从而跳转到程序的其他部分执行。语法:B <label>,其中<label>是跳转的目标标签。
BL (Branch with Link):这个指令不仅跳转到指定的地址,还将当前指令的下一个指令的地址(即返回地址)保存到链接寄存器(LR,通常是R14)中。这允许在跳转后通过返回指令(如MOV PC, LR)返回到跳转前的位置。语法:BL <label>。
不保存返回地址
jump `
` LDR R3,=0X11111111`
` LDR R2,=0X22222222`
` LDR R1,=0X33333333`
` LDR R0,=0X44444444`
`main`
` LDR R0,=0X11111111`
` LDR R1,=0X22222222`
` LDR R2,=0X33333333`
` LDR R3,=0X44444444`
` LDR R4,=0X08000008`
` B jump`
`
保存返回地址
jump `
` LDR R3,=0X11111111`
` LDR R2,=0X22222222`
` LDR R1,=0X33333333`
` LDR R0,=0X44444444`
` MOV PC,LR`
`main`
` LDR R0,=0X11111111`
` LDR R1,=0X22222222`
` LDR R2,=0X33333333`
` LDR R3,=0X44444444`
` LDR R4,=0X08000008`
` BL jump`
` LDR R0,=0X11111111`
` LDR R1,=0X22222222`
` LDR R2,=0X33333333`
` LDR R3,=0X44444444`
`
load/store指令
单寄存器操作指令 ldr / str
对内存的读写操作,将运算结果从cpu写到内存
LDR` `R1,=0XFF00FF00`
`LDR` `R2,=0X20000000`
`STR` `R1,[R2] ;把R1当中的数存到R2的地址中`
`LDR` `R3,[R2] ;把内存R2地址中的数据读取到CPU` `R3寄存器中`
`
索引形式
1> 前索引
MOV R1,#0XFFFFFFFF`
`MOV R2,#0X20000000`
`STR R1,[R2,#8]` `;基址加变址寻址`
`
讲原理加法器 R2加8,如果STR R1,[R2] 也走加法器 R2+0
所以可以根据汇编的语法格式反思CPU的硬件设计
比如 int a[10] 编译器只知道a的首地址,其他的没统计,如a[6] 找地址可以a
2>后索引
MOV R1,#0XFFFFFFFF`
`MOV R2,#0X20000000`
`STR R1,[R2],#4` `; 将R1寄存器的内容存到【R2】地址,然后R2=R2+4`
`
这样做的目的可以做连续存储,压栈时用的比较多 存完一个数,他就把地址自动指向下一个了
3>自动索引(前后索引)
MOV R1,#0XFFFFFFFF`
`MOV R2,#0X20000000`
`STR R1,[R2,#4]!` `; 将R1寄存器的内容存到【R2+4】地址,然后R2=R2+4`
`
LDR同样支持三种索引方式
批量寄存器操作指令ldm/stm
将r1到r4中的值存储到r0指向地址空间中,连续16个字节的地址空间
stm r0, {r1-r4}
` `LDR` `R1,=0X1`
`LDR` `R2,=0X2`
`LDR` `R3,=0X3`
`LDR` `R4,=0X4`
`LDR` `R0,=0X20000000`
`STM` `R0!,{R1-R4}`
`
` `LDR` `R1,=0X1`
`LDR` `R2,=0X2`
`LDR` `R3,=0X3`
`LDR` `R4,=0X4`
`LDR` `R0,=0X20000000`
`STM` `R0!,{R1-R3,R4}`
`
` `LDR` `R1,=0X1`
`LDR` `R2,=0X2`
`LDR` `R3,=0X3`
`LDR` `R4,=0X4`
`LDR` `R0,=0X20000000`
`STM` `R0!,{R1,R3,R2,R4}`
`
栈的操作指令 stmfd / ldmfd
栈的种类
空栈(Empty)
栈指针指向的地址是空的,在栈中存储数据时,可以直接存储,存储完成之后需要将栈指针再次指向空的位置。
满栈(Full)
栈指针指向的地址有数据,在栈中存储数据时,需要先将栈指针,指向一个空的位置,然后在存储数据。
增栈(Ascending)
栈指针向高地址方向移动
减栈(Descending)
栈指针向低地址方向移动
操作栈的方式
满增栈,满减栈,空增栈,空减栈
FA:Full Ascending 满增
FD:Full Descending 满减
EA:Empty Ascending 空增
ED:Empty Descending 空减
ARM默认采用的是满减栈
stmfd/ldmfd<code> sp!, {寄存器列表}
stmfd sp!, {r1-r4}(写) (压栈)
ldmfd sp!, {r0-r3}(读) (出栈)
LDR R1,=0X11111111`
` LDR R2,=0X22222222`
` LDR R3,=0X33333333`
` LDR R4,=0X44444444`
` ADD SP,SP,#0X100 ;将栈指针地址增加,默认是0X20000000`
` STMFD SP!,{R1-R4}` `;压栈`
` LDMFD SP!,{R0-R3}` `;出栈`
`
程序中运用
stmfd sp!, {r1-r4,lr}(写) (压栈)
ldmfd sp!, {r1-r4,pc}(读) (出栈) //r1-r4出栈给r1-r4, 将lr的值出栈给pc
MY_ADD`
` STMFD SP!,{R1-R4,LR}` `;压栈`
` LDR R1,=6`
` LDR R2,=8`
` ADD R5,R1,R2`
` LDMFD SP!,{R1-R4,PC}` `;出栈`
`main`
` LDR R1,=0X11111111`
` LDR R2,=0X22222222`
` LDR R3,=0X33333333`
` LDR R4,=0X44444444`
` ADD SP,SP,#0X100 ;将栈指针地址增加,默认是0X20000000`
` BL MY_ADD`
` ADD R5,R1,R2`
`
push/pop指令
`
`JUMP`
` PUSH {R1-R4,LR}` `;压栈`
` LDR R1,=0X11111111`
` POP{R0-R3,PC}` `;出栈`
`main`
` LDR R1,=0X11111111`
` LDR R2,=0X22222222`
` LDR R3,=0X33333333`
` LDR R4,=0X44444444`
` ADD SP,SP,#0X100 ;将栈指针地址增加,默认是0X20000000`
` BL JUMP`
`
-栈的应用-》叶子函数的调用过程
叶子函数是指一个函数内部没有调用其他函数的函数,也就是说,它是程序调用树的末端节点,不依赖于其他函数。
F`
` PUSH {R1,R2,LR} ;压栈`
` MOVS R1,#5`
` MOVS R2,#4`
` ADDS R3,R1,R2`
` POP{R1,R2,PC} ;出栈`
`main`
` ADD SP,SP,#0X100`
` MOVS R1,#3`
` MOVS R2,#2`
` BL F`
` ADDS R3,R1,R2`
`T`
` B T `
`
-栈的应用-》非叶子函数的调用过程
非叶子函数是指一个函数内部调用了其他函数的函数,也就是说,它不是程序调用树的末端节点,可以被其他函数调用。
F`
` PUSH {R1,R2,LR}` `;压栈`
` MOVS R1,#5`
` MOVS R2,#4`
` BL D`
` ADDS R3,R1,R2`
` POP{R1,R2,PC}` `;出栈`
`D`
` PUSH {R1,R2,LR}` `;压栈`
` MOVS R1,#7`
` MOVS R2,#6`
` ADDS R3,R1,R2`
` POP{R1,R2,PC}` `;出栈`
`main`
` ADD SP,SP,#0X100`
` MOVS R1,#3`
` MOVS R2,#2`
` BL F`
` ADDS R3,R1,R2`
`T`
` B T `
`
专用寄存器操作指令(了解)
ADD SP,SP,#0X100`
` MOVS R1,#3`
` MOVS R2,#2`
` MRS R3,PSR ;读PSR的内容`
` LDR R4,=0XF0123456`
` MSR PSR,R4 ;向PSR写内容`
` MRS R3,PSR`
`
异常中断产生指令(了解)
不讲,因为咱们用不到 一般用到的人写内核的
CPU执行完这个指令后产生一个软中断
协处理器指令(了解)
操作协处理器的指令(一般用不到-----协助cpu处理数据)
1.数据运算`
`2.内存访问`
`3.与主处理器通信`
`MRC 将协处理器中寄存器的内容读取到ARM处理器的寄存器中`
`MCR 将ARM理器中寄存器的内容读取到协处理器的寄存器中`
`
协处理器指令
- 协处理器数据运算指令
CDP
- 协处理器储存器访问指令
STC 将协处理器中的数据储存到存储器
LDC 将存储器中的数据读取到协处理器中
- 协处理器寄存器传送指令
MRC 将协处理器中寄存器的数据传送到ARM处理器中的寄存器
MCR 将ARM处理器寄存器中的数据读取到协处理器寄存器中
伪指令
本质:本身不是指令,但是cpu替换成等效的操作。
举例1:`
`延时一个指令周期(耗时一条指令的时间) (cpu没有这个指令)`
` NOP ;执行NOP和MOV R0,R0一个效果,执行NOP,cpu替换成MOV R0,R0`
` MOV R0,R0 `
`举例2:`
`LDR的两种形式`
`;->指令`
` LDR R1,[R2]`
`;->伪指令`
` LDR R1,=0x12345678 ;R1 =` `0x12345678`
`;可以将任何一个32bit的数据放入寄存器`
`
伪操作
指令是arm公司规定的,而伪操作是编译器规定的,不同的编译器伪操作指令不同。
(后期我们学的linux,用linux的编译器)