自学内容网 自学内容网

ARM总复习

1.计算机的组成

输入设备 输出设备 存储设备 运算器 控制器、总线

2.指令和指令集

2.1 机器指令

机器指令又叫机器码,在运算器内部存在各种运算电路,当处理器从内存中获取一条机器指令,就可以按照指令让运算器内部的指定的运算电路进行运算

2.2 汇编指令

由于机器指令对人类不友好(记不住),当我们和计算机进行交互时不能直接通过机器指令来进行,这个时候汇编就出现了,每一条汇编指令都可以理解为一条机器指令的标识,当我们编写一条汇编指令时,相当于得到了一条机器指令,就可以让运算器进行对应的运算

2.3 指令集

指令集就是指令的集合。当设计一个处理器内核时,需要提前设计内核中应该有的运算器的运算电路的种类和数量,每一个运算电路都对应一条指令。所以设计内核之前先将每一条运算电路对应的指令设计出来,组成一个指令集,用于作为内核设计的框架。

2.4 一条指令的执行过程分析

  • 取指阶段:控制器将要执行的指令地址发送给内存,内存将指令返回给控制器
  • 译码阶段:对取到的指令进行译码操作,分析当前指令的意义,通知运算器进行对应运算
  • 执行阶段:指令译码完毕后运算器进行对应的运算操作,将运算的结果进行保存

3 复杂指令集

CISC:Complex Instruction Set Computer 侧重于硬件执行指令的功能性,CISC指令集处理器的硬件结构复杂。

CISC指令复杂,指令长度与周期不固定,在处理能力上有优势,一般相对应功耗、体积成本也会更高

指令长度:一条指令占用的存储空间的大小

指令的周期:执行一条指令的时间

4 精简指令集

RISC:Reduced Instruction Set Computer RISC结构简单,选取了使用频率高的简单指令,指令长度固定,指令大小固定,多为单周期指令。在功耗、体积、价格等方面有很大优势,多用于嵌入式领域

5 ARM的产品分布

Cortex-A系列

Cortex-A系列 的处理器一般是arm的高端芯片,用于进行复杂的逻辑处理,在手机、pad或者手持电脑等方面都被广泛的应用

Cortex-R系列

Cortex-R系列 用于对实时性要求比较高的场景,要求响应速度快 一般用于通信领域或者汽车、医疗等方面

Cortex-M系列

Cortex-M系列是ARM的低端产品,基于Cortex-M系列 内核设计的芯片可以进行一些设备的基本控制和数据通信,但是没有办法进行复杂的数据处理 Cortex-M系列 的芯片成本低,也能够完成日常的开发使用,所以被广泛的应用

6.ARM处理器的工作模式

ARM处理器基本的工作模式有7种(user\fiq\irq\undef\abort\system\svc),cortex-A系列还有两种特有的模式:moitor模式以及hyp

7.ARMv7架构下的一些特殊寄存器

7.1 R15寄存器

R15寄存器又被称为(PC:program counter)程序计数器。 PC寄存器保存即将被从内存中取出来的指令的地址 当PC保存的指令地址被处理器发送给内存之后PC的值会自动向下加一条指令的大小, 这也是程序可以继续向下顺序执行的原因

实现程序跳转的本质就是修改PC的值

一条指令的执行过程:

取指:将要执行的指令从内存中取出来

译码:对取出来的指令进行分析和译码操作

执行:将译码之后的指令交给运算器进行运算处理

7.2 R14寄存器

R14又被称为(LR:link register)链接寄存器 LR寄存器的作用是当执行指令时进行程序跳转时,LR会保存跳转指令下一条指令的地址,方便程序返回

7.3 R14寄存器

R14又被称为(LR:link register)链接寄存器 LR寄存器的作用是当执行指令时进行程序跳转时,LR会保存跳转指令下一条指令的地址,方便程序返回

7.4 CPSR/SPSR

CPSR寄存器又叫(current program status register)当前程序状态寄存器 这个寄存器的作用是保存当前程序的工作状态 工作状态中包含程序的工作模式、中断禁止位以及程序的运算结果条件位等信息

SPSR寄存器又叫(saved program status register)被保存的程序状态寄存器 这个寄存器当处理器因为各种异常切换到异常模式工作时会将CPSR值提前保存到SPSR中,处理器处理完异常之后会将SPSR的值 赋值给CPSR,用于恢复处理器的工作状态

8【汇编指令】

1.数据搬移指令

mov{条件码} 目标寄存器,第一操作数 解释:将第一操作数的值搬移到目标寄存器中,第一操作数可以是一个立即数,也可以是一个寄存器

mvn{条件码} 目标寄存器,第一操作数 解释:将第一操作数的值按位取反后搬移到目标寄存器中,第一操作数可以是一个立即数,也可以是一个寄存器

