Cortex-A7中断

Cortex-A7中断系统

STM32中断系统

请参考这位写的博文STM32启动过程详解。想详细了解中断系统,务必先阅读推荐的这篇博客

在启动文件中,做一下步骤,最后进入我们的main函数

;* - Set the initial SP
;* - Set the initial PC == Reset_Handler
;* - Set the vector table entries with the exceptions ISR address
;* - Configure the system clock and the external SRAM mounted on
;* STM324xG-EVAL board to be used as data memory (optional,
;* to be enabled by user)
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the CortexM4 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.

在ARM Cortex-M架构中,中断向量表的默认位置是地址0x00000000。然而,STM32微控制器的Flash存储器通常从地址0x08000000开始。为了解决这个问题,Cortex-M架构引入了中断向量表偏移(Vector Table Offset)的概念,允许将中断向量表放置在任意地址。

中断向量表偏移的配置

中断向量表偏移是通过向SCB->VTOR寄存器写入新的中断向量表首地址来实现的。以下是一个典型的配置示例:

#define FLASH_BASE ((uint32_t)0x08000000)
#define VECT_TAB_OFFSET 0x0

void SystemInit(void)
{
    // 初始化时钟等
    // ...

    // 设置中断向量表偏移
    SCB->VTOR = FLASH_BASE + VECT_TAB_OFFSET;
}

中断向量表示例

以下是一个典型的STM32中断向量表示例:

__attribute__((section(".isr_vector")))
void (*const g_pfnVectors[])(void) = {
    &_estack,                  // 栈顶地址
    Reset_Handler,             // 复位处理程序
    NMI_Handler,               // NMI处理程序
    HardFault_Handler,         // 硬故障处理程序
    MemManage_Handler,         // 内存管理故障处理程序
    BusFault_Handler,          // 总线故障处理程序
    UsageFault_Handler,        // 使用故障处理程序
    0,                         // 保留
    0,                         // 保留
    0,                         // 保留
    0,                         // 保留
    SVC_Handler,               // SVC处理程序
    DebugMon_Handler,          // 调试监控处理程序
    0,                         // 保留
    PendSV_Handler,            // PendSV处理程序
    SysTick_Handler,           // SysTick处理程序
    // 外设中断向量...
};

详细解释

  1. 中断向量表的默认位置
    • 在ARM Cortex-M架构中,中断向量表的默认位置是地址0x00000000。
    • 中断向量表的第一项是栈顶指针(__initial_sp),第二项是复位处理程序的入口地址(Reset_Handler),依此类推。
  2. STM32的Flash存储器
    • STM32微控制器的Flash存储器通常从地址0x08000000开始。
    • 因此,中断向量表需要放置在Flash存储器的起始地址(0x08000000)。
  3. 中断向量表偏移
    • 为了将中断向量表放置在Flash存储器的起始地址,Cortex-M架构引入了中断向量表偏移的概念。
    • 通过向SCB->VTOR寄存器写入新的中断向量表首地址(例如0x08000000),可以实现中断向量表的偏移。

示例代码解释

以下是示例代码的详细解释:

#define FLASH_BASE ((uint32_t)0x08000000)
#define VECT_TAB_OFFSET 0x0

void SystemInit(void)
{
    // 初始化时钟等
    // ...

    // 设置中断向量表偏移
    SCB->VTOR = FLASH_BASE + VECT_TAB_OFFSET;
}
  • FLASH_BASE:定义Flash存储器的起始地址(0x08000000)。
  • VECT_TAB_OFFSET:定义中断向量表的偏移量(0x0)。
  • SystemInit函数:在系统初始化时,设置中断向量表偏移。

总结

通过设置中断向量表偏移,可以将中断向量表放置在STM32微控制器的Flash存储器起始地址(0x08000000),从而解决中断向量表默认位置与实际存储位置不一致的问题。这种方法在Cortex-M架构中非常常见,确保了中断处理的正确性和可靠性。

NVIC(Nested Vectored Interrupt Controller)

NVIC是Cortex-M内核的中断管理系统,负责管理中断的优先级、使能和禁用中断等功能。NVIC通过向量表来管理中断向量,每个中断向量对应一个中断服务程序(ISR)

中断使能

在STM32中,使用NVIC来使能和配置中断。以下是一个使能外部中断的示例:

NVIC_InitTypeDef NVIC_InitStructure;

// 配置EXTI2中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;        // 子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // 使能外部中断通道
NVIC_Init(&NVIC_InitStructure);

中断服务函数

中断服务函数是中断发生时执行的代码。在STM32中,中断服务函数通常定义在启动文件中,并在中断向量表中注册。以下是一个STM32中断服务函数的示例:

/* 外部中断2服务程序 */
void EXTI2_IRQHandler(void)
{
    // 中断处理代码
    // ...

    // 清除中断标志
    EXTI_ClearITPendingBit(EXTI_Line2);
}

Cortex-A7 中断系统简介

和STM32一样,Cortex-A7也有属于自己的中断向量表,放在代码的最前面,Cortex-A7 内核有 8 个异常中断。

在这里插入图片描述

Cortex-A7虽然有八个中断,但实际上只有七个中断,相比于STM32看起来少了很多,难道一个能跑Linux的IC只有这么少的中断???明显不可能。

对于Cortex-A7,内核CPU的所有外部中断都属于IRQ中断,当任意一个外部中断发生都会触发IRQ中断,在IRQ中断服务函数中可以读取指定寄存器来判断发生的具体是什么中断。

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

中断向量表编写

