正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-15-GPIO中断控制实验

前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

引用:

正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》

正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档

正文:

本文是 “正点原子[第二期]Linux之ARM(MX6U)裸机篇--第15.1, 15.2,15.3 讲” 的读书笔记。第15讲主要是介绍I.MX6U处理器GPIO中断控制实验。本节将参考正点原子的视频教程第15讲和配套的正点原子开发指南文档进行学习。

0. 概述

中断系统是一个处理器的重要的组成部分,中断系统极大的提高了CPU的执行效率,在学习STM32的时候就经常用到中断。本章就是通过与STM32的对比来学习一下 Cortex-A7(I.MX6U)中断系统和Cortex-M(STM32)中断系统的异同,同时,本章会将I.MX6U的一个IO作为输入中断,借此来讲解如何对I.MX6U的中断系统进行编程。

1. Conrtex-A7 中断系统详解

1.1 STM32中断系统回顾

STM32的中断系统主要有以下几个关键点:

  1. 中断向量表。
  2. NVIC(内嵌向量中断控制器,Nested Vector Interrupt Controller)。
  3. 中断使能。
  4. 中断服务函数。
1. 中断向量表

中断向量表是一个表,这个表里面存放的是中断向量。中断服务程序的入口地址或存放中断服务程序的首地址称为中断向量表,因此中断向量表是一些列中断服务程序入口地址组成的表。这些中断服务程序(函数)在中断向量表中的位置是由半导体厂商定好的,当某个中断被触发以后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。中断向量表在整个程序的组前面,比如 STM32F103 的中断向量表如下所示:

上图示例代码就是 STM32F103 的中断向量表,中断向量表都是链接到代码的最前面,比如一般ARM处理器都是从 0x00000000 开始执行指令的,那么中断向量表就是从 0x0000_0000 开始存放的。示例代码中的第一行 “__initial_sp” 就是第一条中断向量,存放的是栈顶指针,接下来第2行复位中断函数 Reset_Handler 的入口地址,依次类推,知道第27行的最后一个中断服务函数 DMA2_Channel4_5_IRQHandler 的入口地址,这样 STM32F103的中断向量表就建好了。

我们说ARM处理器都是从地址 0x0000_0000 开始运行的,但是我们学习STM32的时候代码是下载到 0x80_0000 开始的存储区中。因此中断向量表是存放到 0x80_0000 地址处的,而不是 0x0000_0000 ,这样不就出错了么?为了解决这个问题,Cortex-M 架构引入了一个新的概念-中断向量表偏移,通过中断向量表偏移就可以将中断向量表存放到任意地址处,中断向量表偏移配置在函数 SystemInit 中完成,通过向 SCB_VTOR 寄存器写入新的中断向量表首地址即可,代码如下所示:

第8行和第10行就是设置中断向量表偏移,第八行是将中断向量表设置到 RAM 中,第10行是将中断向量表设置到 ROM 中,也就是地址 0x80_0000 处。第10行用到了 FLAS_BASSE 和 VEC_TAB_OFFSET ,这两个都是宏,定义如下所示:

#define FLASH_BASE                ((uint32_t)0x08000000)

#define VECT_TAB_OFFSET      0x0 

第10行的代码就是:SCB->VTOR = 0x0800_0000,中断向量表偏移设置完成。通过上面的讲解我们了解了STM32中断有关的概念:中断向量表和中断偏移,那么这跟  I.MX6U 有什么关系呢?因为I.MX6U 使用的是 Cortex-A7 内核也有中断向量表和中断向量表偏移,而且其含义和 STM32 是一模一样的!指示用到的寄存器不同而已,概念完全相同。

2. NVIC(内嵌向量中断控制器)

中断系统得有个管理结构,对于 STM32 这种 Cortex-M 内核的单片机来说这个管理机构叫做 NVIC,全程叫做 Nested Vector Interrupt Controller。关于 NVIC 本教程不做详细的讲解,既然 Corex-M 内核有个中断系统的管理结构--NVIC,那么 I.MX6U 所使用的 Corex-A7 内核是不是也应该有一个中断管理机构?答案是肯定的,不过 Cortex-A7 内核的中断管理机构不叫做 NVIC ,而是叫做 GIC,全程是 Geneal Interrupt Controller ,后面我们会详细的讲解 Cortex-A7 内核的 GIC。

3. 中断使能

要使用某个外设的中断,肯定要先使能这个外设的中断,以 STM32F103 的 PE2 这个IO为例,假如我们要使用 PE2 的输入中断肯定要使用下面的代码来使能对应的中断:

NVIC_InitStructure.NVIC_IRQChannel = EXIT2_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2

NVIC_InitStructure.NVIC_IRQChannelSubPriority = x002;             //子优先级2