2 立即数

能够当作指令的一部分直接去执行的操作数就是立即数。在指令32位空间中为操作数预留了12位的空间,操作数经过处理后能够保存到12位的空间中就是立即数,否则就不是

想要将数据当作指令的一部分保存到指令空间中,需要进行以下步骤:

假设存在立即数x,将x循环右移偶数位y,将会得到一个0-255内的数据z.此时,就说明x是一个立即数,当x当作指令的一部分执行时,实际保存到指令空间的时数据y和数据z,在指令空间的[7:0]保存的是数据z,[11:8]保存的是数据(32-y)/2.

循环右移:最低位移出的数据补到最高位

ex:0X104是一个立即数

注意:判断一个数据是不是立即数时,只需要判断这个数据或者它的取反值能不能循环右移偶数位得到一个0-255内的数据,如果能吗,这个数据就是一个立即数

如果想要将一个非立即数放到寄存器中,可以使用一个伪指令ldr

格式:ldr 目标寄存器,=数据

2.移位运算指令

逻辑左移:一个32位数据进行逻辑左移,最高位移出,最低位补0

逻辑右移:一个32位的数据进行逻辑右移,最低位移出,最高位补0

循环右移:一个32位的数据进行循环右移,最低位移出,补到最高位

lsl{条件码} 目标寄存器,第一操作寄存器,第二操作数 解释:将第一操作寄存器的数值进行逻辑左移第二操作数对应的位数,结果保存到目标寄存器中

lsl r1,r0,#3 //将R0的值左移3位,结果保存到r1

lsr{条件码} 目标寄存器,第一操作寄存器,第二操作数 解释:将第一操作寄存器的数值进行逻辑右移第二操作数对应的位数,结果保存到目标寄存器中

ror{条件码} 目标寄存器,第一操作寄存器,第二操作数 解释:将第一操作寄存器的数值进行循环右移第二操作数对应的位数,结果保存到目标寄存器中

9.单寄存器内存读写指令

通过指令将一个寄存器的数据向内存中写或者从内存中读取数据保存到一个寄存器中

9.1 指令码以及格式

向内存写: str 目标寄存器,[保存目标地址的寄存器] @将目标寄存器4字节数据写入到目标地址对应的内存中

strh 目标寄存器,[保存目标地址的寄存器] @将目标寄存器2字节数据写入到目标地址对应的内存中

strb 目标寄存器,[保存目标地址的寄存器] @将目标寄存器1字节数据写入到目标地址对应的内存中

从内存读: ldr 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取4字节数据写入到目标寄存器

ldrh 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取2字节数据写入到目标寄存器

ldrb 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取1字节数据写入到目标寄存器

9.2 单寄存器内存读写常见地址索引方式

方法1

