【ARM】ARM体系结构与开发基础

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 汇编控制伪操作

汇编控制伪操作用于控制汇编程序的执行流程,常用的汇编控制伪操作包括以下几条。

  1. .if.else.endif
  2. .macro.endm

.macro 伪操作可以将⼀段代码定义为⼀个整体,称为宏指令,然后就可以在程序中通过宏指令多次调用该段代码。其中,$标号在宏指令被展开时,标号会被替换为用户定义的符号。

宏操作可以使用⼀个或多个参数,当宏操作被展开时,这些参数被相应的值替换。

宏操作的使用方式和功能与子程序有些相似,子程序可以提供模块化的程序设计、节省存储空间并提

高运行速度。但在使用子程序结构时需要保护现场,从而增加了系统的开销,因此,在代码较短且需

要传递的参数较多时,可以使用宏操作代替子程序。

  1. .mexit
    .mexit 用于从宏定义中跳转出去。

  2. 杂项伪操作

    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寄存器使用规则如下:

子程序间通过寄存器R0R1R2R3来传递参数。如果参数多于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

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!

相关推荐
三佛科技-134163842121 小时前
PL3367C 离线反激式PWM控制芯片兼容DP2525JD 典型应用电路
单片机·嵌入式硬件·智能家居·pcb工艺
研华嵌入式1 小时前
用Edge Impulse开启你的边缘AI之旅
arm开发·图像处理·嵌入式硬件·目标检测·视觉检测·边缘计算
武汉唯众智创1 小时前
物联网实训室系列产品介绍
物联网·物联网实训平台·综合布线·综合布线实训装置·物联网实训装置
研华嵌入式1 小时前
从零到边缘:在AOM-2721 OSM开发套件上构建基于Edge Impulse的优化物体检测方案
arm开发·目标检测·计算机视觉·视觉检测
知南x1 小时前
【正点原子STM32MP157 启动篇】(3) STM32MP1 二进制头部信息+Linux 系统启动过程
linux·stm32·嵌入式硬件·stm32mp157
御控工业物联网1 小时前
二供泵房物联网控制解决方案
物联网·数据采集·plc·远程控制·工业自动化
数峦云数字孪生三维可视化1 小时前
魔观3DS智慧工厂数字孪生立体监测系统:让数字孪生“立体可感”的智能中枢
大数据·人工智能·物联网·信息可视化·数字孪生
硅农深芯1 小时前
先进封装 – 从 2D、3D 到 4D 封装
单片机·芯片·封装
全栈视界师2 小时前
[AWS IoT 实战3] 设备事件报警 上传图片到 DynamoDB 和 S3功能
物联网·struts·aws