NVIC_InitStructure.NVIC_IRQChannelCmd = ENALBE;                 //使能外部中断通道

NVIC_Init(&NVIC_InitStructure);

上述代码就是使能 PE2 对应的 EXIT2 中断,同理,如果要使用 I.MX6U 的某个中断的话也需要使能其对应的中断。

4. 中断服务函数

我们使用中断的目的就是为了使用中断服务函数,当中断发生以后中断服务函数就会被调用,我们要处理的工作就可以放到中断服务函数中去完成。同样以 STM32F103 的 PE2 为例,其中断服务函数如下图所示:

/* 外部中断2 服务程序 */

void EXIT2_IRQHandler(void)

{

        /* 中断处理代码 */

}

当PE2引脚的中断触发以后就会调用其对应的中断服务函数 EXIT2_IRQHandler,我么可以在函数 EXIT2_IRQHandler 中提那家中断处理代码。同理,I.MX6U 也有中断服务函数,当某个外设发生中断以后就会调用其对应的中断服务函数。

通过对 STM32 中断系统的回顾,我们知道了 Cortex-M 内核的中断处理过程,那么 Cortex-A7 内核的中断处理过程是否是一样的,有什么异同呢?接下来我们就带着这样的疑问来学习 Cortex-A7 内核的中断处理系统。

1.2 Cortex-A7 中断系统简介

 跟STM32一样,Cortex-A7 内核也有中断向量表,中断向量表也是在代码的最前面。Cortex-A7 内核有8个异常中断,这8个异常中断的中断向量表如下表所示:

向量地址中断类型中断模式
0x00复位中断(Reset)特权模式(SVC)
0x04未定义指令中断(Undefined Instruction)未定义指令终止模式(Undef)
0x08软中断(Software Interrupt, SWI)特权模式(SVC)
0x0C指令预取中止中断(Prefetch Abort)中止模式
0x10数据访问中止 (Data Abort)中止模式
0x14未使用(Note Used)未使用
0x18IRQ中断(IRQ Interrupt)外部中断模式(IRQ)
0x1CFIRQ中断(FIRQ Interrupt)快速中断模式(FIRQ)

中断向量表里都是中断服务函数的入口地址,因此一款新芯片有什么中断都是可以从中断向量表看出来的。从上表中可以看出,Cortex-A7 内核一共有 8 个中断,而且还有一个中断向量未使用,实际只有7个中断。和“示例代码 17.1.1.1”中的 STM32F103 中断向量表比起来少了很多!难道一个能跑 Linux 的芯片只有这7个中断?明显是不可能的!那类似 STM32 中的 EXIT9-5_IRQHandler, TIM2_IRQHandler 这样的中断向量在哪里?I2C, SPI,定时器等等的中断怎么处理呢?

这个就是 Cortex-A 和 Cortex-M 在中断向量表这一块的区别,对于 Cortex-M 内核来说,中断向量表列举出了一款芯片所有的中断向量,包括芯片外设的所有中断。对于 Cortex-A 内核来说并没有这么做,在上表中有个 IRQ 中断,Cortex-A 内核CPU的所有外部中断都属于这个 IRQ 中断,当任意一个外设中断发生的时候都会触发 IRQ 中断。在IRQ中断服务函数中就可以读取指定的寄存器来判断发生的具体事什么中断,进而根据具体的中断做出相应的处理。这些外部中断和IRQ的关系如下图所示:

在图 17.1.2.1 中,,左侧的 Softwre0_IRQn~PMU_IRQ2_IRQ 这些都是 I.MX6U 的中断,它们都属于 IRQ 中断。当图 17.1.2.1左侧这些中断中任意一个发生的时候 IRQ 中断都会被触发,所以我们需要在 IRQ 中断服务函数中判断究竟是左侧的哪个中断发生了,然后在坐车具体的处理。

在表 17.1.2.1 中一共有7个中断,简单介绍一下这7个中断:

  1. 复位中断(Reset),CPU复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化SP指针,DDR等等。
  2. 未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。
  3. 软中断(Software Interrupt,SWI),由SWI指令引起的中断,Linux系统调用会用 SWI 指令来一起软中断,通过软中断来陷入到内核空间。
  4. 指令预取中止中断(Prefetch Abort),预取指令出错的时候会产生此中断。
  5. 数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断
  6. IRQ中断(IRQ Interrupt),外部中断,前面已经说过了,芯片内部的外设中断都会引起此中断的发生。
  7. FIQ中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中断。

在上面的7个中断中,我们常用的就是复位中断和IRQ中断,所以我们需要编写这两个中断的中断服务函数,稍后我们会讲解如何编写对应的中断服务函数。首先我们要将表 17.1.2.1 的内容来创建中断向量表,中断向量表处于程序最开始的地方,比如我们前面例程 Start.S 的文件最前面,中断向量表如下:

                                         示例代码 17.1.1.1 Cortex-A7 向量表模板