.global _start
_start:
	ldr pc, =Reset_Handler		/* 复位中断 					*/	
	ldr pc, =Undefined_Handler	/* 未定义中断 					*/
	ldr pc, =SVC_Handler		/* SVC(Supervisor)中断 		*/
	ldr pc, =PrefAbort_Handler	/* 预取终止中断 					*/
	ldr pc, =DataAbort_Handler	/* 数据终止中断 					*/
	ldr	pc, =NotUsed_Handler	/* 未使用中断					*/
	ldr pc, =IRQ_Handler		/* IRQ中断 					*/
	ldr pc, =FIQ_Handler		/* FIQ(快速中断)未定义中断 
	
	
	

通过使用ldr pc, =address指令,可以将中断服务函数的地址加载到程序计数器(PC),从而实现中断向量表的定义。这种定义方式是基于Cortex-A内核的硬件支持和汇编指令的特性,确保了中断发生时能够正确跳转到相应的中断服务函数

GIC控制器

GIC(General Interrupt Controller)是ARM Cortex-A内核的中断控制器,负责管理多个中断源的中断请求,并将其分发给合适的处理器核心。GIC支持多种中断类型,包括软件触发中断(SGI)、私有外设中断(PPI)和共享外设中断(SPI)。

在这里插入图片描述

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

GIC的结构

GIC由以下几个主要部分组成:

  1. Distributor(分发器)

    • 负责管理所有中断源,包括中断使能、中断优先级、中断目标处理器等。
    • 分发器将中断请求分发给合适的处理器核心。
    • 主要做如下工作
      • 全局中断使能控制。
      • 控制每一个中断的使能或者关闭。
      • 设置每个中断的优先级
      • 设置每个中断的目标处理器列表。
      • 设置每个外部中断的触发模式:电平触发或边沿触发
      • 设置每个中断属于组 0 还是组 1。
  2. CPU Interface(CPU接口)

    • 每个处理器核心都有一个CPU接口,负责与分发器通信。
    • CPU接口接收来自分发器的中断请求,并将其传递给处理器核心。
    • 主要工作如下
      • 使能或者关闭发送到 CPU Core 的中断请求信号。
      • 应答中断
      • 通知中断处理完成
      • 设置优先级掩码,通过掩码来设置哪些中断不需要上报给 CPU Core。
      • 定义抢占策略
      • 当多个中断到来的时候,选择优先级最高的中断通知给 CPU Core。
  3. Interrupt Controller(中断控制器)

    • 负责处理中断请求的优先级、中断使能和中断状态等。

GIC的寄存器

GIC的寄存器分为两部分:分发器寄存器和CPU接口寄存器。以下是一些常用的寄存器:

  1. Distributor寄存器

    • GICD_CTLR:控制分发器的全局使能和禁用。
    • GICD_ISENABLERn:使能中断。
    • GICD_ICENABLERn:禁用中断。
    • GICD_IPRIORITYRn:设置中断优先级。
    • GICD_ITARGETSRn:设置中断目标处理器。
    • GICD_ICFGRn:配置中断类型(电平触发或边沿触发)。
  2. CPU Interface寄存器

    • GICC_CTLR:控制CPU接口的全局使能和禁用。
    • GICC_PMR:设置中断优先级屏蔽寄存器。
    • GICC_BPR:设置二进制优先级寄存器。
    • GICC_IAR:读取当前中断的ID。
    • GICC_EOIR:结束中断处理。

    GIC控制器结构体

    以下是一个典型的GIC控制器结构体定义,展示了GIC的分发器端和CPU接口端的寄存器:

    c

    复制

    /*
    * GIC 寄存器描述结构体,
    * GIC 分为分发器端和 CPU 接口端
    */
    typedef struct
    {
        /* 分发器端寄存器 */
        uint32_t RESERVED0[1024];
        __IOM uint32_t D_CTLR; /* Offset: 0x1000 (R/W) */
        __IM uint32_t D_TYPER; /* Offset: 0x1004 (R/ ) */
        __IM uint32_t D_IIDR; /* Offset: 0x1008 (R/ ) */
        uint32_t RESERVED1[29];
        __IOM uint32_t D_IGROUPR[16]; /* Offset: 0x1080 - 0x0BC (R/W) */
        uint32_t RESERVED2[16];
        __IOM uint32_t D_ISENABLER[16];/* Offset: 0x1100 - 0x13C (R/W) */
        uint32_t RESERVED3[16];
        __IOM uint32_t D_ICENABLER[16];/* Offset: 0x1180 - 0x1BC (R/W) */
        uint32_t RESERVED4[16];
        __IOM uint32_t D_ISPENDR[16]; /* Offset: 0x1200 - 0x23C (R/W) */
        uint32_t RESERVED5[16];
        __IOM uint32_t D_ICPENDR[16]; /* Offset: 0x1280 - 0x2BC (R/W) */
        uint32_t RESERVED6[16];
        __IOM uint32_t D_ISACTIVER[16];/* Offset: 0x1300 - 0x33C (R/W) */
        uint32_t RESERVED7[16];
        __IOM uint32_t D_ICACTIVER[16];/* Offset: 0x1380 - 0x3BC (R/W) */
        uint32_t RESERVED8[16];
        __IOM uint8_t D_IPRIORITYR[512];/* Offset: 0x1400 - 0x5FC (R/W) */
        uint32_t RESERVED9[128];
        __IOM uint8_t D_ITARGETSR[512];/* Offset: 0x1800 - 0x9FC (R/W) */
        uint32_t RESERVED10[128];
        __IOM uint32_t D_ICFGR[32]; /* Offset: 0x1C00 - 0xC7C (R/W) */
        uint32_t RESERVED11[32];
        __IM uint32_t D_PPISR; /* Offset: 0x1D00 (R/ ) */
        __IM uint32_t D_SPISR[15]; /* Offset: 0x1D04 - 0xD3C (R/ ) */
        uint32_t RESERVED12[112];
        __OM uint32_t D_SGIR; /* Offset: 0x1F00 ( /W) */
        uint32_t RESERVED13[3];
        __IOM uint8_t D_CPENDSGIR[16];/* Offset: 0x1F10 - 0xF1C (R/W) */
        __IOM uint8_t D_SPENDSGIR[16];/* Offset: 0x1F20 - 0xF2C (R/W) */
        uint32_t RESERVED14[40];
        __IM uint32_t D_PIDR4; /* Offset: 0x1FD0 (R/ ) */
        __IM uint32_t D_PIDR5; /* Offset: 0x1FD4 (R/ ) */
        __IM uint32_t D_PIDR6; /* Offset: 0x1FD8 (R/ ) */
        __IM uint32_t D_PIDR7; /* Offset: 0x1FDC (R/ ) */
        __IM uint32_t D_PIDR0; /* Offset: 0x1FE0 (R/ ) */
        __IM uint32_t D_PIDR1; /* Offset: 0x1FE4 (R/ ) */
        __IM uint32_t D_PIDR2; /* Offset: 0x1FE8 (R/ ) */
        __IM uint32_t D_PIDR3; /* Offset: 0x1FEC (R/ ) */
        __IM uint32_t D_CIDR0; /* Offset: 0x1FF0 (R/ ) */
        __IM uint32_t D_CIDR1; /* Offset: 0x1FF4 (R/ ) */
        __IM uint32_t D_CIDR2; /* Offset: 0x1FF8 (R/ ) */
        __IM uint32_t D_CIDR3; /* Offset: 0x1FFC (R/ ) */
    
        /* CPU 接口端寄存器 */
        __IOM uint32_t C_CTLR; /* Offset: 0x2000 (R/W) */
        __IOM uint32_t C_PMR; /* Offset: 0x2004 (R/W) */
        __IOM uint32_t C_BPR; /* Offset: 0x2008 (R/W) */
        __IM uint32_t C_IAR; /* Offset: 0x200C (R/ ) */
        __OM uint32_t C_EOIR; /* Offset: 0x2010 ( /W) */
        __IM uint32_t C_RPR; /* Offset: 0x2014 (R/ ) */
        __IM uint32_t C_HPPIR; /* Offset: 0x2018 (R/ ) */
        __IOM uint32_t C_ABPR; /* Offset: 0x201C (R/W) */
        __IM uint32_t C_AIAR; /* Offset: 0x2020 (R/ ) */
        __OM uint32_t C_AEOIR; /* Offset: 0x2024 ( /W) */
        __IM uint32_t C_AHPPIR; /* Offset: 0x2028 (R/ ) */
        uint32_t RESERVED15[41];
        __IOM uint32_t C_APR0; /* Offset: 0x20D0 (R/W) */
        uint32_t RESERVED16[3];
        __IOM uint32_t C_NSAPR0; /* Offset: 0x20E0 (R/W) */
        uint32_t RESERVED17[6];
        __IM uint32_t C_IIDR; /* Offset: 0x20FC (R/ ) */
        uint32_t RESERVED18[960];
        __OM uint32_t C_DIR; /* Offset: 0x3000 ( /W) */
    } GIC_Type;
    

