ARM嵌入式学习(二)---ARM 汇编与中断学习笔记

目录

一、C语言基础知识

[二、ARM 汇编常用伪操作](#二、ARM 汇编常用伪操作)

[三、ARM 汇编源程序基本结构](#三、ARM 汇编源程序基本结构)

[四、ARM 指令集(六大类,核心重点)](#四、ARM 指令集(六大类,核心重点))

[五、ARM 指令格式与符号说明](#五、ARM 指令格式与符号说明)

[1. 指令格式(通用格式)](#1. 指令格式(通用格式))

六、数据处理指令与条件码

[1. 数据处理指令细节](#1. 数据处理指令细节)

[2. 条件码(核心重点)](#2. 条件码(核心重点))

[七、ARM 函数调用原理(核心难点)](#七、ARM 函数调用原理(核心难点))

[1. ARM 堆栈类型(延续之前栈结构知识)](#1. ARM 堆栈类型(延续之前栈结构知识))

[2. 栈操作核心指令](#2. 栈操作核心指令)

[3. 栈的初始化与使用](#3. 栈的初始化与使用)

[4. 跨文件汇编与混合编程](#4. 跨文件汇编与混合编程)

[八、更改 ARM 工作模式](#八、更改 ARM 工作模式)

[九、中断原理(重点:SWI 软中断)](#九、中断原理(重点:SWI 软中断))

[1. 软中断触发](#1. 软中断触发)

[2. 软中断处理函数核心步骤](#2. 软中断处理函数核心步骤)


一、C语言基础知识

1.数据类型

2.变量类型

**注:**定义一个变量,如果是全局的变量则默认就是0;如果是局部变量若不初始化为0,则默认是随机值。

二、ARM 汇编常用伪操作

伪操作是汇编源程序中用于辅助汇编、定义数据 / 符号的指令,不直接生成机器码,仅指导汇编器工作,常用核心伪操作:

  1. 数据定义伪操作:DCB(定义字节)、DCW(定义半字)、DCD(定义字),用于在内存中分配空间并初始化数据。
  2. 符号定义伪操作:EQU(等价定义,类似宏定义)、AREA(定义代码段 / 数据段)、ENTRY(指定程序入口)、END(指定程序结束)。
  3. 其他常用伪操作:ALIGN(字节对齐,混合编程必备)、EXTERN(声明外部符号,跨文件调用使用)、EXPORT(导出符号,供其他文件调用)。

三、ARM 汇编源程序基本结构

汇编源程序采用 "段式结构",核心分为代码段和数据段,结构清晰、规范:

  1. 数据段(DATA):用于存放常量、变量等数据,使用 AREA 伪操作定义,可通过数据定义伪操作初始化数据。
  2. 代码段(CODE):用于存放汇编指令,是程序执行的核心,需指定 ENTRY 入口,END 结束,指令按执行顺序编写。
  3. 基本框架:先定义数据段,再定义代码段,明确入口地址,指令遵循 ARM 指令格式,配合伪操作完成程序编写。

四、ARM 指令集(六大类,核心重点)

ARM 指令集属于 RISC 精简指令集,指令简洁、执行效率高,按功能分为六大类,重点掌握指令功能与适用场景:

  1. 数据处理指令:核心指令,用于数据运算、逻辑操作,如 MOV(数据传送)、ADD(加法)、SUB(减法)、AND(与运算)、ORR(或运算)、CMP(比较)。
  2. 数据加载与存储指令:用于实现寄存器与内存之间的数据传输,如 LDR(从内存加载数据到寄存器)、STR(将寄存器数据存储到内存)。
  3. 分支指令:用于控制程序执行流程,如 B(无条件跳转)、BL(带返回的跳转,用于函数调用)、BX(跳转并切换指令集)。
  4. 堆栈操作指令:用于栈的压栈、出栈,如 STMFD(满栈递减压栈)、LDMFD(满栈递减出栈),是函数调用、现场保存的核心。
  5. 异常处理指令:用于触发异常、中断返回,如 SWI(软中断指令)、BX LR(中断 / 函数返回)。
  6. 协处理器指令:用于操作协处理器(如浮点协处理器),日常裸机开发中使用较少。

五、ARM 指令格式与符号说明

1. 指令格式(通用格式)

指令助记符 {条件码} 目标操作数, 源操作数1, 源操作数2

  • 指令助记符:指令核心功能(如 ADDMOV)。
  • 条件码:可选,用于条件执行(如 EQ 相等、NE 不相等),满足条件时指令才执行。
  • 操作数:多为寄存器(如 R0R1),部分指令支持立即数(如 #0x10)、内存地址。

六、数据处理指令与条件码

1. 数据处理指令细节

  • 核心功能:对寄存器中的数据进行算术、逻辑运算,运算结果通常存入目标寄存器。
  • 重点指令:
    • CMP R1, R2:比较 R1 和 R2,结果不存入寄存器,仅更新 CPSR 中的条件标志位(N、Z、C、V)。
    • MOV R0, #0x01:将立即数 0x01 传送到 R0 寄存器。
    • ADD R0, R1, R2:R0 = R1 + R2,运算后更新 CPSR 标志位。

2. 条件码(核心重点)

条件码基于 CPSR 中的标志位,实现指令的条件执行,常用条件码:

条件码 含义 标志位要求 常用场景
EQ 相等 Z=1 比较后相等时执行
NE 不相等 Z=0 比较后不相等时执行
GT 大于(无符号) C=1 且 Z=0 无符号数比较大于时执行
LT 小于(无符号) C=0 且 Z=0 无符号数比较小于时执行
  • 示例:ADD EQ R0, R1, R2,仅当 Z=1(上一次比较相等)时,执行 R0=R1+R2。

七、ARM 函数调用原理(核心难点)

函数调用的核心是利用栈保存现场、传递参数、返回地址,结合 ARM 栈结构与指令实现,重点掌握以下内容:

1. ARM 堆栈类型(延续之前栈结构知识)

  • 核心组合:减栈 + 满栈(ARM 默认),即压栈时 SP 向低地址递减,SP 指向栈中最后一个有效数据。
  • ARM 最常用的是 满递减堆栈(FD, Full Descending) :SP 指向最后压入的数据 (即栈顶)。递减 :压栈时 SP 向低地址移动(先减后存),出栈时向高地址移动(先取后加)。

2. 栈操作核心指令

  • STMFD SP!, {R0-R3, LR}:满栈递减压栈,将 R0~R3(函数参数)、LR(返回地址)压入栈中,保存现场。
  • LDMFD SP!, {R0-R3, PC}:满栈递减出栈,将栈中数据恢复到 R0~R3、PC,恢复现场并返回。

3. 栈的初始化与使用

  1. 初始化栈 SP 寄存器:程序启动时,需指定栈的起始地址,将栈地址赋值给 SP(如 LDR SP, =0x80000000),确保栈有足够空间。
  2. 函数调用原理:
    • 调用方:将函数参数存入 R0~R3(ARM 约定),使用 BL 函数名 跳转,BL 会自动将返回地址存入 LR。
    • 被调用方:用 STMFD 压栈保存现场,执行函数逻辑,执行完成后用 LDMFD 出栈恢复现场,通过 BX LR 或出栈 PC 返回调用方。

4. 跨文件汇编与混合编程

  • 跨文件汇编:通过 EXPORT 导出被调用函数 / 符号,EXTERN 声明外部函数 / 符号,实现不同汇编文件之间的调用。
  • C 语言与汇编混合编译:
    • 汇编调用 C 函数:C 函数名需用 EXPORT 导出,汇编中通过 BL 函数名 调用,参数存入 R0~R3。
    • C 调用汇编函数:汇编函数用 EXPORT 导出,C 中用 extern 声明,直接调用即可。
    • 关键要求:混合编程时,需通过 ALIGN 伪操作实现字节对齐(通常 4 字节对齐),避免内存访问异常。

八、更改 ARM 工作模式

工作模式切换的核心是修改 CPSR 中的模式位(M [4:0]),结合指令实现,常用两种方式:

  1. 通过异常 / 中断触发:如 SWI 软中断、IRQ 外部中断,自动切换到对应特权模式(如 SVC、IRQ 模式)。
  2. 通过指令手动切换:在特权模式下,通过 MSR 指令修改 CPSR 的模式位,切换到目标模式(如从 SVC 模式切换到 User 模式)。
  • 注意:只有特权模式才能手动切换工作模式,User 模式(非特权)无法修改 CPSR 模式位。

九、中断原理(重点:SWI 软中断)

中断是 ARM 处理器响应内部 / 外部事件的核心机制,本次重点学习 SWI 软中断(软件触发的中断),完整流程如下:

1. 软中断触发

通过 SWI #中断号 指令触发软中断(如 SWI #0x01),触发后处理器自动执行以下操作:

  1. 硬件自动保存现场:将当前 CPSR 存入 SVC 模式的 SPSR,将下一条指令地址存入 SVC 模式的 LR。
  2. 自动切换到 SVC 模式(管理模式),禁止 IRQ 中断(I=1)。
  3. 跳转到异常向量表的 SWI 异常入口地址(0x08),执行异常处理函数。

2. 软中断处理函数核心步骤

  1. 计算 SWI 指令地址:通过 LR 减去偏移量(因流水线影响),得到当前 SWI 指令的实际地址。
  2. 读取 SWI 指令:从计算出的地址中读取 SWI 指令(32 位)。
  3. 提取 24 位软件中断号:SWI 指令的低 24 位为中断号,通过指令运算提取(如屏蔽高 8 位)。
  4. 根据中断号执行对应处理逻辑:不同中断号对应不同的处理函数,实现多软中断区分。
  5. 恢复现场并返回:通过 LDMFD 出栈恢复 CPSR(从 SPSR)和 PC(从 LR 修正后),回到中断触发前的程序位置继续执行。
cs 复制代码
deal_swi
    stmfd sp!, {r4-r12, lr}          ; 保存现场
    sub r0, lr, #4                   ; 计算 SWI 指令的地址
    ldr r1, [r0]                     ; 读取 SWI 指令本身
    bic r0, r1, #(0xff << 24)        ; 提取低 24 位(SWI 号)
    import c_deal_swi                ; 声明外部 C 函数
    bl c_deal_swi                    ; 调用 C 函数,参数在 r0 中
    ldmfd sp!, {r4-r12, pc}^         ; 恢复现场,切回用户模式,返回
逐行解释
1. deal_swi
  • 这是 软中断异常处理函数的标签
  • 当 CPU 执行到 SWI #xxx 时,硬件会自动跳转到这里执行

2. stmfd sp!, {r4-r12, lr}

功能:压栈保存现场

  • stmfd:满栈递减方式压栈(ARM 标准栈)
  • sp!:栈指针自动更新
  • {r4-r12, lr}:把这些寄存器全部压入栈
  • **为什么要保存?**因为进入软中断后,这些寄存器的值会被覆盖,不保存的话,回到用户程序时数据就乱了。
  • lr 这里保存的是:中断的返回地址

3. sub r0, lr, #4

功能:计算出真正的 SWI 指令地址

为什么要减 4?

因为 ARM 是 3 级流水线

  1. 取指
  2. 译码
  3. 执行

当执行到 SWI 指令时,PC 已经指向下下一条指令硬件自动把返回地址存在 LR 里 = SWI地址 + 4

所以要得到 SWI 指令本身的地址,必须:

复制代码
SWI指令地址 = LR - 4

结果存在 r0


4. ldr r1, [r0]

功能:读取 SWI 指令的 32 位机器码

  • r0 = SWI 指令的地址
  • [r0] = 取该地址的 32 位数据
  • 读到 r1

这条指令读取到的是:

复制代码
0xEFXXXXXX   (SWI 指令的机器码)

高 8 位是操作码,低 24 位是我们写的 SWI #编号


5. bic r0, r1, #(0xff << 24)

功能:提取 SWI 指令的低 24 位中断号

  • bic:位清除指令
  • 0xff <<24 = 高 8 位(11111111 00000000 ...)
  • 作用:把高 8 位抹成 0,只保留低 24 位

最终:

复制代码
r0 = SWI中断号(0~2^24-1)

6. import c_deal_swi

告诉汇编器:c_deal_swi 是外部 C 语言函数,不是本文件的


7. bl c_deal_swi

跳转到 C 语言函数处理软中断

ARM 规则:

  • r0 存放第一个参数
  • 所以 C 函数会收到 r0 = SWI 中断号

C 函数原型:

复制代码
void c_deal_swi(unsigned int swi_num);

8. ldmfd sp!, {r4-r12, pc}^

最重要一句:恢复现场 + 返回用户模式

逐部分解释:

ldmfd

满栈递减出栈

sp!

恢复栈指针

{r4-r12, pc}

  • 恢复 r4~r12
  • 把栈中保存的 LR 赋值给 PC
  • PC 就会实现 函数返回

④ 最后的 ^ 符号(关键!)

作用: 恢复 SPSR 到 CPSR 自动从 SVC 模式切回 USER 模式

没有这个 ^,中断返回后 模式不会切换,程序直接崩溃!


整段代码执行流程总结
  1. 进入软中断
  2. 压栈保存所有寄存器(防止破坏用户现场)
  3. 计算 SWI 指令地址(LR - 4)
  4. 读取 32 位 SWI 指令
  5. 提取低 24 位作为中断号
  6. 把中断号放入 r0,调用 C 函数处理
  7. 出栈恢复所有寄存器
  8. ^ 符号恢复 CPSR,切回用户模式
  9. 跳回用户程序继续执行

一句话总结

这段代码就是软中断的标准底层处理:保存现场 → 取中断号 → 交给 C 语言处理 → 恢复现场并返回用户模式。

学习小结

今日重点掌握了 ARM 汇编伪操作、指令集、函数调用原理及 SWI 软中断机制,明确了汇编源程序结构、数据处理指令与条件码的使用,深入理解了栈在函数调用中的作用、混合编程的注意事项,以及软中断的完整处理流程。这些内容是 ARM 裸机开发、中断驱动编写的核心,需结合指令练习,巩固栈操作、现场保存与恢复的细节,为后续实操开发打下基础。

相关推荐
姜太小白4 小时前
【其他】QEMU 在 Windows 和 CentOS 7 下安装及运行 ARM 操作系统指南
arm开发·windows·centos
somi75 小时前
ARM-08-I.MX6U UART 串口
arm开发·单片机·嵌入式硬件·自用
observe1015 小时前
ARM学习之时钟,EPIT,GPT
arm开发·学习
誰能久伴不乏5 小时前
从数字世界到物理引擎:用 PWM 撕开 0 和 1 的结界
linux·arm开发·c++·qt
果果燕5 小时前
ARM嵌入式学习(一)---ARM基础概念学习
arm开发·学习
惶了个恐6 小时前
嵌入式硬件第六弹——ARM(3)
arm开发·stm32·嵌入式硬件·arm
senijusene6 小时前
从启动到中断:基于i.MX6UL的ARM Cortex-A7中断系统详解
arm开发·嵌入式硬件
cici158741 天前
基于RT-Thread的数字焊机与工业机器人通信网关设计
arm开发·机器人
陌上花开缓缓归以1 天前
rk3568 mmc 驱动之u-boot代码分析
arm开发