.global _start

_start:

        ldr pc, =Reset_Handler                /*复位中断*/

        ldr pc, =Undefined_Handler        /*未定义指令中断*/

        ldr pc, =SVC_Handler                 /*SVC(SuperVisor)中断*/

        ldr pc, =PrefAbort_Handler         /*预取中止中断*/

        ldr pr, =DataAbort_Handler         /*数据中止中断*/

        ldr pc, =NotUsed_Hanlder          /*未使用中断*/

        ldr pc, =IRQ_Handler                 /*IRQ中断*/

        ldr pc, =FIQ_Handler                  /*FIQ(快速中断)*/

/* 复位中断 */

Reset_Handler:

        /*复位中断具体处理过程*/

/*未定义中断*/

Undefined_Handler:

        ldr r0, =Undefined_Handler

        bx r0

/*SVC中断*/

SVC_Handler:

        ldr r0, =SVC_Handler
        bx r0

/*预取中止中断*/

PrefAbort_Handler:

        ldr r0, =PerfAbort_Handler

        bx r0

/*数据中止中断*/

DataAbort_Handler:

        ldr r0, =DataAbort_Handler

        bx r0


/*未使用的中断*/

NotUsed_Handler:

        ldr r0, =NotUserd_Handler

        bx r0


/* IRQ中断!重点!!!!*/

IRQ_Handler:

        /*IRQ中断具体处理过程*/

/*FIQ中断*/
FIQ_Handler:

        ldr r0, =FIQ_Handler

        bx r0

第4到11行时中断向量表,当指定的中断发生以后就会调用对应的中断服务函数,比如复位中断发生以后会执行第4行的代码,也就是调用 Reset_Handler,函数 Reset_Handler 就是复位中断的中断服务函数,其它中断同理。

第14到50行就是对应的中断服务函数,中断服务函数都是用汇编编写的,我们实际需要编写的只有复位中断服务函数 Reset_Handler 和 IRQ中断服务函数  IRQ_Handler,其它的中断本教程没有用到,所以是死循环。在编写复位(Reset)中断服务函数和IRQ中断服务函数之前我们还需要了解一些其他的知识,否则的话就没法编写。

1.3 GIC 控制器简介
1. GIC控制器总览

STM32(Cortex-M)的中断控制器叫做NVIC,I.MX6U(Cortex-A7)的中断控制器叫做GIC,关于GIC的详细内容参考开发板光盘中的文档《ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf》。

GIC是ARM公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核中的 NVIC。目前GIC有4个版本:V1~v4,V1是最老的版本,已经被废弃了。V2~V4目前正在大量使用。GIC V2 是给 ARMv7-A 构架使用的,比如 Cortex-A7,Cortex-A9, Cortex-A15等,CIC V3 和 V4 是给 ARMv8-A/R 构架使用的,也就是64为芯片使用的。I.MX6U 是 Cortex-A7 内核的,所以我们主要讲解 GIC V2。GIC V2 最多支持8个内核。ARM会根据GIC版本的不同研发出不同的IP核,哪些半导体厂商直接购买对应的IP核即可,比如ARM针对 GIC  V2 就开发出了 GIC400 这个中断控制器IP核。

当GIC接收到外部中断信号以后就会报给ARM内核,但是ARM内核只提供了4个信号给GIC来汇报中断情况:VFIQ,VIRQ,FIQ,IRQ,他们之间的关系如下图所示:

在图 17.1.3.1 中,GIC接收众多的外部中断,然后对其进行处理,最终就只通过四个信号报给ARM内核,这四个信号的含义如下:

  • VFIQ:虚拟快速FIQ
  • VIRQ:虚拟外部IRQ
  • FIQ:快速中断IRQ
  • IRQ:外部中断IRQ

VFIQ和VIRQ是针对虚拟化的,我们不讨论虚拟化,剩下的就是FIQ和IRQ了,我们前面讲过很多次了。本教程我们只使用IRQ,所以相当于GIC最终向ARM内核就上报了一个IRQ信号。那么GIC是如何完成工作的呢?GICV2的裸机结构如图17.1.3.2 所示:

图 17.1.3.2 中左侧部分就是中断源,中间部分就是GIC控制器,最右侧就是中断控制器向处理器内核发送的中断信息。我们重点要看的肯定是中间的GIC部分,GIC将众多的中断源分为三类:

  1. SPI(Shared Peripheral Interrupt),共享中断,顾名思义,所有的Core共享的中断,这个是最常见的,哪些外部中断都属于SPI中断(注意!不是SPI总线的那个中断)。比如按键中断,串口中断等等,这些中断所有的Core都可以处理,不限定特定的Core。
  2. PPI(Private Peripheral Interrupt),私有中断,我们锁了GIC是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。
  3. SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器 GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