第 59行是 GIC 的分发器端相关寄存器,其相对于 GIC 基地址偏移为 0X1000,因此我们获
取到 GIC 基地址以后只需要加上 0X1000 即可访问 GIC 分发器端寄存器

中断处理

在IRQ中断服务函数中,需要读取GIC的寄存器来判断具体的中断源(中断ID),并调用相应的中断服务函数进行处理

每个中断源都有一个唯一的中断ID(Interrupt ID),用于区分不同的中断源。中断ID的范围是从ID0到ID1019,总共1020个中断ID。这些中断ID被分配给不同类型的中断,包括软件触发中断(SGI)、私有外设中断(PPI)和共享外设中断(SPI)。

中断ID的分配

  1. SGI(Software Generated Interrupt)
    • ID范围:ID0~ID15
    • 数量:16个
    • 用途:用于处理器核心之间的通信,通常用于触发软件中断。
  2. PPI(Private Peripheral Interrupt)
    • ID范围:ID16~ID31
    • 数量:16个
    • 用途:用于处理器核心的私有外设中断,例如定时器中断、看门狗中断等。
  3. SPI(Shared Peripheral Interrupt)
    • ID范围:ID32~ID1019
    • 数量:988个
    • 用途:用于共享外设中断,例如GPIO中断、串口中断等。具体的中断ID对应哪个中断由半导体厂商根据实际情况定义。

I.MX6U的中断ID分配

I.MX6U使用了128个中断ID,加上前面属于PPI和SGI的32个ID,总共160个中断ID。这些中断ID的具体分配可以在《I.MX6ULL参考手册》的“3.2 Cortex A7 interrupts”小节中找到。

CP15协处理器

CP15协处理器是ARM Cortex-A内核中的一个重要组件,主要用于存储系统管理。它包含16个32位寄存器(c0~c15),可以通过特定的指令(MRC和MCR)进行读写操作。以下是对CP15协处理器及其相关寄存器的详细解释。

在这里插入图片描述

