韦东山老师的单片机核心课程学习笔记(三)

本文深入探讨了ARM芯片的架构,强调其内部地址空间通信方式,以及RISC特性。Cortex-M3/M4内核的运作过程被详细阐述,包括数据处理、内存访问和CPU内部构造。此外,还介绍了 Thumb和Thumb2指令集,以及数据处理、跳转指令的使用。文中通过实例展示了如何进行内存读写和数据运算,帮助读者理解ARM芯片的工作原理。
摘要由CSDN通过智能技术生成

ARM芯片的那些事

ARM架构

ARM的芯片都使用的同一种架构,这种架构可使CPU只与内部地址空间通信,而不用考虑与芯片外设直接的通信方式。

其通信架构图如下图所示:

 图中的内存,UART,USB控制器等内部外设都是在同一片地址空间中的,CPU访问这些外设的方法都是一样的,即通过向内存控制器发出不同的地址选择指令,对接到不同的地址空间上。

ARM芯片术语精简指令集计算机(RISC),它具有以下特点:

  • 对内存只有读、写指令
  • 对于数据的运算是在CPU内部完成的
  • 使用RISC指令的CPU复杂度小一些,便于设计

与之相对的是CISC,相比于RISC而言,该指令能力强,指令更加灵活,也更加复杂。

CPU内部构造

Cortex-M3 是一个 32 位处理器内核。内部的数据路径是 32 位的,寄存器是 32 位的,存储器 接口也是 32 位的。

下图是一个cortex-M3/M4内核在进行两个数据加法运算时的通信过程

CPU通过调取4调指令来完成一次加法运算,CPU从内存对应地址空间中读取a和b的值,并将其存入CPU内部的通用目的寄存器中(R0~R12),在ALU中可以直接访问这些内存目的寄存器,并将a和b相加,存回R0中,最终再将R0的数据写回a所在的地址空间,便完成了a = a+b的运算。

CPU内部构造详见Cortex-M3权威指南

关于内部存储情况,Cortex-M3 处理器拥有 R0-R15 的寄存器组。

其中R0~R12都是 32 位通用寄存器,用于数据操作

R13:作为堆栈指针SP。Cortex-M3 拥有两个堆栈指针,然而它们是联组工作的(banked),因此任一时刻只能使用其中的一个。

  • 主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程
  • 进程堆栈指针(PSP):由用户的应用程序代码使用

R14:连接寄存器LR,当呼叫一个子程序时,由 R14 存储返回地址。

R15:程序计数寄存器PC,指向当前的程序地址。如果修改它的值,就能改变程序的执行流。

此外,Cortex-M3/M4还在内核的基础上搭建了若干特殊寄存器,比如:

xPSR寄存器:程序状态寄存器,用来记录 ALU 标志(0 标志,进位标志,负数标志,溢出标志),执行状态,以及当前正服务的中断号。

 我们关注高四位的位域代表的信息,当指令程序进行CMP运算时,高四位就会表示运算后的结果

CPU与访问内存的方式

CPU中执行的指令代码是用汇编语言编写的,其中主要包括下面几种指令代码:

  • 内存R/W
  • 运算
  • 跳转/分支
  • 比较

这些指令组合在一起就组成了CPU内部调用的指令集,常见的指令集有:ARM,Thumb,Thumb2等

ARM指令集是32位的指令集,每条指令都占用32位,我们在编程时利用CODE 32表示ARM指令集

Thumb指令集是16位的指令集,每条指令都占用16位,我们在编程时利用CODE 16表示为Thumb指令集

我们利用CODE 16和CODE 32来区分Thumb和ARM指令集,要节省空间时使用Thumb指令集,要高效时使用ARM指令集,但如果涉及到两种指令集中的代码互相调用时,就需要在调用时设置PC寄存器的bit0位来使能/失能Thumb指令集,使用起来比较繁琐。

于是,ARM引入了Thumb2指令集,该指令集会自动区分不同的指令,对不同的指令采用划分不同的指令位,我们编程时利用Thumb来表明使用的是Thumb2指令集