2. 中断ID

中断源有很多,为了区分这些不同的中断源肯定要给他们分配一个唯一的ID,这些ID就是中断ID。每一个CPU最多支持1020个中断ID,中断ID号为0~1019。这1020个ID包含了PPI,SPI,和SGI,那么这三类中断是如何分配这1020个中断ID的呢?这1020个ID分配如下:

ID0~ID15:这个16个ID分配给SGI

ID16~ID31:这16个ID分配给PPI。

ID31~ID1019:这998个ID分配给SPI,像GPIO中断,串口中断灯这些外部中断,至于具体到某个ID对应按个中断那就有半导体厂商根据实际情况去定义了。

比如I.MX6U总共使用了128个中断ID,加上前面的PPI和SGI的32个ID,I.MX6U的中断源总共有 128 + 32 = 160 个,这128个中断ID对应的中断在《I.MX6ULL 参考手册》的“3.2 Cortex A7 interrupts”小节,中断源如表 17.1.3.1 所示:

IRQID中断源描述
032boot用于启动异常的时候通知内核
133ca7_paltformDAP中断,调试端口访问请求中断
234sdmaSDMA中断
335tcsTSC(触摸)中断
436snvs_lp_wrapper
snvs_hp_wrapper
SNVS中断
......
125157保留
126158保留
127159pmuPMU中断

限于篇幅原因,表 17.1.3.1 中并没有给出并没有给出 I.MX6U 完整的中断源,完整的中断源自行查阅《I.MX6ULL 参考手册》的 3.2 小节。代开裸机例程 “9_int”,我们在前面移植了 NXP SDK 中的文件  MCIMX6Y2C.h ,在此文件中定义了一个枚举类型 IRQn_Type ,此枚举类型就枚举出了 I.MX6U 的所有中断,代码如下所示:

 3. GIC逻辑分块

GIC构架分为两个逻辑块:Distributor 和 CPU Interface,也就是分发器端和CPU接口端。这两个逻辑块的含义如下:

Distrubutor(分发器端):从图 17.1.3.2 可以看出,此逻辑块负责处理各个中断事件的分发问题,也就是中断事件应该发送到哪个CPU Interface 上去。分发器搜集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到CPU接口端。分发其端要做的主要工作如下:

  1. 全局中断使能控制
  2. 控制每一个中断的使能或者关闭
  3. 设置每个中断的优先级
  4. 设置每个中断的目标处理器列表
  5. 设置每个外部中断的触发方式:电平触发或边沿触发
  6. 设置每个中断属于组0还是组1

CPU Interface (CPU接口端):CPU接口端听名字就知道和CPU Core 相连接的,因此在图 17.1.3.2 中每个CPU Core 都可以在 GIC 中找到一个与之对应的 CPU Interface。CPU接口端就是分发器和CPU Core之间的桥梁,CPU接口端的主要工作如下:

  1. 使能或者关闭发送到CPU Core的中断请求信号。
  2. 应答中断。
  3. 通知中断处理完成。
  4. 设置优先级掩码,通过掩码来设置哪些中断不需要上报给CPU Core。
  5. 定义抢占策略。
  6. 当多个中断到来的时候,选择优先级最高的中断通知给CPU Core。

例程 "9_int" 的文件中 core_ca7.h 定义了GIC结构体,此结构体里面的寄存器分为了发送端和CPU接口端,寄存器定义如下所示:

“示例代码 17.1.3.2”中的结构体 GIC_Type 就是 GIC 控制器,列举出了GIC控制器的所有寄存器,可以通过结构体 GIC_Type 来冯文GIC的所有寄存器。

第5行GIC分发器端的寄存器,其相对于GIC基地址偏移 x01000(4KB),因此我们获取到GIC基地址以后只需要加上 0x1000 即可以访问 GIC分发器端寄存器。

第 51 行是GIC的CPU接口端相关寄存器,其相对于GIC的基地址偏移为0x2000(8KB),同样的获取到GIC的基地址之后值需要加上 0x2000 即可访问GIC的GPU接口寄存器。那么问题来了,GIC控制器的地址在哪里呢?这个就要用到 Cortex-A 的 CP15 协处理器了,下一节就讲解 CP15 协处理器。

1.4 CP15协处理器

关 于 CP15 (CP: coprocessor )协处理 器和其 相关寄存 器的详细 内容 请参考下 面两份文 档: 《 ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition.pdf》 第 1469 页“B3.17 Oranizationof the CP15 registers in a VMSA implementation”。《Cortex-A7 Technical ReferenceManua.pdf》 第55 页“Capter 4 System Control”。