CP15协处理器的访问指令

  1. MRC(Move to ARM Register from Coprocessor)
    • 将CP15协处理器中的寄存器数据读到ARM寄存器中。
    • 指令格式:MRC{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
  2. MCR(Move to Coprocessor from ARM Register)
    • 将ARM寄存器的数据写入到CP15协处理器寄存器中。
    • 指令格式:MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
  3. cond:指令执行的条件码,如果忽略的话就表示无条件执行。
  4. opc1:协处理器要执行的操作码
  5. Rt:ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中。
    1. MRC 的指令格式和 MCR 一样,只不过在 MRC 指令中 Rt 就是目标寄存器,也就是从
      CP15 指定寄存器读出来的数据会保存在 Rt 中。而 CRn 就是源寄存器,也就是要读取的写处
      理器寄存器
  6. CRn :CP15 协处理器的目标寄存器。
  7. CRm: :协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将
  8. opc2 :可选的协处理器特定操作码,当不需要的时候要设置为 0。

CP15寄存器

c0寄存

在这里插入图片描述

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

当 MRC/MCR 指令中的 CRn=c0,opc1=0,CRm=c0,opc2=0 的时候就表示
此时的 c0 就是 MIDR 寄存器,也就是主 ID 寄存器,这个也是 c0 的基本作用

2. c1寄存

c1寄存器在不同的配置下有不同的含义。以下是一些常见的配置:

在这里插入图片描述

如果要读写 SCTLR 的话,就可以使用如下命令:
MRC p15, 0, <Rt>, c1, c0, 0 ;读取 SCTLR 寄存器,数据保存到 Rt 中。
MCR p15, 0, <Rt>, c1, c0, 0 ;将 Rt 中的数据写到 SCTLR(c1)寄存器中。

  • SCTLR(System Control Register)

    • 配置:MRC p15, 0, <Rt>, c1, c0, 0

    • 作用:控制系统的各种功能,如MMU、I/D Cache等。

    • 在这里插入图片描述

    • 含义:

      • bit13:V,中断向量表基地址选择位。
      • bit12:I,I Cache使能位。
      • bit11:Z,分支预测使能位。
      • bit10:SW,SWP和SWPB使能位。
      • bit2:C,D Cache和缓存一致性使能位。
      • bit1:A,内存对齐检查使能位。
      • bit0:M,MMU使能位。
3. c12寄存器

c12寄存器在不同的配置下有不同的含义。以下是一些常见的配置:

在这里插入图片描述

  • VBAR(Vector Base Address Register)

    • 配置:MRC p15, 0, <Rt>, c12, c0, 0

    • 作用:设置中断向量表的基地址。

    • 示例: VBAR 寄存器,也就是向量表基地址寄存器。设置中断向量表偏移的时候就需要
      将新的中断向量表基地址写入 VBAR 中,比如在前面的例程中,代码链接的起始地址为
      0X87800000,而中断向量表肯定要放到最前面,也就是 0X87800000 这个地址处。所以就需要
      设置VBAR0X87800000,设置命令如下

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

c15寄存器在不同的配置下有不同的含义。以下是一些常见的配置:

在这里插入图片描述

  • CBAR(Configuration Base Address Register)

    • 配置:MRC p15, 4, <Rt>, c15, c0, 0

    • 作用:获取GIC(General Interrupt Controller)的基地址。

    • 示例:

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

MRC p15, 4, r1, c15, c0, 0 ;获取 GIC 基地址
ADD r1, r1, #0X2000 ;GIC 基地址加 0X2000 得到 CPU 接口端寄存器起始地址  可以看看前面的GIC控制器的定义代码
LDR r0, [r1, #0XC] ;读取 CPU 接口端起始地址+0XC 处的寄存器值,也就是寄存器GIC_IAR 的值

示例代码

以下是一个示例代码,展示了如何使用CP15协处理器寄存器:

; 读取MIDR寄存器
MRC p15, 0, r0, c0, c0, 0  ; 将MIDR寄存器的值读取到r0中

; 读取SCTLR寄存器
MRC p15, 0, r1, c1, c0, 0  ; 将SCTLR寄存器的值读取到r1中

; 设置SCTLR寄存器
ldr r2, =0x00000001  ; 使能MMU
MCR p15, 0, r2, c1, c0, 0  ; 将r2的值写入到SCTLR寄存器中

; 设置VBAR寄存器
ldr r3, =0x87800000  ; 设置中断向量表基地址为0x87800000
MCR p15, 0, r3, c12, c0, 0  ; 将r3的值写入到VBAR寄存器中

; 获取GIC基地址
MRC p15, 4, r4, c15, c0, 0  ; 将GIC基地址读取到r4中

总结

  1. c0寄存器:用于获取处理器的主ID信息。
  2. c1寄存器:用于控制系统功能,如MMU、I/D Cache等。
  3. c12寄存器:用于设置中断向量表的基地址。
  4. c15寄存器:用于获取GIC的基地址。

通过合理配置和使用CP15协处理器寄存器,可以有效地管理系统资源和中断处理,提高系统的性能和可靠性。

在ARM Cortex-A内核中,中断使能包括两个主要部分:IRQ和FIQ总中断使能,以及ID0~ID1019这1020个中断源的使能。以下是对这两个部分的详细解释。

IRQ和FIQ总中断使

IRQ和FIQ分别是外部中断和快速中断的总开关。要使用I.MX6U上的外设中断,必须先打开IRQ中断(本教程不使用FIQ)。可以通过设置CPSR寄存器的I位和F位来控制IRQ和FIQ的使能和禁止。此外,还可以使用特定的汇编指令来完成IRQ和FIQ的使能和禁止。

开关中断指令

指令描述
cpsid i禁止IRQ中断。
cpsie i使能IRQ中断。
cpsid f禁止FIQ中断。
cpsie f使能FIQ中断。

ID0~ID1019中断使能和禁止

GIC(General Interrupt Controller)寄存器GICD_ISENABLERnGICD_ICENABLERn用于完成外部中断的使能和禁止。对于Cortex-A7内核,中断ID只使用了512个。一个bit控制一个中断ID的使能,因此需要512/32=16个GICD_ISENABLER寄存器来完成中断的使能,同样也需要16个GICD_ICENABLER寄存器来完成中断的禁止。

GICD_ISENABLERn和GICD_ICENABLERn寄存器
  • GICD_ISENABLER0
    • bit[15:0]:对应ID15~0的SGI中断。
    • bit[31:16]:对应ID31~16的PPI中断。
  • GICD_ISENABLER1~GICD_ISENABLER15
    • 控制ID32~ID511的SPI中断。

示例代码

以下是一个示例代码,展示了如何使能和禁止IRQ和FIQ中断,以及如何使能和禁止特定的中断ID。

使能和禁止IRQ和FIQ中断
; 使能IRQ中断
cpsie i

; 禁止IRQ中断
cpsid i

; 使能FIQ中断
cpsie f

; 禁止FIQ中断
cpsid f
使能和禁止特定中断ID
void GIC_EnableIRQ(uint32_t irq_number)
{
    uint32_t reg_index = irq_number / 32;
    uint32_t bit_index = irq_number % 32;

    GICD->ISENABLER[reg_index] |= (1 << bit_index);
}

void GIC_DisableIRQ(uint32_t irq_number)
{
    uint32_t reg_index = irq_number / 32;
    uint32_t bit_index = irq_number % 32;

    GICD->ICENABLER[reg_index] |= (1 << bit_index);
}

总结

  1. IRQ和FIQ总中断使能
    • 通过设置CPSR寄存器的I位和F位,或使用cpsid icpsie icpsid fcpsie f指令来控制IRQ和FIQ的使能和禁止。
  2. ID0~ID1019中断使能和禁止
    • 使用GIC寄存器GICD_ISENABLERnGICD_ICENABLERn来使能和禁止特定的中断ID。

通过合理配置IRQ和FIQ总中断使能,以及使能和禁止特定的中断ID,可以有效地管理系统的中断,提高系统的实时性和可靠性。

优先级数配置

GIC控制器最多可以支持256个优先级,数字越小,优先级越高。Cortex-A7选择了32个优先级。在使用中断时,需要初始化GICC_PMR寄存器,此寄存器用来决定使用几级优先级。

GICC_PMR寄存器

GICC_PMR寄存器只有低8位有效,这8位最多可以设置256个优先级。

bit7:0优先级数
11111111256个优先级
11111110128个优先级
1111110064个优先级
1111100032个优先级
1111000016个优先级

对于I.MX6U(Cortex-A7内核),支持32个优先级,因此GICC_PMR要设置为0b11111000

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

抢占优先级和子优先级各占多少位是由寄存器GICC_BPR来决定的。

GICC_BPR寄存器

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个优先级位全部为抢占优先级。

3. 优先级设

前面已经设置好了I.MX6U一共有32个抢占优先级,数字越小优先级越高。具体要使用某个中断时,可以设置其优先级为0~31。某个中断ID的中断优先级设置由寄存器GICD_IPRIORITYR来完成。Cortex-A7使用了512个中断ID,每个中断ID配有一个优先级寄存器,所以一共有512个GICD_IPRIORITYR寄存器。如果优先级个数为32的话,使用寄存器GICD_IPRIORITYR的bit7:4来设置优先级,也就是说实际的优先级要左移3位。

示例代码

以下是一个示例代码,展示了如何设置中断优先级:

void GIC_SetPriority(uint32_t irq_number, uint8_t priority)
{
    uint32_t reg_index = irq_number / 4;
    uint32_t shift = (irq_number % 4) * 8;

    // 设置优先级,实际优先级要左移3位
    GICD->IPRIORITYR[reg_index] &= ~(0xFF << shift);
    GICD->IPRIORITYR[reg_index] |= (priority << 3) << shift;
}

总结

  1. 优先级数配置
    • 设置GICC_PMR寄存器,配置优先级个数,例如I.MX6U支持32级优先级。
  2. 抢占优先级和子优先级位数设置
    • 设置GICC_BPR寄存器,配置抢占优先级和子优先级的位数。一般为了简单起见,将所有的位数都设置为抢占优先级。
  3. 优先级设置
    • 设置指定中断ID的优先级,使用GICD_IPRIORITYR寄存器。实际的优先级要左移3位。

通过合理配置中断优先级,可以有效地管理系统的中断,提高系统的实时性和可靠性。

代码实现

start.S

.global _start  				/* 全局标号 */

/*
 * 描述:	_start函数,首先是中断向量表的创建
 * 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P42,3 ARM Processor Modes and Registers(ARM处理器模型和寄存器)
 * 		 	ARM Cortex-A(armV7)编程手册V4.0.pdf P165 11.1.1 Exception priorities(异常)
 */
_start:
	ldr pc, =Reset_Handler		/* 复位中断 					*/	
	ldr pc, =Undefined_Handler	/* 未定义中断 					*/
	ldr pc, =SVC_Handler		/* SVC(Supervisor)中断 		*/
	ldr pc, =PrefAbort_Handler	/* 预取终止中断 					*/
	ldr pc, =DataAbort_Handler	/* 数据终止中断 					*/
	ldr	pc, =NotUsed_Handler	/* 未使用中断					*/
	ldr pc, =IRQ_Handler		/* IRQ中断 					*/
	ldr pc, =FIQ_Handler		/* FIQ(快速中断)未定义中断 			*/

/* 复位中断 */	
Reset_Handler:

	cpsid i						/* 关闭全局中断 */

	/* 关闭I,DCache和MMU 
	 * 采取读-改-写的方式。
	 */
	mrc     p15, 0, r0, c1, c0, 0     /* 读取CP15的C1寄存器到R0中       		        	*/
    bic     r0,  r0, #(0x1 << 12)     /* 清除C1寄存器的bit12位(I位),关闭I Cache            	*/
    bic     r0,  r0, #(0x1 <<  2)     /* 清除C1寄存器的bit2(C位),关闭D Cache    				*/
    bic     r0,  r0, #0x2             /* 清除C1寄存器的bit1(A位),关闭对齐						*/
    bic     r0,  r0, #(0x1 << 11)     /* 清除C1寄存器的bit11(Z位),关闭分支预测					*/
    bic     r0,  r0, #0x1             /* 清除C1寄存器的bit0(M位),关闭MMU				       	*/
    mcr     p15, 0, r0, c1, c0, 0     /* 将r0寄存器中的值写入到CP15的C1寄存器中	 				*/

	
#if 0
	/* 汇编版本设置中断向量表偏移 */
	ldr r0, =0X87800000

	dsb
	isb
	mcr p15, 0, r0, c12, c0, 0
	dsb
	isb
#endif
    
	/* 设置各个模式下的栈指针,
	 * 注意:IMX6UL的堆栈是向下增长的!
	 * 堆栈指针地址一定要是4字节地址对齐的!!!
	 * DDR范围:0X80000000~0X9FFFFFFF
	 */
	/* 进入IRQ模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/
	orr r0, r0, #0x12 	/* r0或上0x13,表示使用IRQ模式					*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/
	ldr sp, =0x80600000	/* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */

	/* 进入SYS模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/
	orr r0, r0, #0x1f 	/* r0或上0x13,表示使用SYS模式					*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/
	ldr sp, =0x80400000	/* 设置SYS模式下的栈首地址为0X80400000,大小为2MB */

	/* 进入SVC模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/
	orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式					*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/
	ldr sp, =0X80200000	/* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */

	cpsie i				/* 打开全局中断 */
#if 0
	/* 使能IRQ中断 */
	mrs r0, cpsr		/* 读取cpsr寄存器值到r0中 			*/
	bic r0, r0, #0x80	/* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */
	msr cpsr, r0		/* 将r0重新写入到cpsr中 			*/
#endif

	b main				/* 跳转到main函数 			 	*/

/* 未定义中断 */
Undefined_Handler:
	ldr r0, =Undefined_Handler
	bx r0

/* SVC中断 */
SVC_Handler:
	ldr r0, =SVC_Handler
	bx r0

/* 预取终止中断 */
PrefAbort_Handler:
	ldr r0, =PrefAbort_Handler	
	bx r0

/* 数据终止中断 */
DataAbort_Handler:
	ldr r0, =DataAbort_Handler
	bx r0

/* 未使用的中断 */
NotUsed_Handler:

	ldr r0, =NotUsed_Handler
	bx r0

/* IRQ中断!重点!!!!! */
IRQ_Handler:
	/*1. 保存上下文*/
	push {lr}					/* 保存链接寄存器lr,lr保存了被中断程序的返回地址 */
	push {r0-r3, r12}			/* 保存通用寄存器r0-r3和r12,这些寄存器在中断处理过程中可能会被修改。 */

	mrs r0, spsr				/* 将当前模式下的状态寄存器spsr读取到r0寄存器中 */
	push {r0}					/* 保存spsr寄存器 */

	/*2. 获取中断号*/
	mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
								* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
								* Cortex-A7 Technical ReferenceManua.pdf P68 P138
								*/							
	add r1, r1, #0X2000			/* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
	ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
								 * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
								 * 这个中断号来绝对调用哪个中断服务函数
								 */
	push {r0, r1}				/* 保存r0,r1 */
	

	/*3.调用C语言中断处理函数*/
	cps #0x13					/* 进入SVC模式,允许其他中断再次进去 */
	
	push {lr}					/* 保存SVC模式的lr寄存器 */
	ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/
	blx r2						/* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */

	/*4. 恢复上下文*/
	pop {lr}					/* 恢复SVC模式的链接寄存器lr */
	cps #0x12					/* 切换回IRQ模式 */
	pop {r0, r1}				/* 恢复中断号和GIC的CPU接口端基地址*/
	str r0, [r1, #0X10]			/* 将中断号写入GICC_EOIR寄存器,表示中断处理完成 */

	pop {r0}					/* 恢复spsr */		
	msr spsr_cxsf, r0			/* 将spsr寄存器的值写回到状态寄存器。 */

	pop {r0-r3, r12}			/* 恢复通用寄存器r0-r3和r12 */
	pop {lr}					/* 恢复IRQ模式的链接寄存器lr */
	subs pc, lr, #4				/* 将lr-4赋给pc,返回到被中断的程序。lr-4是因为ARM架构中,lr保存的是被中断程序的下一条指令的地址,减去4后才是被中断的指令地址 */
	
	

/* FIQ中断 */
FIQ_Handler:

	ldr r0, =FIQ_Handler	
	bx r0									


IRQ中断处理的流程

  1. 保存上下文
    • 保存链接寄存器lr、通用寄存器r0-r3r12、状态寄存器spsr
  2. 获取中断号
    • 从GIC的CPU接口端基地址读取GICC_IAR寄存器,获取中断号。
  3. 调用C语言中断处理函数
    • 切换到SVC模式,调用C语言中断处理函数。
  4. 恢复上下文
    • 恢复之前保存的上下文,包括链接寄存器lr、通用寄存器r0-r3r12、状态寄存器spsr
  5. 返回
    • lr-4赋给pc,返回到被中断的程序。

通用中断管理框架

bsp_int.h
#ifndef __BSP_INT_H__
#define __BSP_INT_H__
#include "imx6ul.h"

/* 中断服务函数指针 */
/*定义了一个函数指针类型system_irq_handler_t,用于指向中断服务函数。*/
typedef void (*system_irq_handler_t) (unsigned int giccIar, void *param);

/* 中断服务函数结构体 */
typedef struct _sys_irq_handle
{
    system_irq_handler_t irqHandler; /* 中断服务函数 */
    void *userParam;                 /* 中断服务函数参数 */
} sys_irq_handle_t;

/* 函数声明 */
void int_init(void);
void system_irqtable_init(void);
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam);
void system_irqhandler(unsigned int giccIar); 
void default_irqhandler(unsigned int giccIar, void *userParam); 

#endif /* __BSP_INT_H__ */
bsp_int.c
#include "bsp_int.h"

/* 中断嵌套计数器 */
static unsigned int irqNesting;

/* 中断服务函数表 */
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];

/*
 * @description	: 中断初始化函数
 * @param		: 无
 * @return 		: 无
 */
void int_init(void)
{
    GIC_Init(); 						/* 初始化GIC 							*/
	system_irqtable_init();				/* 初始化中断表 							*/
	__set_VBAR((uint32_t)0x87800000); 
}

/*
 * @description	: 初始化中断服务函数表 
 * @param		: 无
 * @return 		: 无
 */
void system_irqtable_init(void)
{
	unsigned int i = 0;
	irqNesting = 0;
	
	/* 先将所有的中断服务函数设置为默认值 */
	for(i = 0; i < NUMBER_OF_INT_VECTORS; i++)
	{
		system_register_irqhandler((IRQn_Type)i, default_irqhandler, NULL);
	}
}

/*
 * @description			: 给指定的中断号注册中断服务函数 
 * @param - irq			: 要注册的中断号
 * @param - handler		: 要注册的中断处理函数
 * @param - usrParam	: 中断服务处理函数参数
 * @return 				: 无
 */
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam) 
{
	irqTable[irq].irqHandler = handler;
  	irqTable[irq].userParam = userParam;
}

