#ARM开发 笔记

课程介绍

ARM开发 --> Linux移植 --> 驱动开发

前后联系:ARM和系统移植为驱动开发学习做准备工作

所需知识:C语言基础及STM32需要的硬件知识

学习方法

学习流程、思想和解决问题的方法即可

知道驱动的基本框架以及基本开发要求

底层课程导学

接口技术

GPIO:通用的输入输出接口(输入:上拉、下拉、浮空、模拟;输出:推挽/开漏)

本质:操作寄存器。使能:配置模式(输出)、输出类型(推挽)、输出速度(默认)、输出数据(相应位写1)。

串口通信协议:UART、IIC、SPI、CAN

电气协议:TTL、RS232、RS485

无线通信:WiFi、蓝牙、NB-IOT、Lora、zigbee

ADC:模数转换(需要得到外界真实数据)

PWM:脉冲宽度调制(用定时器调节平均功率)

MCU与MPU的区别

【三分钟了解什么是CPU_SOC_MPU和MCU】 三分钟了解什么是CPU_SOC_MPU和MCU_哔哩哔哩_bilibili

STM32G030(M0+) S5P6818(A53)

微控制单元(Microcontroller Unit;MCU) ,又称单片微型计算机或者单片机,是把中央处理器的频率与规格做适当缩减,并将内存、计数器(Timer)、USB、A/D转换、UART、、DMA等周边接口整合在单一芯片上,形成芯片级的计算机,为不同的应用场合做不同组合控制。

微处理器单元 (Microprocessor Unit;MPU)。就是把很多 CPU 集成在一起并行处理数据的芯片。通俗来说,MCU 集成了 RAM,ROM 等设备;MPU 则不集成这些设备,是高度集成的通用结构的中央处理器矩阵,也可以认为是去除了集成外设的 MCU。

传统来说,MPU更多采用Arm Cortex-A系列核心,具备丰富的外设接口,MCU则大多采用Arm Cortex-M系列核心,也比MPU更便宜,更容易安装和使用。主要区分是MMU。

更简单粗暴地划分二者的方式是运行Linux就是MPU,基于实时操作系统(RTOS)就是MCU。MCU的目的是永远运行一个相当简单的控制循环,或者直到它中断或停止。

随着技术和应用的发展,某些公司开始逐渐越界,界限开始模糊。

CPU运行原理

根据下载的程序运行,程序是指令和数据的有序集合

指令的解析

一条指令机器码的执行通常分为三个阶段:

1)取指:控制器将PC寄存器中的值发送给内存,内存将对应地址中的指令(机器码)传送回CPU的指令寄存器IR中