我们通过STR和LDR指令就可以从相应的内存地址中读取数据或写入数据,通过STM和LDM就可以一次写入或读取多个数据。STM和LDM指令有四种工作模式:

  • IA:Increment After,每次传输后 增加待存放的数据地址位,从数据低地址位开始存
  • IB:Increment Before,每次传输前 增加待存放的数据地址位,从数据低地址位为开始存储
  • DA:Decrement After,每次传输后 减小待存放的数据地址位,从数据高地址位开始存储
  • DB:Decrement Before,每次传输前 减小待存放的数据地址位,从数据高地址位为开始存储

注意:内存总是从最低位开始存储数据,也可以理解为数据存储是以小端对齐的方式进行的。

这种内存的方式与栈的访问方式是对应的:

  • 根据栈指针的方向不同可分为满栈空栈
    • 若为空栈,则先入栈(存数据)再调整SP
    • 若为满栈,即栈内有数据,则先调整SP,再入栈(存数据)
  • 根据压栈时SP的增长方向可以分为/
    • 增:SP变大
    • 减:SP变小
  • 访问栈的四种方式为:
    • 满增FI,这就等于IB
    • 满减FD,这就等于DB
    • 空增EI,这就等于IA
    • 空减ED,这就等于DA

但是注意:同一种访问方式对于压栈和出栈的表现不同,比如拿最常用的满减(FD)举例,对于压栈STM而言,是在内存中向下位移SP后存入数据,对于出栈LDM而言,是在内存先读取当前SP指向的内存在向上位移SP。

可以看到,对于同一种访问栈的方式,压栈和出栈时的操作是正好相反的,对于满减的栈,在入栈时需要先减SP后存数据,在出栈时就需要先读数据后加SP。

 数据处理指令

由于ARM属于精简指令计算机,其对于内存只有读/写两种指令,而数据的运算都在CPU内部实现

其中主要的数据处理指令有:

  • ADD R0,R1,R2:判断R0 == R1+R2
  • SUB R0,R1,R2:判断R0 == R1-R2
  • AND R0,R1,R2:判断R0 == R1&R2
  • ORR R0,R1,R2:判断R0 == R1|R2
  • BIC   R0,R1,R2:判断R0 == R1&~R2,清除某位
  • CMP R0,R1:得出比较的结果:R0-R1
  • TST  R0,R1:得出测试的结果:R0&R1

除此之外,写在CPU内部的汇编指令中也需要一些跳转指令,来与外部程序接轨。

在使用跳转指令之前要注意保存返回地址,使用的就是上面介绍到的R14:LR寄存器

常用的跳转指令有:

  • B:Branch,只跳转
  • BL:Branch with Link,跳转之前先把返回地址保存在LR寄存器中
  • BX:Branch and eXchange,根据跳转地址的指令集类型切换BIT0状态
  • BLX:Branch with Link and eXchange,根据跳转地址的指令集类型切换BIT0状态
    • BIT0 = 0:ARM状态
    • BIT0 = 1:Thumb状态

练习BL指令的代码如下:

    BL DELAY        ;BL执行后,跳转到DELAY地址段的指令,并且LR寄存器指向下一条指令的地址
    MOV R1,#1
DELAY
    MOV R0,#5
LOOP
    SUB R0,R0,#1    ;R0=R0-1
    BNE LOOP        ;NE代表不相等,BNE代表仅在R0!=R0-1时跳转
    MOV PC,LR       ;将LR寄存器的值赋给PC

从上面代码可以看到BL执行后会跳转到对应标号的地址,并且会将BL指向下一条指令的地址

于是上面的代码还可以进一步简化

    ADR LR,RET      ;对LR赋值,当回调完成后跳转到对应标号地址处
    ADR PC,DELAY    ;对PC寄存器直接赋值,使代码直接跳转到对应标号地址处
RET
    MOV R1,#1
DELAY
    MOV R0,#5
LOOP
    SUB R0,R0,#1
    BNE LOOP
    MOV PC,LR

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值