str 目标寄存器,[保存目标地址的寄存器,#操作数]

解释:将目标寄存器的数据写入到目标地址+操作数为首地址的内存中

ldr 目标寄存器,[保存目标地址的寄存器,#操作数]

解释:从目标地址+操作数为首地址的内存中读取数据写入到目标寄存器

方法 2

str 目标寄存器,[保存目标地址的寄存器],#操作数

解释:将目标寄存器的数据写入到目标地址为首地址的内存中,然后保存目标地址的寄存器数值变为原来的数据+操作数大小

ldr 目标寄存器,[保存目标地址的寄存器],#操作数

解释:从目标地址为首地址的内存中读取数据写入到目标寄存器,然后保存目标地址的寄存器数值变为原来的数据+操作数大小

10.批量寄存器内存读写

写:将多个寄存器的数据写入到指定内存中

读:从指定的内存中读取数据写入到多个寄存器

10.1 指令码以及格式

写: stm 保存目标地址的寄存器,{寄存器列表}

解释:将寄存器列表中各个寄存器的数据写入到目标地址对应的内存中

读: ldm 保存目标地址的寄存器,{寄存器列表}

解释:从目标地址对应的内存中读取数据保存到寄存器列表中的各个寄存器中

注意: 1.寄存器列表中寄存器编号如果连续,可以用-表示一个寄存器范围 {r1-r5}

如果寄存器编号散乱,则用 ,分隔每一个寄存器 {r1,r3,r5,r7} {r1-r3,r5,r7}

2.无论寄存器列表中寄存器的顺序是什么样的,低内存地址始终和小编号寄存器对应

10.2 批量寄存器内存读写时的地址增长方式

当指定一个内存基地址后,我们对这个基地址内存进行读写操作,然后根据地址增长方式可以让我们操作的基地址发生变化

stm{地址增长后缀} 保存目标地址的寄存器!,{寄存器列表}

解释:将寄存器列表中的寄存器数据写入到目标地址中时,保存目标地址的寄存器保存的目标地址会发生对应的改变

地址增长后缀:

ia:先向目标地址对应的内存中写入数据,然后保存目标地址的寄存器保存的目标地址数值+4

ib:先让保存目标地址的寄存器保存的地址+4,再向目标地址对应的内存中写入数据

da:先向目标地址对应的内存中写入数据,然后保存目标地址的寄存器保存的目标地址数值-4 db:先让保存目标地址的寄存器保存的地址-4,再向目标地址对应的内存中写入数据

ldm指令同样遵循这个规则

11.栈内存读写

11.1 栈的介绍

一般在内存中会指定一段内存作为栈区内存

栈内存用来保存一些临时数据

操作栈内存需要知道栈顶的地址,而栈顶地址保存在SP寄存器中

根据对于栈内存的读写的不同方式,我们会划分出不同类型的栈

划分标准:

增栈:每次压栈结束后栈指针寄存器保存的栈顶地址往高地址方向增长

减栈:每次压栈结束后栈指针寄存器保存的栈顶地址往低地址方向增长

空栈:每次压栈结束后栈指针寄存器指向的栈顶空间没有保存的有效数据

满栈:每次压栈结束后栈指针寄存器指向的栈顶空间存在有效数据

根据上面的划分标准,栈可以分为空增栈(EA)、空减栈(ED)、满增栈(FA)、满减栈(FD)

注意:ARMv7-a架构处理器默认使用满减栈实现压栈和出栈

压栈命令: push {寄存器列表} @将寄存器列表中每一个寄存器的数值进行压栈

出栈命令: pop {寄存器列表}

11.2 手动实现满减栈

方法1:db后缀压栈 ia后缀出栈

方法2:使用满减栈对应的后缀fd实现

12.状态寄存器传送指令

内核中存在一个寄存器CPSR,这个寄存器保存的是当前程序的状态,我们可以通过状态寄存器传送指令实现对当前程序状态的读取以及修改程序的状态

1.MRS 目标寄存器,CPSR

解释:读取CPSR保存的状态值到目标寄存器中

2.MSR CPSR,操作数

解释:将操作数写入到CPSR寄存器中进而修改程序的状态

注意:在特权模式下可以通过修改cpsr寄存器的数值切换到USER模式 但是USER模式下无法通过修改CPSR数值切换到特权模式

想要在user模式下切换到其他模式,只要发生对应的事件后处理器自动进入对应的模式

13.软中断产生指令

13.1 指令码以及格式

软中断产生指令执行时会在处理器上发起一个软中断,处理器在软中断产生之后会进行对应的中断处理工作

格式: swi 中断号

解释:中断号是一个12位的立即数,不同的中断号用来标识不同的中断

14 异常模式和异常源

异常模式:当发生了异常之后处理器进入的工作模式就是异常模式

异常源:引发处理器进入异常模式的源头叫做异常源

5种异常模式和7种异常源

异常模式

异常源

IRQ 模式

产生了一个IRQ中断

FIQ模式

产生了一个FIQ中断

undef

处理器遇到未定义指令导致程序中止

SVC

复位

产生软中断

abort

存取数据异常导致程序中止

存取指令异常导致程序中止

14.1 异常向量表

ARM内部引入了异常向量机制,即在程序最开始的内存中预留了一段内存,这段内存中记录了处理每一种异常源的异常处理程序的地址,当产生了一个异常之后,处理器会在异常向量表中找到这个异常的异常处理程序并跳转过去执行

异常向量表的大小是32字节,这32字节分成8份,每一份4字节空间中都是一种异常处理程序的跳转命令,还有一份保留。每一种异常源在异常向量表中的位置都是固定的,无法改变。

14.2  ARM处理器异常处理过程分析(四大步三小步)

处理器自动完成

一.保存程序的工作状态到对应的异常模式的SPSR寄存器中

二.修改CPSR数值

        1.修改程序的工作模式为对应的异常模式[4:0]

        2.修改程序的工作状态为ARM状态[5]

        3.根据当前异常的优先级设置程序合适的中断禁止[7:6]

三.将程序的返回地址保存到对应的异常模式的LR寄存器中 四.修改PC寄存器的值为当前异常在异常向量表中的位置

14.3 处理异常完毕后的异常返回工作

手动完成

1.恢复程序状态 SPSR->CPSR

2.返回主程序执行 LR-》PC

15.PCB

PCB( Printed Circuit Board),中文名称为印制电路板,又称印刷线路板,是重要的电子部件,是电子元器件的支撑体,是电子元器件电气相互连接的载体。由于它是采用电子印刷术制作的,故被称为“印刷”电路板。

16.常用电器元件

电阻:保护电路,分压限流,温度越高阻值越大

二极管:单向导电

电容:通交流电,阻直流电,通过充电放电实现

16.1 三极管

三极管分为PNP和NPN两种,每一个三极管都有三个极,集电极(e) 、发射极(c)、基极(b)

NPN特点:当基极有一个高电平输入时,集电极和发射极导通

基极有一个低电平输入时,集电极和发射极截止

PNP特点:当基极有一个高电平输入时,集电极和发射极截止

当基极有一个低电平输入时,集电极和发射极导通

16.2 mos管

mos管分为N-MOS和P-MOS,每一个mos管都有一个栅极、源极和漏极

N-MOS特点:当栅极有一个高电平输入时,源极和漏极导通

当栅极有一个低电平输入时,源极和漏极截止

P-MOS特点:当栅极有一个高电平输入时,源极和漏极截止

当栅极有一个低电平输入时,源极和漏极导通

17【软件编程控制硬件的原理】

        在芯片内部的各个外设都会有自己的寄存器,就是外设控制寄存器,芯片厂商在设计芯片时会给各个外设的寄存器分配一个地址编号,外部外接的DDR3也会有自己的地址编号,这些外设寄存器和DDR分配的地址编号组成了一个连续的0-4G连续寻址空间。我们现在想要编写程序控制硬件,只需要知道用到的硬件的寄存器地址,通过对寄存器进行内存数据的读写就可以完成硬件的控制了

内核通用寄存器: 存在于处理器内部,由ARM设计,想要访问这些寄存器只需要知道寄存器编号

外设控制寄存器: 存在于芯片的各个外设内部,由芯片厂商设计,想要访问这些寄存器空间需要知道寄存器的内存地址

18.什么叫做GPIO

General-purpose I/Os (GPIO)是通用输入输出端口。在芯片内部会存在若干个GPIO端口用于输出高低电平,或者芯片可以读取GPIO端口的电平值,根据电平值做一些数据的处理。

GPIO控制器输出模式分为两种:推挽输出和开漏输出

推挽输出

GPIO内部有两个不同特性的mos管配合输出高低电平

开漏输出

开漏输出在GPIO内部只有一个mos管起作用,所以正常只能输出一种特性的电平,想要输出另一种电平,需要外接上拉电阻或者下拉电阻

输出高电平,需要外接上拉电阻

18.1 GPIO输出高低电平需要做的配置

1.使能GPIO控制器的外设时钟

2.设置PE10管脚为输出功能

3.设置管脚输出类型、输出速度、有无上拉下拉电阻

4.输出高低电平

18.2 编写LED控制代码---常亮 

.text 
.global _start
_start: 
    @设置GPIOE外设时钟使能  0X50000A28  [4]->1
    LDR R0,=0X50000A28
    LDR R1,[R0]@将寄存器原来的数据读取出来
    ORR R1,R1,#(0X1<<4) @将第4位设置为1
    STR R1,[R0]  @将修改后的数据写回内存

    @s设置PE10为输出功能   0X50006000  [21:20]->01
    LDR R0,=0X50006000
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X3<<20) @将第[21:0]位设置为0
    ORR R1,R1,#(0x1<<20)  @设置指定的数值
    STR R1,[R0]  @将修改后的数据写回内存

    @设置PE10为挽输出     0X50006004  [10]->0
    LDR R0,=0X50006004
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X1<<10) @将第[10]位设置为0
    STR R1,[R0]  @将修改后的数据写回内存

    @设置PE10为低速输出   0X50006008  [21:20]->00
    LDR R0,=0X50006008
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X3<<20) @将第[21:20]位设置为00
    STR R1,[R0]  @将修改后的数据写回内存

    @设置PE10输出无上拉下拉电阻  0X5000600C [21:20]->00
    LDR R0,=0X5000600C
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X3<<20) @将第[21:20]位设置为00
    STR R1,[R0]  @将修改后的数据写回内存