/*
 * @description			: C语言中断服务函数,irq汇编中断服务函数会
 						  调用此函数,此函数通过在中断服务列表中查
 						  找指定中断号所对应的中断处理函数并执行。
 * @param - giccIar		: 中断号
 * @return 				: 无
 */
void system_irqhandler(unsigned int giccIar) 
{

   uint32_t intNum = giccIar & 0x3FFUL;
   
   /* 检查中断号是否符合要求 */
   if ((intNum == 1023) || (intNum >= NUMBER_OF_INT_VECTORS))
   {
	 	return;
   }
 
   irqNesting++;	/* 中断嵌套计数器加一 */

   /* 根据传递进来的中断号,在irqTable中调用确定的中断服务函数*/
   irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);
 
   irqNesting--;	/* 中断执行完成,中断嵌套寄存器减一 */

}

/*
 * @description			: 默认中断服务函数
 * @param - giccIar		: 中断号
 * @param - usrParam	: 中断服务处理函数参数
 * @return 				: 无
 */
void default_irqhandler(unsigned int giccIar, void *userParam) 
{
	while(1) 
  	{
   	}
}
主要内容
  1. 中断嵌套计数器

    • static unsigned int irqNesting;
    • 用于记录中断嵌套的层数。
  2. 中断服务函数表

    • static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];
    • 用于存储每个中断号对应的中断服务函数和参数。
  3. 中断初始化函数

    • void int_init(void)
    • 初始化GIC(Generic Interrupt Controller)。
    • 初始化中断服务函数表。
    • 设置中断向量表基地址。
  4. 初始化中断服务函数表

    • void system_irqtable_init(void)
    • 将所有中断服务函数初始化为默认值default_irqhandler
  5. 注册中断服务函数

    • void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam)
    • 将指定的中断服务函数和参数注册到中断服务函数表中。
  6. C语言中断服务函数

    • void system_irqhandler(unsigned int giccIar)
    • 从GICC_IAR寄存器中提取中断号。
    • 检查中断号是否有效。
    • 增加中断嵌套计数器。
    • 调用对应的中断服务函数。
    • 减少中断嵌套计数器。
  7. 默认中断服务函数

    • void default_irqhandler(unsigned int giccIar, void *userParam)
    • 默认的中断服务函数,通常用于未处理的中断。

    GPIO管理宽假

