ARM体系结构与开发基础
- [一. ARM 汇编实验](#一. ARM 汇编实验)
-
- [1. 打开 eclipse 创建工程](#1. 打开 eclipse 创建工程)
- [2. 在工程中创建相关文件](#2. 在工程中创建相关文件)
-
- [2.1. 新建一个 Makefile 文件](#2.1. 新建一个 Makefile 文件)
- [2.2. 创建start.S汇编程序](#2.2. 创建start.S汇编程序)
- [2.3. 创建链接脚本文件](#2.3. 创建链接脚本文件)
- [2.4. 创建脚本文件](#2.4. 创建脚本文件)
- [⼆. ARM 汇编语言程序设计](#⼆. ARM 汇编语言程序设计)
-
- [1. GNU ARM 汇编器⽀持的伪操作](#1. GNU ARM 汇编器⽀持的伪操作)
-
- [1.1. 伪操作概念](#1.1. 伪操作概念)
- [1.2 数据定义(Data Definition )伪操作](#1.2 数据定义(Data Definition )伪操作)
- [1.3 汇编控制伪操作](#1.3 汇编控制伪操作)
- [2,ARM 汇编器⽀持的伪指令](#2,ARM 汇编器⽀持的伪指令)
-
- [2.1. ADR 伪指令](#2.1. ADR 伪指令)
- [2.2. LDR 伪指令](#2.2. LDR 伪指令)
- [3. ARM 汇编语言的程序结构](#3. ARM 汇编语言的程序结构)
-
- [3.1. 格式](#3.1. 格式)
- [3.2 过程调用标准 AAPCS](#3.2 过程调用标准 AAPCS)
- [4,ARM 伪指令实验](#4,ARM 伪指令实验)
- [5. ARM 内联汇编(C和汇编混合编程)实验](#5. ARM 内联汇编(C和汇编混合编程)实验)
-
- [5.1. GCC内联汇编的⼀般格式:](#5.1. GCC内联汇编的⼀般格式:)
- [三. Cortex-A7 GPIO 点灯 编程](#三. Cortex-A7 GPIO 点灯 编程)
-
- [1. GPIO 功能介绍](#1. GPIO 功能介绍)
-
- [1.1. STM32MP1 芯片的 GPIO 控制器详解](#1.1. STM32MP1 芯片的 GPIO 控制器详解)
- [1.2 常用 寄存器详解](#1.2 常用 寄存器详解)
- [2. GPIO 控制 控制 点灯 实验](#2. GPIO 控制 控制 点灯 实验)
-
- [2.1. 找打板子上的led](#2.1. 找打板子上的led)
- [2.2. 找到对应的原理图(主板原理图)](#2.2. 找到对应的原理图(主板原理图))
- [2.3. 打开芯片手册,查看led控制寄存器](#2.3. 打开芯片手册,查看led控制寄存器)
一. ARM 汇编实验
汇编:

1. 打开 eclipse 创建工程
详细信息请参考文档:

选择C工程:

取工程名称:

2. 在工程中创建相关文件
2.1. 新建一个 Makefile 文件

内容如下:
bash
SHELL = C:\Windows\System32\cmd.exe
CROSS_COMPILE := arm-none-eabi-
NAME := h_program
CPPFLAGS := -nostdlib -nostdinc -g
CFLAGS := -Wall -O2 -fno-builtin -g
LD := $(CROSS_COMPILE)ld
CC := $(CROSS_COMPILE)gcc
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
export CC LD OBJCOPY OBJDUMP CPPFLAGS CFLAGS
objs := start.o
all: $(objs)
$(LD) -T map.lds -o $(NAME).elf $^
$(OBJCOPY) -O binary $(NAME).elf $(NAME).bin
$(OBJDUMP) -D $(NAME).elf > $(NAME).dis
%.o: %.S
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis
2.2. 创建start.S汇编程序
bash
.text
.global _start
_start:
mov r0, #0x9
nop
mov r1, #0x7
bl add_sub
stop:
b stop
add_sub:
add r2, r0,r1
sub r3, r0,r1
mul r4, r0,r1
mov pc,lr
2.3. 创建链接脚本文件
javascript
map.lds:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
ENTRY(_start)
OUTPUT_ARCH(arm)
SECTIONS
{
. = 0xc2000040;
. = ALIGN(4);
.text :
{
start.o(.text)
*(.text)
}
. = ALIGN(4);
.rodata :
{
*(.rodata)
}
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
.bss :
{
*(.bss)
}
}
2.4. 创建脚本文件
bash
STM32MP1.init:
target remote localhost:3334
monitor halt
⼆. ARM 汇编语言程序设计
1. GNU ARM 汇编器⽀持的伪操作
1.1. 伪操作概念
在 ARM 汇编语言程序中,有⼀些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对
应的操作码,通常称这些特殊指令助记符为伪操作标识符(directive),它们所完成的操作称为伪操
作。
伪操作在源程序中的作用是为了完成汇编程序做各种准备⼯作的,这些伪操作仅在汇编过程中起作
用,⼀旦汇编结束,伪操作的使命就完成。
在 ARM 的汇编程序中,伪操作主要有符号定义伪操作、数据定义伪操作、汇编控制伪操作及其杂项
伪操作等。
1.2 数据定义(Data Definition )伪操作
数据定义伪操作⼀般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。常⻅
的数据定义伪操作有.byte、.short、.long、.quad、.float、.string、.asciz、.ascii 和.rept。数据定
义伪操作如下。

1.3 汇编控制伪操作
汇编控制伪操作用于控制汇编程序的执行流程,常用的汇编控制伪操作包括以下几条。
.if、.else、.endif.macro、.endm
.macro 伪操作可以将⼀段代码定义为⼀个整体,称为宏指令,然后就可以在程序中通过宏指令多次调用该段代码。其中,$标号在宏指令被展开时,标号会被替换为用户定义的符号。
宏操作可以使用⼀个或多个参数,当宏操作被展开时,这些参数被相应的值替换。
宏操作的使用方式和功能与子程序有些相似,子程序可以提供模块化的程序设计、节省存储空间并提
高运行速度。但在使用子程序结构时需要保护现场,从而增加了系统的开销,因此,在代码较短且需
要传递的参数较多时,可以使用宏操作代替子程序。
-
.mexit
.mexit用于从宏定义中跳转出去。 -
杂项伪操作
ARM汇编中还有⼀些其他的伪操作,在汇编程序中经常会被使用,包括以下几条。

2,ARM 汇编器⽀持的伪指令
2.1. ADR 伪指令
ADR 伪指令为小范围地址读取伪指令。ADR 伪指令将基于 PC 相对偏移地址或基于寄存器相对偏移
地址值读取到寄存器中,当地址值是字节对⻬时,取值范围为−255~255,当地址值是字对⻬时,
取值范围为−1020~1020。当地址值是 16字节对⻬时其取值范围更大。
示例:
bash
.text
.global _start
_start:
mov r0, #0x9
nop
mov r1, #0x7
adr pc, add_sub ;adr的⽤法:给pc加add_sub,可以查看反汇编
bl add_sub
stop:
b stop
add_sub:
add r2, r0,r1
sub r3, r0,r1
mul r4, r0,r1
mov pc,lr
2.2. LDR 伪指令
LDR 伪指令装载⼀个 32位 的常数或⼀个地址到寄存器。
当要加载的常量没有超过MOV或者MVN指令的范围时用MOV或MVN指令来替换LDR伪指令,
如果超过了MOV或者MVN的范围时,汇编器会将常数先放入缓存池中,同时用⼀条基于PC偏移地址 的LDR指令来读取该常数。
3. ARM 汇编语言的程序结构
3.1. 格式
在 ARM(Thumb)汇编语言程序中可以使用.section 来进行分段,其中每⼀个段用段名或者文件结尾为
结束,这些段使用默认的标志,如a为允许段,w为可写段,x为执行段。在⼀个段中,我们可以定
义.text、.data、.bss子段。由此我们可知道,段可以分为代码段、数据段及其他存储用的
段,.text(正文段)包含程序的指令代码:.data(数据段)包含固定的数据,如常量、字符串:.bss(未初始
化数据段)包含未初始化的变量、数组等,当程序较长时,可以分割为多个代码段和数据段,多个段在
程序编译链接时最终形成⼀个可执行的映像文件。
bash
.section.data
< initialized data here>
.section .bss
< uninitialized data here>
.section .text
.globl _start
_start:
<instruction code goes here>
3.2 过程调用标准 AAPCS
为了使不同编译器编译的程序之间能够相互调用,必须为子程序间的调用规定⼀定的规则。AAPCS就 是这样⼀个标准。所谓 AAPCS,其英文全称为
Procedure Call Standard for the ARM Architecture (AAPCS),即ARM体系结构过程调用标准。它是 ABI(Application Binary Interface(ABI)for the
ARMArchitecture (base standard) [BSABI])标准的⼀部分。
ARM寄存器使用规则
AAPCS中定义了 ARM寄存器使用规则如下:
子程序间通过寄存器
R0、R1、R2、R3来传递参数。如果参数多于4个,则多出的部分用堆栈传递。被调用的子程序在返回前⽆须恢复寄存器 R0-R3的内容。
在子程序中,使用寄存器 R4-R11 来保存局部变量。如果在子程序中使用到了寄存器
R4-R11中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序 中没有用到的寄存器则不必进行这些操作。在 Thumb 程序中,通常只能使用寄存器R4-R7来保存 局部变量。寄存器
R12用做子程序间scratch寄存器(用于保存SP,在函数返回时使用该寄存器出栈),记作ip。在子程序间的连接代码段中常有这种使用规则。
寄存器
R13用做数据栈指针,记作sp。在子程序中寄存器R13不能用做其他用途。寄存器sp在进入子程序时的值和退出子程序时的值必须相等。
寄存器
R14称为连接寄存器,记作lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用做其他用途。寄存器
R15是程序计数器,记作pc。它不能用做其他用途。
4,ARM 伪指令实验
参考开发教程:40.5 ARM 伪指令实验
对代码进行仿真分析
5. ARM 内联汇编(C和汇编混合编程)实验
5.1. GCC内联汇编的⼀般格式:
asm(
代码列表
:输出运算符列表
:输入运算符列表
:被更改资源列表
);
在代码列表中,每个汇编语句都要用""括起来。
c
asm(
"add %0,%1,%2\n\t"
"mov r1,%1\n\t"
:"+r"(sum)
:""(a),"r"(b)
:"r0"
);
在C代码中嵌入汇编需要使用 asm 关键字,其用法为 asm() ;
" " 引号内部包含的是指令部分"
: 参数输出部分,函数的返回值
: 参数输入部分,函数的形参
: 修改列表);内联汇编的声明部分,指明要被更改的资源。
修饰符中
"r" 用寄存器来保存参数,
"i" 是立即数,
"m" 是⼀个有效的内存地址,
"x" 只能做输入。
"+" 表示参数可读可写;不写表示参数只读;
"=" 表示参数只写。
"&" 只能做输出
注意 "&" 并⾮标准的参数输出属性修饰符。
占位符:
"%0" 输出和输入列表的第1个成员
"%1" 输出和输入列表的第2个成员
"%2" 输出和输入列表的第3个成员。
示例:字节交换
c
unsigned long ByteSwap(unsigned long val)
{
int ch;
asm volatile(
"eor r3,%1,%1, ror #16\n\t"
"bic r3, r3, #0x00ff0000\n\t"
"mov %0,%1,ror #8\n\t"
"eor %0,%0,r3,lsr #8"
: "=r"(val)
: "%0"(va1)
: "r3"
);
}
int main(void)
{
unsigned long test_a = 0x1234, result;
result = ByteSwap(test_a);
printf("Result:%d\r\n", result);
return 0;
}
三. Cortex-A7 GPIO 点灯 编程
1. GPIO 功能介绍
⾸先应该理解什么是 GPIO。GPIO 的英文全称为 General-Purpose IO ports,也就是通用 IO 接
口。在嵌入式系统中常常有数量众多,但是结构却比较简单的外部设备/电路,对这些设备/电路,有
的需要 CPU 为之提供控制⼿段,有的则需要被 CPU 用做输入信号。而且,许多这样的设备/电路只
要求⼀位,即只要有开/关两种状态就够了。比如,控制某个 LED 灯亮与灭,或者通过获取某个引脚
的电平属性来达到判断外围设备的状态。对这些设备/电路的控制,使用传统的串行口或并行口都不
合适。所以在微控制器芯片上⼀般都会提供⼀个"通用可编程 IO 接口",即 GPIO。接口⾄少有两个
寄存器,即"通用 IO 控制寄存器"与"通用 IO 数据寄存器"。数据寄存器的各位都直接引到芯片外
部,而对这种寄存器中每⼀位的作用,即每⼀位的信号流通方向,则可以通过控制寄存器中对应位独
立地加以设置。
比如,可以设置某个引脚的属性为输入、输出或其他特殊功能。
在实际的 MCU 中,GPIO 是有多种形式的。比如,有的数据寄存器可以按照位寻址,有些却不能按
照位寻址,这在编程时就要区分了。比如传统的 8051 系列,就区分成可位寻址和不可位寻址两种寄
存器。另外,为了使用的方便,很多 MCU 的 GPIO 接口除必须具备两个标准寄存器外,还提供上拉
寄存器,可以设置 IO 的输出模式是高阻,还是带上拉的电平输出,或者不带上拉的电平输出。这在电路设计中,
外围电路就可以简化不少。
1.1. STM32MP1 芯片的 GPIO 控制器详解
每个通用 I/O 端口有四个 32位配置寄存器(GPIOx_MODER,GPIOx_OTYPE,GPIOx_OSPEEDR
和 GPIOx_PUPDR),两个 32位数据寄存器(GPIOx_IDR 和 GPIOx_ODR)和 32 位设置/重置寄存器
(GPIOx_BSRR)。此外,所有 GPIO 都有⼀个 32 位的锁定寄存器(GPIOx_LCKR)和两个 32 位的备用
函数选择寄存器(GPIOx_AFRH 和 GPIOx_AFRL)。
每个通用 I/O (GPIO)端口的端口位都可以通过软件在几种模式下单独配置:
• 输入浮动
当CPU外部的10口是悬空状态的情况下,读取GPI0控制器时读取到的状态是不⼀定的,有可能是
高电平也有可能是低电平。
• 输入上拉
在CPU内部默认使能了上拉电路,从而保证在!0口悬空的状态下,GPIO控制器读取对应管脚状态的
时候默认读取到的是高电平。当外部有输入的时候则反应的状态是GPI0的实际状态,
• 输入下拉
在CPU内部默认实现了下拉电路,从而保证在I0口悬空的状态下,GPIO控制器读取对应的管脚状志的
时候默认读取到的是低电平,当外部有输入的时候则反应的状态是GPIO的实际状态。
• 模拟输入
在CPU中模拟输入主要是用于采集管脚上真实的电压变化的,通常是通过ADC控制器实现
• 开漏输出
在CPU内部有⼀个N.MOS,当GPIO控制器输出高电平时,对应IO口处于浮空状态,既IO口的状态取决于
IO口上的驱动电路,而当GPIO控制器输出低电平时10口可以轮出低电平,所以在开漏输出模式下CPU不能在
端口输出高电平,只能输出低电平,在12C总线通讯时可以使用该模式,实现主从机对总线的控制。
• 推挽输出
推挽输出是在CPU内部在控制IO口时加入了P-MOS和N-MOS。用于提高IO口的带载能力。
• 推挽复用输出
• 开漏复用输出
1.2 常用 寄存器详解
参考:⼿册
I/O 端口模式寄存器
输出类型寄存器
输出速度寄存器
I/O 端口上拉/下拉寄存器
I/O 端口设置/重置寄存器
输出数据寄存器
输入数据寄存器
2. GPIO 控制 控制 点灯 实验
2.1. 找打板子上的led

2.2. 找到对应的原理图(主板原理图)

2.3. 打开芯片手册,查看led控制寄存器
GPI0Z控制器所在总线:AHB5
GPIOZ控制器基址:0x54004000
LED1->PZ5
LED2->PZ6
LED3->PZ7
GPIOx_MODER(+0x00):用于设置对应I0的模式(输入、输出、模拟输入、复用)
PZ5 [11:10]= 0x1
PZ6 [13:12] = 0x1
PZ7 [15:14]= 0x1
GPIOX_OTYPER(+0x04):用于设置对应10的输出类型(推挽、开漏)
PZ5 [5] = 0x0
PZ6 [6]= 0x0
PZ7 [7]= 0x0
GPIOX OSPEEDR(+0x08):用于设置对应10的状态反正速度(低、中、高、⾮常高)
PZ5 [11:10]= 0x1
PZ6 [13:12] = 0x1
PZ7 [15:14]= 0x1
GPIOX_PUPDR(+0x0C):用于设置对应I0的上下拉电阻(浮空、上拉、下拉)
PZ5 [11:10]= 0x0
PZ6 [13:12] = 0x0
PZ7 [15:14]= 0x0
GPIOx_IDR(+0x10):用于反应IO口实际的高低电平
status=PZ5 [5]
status=PZ6 [6]
status=PZ7 [7]
GPIOx_ODR(+0x14):用于设置对应I0口的高低电平
PZ5 [5] = 0x1
PZ6 [6] = 0x1
PZ7 [7] = 0x1
以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!