LOOP:
    @PE10输出高电平,灯亮   0X50006014 [10]->1
    LDR R0,=0X50006014
    LDR R1,[R0]@将寄存器原来的数据读取出来
    ORR  R1,R1,#(0X1<<10) @将第[21:20]位设置为00
    STR R1,[R0]  @将修改后的数据写回内存
    B LOOP


.end

18.3 编写LED控制代码---闪烁

.text 
.global _start
_start: 
    @设置GPIOE外设时钟使能  0X50000A28  [4]->1
    LDR R0,=0X50000A28
    LDR R1,[R0]@将寄存器原来的数据读取出来
    ORR R1,R1,#(0X1<<4) @将第4位设置为1
    STR R1,[R0]  @将修改后的数据写回内存

    @s设置PE10为输出功能   0X50006000  [21:20]->01
    LDR R0,=0X50006000
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X3<<20) @将第[21:0]位设置为0
    ORR R1,R1,#(0x1<<20)  @设置指定的数值
    STR R1,[R0]  @将修改后的数据写回内存

    @设置PE10为挽输出     0X50006004  [10]->0
    LDR R0,=0X50006004
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X1<<10) @将第[10]位设置为0
    STR R1,[R0]  @将修改后的数据写回内存

    @设置PE10为低速输出   0X50006008  [21:20]->00
    LDR R0,=0X50006008
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X3<<20) @将第[21:20]位设置为00
    STR R1,[R0]  @将修改后的数据写回内存

    @设置PE10输出无上拉下拉电阻  0X5000600C [21:20]->00
    LDR R0,=0X5000600C
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X3<<20) @将第[21:20]位设置为00
    STR R1,[R0]  @将修改后的数据写回内存