bsp_gpio.h
#ifndef _BSP_GPIO_H
#define _BSP_GPIO_H
#define _BSP_KEY_H
#include "imx6ul.h"


/* 
 * 枚举类型和结构体定义 
 */
typedef enum _gpio_pin_direction
{
    kGPIO_DigitalInput = 0U,  		/* 输入 */
    kGPIO_DigitalOutput = 1U, 		/* 输出 */
} gpio_pin_direction_t;

/*
 * GPIO中断触发类型枚举
 */
typedef enum _gpio_interrupt_mode
{
    kGPIO_NoIntmode = 0U, 				/* 无中断功能 */
    kGPIO_IntLowLevel = 1U, 			/* 低电平触发	*/
    kGPIO_IntHighLevel = 2U, 			/* 高电平触发 */
    kGPIO_IntRisingEdge = 3U, 			/* 上升沿触发	*/
    kGPIO_IntFallingEdge = 4U, 			/* 下降沿触发 */
    kGPIO_IntRisingOrFallingEdge = 5U, 	/* 上升沿和下降沿都触发 */
} gpio_interrupt_mode_t;	

/*
 * GPIO配置结构体
 */	
typedef struct _gpio_pin_config
{
    gpio_pin_direction_t direction; 		/* GPIO方向:输入还是输出 */
    uint8_t outputLogic;            		/* 如果是输出的话,默认输出电平 */
	gpio_interrupt_mode_t interruptMode;	/* 中断方式 */
} gpio_pin_config_t;