CP15协处理器一般用于存储系统管理,但是在中断中也会用到,CP15协处理器一共有16个32为寄存器。CP15协处理器的访问通过武侠两个指令完成:

  • MRC:将CP15协处理器中的寄存器读到ARM寄存器中。
  • MCR:将ARM寄存器的数据写入到CP15协处理器寄存器中。

MRC就是读CP15寄存器,MCR就是协写CP15寄存器。
MCR的指令格式如下:

        MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

  • cond: 指令执行的条件码,如果忽略的话就表示无条件执行
  • opc1:协处理要执行的操作码。
  • Rt:ARM源寄存器,要写入到CP15协处理器寄存器的数据就保存在此寄存器中。
  • CRn:CP15协处理器的目标寄存器
  • CRm:协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm设置为C0,否则结果不可预测。
  • opc2:可选的协处理器特定操作码,当不需要的时候要设置0。

MRC的指令格式和MCR一样,只不过在MRC指令中的Rt就是目标寄存器,也就是从C15协处理器指定寄存器读出来的数据会保存在Rt中。而CRn就是源寄存器,也就是要读取的协处理器寄存器。

假如我们要将CP15协处理器中 C0 寄存器的值读取到 R0 寄存器中,那么可以使用如下的命令:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

 MRC p15,0,r0,c0,c0,0

CP15协处理器有16个32位寄存器,c0~c15,在使用MRC或者MCR指令访问者16个寄存器的时候,指令中的 CRn,opc1, CRm, opc2 通过不同搭配,其得到的寄存器含义是不同的。

1. c0寄存器:

比如 c0 在不同搭配情况情况下含义如17.1.4.1 所示:

在图 17.1.4.1 中当MRC/MCR指令中的CRn=c0,opc1=0,CRm=c0,opc2=0 的时候就表示此时的c0就是MIDR寄存器,也就是主ID寄存器,这个也是c0的基本作用。对于Cortex-A7内核来说,c0作为MDIR寄存器的时候其含义如图 17.1.4.2 所示:

在图 17.1.4.2 中各位所代表的含义如下:

  • bit31:24:厂商编号, 0X41, ARM。
  • bit23:20:内核架构的主版本号, ARM 内核版本一般使用 rnpn 来表示,比如 r0p1,其中 r0 后面的 0 就是内核架构主版本号。
  • bit19:16:架构代码, 0XF, ARMv7 架构。
  • bit15:4:内核版本号, 0XC07, Cortex-A7 MPCore 内核。
  • bit3:0:内核架构的次版本号, rnpn 中的 pn,比如 r0p1 中 p1 后面的 1 就是次版本号。
2. c1寄存器

c1 寄存器同样通过不同的配置,其代表的含义也不同,如图 17.1.4.3 所示

在图 17.1.4.3 中当 MRC/MCR 指令中的 CRn=c1, opc1=0, CRm=c0, opc2=0 的时候就表示
此时的 c1 就是 SCTLR(System Control Register) 寄存器,也就是系统控制寄存器,这个是 c1 的基本作用。SCTLR 寄存
器主要是完成控制功能的,比如使能或者禁止 MMU、 I/D Cache 等, c1 作为 SCTLR 寄存器的时
候其含义如图 17.1.4.4 所示

SCTLR 的位比较多,我们就只看本章会用到的几个位:

  • bit13: V , 中断向量表基地址选择位,为 0 的话中断向量表基地址为 0X00000000,软件可以使用 VBAR 来重映射此基地址,也就是中断向量表重定位。为 1 的话中断向量表基地址为 0XFFFF0000,此基地址不能被重映射。
  • bit12: I, I Cache 使能位,为 0 的话关闭 I Cache,为 1 的话使能 I Cache。
  • bit11: Z,分支预测使能位,如果开启 MMU 的话,此位也会使能。
  • bit10: SW, SWP 和 SWPB 使能位,当为 0 的话关闭 SWP 和 SWPB 指令,当为 1 的时候就使能 SWP 和 SWPB 指令。
  • bit9:3:未使用,保留。
  • bit2: C, D Cache 和缓存一致性使能位,为 0 的时候禁止 D Cache 和缓存一致性,为 1 时使能。
  • bit1: A,内存对齐检查使能位,为 0 的时候关闭内存对齐检查,为 1 的时候使能内存对齐检查。
  • bit0: M, MMU 使能位,为 0 的时候禁止 MMU,为 1 的时候使能 MMU。

如果要读写 SCTLR 的话,就可以使用如下命令:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

MRC p15, 0, <Rt>, c1, c0, 0 ;读取 SCTLR 寄存器,数据保存到 Rt 中。
MCR p15, 0, <Rt>, c1, c0, 0 ;将 Rt 中的数据写到 SCTLR(c1)寄存器中。
3. c12寄存器