LOOP:
    BL LED1_ON
    BL delay
    BL LED1_OFF
    BL delay
    B LOOP

LED1_ON:
    @PE10输出高电平,灯亮   0X50006014 [10]->1
    LDR R0,=0X50006014
    LDR R1,[R0]@将寄存器原来的数据读取出来
    ORR  R1,R1,#(0X1<<10) @将第[21:20]位设置为00
    STR R1,[R0]  @将修改后的数据写回内存
    mov pc,lr    
LED1_OFF:
    @PE10输出低电平,灯灭   0X50006014 [10]->0
    LDR R0,=0X50006014
    LDR R1,[R0]@将寄存器原来的数据读取出来
    BIC  R1,R1,#(0X1<<10) @将第[10]位设置为0
    STR R1,[R0]  @将修改后的数据写回内存
    mov pc,lr    

delay:
    LDR R3,=0x10000000
mm:
    sub r3,r3,#1
    cmp R3,#0
    bne mm
    mov pc,lr

.end

19【总线相关概念】

19.1 什么是总线

总线:连接多个部件的信息传输线,是各部件共享的传输介质。

19.2 总线通信方式的划分

19.2.1 串行通信、并行通信
  • 串行通信:指的是同一时刻只能收或发一个bit位信息。因此只用1根信号线即可。
  • 并行通信:指的是同一时刻可以收或发多个bit位的信息,因此需要多根信号线才行

  • -串行传输:数据按位顺序传输。
    • -优点:占用引脚资源少
    • -缺点:速度相对较慢
  • 并行传输:数据各个位同时传输。
    • -优点:速度快
    • -缺点:占用引脚资源多
19.2.2 单工、全双工、半双工通信
  • 单工:要么收,要么发,只能做接收设备或者发送设备。
  • 半双工:可以收,可以发,但是不能同时收发,
  • 全双工:可以在同一时刻既接收,又发送

19.2.3 同步通信、异步通信

同步通信:

一般情况下同步通信指的是通信双方根据同步信号进行通信的方式。比如通信双 方有一个共同的时钟信号,大家根据时钟信号的变化进行通信

异步通信:

是指数据传输速度匹配依赖于通信双方有自己独立的系统 时钟,大家约定好通信的速度。异步通信不需要同步信号,但是并不 是说通信的过程不同步

UART:异步全双工串行通信

SPI:同步全双工串行

IIC:同步半双工串行

can:异步半双工串行

RS485:异步半双工串行

20.串口通信的硬件接线方式以及物理层约定

20.1 通过TTL格式电平进行数据收发

TTL标准电平抗干扰能力特别弱,只能用于同一块板子的两个芯片之间进行串口通信 

20.2 通过RS232标准电平进行串口通信

rs232进行串口通信最大传输距离能到15m 

21.串口通信的波特率和数据格式

21.1 串口的波特率

串口通信是异步通信方式,异步通信的双方进行数据收发时需要约定好数据传输速度

这个约定的速度就是波特率,单位是bps(位/s)

常用的波特率:115200 38400 9600 4800.。

21.2 串口数据帧格式

串口是异步通信,通信双方由于时钟系统的不一致,虽然约定好了波特率,但是随着数据的传输率的增加,会累积误差,最终导致数据失真。为了避免数据失真,我们需要设置一定的数据传输协议,用来校准双方时钟,避免误差的产生

空闲位:串口线在空闲状态下处于高电平状态

起始位:1bit起始位,低电平,数据传输开始的标志

数据位:5-8bit,传输的数据

校验位:用于进行数据传输的校验

奇校验:一帧数据中数据位的1的个数和校验位1的个数之和要是一个奇数 ex:0X15->0001 0101,如果发送数据0X15,设置奇校验,此时校验位会被设置为0 0X17->0001 0111,如果发送数据0X17,设置奇校验,此时校验位会被设置为1

偶校验:一帧数据中数据位的1的个数和校验位1的个数之和要是一个偶数 ex:0X15->0001 0101,如果发送数据0X15,设置偶校验,此时校验位会被设置为1 0X17->0001 0111,如果发送数据0X17,设置偶校验,此时校验位会被设置为0

