ARM Cortex-M4微处理器
- 写在前面
- ARM Cortex-M4微处理器简介
- ARM Cortex-M4微处理器内部结构概要
-
- 寻址方式与机器码获取方法
- 指令保留字简表与寻址方式
- 指令保留字简表
- 寻址方式
- 立即数寻址
- 寄存器寻址
- 直接寻址
- 偏移寻址及寄存器间接寻址
- 机器的指令码
-
- 基本指令分类解析
-
- 汇编语言的基本语法
- 汇编语言的格式
-
- 常用伪指令简介
- 系统预定义的段
- 常量的定义
- 程序中插入常量
- 条件伪指令
- 文件包含伪指令
- 其他常用伪指令
写在前面
本系列学习主要参照了王宜怀老师主编,清华大学出版社出版的《嵌入式技术基础与实践(第六版)》一书,有兴趣的可以自行购买,正版书籍附赠开发版供实验所需
ARM Cortex-M4微处理器简介
ARM Cortex-M4微处理器内部结构概要
具体介绍请参考书籍
位数
Cortex-M4系列处理器为32位处理器,内部存储器,数据总线均为32位,采用Thumb-2技术,同时支持16位与32位指令
总线结构
采用哈佛架构(感兴趣可以自行查阅),统一的存储空间编址,32位寻址,最多支持4GB的存储空间;三级流水线设计;片上接口基于AMBA架构
中断控制
采用了集成嵌套向量中断控制器(NVIC),支持8~256个中断优先级,最多240个中断请求
存储器保护
可选的MPU
低功耗
多种低功耗特性和休眠模式
内部寄存器
M4微处理器的寄存器包含用于数据处理与控制的寄存器、特殊功能寄存器与浮点寄存器。特殊功能寄存器有预定义的功能,必须通过专用指令来访问。
寄存器类型 | 序号 |
---|
低位寄存器 | R0~R7 |
高位寄存器 | R8~R12 |
SP | R13 |
LP | R14 |
PC | R15 |
PSR(程序状态寄存器) | 特殊功能寄存器 |
PRIMASK、FAULTMASK、BASEPRI(异常屏蔽寄存器) | 同上 |
CONTROL(控制寄存器) | 同上 |
寻址方式与机器码获取方法
CPU可以执行特定功能的操作命令被称为指令
CPU所能执行的各种指令的集合,成为该CPU的指令系统
指令保留字简表与寻址方式
指令保留字简表
其他指令请查阅《ARM v7-M 参考手册》
类型 | 保留字 | 含义 |
---|
数据传送类 | ADR | 生成与PC指针相关的地址 |
| LDR、LDRH、LDRB、LDRSB、LDRSH、LDMIA | 将存储器中的内容加载到寄存器中 |
| STR、STRH、STRB、STMIA | 将寄存器中的内容存储到存储器中 |
| MOV、MVN | 寄存器间数据传送 |
| PUSH、POP | 进栈出栈 |
数据操作类 | 下为子类 | |
算数运算类 | ADC、ADD、SBC、SUB、MUL | 加、减、乘指令 |
| CMN、CMP | 比较指令 |
逻辑运算类 | AND、ORR、EOR、BIC | 按位与、或、异或、位段清零 |
数据序转类 | REV、REVSH、REVH | 反转字节序 |
扩展类 | SXTB、SXTH、UXTB、UXTH | 无符号扩展字节、有符号扩展字节 |
…… | …… | …… |
寻址方式
立即数寻址
操作数直接通过指令给出。用“#”作为立即数的前导标识符。M4微处理器的立即数范围是0x00~-0xFF。例如:
MOV R0, # 0xFF //立即数0xFF装入R0寄存器
SUB R1, R0, # 1 //R1←R0 - 1
寄存器寻址
操作数来自寄存器。例如:
MOV R0, R1 //将R1寄存器内容装入R0寄存器
直接寻址
操作数来自存储单元,指令中直接给出存储单元地址。指令码中显示给出数据的位数,如:字、半字、单字节。例如:
LDR Rt, label //从标号label处连续读取4字节到寄存器中
LDRH Rt, label //从地址label处读取半字到Rt中
LDRB Rt, label //从地址label处读取字节到Rt中
偏移寻址及寄存器间接寻址
偏移寻址的操作数来自存储单元,指令中通过寄存器及偏移量给出存储单元的地址。偏移量不超过4KB。偏移量为0的偏移寻址也成为寄存器间接寻址。例如:
LDR R3, [PC, # 100] //从地址(PC+100)处读取4字节到R3中
LDR R3, [R4] //以R4中内容为地址,读取4字节到R3中
机器的指令码
运行源文件
利用开发环境打开工程…\CH02-1。测试代码如下:
Label:
MOV R0, # 0xDE
LDR R0, = data_format1
LDR R1, = Label
LDR R2, [R1]
BL printf
执行结果如下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/58874539307c4334a97fac291192b6f5.png)
执行程序所获得的信息
从上图现实的内容可以看出,标号代表的地址为0900D87E,这就是指令MOV R0,# 0xDE机器码要存放的开放地址,各地址存储内容如下表:
地址 | 0800D87E | 0800D87F | 0800D880 | 0800D881 |
---|
内容 | 4F | F0 | DE | 00 |
STM32数据存储采用的是小段模式,即将2个字节以上的一个数据的低字节放在存储器低地址单元,高字节放在高地址单元。
基本指令分类解析
数据传送类指令
有两种情况:一是取存储器地址空间中的数传送到寄存器中,二是将寄存器中的数传送到另一寄存器或存储器地址空间中
取数指令
编号 | 指令 | 说明 |
---|
(1) | LDR Rt, [< Rn | SP > {, #imm }] | 从地址{ SP/Rn + # imm}处,取字到Rt中,imm = 0, 4, 8, …, 1020 |
| LDR Rt, [Rn, Rm] | 从地址Rn + Rm处读取字到Rt中 |
| LDR Rt, label | 从标号label指定的存储器单元取数到寄存器,标号label必须在当前指令的-4~4KB范围内,且应4字节对齐 |
(2) | LDRH Rt, [Rn {, #imm}] | 从地址{Rn + #imm}处,取半字到Rt中,imm = 0, 2, 4, …, 62 |
| LDRH Rt, [Rn, Rm] | 从地址Rn + Rm处读取半字到Rt中 |
(3) | LDRB Rt, [Rn {, #imm}] | 从地址{Rn + #imm}处,取半字到Rt中,imm = 0~31 |
| LDRB Rt, [Rn, Rm] | 从地址Rn + Rm处读取字节到Rt中 |
(4) | LDRSH Rt, [Rn, Rm] | 从地址Rn + Rm处读取半字到Rt中,并带符号扩展至32位 |
(5) | LDRSB Rt, [Rn, Rm] | 从地址Rn + Rm处读取字节到Rt中,并带符号扩展至32位 |
(6) | LDM Rt{ ! }, reglist | 从地址Rn处读取多个字, 加载到reglist列表寄存器中,每读一个字后Rn自增一次 |
存数指令
Rt,Rn,Rm必须为R0~R7中的一个
编号 | 指令 | 说明 |
---|
(7) | STR Rt, [< Rn | SP > {, #imm }] | 把Rt中的字存储到地址SP/Rn + #imm处,imm = 0, 4, 8,…, 1020 |
| STR Rt, [Rn, Rm] | 把Rt中的字存储到地址Rn + Rm处 |
(8) | STRH Rt, [Rn {, #imm}] | 把Rt中的低半字存储到地址SP/Rn + #imm处,imm = 0, 2, 4, …, 62 |
| STRH Rt, [Rn, Rm] | 把Rt中的低半字存储到地址Rn + Rm处 |
(9) | STRB Rt, [Rn {, #imm}] | 把Rt中的低字节存储到地址SP/Rn + #imm处,imm = 0~31 |
| STRB Rt, [Rn, Rm] | 把Rt中的低字节存储到地址Rn + Rm处 |
(10) | STM Rn!, reglist | 存储多个字到Rn处,每存一个字后Rn自增一次 |
寄存器间数据传送指令
编号 | 指令 | 说明 |
---|
(11) | MOV Rd, Rm | Rd←Rm,Rd只可以是R0~R7 |
(12) | MOVS Rd, # imm | 功能同MOV, 且影响N、Z标志 |
(13) | MVN Rd, Rm | 将寄存器Rm中的数据取反,传送给寄存器Rd,影响N、Z标志 |
其他指令请参考书籍,在此不再罗列
汇编语言的基本语法
汇编语言源程序以行为单位进行设计,每行最多可以包含一下4部分
汇编语言的格式
标号
- 如果一个语句有标号,则标号必须书写在汇编语句的开头部分
- 可以组成标号的字符有字母A ~ Z、 a ~ z、数字0 ~ 9、下划线(_),美元符号($),但开头的第一个符号不能为数字和 $
- 编译器对标号中字母的大小写敏感,但指令不区分大小
- 标号长度基本不受限制,但实际使用时通常不超过20个字符。
- 标号后必须带冒号(:)
- 一个标号在一个文件(程序)中只能被定义一次,否则出现重复定义,不能通过编译
- 一行语句只能有一个标号,编译器将把当前程序计数器的值赋给该标号
操作码
操作码包括指令码和伪指令。
对于有标号的行,必须至少用一个空格或制表符将标号与操作码隔开;对于没有标号的行,不能从第一列开始写指令码,应以空格或制表符开头。
操作数
操作数可以使地址,标号或指令码定义的常数,也可以是有伪运算符构成的表达式。
如果一条指令或伪指令有操作数,则操作数与操作码之间必须用空格隔开书写。操作数多于一个的,操作数之间用逗号分隔。操作数也可以是M4内部寄存器,或者另一条指令的特定参数。
操作数一般都有一个存放结果的寄存器,这个寄存器在操作数的最前面
1、常数标识
编译器识别的常数有十进制、十六进制(0x)、二进制(0b)
2、“#”表示立即数
一个常数前添加“#”表示一个立即数;不加“#”时,表示一个地址。
3、圆点
如果圆点(.)单独出现在语句操作码之后的操作数位置上,则代表当前程序计数器的值被放置在圆点的位置。
4、伪运算符
略。详情请查阅资料
注释
类似于C语言
常用伪指令简介
在CCS开发环境下,所有的汇编命令都是以“.”开头的
系统预定义的段
C语言程序在经过gcc编译器最终生成.elf格式的可执行程序。.elf可执行程序是以段为单位来组织文件的。通常划分为如下三个段:
.text @只读的代码区
.data @可读可写的数据区
.bss @是可读可写且没有初始化的数据区
常量的定义
常量的定义可以使用.equ汇编指令,例如:
.equ _NVIC_ICER, 0xE00E180
...
LDR R0, = _NVIC_ICER @将0xE00E180放到R0中
常量的定义还可以使用.set汇编指令,例如:
.set _NVIC_ICER, 0xE00E180
程序中插入常量
插入数据的类型 | 伪指令 |
---|
字 | .word |
半字 | .hword |
字节 | .byte |
字符串 | .ascii.asciz |
LDR R3, = NUMBER
LDR R4, [R3]
...
LDR R0, = HELLO_TEXT
BL PrintText
...
ALIGN 4
NUMBER:
.word 0x123456789
HELLO_TEXT:
.asciz "hello\n"
条件伪指令
.if条件伪指令后面紧跟一个恒定的表达式,并且最后要以.endif结尾。中间如果有其他条件,可以用.else编写汇编语句。
.ifdef标号表示如果标号被定义,则执行下面的代码。
文件包含伪指令
.include "filename"
其他常用伪指令
(1).section:用户可以通过该指令来自定义一个段
.section .isr_vector, "a" @定义一个.isr_vector段,"a"表示允许段
(2).global:用来定义一个全局符号
.global symbol
(3).extern:.extern symbol 声明symbol为外部函数,调用时可以遍历所有文件找到该函数并使用
.global main
bl main
(4).align:通过填充字节使当前位置满足一定的对齐方式
(5).end:声明汇编文件的结束
其余详见《GNU汇编语法》
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)