/* 函数声明 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
int gpio_pinread(GPIO_Type *base, int pin);
void gpio_pinwrite(GPIO_Type *base, int pin, int value);
void gpio_intconfig(GPIO_Type* base, unsigned int pin, gpio_interrupt_mode_t pinInterruptMode);
void gpio_enableint(GPIO_Type* base, unsigned int pin);
void gpio_disableint(GPIO_Type* base, unsigned int pin);
void gpio_clearintflags(GPIO_Type* base, unsigned int pin);

#endif

bsp_gpio.c
#include "bsp_gpio.h"

/*
 * @description		: GPIO初始化。
 * @param - base	: 要初始化的GPIO组。
 * @param - pin		: 要初始化GPIO在组内的编号。
 * @param - config	: GPIO配置结构体。
 * @return 			: 无
 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
	base->IMR &= ~(1U << pin);
	
	if(config->direction == kGPIO_DigitalInput) /* GPIO作为输入 */
	{
		base->GDIR &= ~( 1 << pin);
	}
	else										/* 输出 */
	{
		base->GDIR |= 1 << pin;
		gpio_pinwrite(base,pin, config->outputLogic);	/* 设置默认输出电平 */
	}
	gpio_intconfig(base, pin, config->interruptMode);	/* 中断功能配置 */
}