停止位:1-2bit,高电平,一帧数据传输结束的标志,停止位期间校准双方时钟

22【串口实验】

22.1 实验目的

通过设置串口配置,让开发板和电脑能够通过串口进行数据收发

        设置一个管脚的复用功能需要一个寄存器的4位数,一个寄存器有32位,可以设置8个管脚的复用功能,一组GPIO有16个管脚,所以需要两个寄存器才可以完成16个管脚的复用功能选择 通过查询,一个管脚可以选择AF0-AF15总共16种复用功能

uart4.c

#include "uart4.h"


//进行串口4的初始化
void uart4_init()
{
    //使能UART4的外设时钟
    RCC->MP_APB1ENSETR |= (0x1<<16);
    //使能GPIOB的外设时钟
    RCC->MP_AHB4ENSETR |= (0x1<<1);
    //使能GPIOG的外设时钟
    RCC->MP_AHB4ENSETR |= (0x1<<6);
    //设置PB2为UART4_RX复用功能
    GPIOB->MODER &= (~(0x3<<4));
    GPIOB->MODER |= (0x1<<5);
    GPIOB->AFRL &= (~(0XF<<8));
    GPIOB->AFRL |= (0x8<<8);
    //设置PG11为UART4_TX复用功能
    GPIOG->MODER &= (~(0x3<<22));
    GPIOG->MODER |= (0x1<<23);
    GPIOG->AFRH &= (~(0XF<<12));
    GPIOG->AFRH |= (0x6<<12);
    //禁用串口  USART_CR1[0]->1
    USART4->CR1 &= (~0x1);
    //设置8bit数据位
    USART4->CR1 &= (~(0x1<<28));
    USART4->CR1 &= (~(0x1<<12));
    //设置16倍过采样
    USART4->CR1 &= (~(0x1<<15));
    //设置1bit停止位
    USART4->CR2 &= (~(0x3<<12));
    //设置无奇偶校验位
    USART4->CR1 &= (~(0x1<<10));
    //设置时钟不分频
    USART4->PRESC &= (~(0xf<<0));
    //设置波特率
    USART4->BRR=0x22b;
    //使能发送器
    USART4->CR1 |= (0x1<<3);
    //使能接收器
    USART4->CR1 |= (0x1<<2);
    //使能串口 
    USART4->CR1 |= (0x1<<0);
}

//发送单个字符
void putc(char ch)
{
    //d当发送数据寄存器中没有数据时,将ch的值写入到发送数据寄存器中
    while(!(USART4->ISR &(0x1<<7)));
    USART4->TDR=ch;
    //等待发送完成后函数执行完毕
    while(!(USART4->ISR &(0x1<<6)));
}

//接收单个字符
char getc()
{
    //等待接收数据寄存器中有数据,将接收数据寄存器的数据返回
    while(!(USART4->ISR &(0x1<<5)));
    return USART4->RDR;
}

//封装字符串的接收
void gets(char *rbuf)
{
    //循环调用字符接收函数
    //等到接收到'\r'时接收完毕
    //在末尾补一个'\0'

}

//封装字符串的发送函数
void puts(char *sbuf)
{
    //循环调用发送单个字符的函数
    //当发送到'\0'结束
    //循环结束后发送一个'\n'.再发送一个'\r'
}

23【按键中断实验】

23.1.实验目的

按键按下,触发中断,在中断处理函数中完成一些任务

23.2 按键消抖

按键按下的检测方式:

1.检测管脚是不是低电平(可以被检测到好多次)

2.检测下降沿(推荐)

3.检测上升沿(滞后性)

23.3.分析按键中断的实现过程

GPIOF:设置PF7 PF8 PF9三个管脚为输入模式

EXTI:检测PF7 PF8 PF9三个管脚产生一个下降沿事件,发送一个中断信号

GIC:对中断信号对应的中断设置优先级、选择处理中断的处理器以及全局使能中断等

23.4 GIC分析 

23.5 GIC功能概述

通过上图可知GIC总共管理0-287总共288个中断

其中0-15是软件产生中断

16-31是私有外设中断

32-287是共享外设中断

23.6 GICD分析

GICD_ISENABLER寄存器的功能是使能指定中断号的中断,中断只有被使能了,才能被处理器处理

寄存器每一位设置为1都可以使能一个中断

GIC总共管理了288个中断,一个寄存器只能使能32个中断

使能288个中断需要288/32=9,所以寄存器编号x的取值为0-8总共9个寄存器

三个按键中断的中断号是97 98 99

如何判断指定中断号的中断被哪个寄存器的哪一位使能?

计算公式: 中断号/32=商。。。余数

结果的商就是配置这个中断的寄存器编号

结果的余数就是设置的寄存器的位数