c12 寄存器通过不同的配置,其代表的含义也不同,如图 17.1.4.4 所示:

在图 17.1.4.4 中当 MRC/MCR 指令中的 CRn=c12, opc1=0, CRm=c0, opc2=0 的时候就表示此时 c12 为 VBAR 寄存器(Vector Base Address Register),也就是向量表基地址寄存器。设置中断向量表偏移的时候就需要将新的中断向量表基地址写入 VBAR 中,比如在前面的例程中,代码链接的起始地址为0X87800000,而中断向量表肯定要放到最前面,也就是 0X87800000 这个地址处。所以就需要设置 VBAR 为 0X87800000,设置命令如下:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

ldr r0, =0X87800000 ; r0=0X87800000
MCR p15, 0, r0, c12, c0, 0 ;将 r0 里面的数据写入到 c12 中,即 c12=0X87800000
4. c15 寄存器

在图 17.1.4.5 中,我们需要 c15 作为 CBAR(Config Base Address Register) 寄存器,因为 GIC 的基地址就保存在 CBAR中,我们可以通过如下命令获取到 GIC 基地址:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

MRC p15,4,R1,c15,c0,0; 获取GIC基础地址,基地址保存在r1中

获取到 GIC 基地址以后就可以设置 GIC 相关寄存器了,比如我们可以读取当前中断 ID,当前中断 ID 保存在 GICC_IAR 中,寄存器 GICC_IAR 属于 CPU 接口端寄存器,寄存器地址相对于 CPU 接口端起始地址的偏移为 0XC,因此获取当前中断 ID 的代码如下:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

MCR p15,4,r1, c15,c0,0    ;获取GIC的基地址
ADD r1, r1, #0x2000       ;GIC低地址+0x2000得到CPU接口端寄存器的起始地址
ADD r1, r1, #0xC          ;读取CPU接口端寄存器起始地址+0XC处的寄存器,
ldr r2, [r1]              ;也就是寄存器 GIC_IAR 的值

关于CP15协处理器就讲解到治理,简单总结一下

  • 通过c0寄存器可以获取到处理器内核信息
  • 通过c1寄存器可以使能或禁用MMU,I/D Cache等
  • 通过c12寄存器可以设置中断向量偏移
  • 通过c15寄存器可以获取到GIC基地址。

关于CP15协处理器寄的其它寄存器大家自行查阅本节前面列举的 2 份 ARM 官方资料。

1.5 中断使能

中断使能包括两部分,一个是IRQ或FIQ的总中断使能,另一个是ID0~ID1019这个1020个中断源的使能。

1. IRQ和FIQ总中断使能

IRQ和FIRQ分别是外部中断和快速中断的的总开关,就类似家里买的进户总电闸,然后 ID0~ID1019 这1020 个中断源就类似家里面的各个电器开关。要想开电视,那肯定要保证进户总电闸是打开的,因此要想使用I.MX6U上的外设中断就必须先打开 IRQ 中断(本教程不使用FIQ)。在“6.3.2 程序状态寄存器”小节已经讲过了,

寄存器 CPSR 的 I=1 禁止 IRQ,当 I=0 使能IRQ;F=1禁止FIQ,F=0使能FIQ。 

 我们还有更简单的指令来完成IRQ或FIQ的使能和禁止,图表 17.1.5.1 所示:

指令描述
cpsid i禁止IRQ中断
cpside i使能IR中断
cpsid f禁止FIQ中断
cpsid f使能FIQ中断
2. ID0~ID1029 中断使能和禁止

GIC寄存器 GICD_ISENABLERn 和 GICD_ICENABLERn 用完成外部中断的使能和禁止,对于 Cortex-A7 内核来说中断ID只使用了512个。一个bit控制一个中断ID的使能,那么就需要 512/32 = 16 个  GICD_ISENABLER 寄存器来完成中断的使能。同理,也需要16个 GICD_ICENABLER 寄存器完完成中断的禁止。

其中 GIC_ISENABLER0 的 bit[15:0] 对应 ID15~0 的SGI中断,GICD_ISENABLER0 的 bit[31:16] 对应 ID31~16 的 PPI中断。剩下的 GICD_ISENBALBER1~GICD_ISENABLER15就是控制SPI中断的。

1.6 中断优先级设置
1. 优先级数设置

学过STM32的都知道 Cortex-M 的中断优先级分为抢占优先级和子优先级,两者是可以配置的。同样的,Cortex-A7 的中断优先级也可以分为抢占优先级和子优先级,两者同样是可以配置的。

GIC控制器最多可以支持256个优先级,数字越小优先级越高!Conrtex-A7选择了32个优先级。