/*
 * @description	 : 读取指定GPIO的电平值 。
 * @param - base	 : 要读取的GPIO组。
 * @param - pin	 : 要读取的GPIO脚号。
 * @return 		 : 无
 */
 int gpio_pinread(GPIO_Type *base, int pin)
 {
	 return (((base->DR) >> pin) & 0x1);
 }

/*
 * @description	 : 指定GPIO输出高或者低电平 。
 * @param - base	 : 要输出的的GPIO组。
 * @param - pin	 : 要输出的GPIO脚号。
 * @param - value	 : 要输出的电平,1 输出高电平, 0 输出低低电平
 * @return 		 : 无
 */
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{
	 if (value == 0U)
	 {
		 base->DR &= ~(1U << pin); /* 输出低电平 */
	 }
	 else
	 {
		 base->DR |= (1U << pin); /* 输出高电平 */
	 }
}

/*
 * @description  			: 设置GPIO的中断配置功能
 * @param - base 			: 要配置的IO所在的GPIO组。
 * @param - pin  			: 要配置的GPIO脚号。
 * @param - pinInterruptMode: 中断模式,参考枚举类型gpio_interrupt_mode_t
 * @return		 			: 无
 */
void gpio_intconfig(GPIO_Type* base, unsigned int pin, gpio_interrupt_mode_t pin_int_mode)
{
	volatile uint32_t *icr;
	uint32_t icrShift;

	icrShift = pin;
	
	base->EDGE_SEL &= ~(1U << pin);

	if(pin < 16) 	/* 低16位 */
	{
		icr = &(base->ICR1);
	}
	else			/* 高16位 */
	{
		icr = &(base->ICR2);
		icrShift -= 16;
	}
	switch(pin_int_mode)
	{
		case(kGPIO_IntLowLevel):
			*icr &= ~(3U << (2 * icrShift));
			break;
		case(kGPIO_IntHighLevel):
			*icr = (*icr & (~(3U << (2 * icrShift)))) | (1U << (2 * icrShift));
			break;
		case(kGPIO_IntRisingEdge):
			*icr = (*icr & (~(3U << (2 * icrShift)))) | (2U << (2 * icrShift));
			break;
		case(kGPIO_IntFallingEdge):
			*icr |= (3U << (2 * icrShift));
			break;
		case(kGPIO_IntRisingOrFallingEdge):
			base->EDGE_SEL |= (1U << pin);
			break;
		default:
			break;
	}
}

/*
 * @description  			: 使能GPIO的中断功能
 * @param - base 			: 要使能的IO所在的GPIO组。
 * @param - pin  			: 要使能的GPIO在组内的编号。
 * @return		 			: 无
 */
void gpio_enableint(GPIO_Type* base, unsigned int pin)
{ 
    base->IMR |= (1 << pin);
}

/*
 * @description  			: 禁止GPIO的中断功能
 * @param - base 			: 要禁止的IO所在的GPIO组。
 * @param - pin  			: 要禁止的GPIO在组内的编号。
 * @return		 			: 无
 */
void gpio_disableint(GPIO_Type* base, unsigned int pin)
{ 
    base->IMR &= ~(1 << pin);
}

/*
 * @description  			: 清除中断标志位(写1清除)
 * @param - base 			: 要清除的IO所在的GPIO组。
 * @param - pin  			: 要清除的GPIO掩码。
 * @return		 			: 无
 */
void gpio_clearintflags(GPIO_Type* base, unsigned int pin)
{
    base->ISR |= (1 << pin);
}

按键中断

bsp_exit.h
#ifndef _BSP_EXIT_H
#define _BSP_EXIT_H

#include "imx6ul.h"

/* 函数声明 */
void exit_init(void);						/* 中断初始化 */
void gpio1_io18_irqhandler(void); 			/* 中断处理函数 */

#endif
bsp_exit.c
#include "bsp_exit.h"
#include "bsp_gpio.h"
#include "bsp_int.h"
#include "bsp_delay.h"
#include "bsp_beep.h"

/*
 * @description			: 初始化外部中断
 * @param				: 无
 * @return 				: 无
 */
void exit_init(void)
{
	gpio_pin_config_t key_config;

	/* 1、设置IO复用 */
	IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);			/* 复用为GPIO1_IO18 */
	IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);

	/* 2、初始化GPIO为中断模式 */
	key_config.direction = kGPIO_DigitalInput;
	key_config.interruptMode = kGPIO_IntFallingEdge;
	key_config.outputLogic = 1;
	gpio_init(GPIO1, 18, &key_config);

	GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);				/* 使能GIC中对应的中断 */
	system_register_irqhandler(GPIO1_Combined_16_31_IRQn, (system_irq_handler_t)gpio1_io18_irqhandler, NULL);	/* 注册中断服务函数 */
	gpio_enableint(GPIO1, 18);								/* 使能GPIO1_IO18的中断功能 */
}

/*
 * @description			: GPIO1_IO18最终的中断处理函数
 * @param				: 无
 * @return 				: 无
 */
void gpio1_io18_irqhandler(void)
{ 
	static unsigned char state = 0;

	/*
	 *采用延时消抖,中断服务函数中禁止使用延时函数!因为中断服务需要
	 *快进快出!!这里为了演示所以采用了延时函数进行消抖,后面我们会讲解
	 *定时器中断消抖法!!!
 	 */

	delay(10);
	if(gpio_pinread(GPIO1, 18) == 0)	/* 按键按下了  */
	{
		state = !state;
		beep_switch(state);
	}
	
	gpio_clearintflags(GPIO1, 18); /* 清除中断标志位 */
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值