97/32=3.。。1 所以将GICD_ISENABLER3的第[1]设置为1就可以使能97号中断 98/32=3.。。2 所以将GICD_ISENABLER3的第[2]设置为1就可以使能98号中断 99/32=3.。。3 所以将GICD_ISENABLER3的第[3]设置为1就可以使能99号中断

key.c

#include"key_it.h"

void key1_it_config()
{

      //1.设置GPIOF时钟使能
      RCC->MP_AHB4ENSETR |= (0X1<<5);
     // 2.将PF9管脚设置为输入
     GPIOF->MODER &= (~(0x3<<18));
      //3.设置由PF9产生EXTI9事件
      EXTI->EXTICR3 &= (0XFF<<8);
      EXTI->EXTICR3 |= (0x05<<8);
     // 4.设置EXTI9事件的检测方式为下降沿检测
     EXTI->FTSR1 |= (0x1<<9);
     // 5.允许中断不屏蔽,可以被转发到GIC
     EXTI->C1IMR1 |= (0x1<<9);
     // 6.允许EXTI9(99号)中断被保存在组0中
     GICD->ISENABLER[3]|= (0x1<<3);
     // 7.设置99号中断优先级
     GICD->IPRIORITYR[24] &= (~(0x1f<<27));
     GICD->IPRIORITYR[24] |= (0x0<<27);
     // 8.设置99号中断可以被CPU0处理
     GICD->ITARGETSR[24] &= (~(0x3<<24));
     GICD->ITARGETSR[24] |= (0x1<<24);
      //9.允许99号中断被转发到CPU接口层
      GICD->CTRL |= 0x1;
      //10.设置中断优先级掩码
      GICC->PMR |= (0x1f<<3);
      //11.允许中断被转发给CPU处理
      GICC->CTRL |=0x1;
    

}
void key2_it_config()
{
     //RCC使能GPIOF时钟
    RCC->MP_AHB4ENSETR |= (0x1<<5);
     //pf7
    GPIOF->MODER &= (~(0x3<<14));
    //设置PF9 PF7 PF8产生EXTI事件 EXTI_EXTICRx
    //pf7
    EXTI->EXTICR2 &=(~(0xFF<<24));
    EXTI->EXTICR2 |= (0x5<<24);
     //设置事件触发方式为下降沿触发EXTI_FTSR1
    //pf7
    EXTI->FTSR1 |= (0x1<<7);
      //设置EXTI事件触发不屏蔽EXTI_IMR1
    //PF7
    EXTI->C1IMR1 |= (0x1<<7);
     //使能中断能转发到特定的CPU接口层GICD_ISENABLERx
    //PF7 97号中断
    GICD->ISENABLER[3] |= (0X1<<1); 
    // GICD_ISENABLERx设置中断优先级
    //PF7
    GICD->IPRIORITYR[24] &= (~(0X1F<<11)); 
      //设置当前中断被转发到哪一个CPU处理GICD_ITARGETSRx
    //pf7 97中断
    GICD->ITARGETSR[24]  &= (~(0X3<<8));
    GICD->ITARGETSR[24] |= (0X1<<8);
    
}
void key3_it_config()
{
     //RCC使能GPIOF时钟
    RCC->MP_AHB4ENSETR |= (0x1<<5);
    GPIOF->MODER &= (~(0x3<<16));
    EXTI->EXTICR3 &=(~(0xFF<<0));
    EXTI->EXTICR3 |= (0x5<<0);
    EXTI->FTSR1 |= (0x1<<8);
    EXTI->C1IMR1 |= (0x1<<8);
    GICD->ISENABLER[3] |= (0X1<<2); 
    GICD->IPRIORITYR[24] &= (~(0X1F<<19)); 
    GICD->ITARGETSR[24]  &= (~(0X3<<16));
    GICD->ITARGETSR[24] |= (0X1<<16);
}

do_irq.c

#include "key_it.h"
extern void printf(const char *fmt, ...);
unsigned int i = 0;
void do_irq(void)
{
    int irqno; // 保存中断号
   
    irqno = GICC->IAR & 0X3FF;
    switch (irqno)
    {
    case 99: // key1
        // 按键1中断处理
        printf("KEY1 INT\n");
        // 清除挂起中断标志位GICD_ICPENDRx
        GICD->ICPENDR[3] |= (0x1 << 3);
        // 清除中断触发标志位EXTI_FPR1
        EXTI->FPR1 |= (0x1 << 9);
        break;
    case 97: // key2
        // 按键2中断处理
        printf("KEY2 INT\n");
        // 清除挂起中断标志位GICD_ICPENDRx
        GICD->ICPENDR[3] |= (0x1 << 1);
        // 清除中断触发标志位EXTI_FPR1
        EXTI->FPR1 |= (0x1 << 7);
        break;
    case 98: // key3
        // 按键3中断处理
        printf("KEY3 INT\n");
        // 清除挂起中断标志位GICD_ICPENDRx
        GICD->ICPENDR[3] |= (0x1 << 2);
        // 清除中断触发标志位EXTI_FPR1
        EXTI->FPR1 |= (0x1 << 8);
        break;
    }
    // 清除处理完的中断号GICC_EOIR
    GICC->EOIR = irqno;
}