在使用中断的时候需要初始化 GICC_PMR 寄存器,此寄存器用来决定使用几级优先级,寄存器的结构如图 17.1.6.1 所示:

GICC_PMR 寄存器只有低 8 位有效,这 8 位最多可以设置 256 个优先级,其他优先级数设置如表 17.1.6.1 所示:

bit7:0优先级数
1111 1111256个优先级
1111 1110 128个优先级
1111 110064个优先级
1111 1000 32个优先级
1111 000016个优先级

I.MX6U 是 Cortex-A7 内核,所以支持32个优先级,一次 GICC_PMR 要设置为 0b1111 1000 。

2. 抢占优先级和子优先级位数设置

抢占优先级和子优先级各占多少位是有寄存器 GICC_BPR 来决定的,GICC_BPR 寄存器结构如图 17.1.6.2 所示:

寄存器 GICC_BPR 只有低 3 位有效,其值不同,抢占优先级和子优先级占用的位数也不同。

Binary Point抢占优先级域子优先级域描述
0[7:1][0]7级抢占优先级,1级子优先级
1[7:2][1:0]6级抢占优先级,2级子优先级
2[7:3][2:0]5级抢占优先级,3级子优先级
3[7:4][3:0]4级抢占优先级,4级子优先级
4[7:5][4:0]3级抢占优先级,5级子优先级
5[7:6][5:0]2级抢占优先级,6级子优先级
6[7:7][6:0]1级抢占优先级,7级子优先级
7[7:0]0级抢占优先级,8级子优先级

 为了简单起见,一般将所有的中断优先级位都配置为抢占优先级,比如 I.MX6U 的优先级位数为5(32个优先级),所以可以设置 Binary point 为 2,表示 5个优先级位全部为抢占优先级。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大语言模型LLM原理篇

大模型席卷全球&#xff0c;彷佛得模型者得天下。对于IT行业来说&#xff0c;以后可能没有各种软件了&#xff0c;只有各种各样的智体&#xff08;Agent&#xff09;调用各种各样的API。在这种大势下&#xff0c;笔者也阅读了很多大模型相关的资料&#xff0c;和很多新手一样&a…

电脑ip地址设置成什么比较好

随着信息技术的快速发展&#xff0c;IP地址已成为电脑在网络世界中的“身份证”。它不仅是电脑在网络中进行通信的基础&#xff0c;也直接关系到网络连接的稳定性、安全性和效率。然而&#xff0c;面对众多IP地址设置选项&#xff0c;许多用户可能会感到困惑。那么&#xff0c;…

图形网络的自适应扩散 笔记

1 Title Adaptive Diffusion in Graph Neural Networks&#xff08;Jialin Zhao、Yuxiao Dong、Ming Ding、Evgeny Kharlamov、Jie Tang&#xff09;【NIPS 2021】 2 Conclusion The neighborhood size in GDC is manually tuned for each graph by conductin…

docker-compose集成elasticsearch7.17.14+kibana7.17.14

1.docker和compose版本必须要高 2.准备ik分词器&#xff08;elasticsearch-analysis-ik-7.17.14&#xff09;&#xff0c;下面会用到 https://github.com/infinilabs/analysis-ik/releases?page2 3.配置es-compose.yml&#xff08;切记映射容器内路径不能更改,es和kibana服务…

每日OJ题_记忆化搜索⑤_力扣329. 矩阵中的最长递增路径

目录 力扣329. 矩阵中的最长递增路径 解析代码1_爆搜递归&#xff08;超时&#xff09; 解析代码2_记忆化搜索 力扣329. 矩阵中的最长递增路径 329. 矩阵中的最长递增路径 难度 困难 给定一个 m x n 整数矩阵 matrix &#xff0c;找出其中 最长递增路径 的长度。 对于每…

【LeetCode算法】389. 找不同

提示&#xff1a;此文章仅作为本人记录日常学习使用&#xff0c;若有存在错误或者不严谨得地方欢迎指正。 文章目录 一、题目二、思路三、解决方案 一、题目 给定两个字符串 s 和 t &#xff0c;它们只包含小写字母。字符串 t 由字符串 s 随机重排&#xff0c;然后在随机位置添…

移动端自动化测试工具 Appium 之 main 启动

文章目录 一、背景二、生成xml文件2.1、创建xml方法2.2、执行主类MainTest2.3、自动生成的xml2.4、工程目录2.5、执行结果 三、命令行执行appium服务四、主方法启动类五、集成Jenkins六、总结 一、背景 Jenkins 做集成测试是不错的工具&#xff0c;那么UI自动化是否可以&#…

macOS12安装 php7.1和apache