2)译码:指令译码器对IR中的指令进行识别,将指令(机器码)解析(翻译)成具体的运算操作(+/-/*...)

3)执行:控制器控制运算器中对应的运算单元进行运算,运算结果写入寄存器

注意:PC每取地址一次,自加一次。PC的值自动增加使PC指向内存中的下一条指令

思考:

1.运算器不同,处理指令不同。不同的处理器上如何运行同一个c语言程序?

2.假设指令集有乘法指令,结果并没有乘法运算器,怎么办?

可以转换为加法指令

指令流水线

指令的执行是按照流水线

取指--》取指器 根据PC值取指令

译码--》译码器

执行--》执行器

以上三个器件,都是单周期的器件,三个器件的工作是独立

指令1 指令2 指令3 指令4 指令5

1 取指

2 译码 取指

3 执行 译码 取指

4 执行 译码 取指

5 执行 译码 取指

6 执行 译码

7 执行

PC永远指向当前取指指令的地址,一旦取到指令,pc后移4byte,保存下一条指令地址。

指令流水线机制的引入确实能够大大的提升指令执行的速度,但在实际执行程序的过程中很多情况下流水线时是无法形成的,比如芯片刚上电的前两个周期、执行跳转指令后的两个周期等。

所以指令流水线的引入以及优化只能使平均指令周期不断的接近1而不可能真正的达到1,且流水线级数越多芯片设计的复杂,程度就越高,芯片的功耗就越高。

指令集架构

ARM的命名有指令集架构、 处理器架构、 处理器型号三类命名规则

架构指支持的汇编指令集(架构不同,汇编指令集不同)

ARM体系结构:ARMv8-A ---> Cortex-A53 --> S5P6818 主频:1.4GHZ

内核:Cortex-A9,A53,A73,A75

ARM公司授权芯片的公司,芯片产家在内核的基础上,增加了一些外设,发布一款芯片,这些芯片可以统称为SoC

SoC:System of Chip(片上系统);例如:S5P6818,骁龙(高通),麒麟(海思)

s5p6818(8核)

ARM的工作模式(A系列)

  1. 用户模式(User ):非特权模式,用户程序的工作模式,运行在操作系统的用户态。它没有权限去操作其他硬件资源,只能执行处理用户自己的数据,并且不能切换到其他模式。要访问硬件资源或切换到其他模式,只能通过软中断或产生异常。
  2. 快速中断模式(FIQ ):当一个高优先级(fast) 中断产生时将会进入这种模式。它主要用于高速数据处理及通道处理。
  3. 中断模式(IRQ):当一个低优先级(normal) 中断产生时将会进入这种模式。硬件产生中断信号后,处理器会自动进入这种模式。
  4. 管理模式/SVC(Supervisor):当复位或软中断指令执行时将会进入这种模式,当用户模式下的用户程序请求使用硬件资源时,通过软件中断可以进入该模式。
  5. 中止模式(Abort) :用于支持虚拟内存或存储器保护。当用户程序访问非法地址或没有权限读取的内存地址时,会进入该模式。
  6. 未定义模式(Undef):当执行未定义指令时ARM会进入这种模式
  7. 系统模式(System特权模式之一,提供给操作系统内核使用。可以访问更多的寄存器和特权指令,用于执行特权级别的操作和管理系统资源。
  8. 监控模式(Monitor) : 是为了安全而扩展出的用于执行安全监控代码的模式。也是一种特权模式(Cortex-A特有模式)

工作模式的理解

不同的模式拥有不同的权限

不同的模式执行的代码不同

不同的模式拥有不同的功能

特定的模式拥有特定的权限,执行特定的代码,完成特定的功能

ARM寄存器

工作模式的切换是依据寄存器去实现的。

ARM7,9,11 有37个寄存器

30 个通用寄存器

1 个用作 PC( program counter)

1个用作CPSR(current program status register)

5个用作SPSR(saved program status registers)

Cortex-A多出3个寄存器,即有40个寄存器

Monitor 模式r13_mon , r14_mon, spsr_mon

R0-R15, CPSR,SPSR 这些寄存器是由ARM公司提供,每个寄存器都是32位的。

这些寄存器没有地址,只有一个唯一的编号,通过这些编号,就可以访问对应的地址空间。R0-R15,cpsr,spsr就是对应的编号,每个编号都对应的32位二进制

专用寄存器

R13(SP,Stack Pointer)

栈指针,用于存储当前模式下的栈顶地址

R14(LR,Link Register)

链接寄存器:函数调用时,保存返回地址(下一条地址的指令的地址

R15(PC,Program Counter)

程序计数器:用于存储当前取址指令的地址(下一条指令的地址

CPSR寄存器

CPSR(Current Program Status Register),程序状态寄存器

存储当前程序运行状态

NZCV这几位叫条件位,后边八位叫控制位(C表示)

Bit[4:0]

[10000]User [10001]FIQ [10010]IRQ [10011]SVC

[10111]Abort [11011]Undef [11111]System [10110]Monitor

Bit[5]

[0]ARM状态 [1]Thumb状态

Bit[6]

[0]开启FIQ [1]禁止FIQ

Bit[7]

[0]开启IRQ [1]禁止IRQ

Bit[28]

> 当运算器中进行加法运算且产生符号位进位时该位自动置1,否则为0

> 当运算器中进行减法运算且产生符号位借位时该位自动置0,否则为1

Bit[29]

> 当运算器中进行加法运算且产生进位时该位自动置1,否则为0

> 当运算器中进行减法运算且产生借位时该位自动置0,否则为1

Bit[30]

当运算器中产生了0的结果该位自动置1,否则为0

Bit[31]

当运算器中产生了负数的结果该位自动置1,否则为0

SPSR寄存器

SPSR:保存程序状态的寄存器saved program status register)

用于保存CPSR(通用寄存器,在切换模式的时候用)

ARM的异常处理

异常

处理器在正常执行过程中可能遇到一些不正常的事件发生,这时处理器就要将当前程序暂停下来转而去执行这个异常事件,异常事件处理完之后返回被异常打断的地方继续执行。

ARM异常源

概念:导致异常产生的事件称为异常源

ARM异常源

FIQ

快速中断请求引脚有效

IRQ

外部中断请求引脚有效

Reset

复位电平有效

Software Interrupt

执行swi指令

Data Abort

数据终止

Prefetch Abort

指令预取终止

Undefined Instruction

遇到不能处理的指令

Reset(复位)

处理器上电或者复位时发生的异常

Data Abort(数据中止)

数据访问错误时发生的异常,一般是与Memory 误操作有关,如对0地址写操作,或对一些Memory 越界操作。或者指令不支持

FIQ (快速中断)、IRQ (普通中断)

FIQ 具有更高的优先级,即当 FIQ 发生时,CPU 会在当前指令执行完成后立即响应 FIQ 中断,而忽略其他 IRQ 中断。这使得 FIQ 更适合处理紧急、实时性要求较高的中断。

Prefetch Abort(指令预取错误时发生的异常)

当CPU在执行指令时,发现指令缓存中没有下一条指令时,就会发生预取指中止异常。这种异常通常是由于程序中的错误或者硬件故障引起的。由于预取指中止异常的信息不够详细,因此很难定位和修复问题。

Software Interrupt(软件中断)

软件中断是一种由应用程序发起的中断,用于请求操作系统提供服务或执行某些操作。当应用程序需要访问受保护的资源或请求操作系统提供服务时,它会发出一个软件中断信号,这个信号会被CPU接收并暂时将控制切换到一个中断处理程序,内核中被中断挂起的进程将在中断被接受后恢复。软件中断可以被看作是同步事件,因为它是由应用程序主动发起的,而不是由外部设备触发的。

Undefined instruction(执行了未定义指令时发生的异常)

Undefined instruction异常是ARM处理器中的一种异常类型,当CPU遇到不认识的指令时,就会触发该异常。在异常处理函数中,可以对该异常进行处理,例如打印debug信息或者进行其他操作。

ARM异常模式

在ARM的基本工作模式中有5个属于异常模式,ARM遇到异常后会切换成对应的异常模式

异常处理机制

不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制。

ARM异常响应(四大步、三小步)

ARM产生异常后的动作(自动完成)

1、拷贝CPSR中的内容到对应的异常模式下的SPSR

2、修改CPSR的值

2.1修改中断禁止位禁止相应的中断

2.1修改模式位进入相应的异常模式

2.3修改状态位进入ARM状态

3、保存返回地址到对应模式下的LR中

  1. 设置PC位相应的异常向量

异常向量表

  1. 异常向量表的本质是内存中的一段代码
  2. 表中为每个异常源分配了四个字节的存储空间
  3. 遇到异常后处理器自动将PC修改为对应的地址
  4. 因为异常向量表空间有限一般我们不会再这里写异常处理程序,而是在对应的位置写一条跳转指令使其跳转到指定的异常处理程序的入口

注:ARM的异常向量表的基地址默认在0x00地址但可以通过配置协处理器来修改其地址

异常返回

ARM异常返回动作(自己编写)

1、将SPSR的值复制给CPSR-->恢复为跳转之前的模式

2、将LR的值复制给PC-->返回跳转之前程序执行的地方

IRQ异常举例

IRQ异常是指当CPU接收到一个中断请求时,会暂停当前正在执行的程序,转而去执行与中断请求相关的处理程序。

例子:键盘中断

当用户按下键盘上的某个键时,键盘会向CPU发送一个中断请求,CPU会暂停当前正在执行的程序,转而去执行与键盘中断相关的处理程序,以响应用户的操作。在处理完中断请求后,CPU会返回到原来的程序继续执行。

异常优先级

  • 环境及工程搭建

汇编编程环境搭建

按照下发的环境安装包安装环境

配置编译工具链

为工程配置链接脚本(map.lds)

将map.lds 复制到工程文件夹(在桌面创建的ARM-ASM文件)

创建汇编文件

接下来我们需要建立一个start.s汇编文件添加到我们的工程中去

  • 汇编指令学习

c语言中哪些代码可以生成汇编指令?

1》带#号预处理,辅助编译器怎么编译,编译什么内容

预处理器是C语言编译器的一个组成部分,它在编译代码之前对代码进行处理。预处理器指令以#号开头,告诉编译器在编译代码之前执行一些操作。其中,#include指令用于将头文件包含到源代码中,#define指令用于定义宏。预处理器的主要作用是辅助编译器编译代码。 例如在编译时将头文件中的函数声明插入到源代码中,或者将宏替换为实际的值。预处理器处理完代码后,编译器将生成目标代码,最终生成可执行文件。

2》带;号的语句,可以编译生成指令

在编译器中,分号是语句结束的标志,编译器会将分号之前的语句编译成指令并添加到指令序列中。

汇编整体分类

1》指令:编译完生成一条机器码存储在内存单元当中,CPU执行时能完成对应的操作(类似于C中的语句)

2》伪操作(相当于c中的#的内容,告诉编译器怎么编译):不会生成机器码也不会占用内存,其作用是告诉编译器怎样编译(类似于C中的预处理指令)

3》伪指令:不是指令,编译器在编译时将其替换成等效的指令 (如:cpu中没有乘法器,对应没有乘法指令,3*3 ---》用加法器实现3+3+3,替换实现)

汇编中注释代码用@或;注释一行 ,/* */注释一段代码

指令分类

1.数据处理指令: 对数据进行逻辑、算术运算

2.跳转指令: 实现程序的跳转,实质是修改PC

3.Load/Store指令: 对内存的读写操作

4.状态寄存器传送指令: 对CPSR进行读写操作

5.异常中断产生指令: 触发软中断,常用于内核的系统调用 //SWI:软中断

6.协处理器指令: 操作协处理器的指令

//如3*3 ---》用加法器实现3+3+3,比较慢。我们可以外接一个协处理器(乘法器)(每个协处理器的功能比较单一),协处理器指令就是操作这个协处理器的,用的比较多的cp15协处理器。

汇编指令代码框架

.text            @声明一段代码
.global _start    @将_start 声明为一个全局的符号,其他.s文件也可以引用
                    @如调用函数  func  :  .global func
_start:            @汇编的入口
                    @汇编代码段.end                @汇编的结束

指令的语法格式(拒绝死记硬背)

<opcode{<cond>}{S}> <Rd>, <Rn>, <Operand2>
<操作码> <目标寄存器Rd> <第一操作寄存器Rn> <第二操作数Operand2>
;第一个位置必须是寄存器,第二操作数可以是寄存器,也可以是立即数

<opcode{<cond>}{S}> <Rd>, <Rn>, <Operand2>

opcode:指令的名字

cond:条件码(if else),可以省略不写,默认指令是无条件执行

S:状态标志

加s,指令的执行结果影响CPSR的NZCV位,

不加s,无影响

Rd:目标寄存器

Rn:第一个操作寄存器

oprand2:第二个操作数,可以是普通寄存器,可以是立即数

注:指令的名字,条件码,s连到一起写,指令名和目标寄存器之间使用空格,寄存器和数据之间使用逗号隔开,指令中的字符不区分大小写

ARM汇编:数据处理指令集:MOV、ADD、ADDS、ADC、SUB、SUBS、SBC、RSB、MUL、AND、ORR、EOR、BIC、CMP、TST、TEQ、LSL、LSR、ASR、RORV_汇编 adds-CSDN博客

  1. 数据处理指令

数据搬移指令 mov

如果是立即数,前边必须加#

什么是立即数?

立即数

立即数通常是指在立即寻址方式指令中给出的数。可以是8位、16位或32位,该数值紧跟在操作码之后。

寻址方式:

寻址方式_百度百科

立即数是保存在指令中的数,取指令的同时将值取过去,和普通变量的区别是,变量保存在内存中的数据,需要单独取值运算。

立即数的本质:立即数是包含在指令当中的数据(即属于指令的一部分)

立即数的优点:读取指令的同时也将立即数读取到了内存中,速度快

立即数的缺点:数量有限

如:MOV ,#0x12345678 @报错,不合法

怎么判断一个数是否立即数?

给定一个数,将这个数中的所有的1,可以组合成一个0-255之间的数,将0-255之间的这个数,循环右移偶数个位数,如果可以得到给定的这个数,说明是立即数。

问:只要挨着的就是立即数?对吗?

如何快速判断立即数-CSDN博客

注:使用mov 给寄存器里面存放值的时候,#号后面需是有效数(1:立即数,2:取反之后是立即数),如果不是立即数需要用ldr指令进行存放。

PC寄存器讲解

指令的执行三步:取址,译码,执行(PC永远指向当前正在取指指令的地址)

算术运算指令

数据运算指令格式

<操作码><目标寄存器><第一操作寄存器><第二操作数>

ADD R3,R1,R2;

操作码 指定当前指令是哪种运算

目标寄存器 存放运算结果

第一操作寄存器 存放参与运算的一个数据(只能是寄存器)

第二操作数 存放参与运算的另一个数据(可以是寄存器/立即数)

算术运算指令

add加法 adc 带进位的加法

sub 减法 sbc 带借位的减法

mul乘法 (乘法运算的R2(第二操作数)不能为立即数) 

add 普通加法

adc 带进位加法

假设2个64位的数相加

第一个64位的数,R0存放低32位,R1存放高32位;

第二个64位的数,R2存放低32位,R3存放高32位;

结果R4存放低32位,R5存放高32位

sub 普通减法

subs 减法(刷新CPSR)

减法指令执行时,没有借位时 CPSR 'C' 位置 1

sbc 带借位减法

mul 乘法

注意:mul r2, r0, #0x4 @ 错误------>乘法指令的第二个操作数只能是一个寄存器

mul r2, r1,r0

与 或 异或 左移 右移

.text          
.global _start_start:            
	MOV R0,#1               @指令
	MOV R1,#2               @指令
	MUL R2,R1,R0            @指令 R3=R1*R0
	AND R3,R1,#1            @指令 与 R3=R1&1
	ORR R3,R2,R1            @指令 或 R3=R2|R1
	EOR R3,R2,R1            @指令 按位异或 R3=R2^R1
	LSL R3,R2,R1            @指令 左移 R3=R2<<R1
	LSR R3,R2,R1            @指令 右移 R3=R2>>R1.end                

  1. 跳转指令

实现程序的跳转,本质是更改PC

修改PC

不建议使用,因为需要查询指令的地址

b bl :指令跳转

格式:b/bl Label

Label: 指令

相当C语言的函数调用

b指令(不带返回的跳转)

不保存返回地址的跳转(返回地址不保存到lr中)

一路向前,不返回

bl指令(带返回的跳转指令)

将LR的值修改成跳转指令下一条指令的地址

再将PC的值修改成跳转标识符下指令的地址

补充了解:

指令条件码表:可跟的判断条件成立跳转(NZCV在用于判断两者之间关系使用比较多)

练习

练习:
实现以下逻辑
unsigned int r1 = 9;
unsigned int r2 = 15;
while(1)
{
    if(r1 == r2)
    goto stop;
    if(r1 > r2)
    r1 = r1 - r2;
    if(r1 < r2)
    r2 = r2 - r1;
}
stop:
    while(1);汇编指令练习答案如下:
    mov r1,#9
    mov r2,#15
loop:
      cmp r1,r2  @cmp 比较指令
      beq  stop
      subhi r1,r1,r2
      subcc r2,r2,r1
      b loop
stop:
    b stop

  1. load/store指令

对内存的读写操作,将运算结果从cpu写到内存

可用地址查找:(我们不用查找,脚本文件中配置了内存空间的分配)

单寄存器操作指令 ldr / str

  1. 格式:ldr/str Rm, [Rn]

ldr:

str:写

Rm: 存储是数据

Rn:存储的数据,地址

	mov r1,#0Xff000000
	mov r2,#0X40000000
	str r1,[r2]        @把R1当中的数存到R2的地址中
	ldr r3,[r2]        @把内存R2地址中的数据读取到CPU R3寄存器中

1> 前索引

	mov r1,#0xffffffff
	mov r2,#0x40000000
	str r1,[r2,#8] @基址加变址寻址 

2>后索引

mov r1,#0xffffffff
mov r2,#0x40000000
str r1,[r2],#4		@将R1寄存器的内容存到[R2]地址,然后R2=R2+4

目的:可以做连续存储,压栈时用的比较多 存完一个数,他就把地址自动指向下一个了

3>自动索引(前后索引)

MOV R1,#0XFFFFFFFF
MOV R2,#0X40000000
STR R1,[R2,#4]! ; 将R1寄存器的内容存到【R2+4】地址,然后R2=R2+4

LDR同样支持三种索引方式

批量寄存器操作指令ldm/stm

1、将r1到r4中的值存储到r0指向地址空间中,连续16个字节的地址空间

stm r0, {r1-r4}

2、将r0指向的地址空间中,连续的16个字节的数据,读到r5-r8寄存器中

ldm r0, {r5-r8}

3、如果寄存器列表中的寄存器编号既有连续又有不连续,连续的使用 - 隔开 不连续的使用 ,

stm r0, {r1-r3,r5}

4、不管寄存器列表中的寄存器编号顺序如何变化,都是小地址对应小编号的寄存器高地址对应大编号的寄存器

stm r0,{r4,r2,r1,r3}

ldm r0,{r8,r6,r5,r7}

栈的操作指令 stmfd / ldmfd

栈的种类

空栈(Empty)

栈指针指向的地址是空的,在栈中存储数据时,可以直接存储,存储完成之后需要将栈指针再次指向空的位置。

满栈(Full)

栈指针指向的地址有数据,在栈中存储数据时,需要先将栈指针,指向一个空的位置,然后在存储数据。

增栈(Ascending)

栈指针向高地址方向移动

减栈(Descending)

栈指针向低地址方向移动

操作栈的方式

满增栈,满减栈,空增栈,空减栈

FA:Full Ascending 满增

FD:Full Descending 满减

EA:Empty Ascending 空增

ED:Empty Descending 空减

ARM默认采用的是满减栈

stmfd/ldmfd<code> sp!, {寄存器列表}

stmfd sp!, {r1-r5}(写) (压栈)

更新栈指针指向的地址空间

ldmfd sp!, {r6-r10}(读) (出栈)

特殊:(寄存器不连续)

stmfd sp!, {r1-r5,lr}(写) (压栈

ldmfd sp!, {r6-r10,pc}(读) (出栈) //r1-r5出栈给r6-r10, 将lr的值出栈给pc

程序中运用

-栈的应用-》叶子函数的调用过程

叶子函数是指一个函数内部没有调用其他函数的函数,也就是说,它是程序调用树的末端节点,不依赖于其他函数。

	MOV SP,#0X40000020
MAIN:
	MOV R1,#3
	MOV R2,#2
	BL F
	ADD R3,R1,R2
T:
	B T
F:
	STMFD SP!,{R1,R2}
	MOV R1,#5
	MOV R2,#4
	ADD R3,R1,R2
	LDMFD SP!,{R1,R2}
	MOV PC,LR

-栈的应用-》非叶子函数的调用过程

非叶子函数是指一个函数内部调用了其他函数的函数,也就是说,它不是程序调用树的末端节点,可以被其他函数调用。

	MOV SP,#0X40000020
MAIN:
	MOV R1,#3
	MOV R2,#2
	BL F
	ADD R3,R1,R2
T:
	B T
F:
	STMFD SP!,{R1,R2,LR}
	MOV R1,#5
	MOV R2,#4
	BL D
	ADD R3,R1,R2
	LDMFD SP!,{R1,R2,LR}
	MOV PC,LR
D:
    STMFD SP!,{R1,R2,LR}
	MOV R1,#6
	MOV R2,#7
	ADD R3,R1,R2
	LDMFD SP!,{R1,R2,LR}
	MOV PC,LR

  1. 状态寄存器操作指令

注:刚上电是在SVC模式下

对CPSR进行读写操作

//其他都不能动CPSR (SWI 指令是linux内核有,所以arm为了匹配才有的指令)(CPSR保存cpu的状态、模式、中断中断开关、运算状态,非常重要,不能任意更改,只有一类指令能操作这个寄存器)

读cpsr指令 mrs

写cpsr指令 msr

一般情况不能修改cpsr,只能用msr命令修改,user模式下不能切换到其他模式。

	@状态寄存器操作指令		mrs / msr
	ldr r1,=0x12345678
	mrs r0,cpsr			@读cpsr的值到r0
	msr cpsr_c,#0x1f	@修改cpsr的值,cpsr_c 指的是后八位控制位
	msr cpsr_c,#0x13	@切换到svc
	msr cpsr_c,#0x10	@切换到user,非特权模式
	ldr r2,=0x87654321
	msr cpsr_c,#0x13	@切换到svc

流程分析

注:修改CPSR的控制域(bit[7:0]),修改CPSR时必须指定修改哪个区域;USER模式下不能修改CPSR的值,防止应用程序修改CPU状态,保护操作系统;CPSR_C修改的是CPSR的低八位ctrl(控制)域,一般都只修改C域

  1. 异常中断指令

触发软中断,常用于内核的系统调用 //SWI:软中断

@异常中断指令		软中断指令swi
	ldr r1,=0x12345678
	ldr r2,=0x22345678
	ldr r4,=0x42345678
	msr cpsr_c,#0x10	@切换到user模式
	ldr r3,=0x32345678
	swi #1		@软中断
	@执行软中断指令:spsr、lr、cpsr、pc 均发生了改变	ldr r6,=0x52345678

过程分析

为什么会出现以上情况?做完的请先自行阅读下方文档

异常源及处理过程

  1. 协处理器指令

操作协处理器的指令(一般用不到-----协助cpu处理数据)

1.数据运算
2.内存访问
3.与主处理器通信
MRC 将协处理器中寄存器的内容读取到ARM处理器的寄存器中
MCR 将ARM理器中寄存器的内容读取到协处理器的寄存器中

协处理器指令

  1. 协处理器数据运算指令

CDP

  1. 协处理器储存器访问指令

STC 将协处理器中的数据储存到存储器

LDC 将存储器中的数据读取到协处理器中

  1. 协处理器寄存器传送指令

MRC 将协处理器中寄存器的数据传送到ARM处理器中的寄存器

MCR 将ARM处理器寄存器中的数据读取到协处理器寄存器中

伪指令

本质:本身不是指令,但是cpu替换成等效的操作。

举例1
延时一个指令周期(耗时一条指令的时间)  (cpu没有这个指令)
	NOP    ;执行NOP和MOV R0,R0一个效果,执行NOP,cpu替换成MOV R0,R0
	MOV R0,R0  
LDR的两种形式
;->指令
        LDR R1,[R2]     
;->伪指令
	    LDR R1,=0x12345678     ;R1 = 0x12345678
;可以将任何一个32bit的数据放入寄存器

伪操作

指令是arm公司规定的,而伪操作是编译器规定的,不同的编译器伪操作指令不同。

(我们学的linux,用linux的编译器)

ARM重点:CPU控制硬件原理

开发板介绍

本地开发和交叉开发

  • 本地开发:本地编写代码,本地编译代码,本地运行代码
  • 交叉开发:本地编写代码,本地编译代码,开发板运行代码

交叉编译工具链

交叉编译工具链的安装

所在位置

cpu控制硬件原理

我们学习的所有指令,六大指令里边,只有内存访问指令(ldr/str)能访问cpu之外的内容。那cpu如何控制硬件?

load/store指令 --》操作外设寄存器

芯片有一个地址映射表。告诉你地址空间是如何映射的,便于我们找到对应的硬件地址。

我们的SoC型号是S5P6818,对应的芯片用户手册为:S5P6818X用户手册V0.00

S5P6818X用户手册V0.00

其中一章是:Memory map或Memory Controller中的一张表中可以看地址隐射关系。

硬件控制原理:

CPU不能直接控制硬件,硬件是由其对应的寄存器(存储器)来控制的

每个寄存器(存储器)都会映射到CPU寻址范围内的一段空间

CPU通过对寄存器(存储器)的读和写实现对硬件间接的控制

LED(红灯)实验

    1. 查看电路原理图

FS6818底板原理图

FS6818核心板原理图

查找时对应硬件板子上有控制灯的名字:RGBLED1,对应查看底板原理图。

    1. 查看用户芯片手册

第二章介绍了每个IO引脚的作用。---》function Table查看功能(重点看:function 0-3)

找到引脚根据自己的情况选择对应功能。

GPIO:通用的输入输出接口。 一共160个,ABCDE每组32个。

CPIOA28:A组的第28个

  1. 查看对应GPIO硬件映射的地址(寄存器:这里将能控制硬件的空间叫寄存器,在cpu外,有地址)--》找到寄存器描述(Register Description)

引脚是硬件,硬件控制都会隐射到内存的一个地址。所以操作硬件就是操作内存。

每个寄存器怎么控制实现不同功能:

1.GPIOxALTFN0和GPIOxALTFN1两个寄存器:控制一个GPIO引脚功能,A类GPIO有32个引脚,即需要八个字节管理A类GPIO引脚。 ‘x’--》ABCDE

2.GPIOxOUTENB寄存器:

  1. GPIOxOUT寄存器:

    1. 实验步骤简写

1.分析电路原理图得出LED的控制方法----》高电平亮/低电平灭

2.分析电路原理图得出LED与SOC的连接关系 - GPIOA28

3.分析芯片手册,找到对应的寄存器将GPIOA28设置成GPIO功能->GPIOAALTFN1(0xC001A024)

4.分析芯片手册,找到对应的寄存器将GPIOA28设置成OUTPUT功能->GPIOAOUTENB(0xC001A004)

5.分析芯片手册,找到对应的寄存器将GPIOA28设置输出高/低->GPIOAOUT(0xC001A000)

    1. 汇编代码实现

1》控制led灯亮代码

.text		
.global _start
_start:			/*
	   	GPIOAALTFN1
	 	地址:0xC001A024	数据:0x00000000
		GPIOAOUTENB
		地址:0xC001A004	数据:0x10000000
		GPIOAOUT
		地址:0xC001A000	数据:0x10000000/0x00000000
	 */	@ 设置GPIOA28为GPIO功能
	LDR R1,=0x00000000
	LDR R2,=0xC001A024
	STR R1,[R2]	@ 设置GPIOA28为OUTPUT功能
	LDR R1,=0x10000000
	LDR R2,=0xC001A004
	STR R1,[R2]	@ 设置GPIOA28输出低电平(因为本来就是亮的,这里让他熄灭)
	LDR R1,=0x00000000
	LDR R2,=0xC001A000
	STR R1,[R2]STOP:
       B STOP      @死循环      
.end		

虚拟机中编译生成的二级制文件.bin 如何在板子上运行?

参考步骤如下:

C语言实现绿灯闪烁

  1. 找绿灯

  1. 启用功能(往地址写值)

void delay_ms(unsigned int ms)
{
	unsigned int i,j;
	for(i = 0; i < ms; i++)
		for(j = 0; j < 1800; j++);
}int main()
{
	*(unsigned int *)0xC001E000 |= 1<<13;
	*(unsigned int *)0xC001E004 |= 1<<13;
	*(unsigned int *)0xC001E020 =0;
	while (1)
	{
		*(unsigned int *)0xC001E000 &= ~(1<<13);
		delay_ms(200);
		*(unsigned int *)0xC001E000 |= 1<<13;
		delay_ms(200);
	}
}

代码优化:结构体实现

void delay_ms(unsigned int ms)
{
	unsigned int i,j;
	for(i = 0; i < ms; i++)
		for(j = 0; j < 1800; j++);
}
typedef struct 
{
	unsigned int OUT;
	unsigned int OUTENB;
	unsigned int A;
	unsigned int B;
	unsigned int C;
	unsigned int A1;
	unsigned int B1;
	unsigned int C1;
	unsigned int ALTFN0;
	unsigned int ALTFN1;
}GPIO;#define GPIOE (*(GPIO *)0XC001E000)
#define GPIOA (*(GPIO *)0XC001A000)
int main()
{
	GPIOA.OUT &= ~(1<<28);
	GPIOA.OUTENB |= 1<<28;
	GPIOA.ALTFN1 =0;
	GPIOE.OUT |= 1<<13;
	GPIOE.OUTENB |= 1<<13;
	GPIOE.ALTFN0 =0;
	while (1)
	{
		GPIOE.OUT &= ~(1<<13);
		delay_ms(200);
		GPIOE.OUT |= 1<<13;
		delay_ms(200);
	}
}

代码终极优化:一条函数实现GPIO配置

自己实现

卡里没东西需要自己烧录

将SD卡插到读卡器

将读卡器插到电脑上

  1. 虚拟机--》可移动设备--》realtek USB3.0-CRW---》连接

  1. 把资料包里的sdtool压缩包放到虚拟机里,并解压(sudo tar -xvf sdtool.tar.xz

4、在ubuntu中执行命令

sudo ./s5p6818-sdmmc.sh /dev/sdb ubootpak.bin

如果打印一下信息表示,部署成功:

6、688+1 records in

689+0 records out

352768 bytes (353 kB, 344 KiB) copied, 0.445815 s, 791 kB/s

^_^ The image is fused successfully

总结:报SD卡只读的错误,将sd卡中开关拨到lock的位置,lock靠近sd卡的触点位置

7、测试sd卡是否制作成功

将sd卡插到开发板板上,

设置开发板上的拨码开关,为sd卡启动

8、开发板重新上电,启动成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/415687.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux小程序-进度条

文章目录 pro.hpro.cmain.cmakefile测试 pro.h #pragma once#include <stdio.h>typedef void(*callback_t)(double, double);void probar(double total, double current);pro.c #include "pro.h" #include <string.h> #include <unistd.h> #defi…

webshell绕过样本初体验

目录 一&#xff1a;前景 二&#xff1a;样本 样本一&#xff1a; 样本二&#xff1a; 样本三&#xff1a; 样本4&#xff1a; 样本5&#xff1a; 一&#xff1a;前景 在我们日常的网站中百分之一百是存在一些安全设备来拦截我们的webshell的&#xff0c;一般情况…

Kafka大厂面试14问(附答案)

怎么保证顺序消费&#xff1f; 同一个生产者发送到同一分区的消息&#xff0c;先发送的比后发送的offset要小。同一生产者发送到不同分区的消息&#xff0c;消息顺序无法保证。 怎么解决这个问题&#xff1f; 给一个topic只设置一个分区 相同key会发给一个分区 怎么保证幂…

[有彩蛋]大模型独角兽阶跃星辰文生图模型Step-1X上线,效果具说很炸裂?快来看一手实测!

先简单介绍一下阶跃星辰吧 公司的创始人兼CEO是姜大昕博士&#xff0c;他在微软担任过全球副总裁&#xff0c;同时也是微软亚洲互联网工程研究院的副院长和首席科学家。 2024年3月&#xff0c;阶跃星辰发布了Step-2万亿参数MoE语言大模型预览版&#xff0c;这是国内初创公司首…

Centos7通过reposync搭建本地Yum源

目录 1. 服务端搭建 1.1. 安装相关软件包 1.2. 加载几个常用的yum源 1.3. 创建文件保存目录 1.4. 把各仓库同步到本地 1.5. 生成仓库信息 1.6. 定时任务更新仓库 1.7. nginx配置下载服务 1.8. 内网测试nginx服务配置是否正确 2. 客户端配置 前言&#xff1a;之前使用…

Nginx负载均衡数据流分析

1、各机器ip信息 客户端IP&#xff1a;192.168.3.239 Nginx代理服务器IP&#xff1a;192.168.3.241 服务端IP&#xff1a;192.168.3.238 2、架构图&#xff08;略&#xff09; 3、 下图是在服务端上面的抓包分析。 下图是在客户端上面的抓包分析&#xff1a; 下图是在代理服务…

动态路由和路由导航守卫及其案例分析

为什么需要动态路由&#xff1f; 动态路由其实用的不多&#xff0c;在实际开发中&#xff0c;如果遇到权限分配问题&#xff0c;比如对于一个公司人员的后台管理系统&#xff0c;那对不同成员的权限肯定不同&#xff0c;对于人事部&#xff0c;他们有权限进入成员表对人员的流…

PHP8、ThinkPHP8框架中间的应用教程详解

前言 虽然PHP的落幕的话题一直不绝&#xff0c;但是实际在WEB端项目中PHP占有率达到了70%以上&#xff0c;一直在WEB一枝独秀&#xff0c;它以快速、高效的开发闻名&#xff0c;出圈了几十年&#xff0c;等待只是下一次的涅槃。而经过PHP8、PHP9的演变发展&#xff0c;PHP逐渐…

【Linux网络编程】协议|OSI模型|TCP/IP模型|局域网通信|跨网络通信|地址管理|流程图

目录 ​编辑 一&#xff0c;协议 协议分层 二&#xff0c;OSI七层模型 三&#xff0c;TCP/IP五层&#xff08;或四层&#xff09;模型 TCP/IP各个层次一些名词解释 为什么要有TCP/IP协议 TCP/IP协议栈与操作系统的宏观关系示意图 四&#xff0c;网络传输基本流程 局…

【书生大模型实战营】MindSearch CPU-only 版部署

MindSearch CPU-only 版部署 MindSearch CPU-only 版部署任务步骤 MindSearch CPU-only 版部署 任务 将 MindSearch 部署到 HuggingFace 并美化 Gradio 的界面&#xff0c;并提供截图和 Hugging Face 的Space的链接。 步骤 按照官方教程&#xff0c;实现在网页上打开MindSe…

llama_factory Qlora微调异常 No package metadata was found for The ‘autoawq‘

importlib.metadata.PackageNotFoundError: No package metadata was found for The ‘autoawq’ distribution was not found and is required by this application. To fix: pip install autoawq 其实问题比较简单 直接安装autoawq 即可 但是对应会有版本问题&#xff1a; 查…

Python自适应光学模态星形小波分析和像差算法

&#x1f3af;要点 &#x1f3af;星形小波分析像差测量 | &#x1f3af;对比傅里叶和小波分析 | &#x1f3af;定义多尺度图像质量度量&#xff0c;矩阵数据 | &#x1f3af;像差校正算法 | &#x1f3af;受激发射损耗显微镜布局 | &#x1f3af;干涉仪分支校准&#xff0c;求…

【unity实战】使用新版输入系统Input System+Rigidbody实现第三人称人物控制器(附项目源码)

最终效果 前言 使用CharacterController实现3d角色控制器&#xff0c;之前已经做过很多了&#xff1a; 【unity小技巧】unity最完美的CharacterController 3d角色控制器&#xff0c;实现移动、跳跃、下蹲、奔跑、上下坡、物理碰撞效果&#xff0c;复制粘贴即用 【unity实战】C…

快速入门Go:Go + gin + MongoDB

Go 进阶:Go + gin + MongDB 极速搭建EcommerceSys电商系统 前言 本章节适合有一定基础的 Golang 初学者,通过简单的项目实践来加深对 Golang 的基本语法和 Web 开发的理解。 本文章持续更新中,请关注… 项目结构 项目流程图 技术栈(待补充)项目结构项目路由 (待补充) …

DataWhale AI夏令营-《李宏毅深度学习教程》笔记-task3

DataWhale AI夏令营-《李宏毅深度学习教程》笔记-task2 第五章 循环神经网络5.1 独热编码5.2 RNN架构5.3 其他RNN5.3.1 Elman 网络 &Jordan 网络5.3.2 双向循环神经网络 第五章 循环神经网络 循环神经网络RNN&#xff0c;RNN在处理序列数据和时间依赖性强的问题上具有独特…

51单片机——实时时钟

1、DS1302介绍 DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时&#xff0c;且具有闰年补偿等多种功能 RTC(Real Time Clock)&#xff1a;实时时钟&#xff0c;是一种集成电路&#xff0c;通常称为时钟…

机器学习项目——基于机器学习(RNN LSTM 高斯拟合 MLP)的锂离子电池剩余寿命预测方法研究(代码/论文)

完整的论文代码见文章末尾 以下为核心内容和部分结果 摘要 机器学习方法在电池寿命预测中的应用主要包括监督学习、无监督学习和强化学习等。监督学习方法通过构建回归模型或分类模型&#xff0c;直接预测电池的剩余寿命或健康状态。无监督学习方法则通过聚类分析和降维技术&…

VS-E5PH3006L-N3 600V 30A 高效低损耗整流器 二极管 电动 / 混动汽车电池充电的可靠之选

VS-E5PH3006L-N3参数特性&#xff1a; 反向电压&#xff08;VR&#xff09;&#xff1a;600V&#xff0c;这表示该整流器在电路中能承受的最大反向电压为 600 伏特&#xff0c;超过此电压可能会导致器件损坏。平均整流电流&#xff08;IF (AV)&#xff09;&#xff1a;30A&…

【系统架构设计】嵌入式系统设计(2)

【系统架构设计】嵌入式系统设计&#xff08;2&#xff09; 嵌入式网络系统嵌入式 Internet 的接入方式嵌入式 TCP/IP 协议栈 嵌入式数据库管理系统数据的一致性高效的事务处理数据的安全性 实时系统与嵌入式操作系统对实时系统划分根据实时性的强弱根据对错失时限的容忍程度或…

Linux学习-虚拟化平台安装和使用

注&#xff1a;系统使用Rock8.6 下载链接 通过百度网盘分享的文件&#xff1a;cirros.qcow2&#xff0c;node_base.xml等2个文件 链接&#xff1a;https://pan.baidu.com/s/1hupGQsMjrXMgngCy3lQLhw?pwdhlr6 提取码&#xff1a;hlr6[rootharbor ~]# cat /etc/redhat-releas…