24【IIC理论】

24.1.IIC相关概述

I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工总线,主要用于连接整体电路。

I2C总线为两线制,只有两根双向信号线。一根是数据线SDA,另一根是 时钟线SCL。

优点:占用的管脚资源少,通信的可靠性比较高,支持一主机多从机通信 i2c总线上可以挂载好多不同的机器,主机可以跟任意从机之间进行通信 每一个i2从机都会有自己的从机地址

每个接到I2C总线上的器件都有唯一的地址。主机与其它器件进行数据传送时总线上发送数据的器件为发送器,总线上接收数据的器件则为接收器。

24.2  I2C总线上的通信过程分析

i2c总线进行数据传输时总是一个字节一个字节的传输,发送方发送一个字节数据后,接收方会回应一位的应答信号

i2c总线进行数据传输时需要根据时钟线进行数据传输,一个时钟周期传输一位数据

24.3  i2c进行数据传输过程中主机发送的几种信号

24.3.1 起始信号

起始信号是i2c总线进行数据传输的开始标志

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号

24.3.2 终止信号

SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信信号

起始和终止信号都是由主机发出,起始信号产生后,总线就处于占用的态;终止信号产生后,总线就处于空闲态。

24.3.3 i2c总线上的应答和非应答信号

i2c总线数据传输以8bit为单位进行传输,一个时钟周期传输1bit数据,所以传输8bit数据需要8个时钟周期

在第九个时钟周期下,接收方会回应一个应答或者非应答信号

应答信号:表示接收方接收到数据后并想要再次接收数据

非应答信号:表示接收方接收到数据后不想要再次接收数据了

在第九个时钟周期,如果数据线电平值是高电平就说明接收方回应了一个非应答信号,如果是在低电平,就说明接收方回应了应答信号

23.4 数据接收和发送的时机

I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定(此时接收方可以读取数据线上的数据)

时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化(此时发送方可以向数据线上写入本次的数据)。

24.5 I2C主从机通信的通信协议

主机给从机发送一个字节的数据

  • 主机发起起始信号
  • 主机发送7bit从机地址+1bit写标志位0
  • 从机回应应答信号
  • 主机发送8bit从机特定功能的寄存器地址
  • 从机回应应答信号
  • 主机发送8bit数据
  • 从机回应应答信号
  • 主机发起终止信号
主机向从机发送多个字节的数据

 

  • 主机发起起始信号
  • 主机发送7bit从机地址+1bit写标志位0
  • 从机回应应答信号
  • 主机发送8bit从机特定功能的寄存器地址
  • 从机回应应答信号
  • 主机发送8bit数据
  • 从机回应应答信号
  • 主机再发8bit数据
  • 从机再回应应答信号
  • 。。。。
  • 主机发起终止信号
主机读取从机的一个字节的数据

  • 主机发起起始信号
  • 主机发送7bit从机地址+1bit写标志位0
  • 从机回应应答信号
  • 主机发送8bit从机特定功能的寄存器地址
  • 从机回应应答信号
  • 主机发起一个重复起始信号
  • 主机发送7bit从机地址+1bit读标志位1’
  • 从机回应一个应答信号
  • 从机发送8bit数据
  • 主机回应非应答信号
  • 主机发起终止信号

  • 主机发起起始信号
  • 主机发送7bit从机地址+1bit写标志位0
  • 从机回应应答信号
  • 主机发送8bit从机特定功能的寄存器地址
  • 从机回应应答信号
  • 主机发起一个重复起始信号
  • 主机发送7bit从机地址+1bit读标志位1
  • 从机回应一个应答信号
  • 从机发送8bit数据
  • 主机回应应答信号
  • 从机再发送8bit数据
  • 。。。。
  • 主机回应非应答信号
  • 主机发起终止信号

25【I2C读取温湿度传感器数据实验】

25.1.实验目的

STM32MP157AAA通过I2C总线和温湿度传感器(si7006/sht20)进行通信,读取温湿度传感器上采集的温湿度数据

25.2.分析温湿度传感器芯片的数据手册

1.温湿度传感器内部的工作原理

2.温湿度传感器芯片的从机地址

3.读取温度和读取湿度的寄存器地址是多少

4.温湿度数据的计算

5.芯片如何进行初始化设置

6.查看温湿度传感器芯片进行I2C总线通信时的通信协议


原文地址:https://blog.csdn.net/m0_75102741/article/details/142338394

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!