1. 安装php 7.1 macOS12不再自带php brew tap shivammathur/php 查看可安装版本 brew search php 安装指定版本&#xff08;禅道适用PHP运行环境(7.0/7.1/7.2版本)&#xff09; brew install php7.1 环境配置 vim ~/.zshrc export PATH"/usr/local/opt/php7.1/bin:…

uni-app 滚动到指定位置

方法1&#xff1a;使用标签&#xff0c;可以将页面横向&#xff08;或纵向&#xff09;滚动到指定位置 无法滚动 将代码放在setTimeout&#xff0c;nextTick里执行 <!-- 左边 --><scroll-view show-scrollbar"false" scroll-y"true" class"…

Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗

Flutter笔记 Widgets Easier组件库&#xff08;13&#xff09;使用底部弹窗 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this …

局域网语音对讲系统_IP广播对讲系统停车场解决方案

局域网语音对讲系统_IP广播对讲系统停车场解决方案 需求分析&#xff1a; 随着国民经济和社会的发展&#xff0c; 选择坐车出行的民众越来越多。在保护交通安全的同时&#xff0c;也给停车场服务部门提出了更高的要求。人们对停车场系统提出了更高的要求与挑战&#xff0c; 需要…

Android 开机启动模式源码分析

在机器关机情况下&#xff0c;长按Power键启动机器&#xff0c;如果这时机器低电&#xff0c;会提示低电&#xff0c;机器不会正常启动&#xff1a; 而代码如下&#xff1a; 如果不是低电&#xff0c;正常情况是可以启动的。 在关机情况下&#xff0c;插入USB&#xff0c;机…

AUS GLOBAL 再次荣登皇家贝蒂斯俱乐部官网

AUS GLOBAL 作为一家备受信赖的金融服务领导者&#xff0c;一直以来都在致力于为客户提供卓越的交易体验和专业的服务。再次登上皇家贝蒂斯俱乐部官网Banner&#xff0c;不仅是对我们过去合作的肯定&#xff0c;更是对未来合作的信心和期待。这标志着我们之间的合作更加稳固和成…

IP报文在设备间传递的封装过程

IP报文传递过程 1、PC1访问PC2报文传递过程1.1、PC1准备数据请求报文封装1.2、PC1准备ARP请求报文1.3、PC2准备ARP响应报文1.4、PC1完成数据请求报文封装 2、PC1访问PC3报文传递过程2.1、PC1准备数据请求报文封装2.2、PC1准备获取网关MAC地址的ARP请求报文2.3、网关准备ARP响应…

kotlin语法快速入门--(完整版)

Kotlin语法入门 文章目录 Kotlin语法入门一、变量声明1、整型2、字符型3、集合3.1、创建array数组3.2、创建list集合3.3、不可变类型数组3.4、Set集合--不重复添加元素3.5、键值对集合Map 4、kotlin特有的数据类型和集合4.1、Any、Nothing4.2、二元组--Pair4.3、三元组--Triple…

vue数据大屏并发请求

并发? 处理并发 因为js是单线程的&#xff0c;所以前端的并发指的是在极短时间内发送多个数据请求&#xff0c;比如说循环中发送 ajax , 轮询定时器中发送 ajax 请求. 然后还没有使用队列, 同时发送 的. 1. Promise.all 可以采用Promise.all处理并发&#xff0c; 当所有pro…

gjfjiv是什么意思

GJFJV-4B1&#xff0c;gjfjv-6a1a&#xff0c;gjfjv光缆 室内光缆型号命名 产品描述 多样的光缆结构选择&#xff0c;可在有限的空间内布设&#xff0c;且无缠绕效应 可于建设物间导管托盘和通道中使用 理想的网络光缆在保证对光纤的保护前提下易于布设&#xff0c;插接和识…

数据链路层之 以太网协议

以太网协议 这个协议即规定了数据链路层&#xff0c;同时也规定了物理层的内容。平时使用到的网线&#xff0c;其实也叫做“以太网线”&#xff08;遵守以太网协议的网线&#xff09;。 以太网帧格式 以太网数据帧 帧头 载荷 帧尾。 帧头&#xff1a;目的地址、源地址、类型…

django项目结构介绍

小白的django学习笔记 五一前的某天 文章目录 django项目结构介绍项目的基本配置templates项目模块manage.pyExternal Libraries django项目结构介绍 项目的基本配置 在这里配置&#xff0c;跟工程名是一样的 templates 放网页、js、css的地方 django 项目模块 项目开发时&…

《前端算法宝典:双指针问题解析与应用》

双指针 双指针&#xff0c;指的是在遍历对象的过程中使用两个相同方向&#xff08;快慢指针&#xff09;或者相反方向&#xff08;对撞指针&#xff09;的指针或者是两个指针构成一个滑动窗口进行扫描&#xff0c;从而达到相应的目的。 双指针方法在某些情况下可以对有序数组…