网站flash效果,丹阳房产网二手房,公众号排版编辑器,海南在线一家10. GPIO中断 回顾stm32中断系统STM32中断向量表中断向量偏移NVIC中断控制器 Cortex_A7 中断系统中断向量表GIC控制器中断IDGIC逻辑分块CP15协处理器c0寄存器c1寄存器c12寄存器c15寄存器 中断使能中断优先级设置优先级数配置 GICC_PMR抢占优先级和子优先级位数设置 GICC_BPR优先… 10. GPIO中断 回顾stm32中断系统STM32中断向量表中断向量偏移NVIC中断控制器 Cortex_A7 中断系统中断向量表GIC控制器中断IDGIC逻辑分块CP15协处理器c0寄存器c1寄存器c12寄存器c15寄存器 中断使能中断优先级设置优先级数配置 GICC_PMR抢占优先级和子优先级位数设置 GICC_BPR优先级设置 D_IPRIORITYR 代码实例移植 SDK 包中断相关文件修改start.s通用中断处理函数编写修改GPIO驱动文件按键中断驱动文件主函数 回顾stm32中断系统
STM32中断向量表
中断向量表存放的是中断向量。中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量。ARM芯片从0x00000000开始运行执行指令。在程序开始的地方存放着中断向量表中断向量表主要功能是描述中断对应的中断服务函数。对于stm32来说代码最开始的地址是存放在栈顶指针
中断向量偏移
通过中断偏移可以将中断向量表放到任意位置。一般ARM从0x00000000地址开始运行对于stm32我们设置连接首地址为0x80000000。如果代码一定要从该地址运行一定要告诉soc内核也就是设置中断向量偏移。设置SCB的VTOR寄存器为新的中断向量表起始地址即可
NVIC中断控制器
NVIC就是中断管理机构例如使能和关闭指定的中断设置中断优先级
Cortex_A7 中断系统 复位中断CPU复位后就会进入可以在复位中断服务函数中做一些初始化工作未定义指令中断如果指令不能识别的话就会产生软中断由 SWI 指令引起的中断Linux 的系统调用会用SWI指令来引起软中断通过软中断可以进入内核空间指令预取中止中断预取指令的出错的时候就会产生数据访问中止中断访问数据出错的时候产生IRQ中断外部中断芯片内部的外部中断都会引起此中断的发生FIQ中断快速中断如果需要快速处理中断的话就可以使用 上述中最常用的是复位中断和IRQ中断
中断向量表
Cortex-A内核CPU的所有外设中断都属于IRQ中断当任意一个外部中断发生的时候都会触发IRQ中断。在IRQ中断服务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断进而做出相应的处理。中断向量表处于程序最开始的地方比如 start.s 文件最前面
.global _start_start:ldr pc, Reset_Handler // 复位中断ldr pc, Undefined_Handler // 未定义指令中断ldr pc, SVC_Handler // SVC 中断ldr pc, PrefAbort_Handler // 预取终止中断ldr pc, DataAbort_Handler // 数据终止中断ldr pc, NotUsed_Handler // 未使用中断ldr pc, IRQ_Handler // IRQ 中断ldr pc, FIQ_Handler // FIQ 快速中断未定义中断
// 以上这一部分就是中断向量表当中断发生时就会调用对应的中断服务函数下面的就是中断服务函数
Reset_Handler:// 具体实现Undefined_Handler:ldr r0, Undefined_Handlerbx r0 // 跳转到未定义中断服务函数位置执行SVC_Handler:ldr r0, SVC_Handler bx r0PrefAbort_Handler:ldr r0, PrefAbort_Handler bx r0DataAbort_Handler:ldr r0, DataAbort_Handler bx r0NotUsed_Handler:ldr r0, NotUsed_Handler bx r0IRQ_Handler:// IRQ中断具体处理过程FIQ_Handler:ldr r0, FIQ_Handlerbx r0GIC控制器
当 GIC 接收到外部中断信号后就会报给 ARM 内核ARM 内核只提供了四个信号给GIC来汇报中断情况VFIQ虚拟快速FIQ、VIFQ虚拟外部IRQ、FIQ和IRQ。这里只探讨IRQ 左侧部分是中断源中间部分是GIC控制器最右侧就是中断控制器向处理器内核发送中断信息。
SPI共享中断所有Core共享的中断外设中断都属于SPI中断这些中断所有的Core都可以处理不限定特定的CorePPI私有中断GIC是支持多核的每个核有自己独立的中断这些中断要指定的核心处理所有这些中断就叫做私有中断SGI网络中断由软件触发引起的中孤单通过向寄存器GICD_SGIR写入数据来触发系统会使用SGI中断来完成多核之间的通信
中断ID
中断源有很多为了区分这些不同的中断源就产出了一个唯一ID。每一个CPU最多支持1020个中断ID包含了SGI0 ~ 15、PPI16 ~ 31和 SPI32 ~ 1019。I.MX6U 的中断源有32128 160 个。
GIC逻辑分块
分了两个逻辑块Distributor 和 CPU Interface也就是分发器端和CPU接口端 分发器端 负责处理各个中断事件的分发问题就是中断事件应该发送到哪个CPU 接口端。分发器收集所有的中断源可以控制每个中断的优先级将优先级高的中断事件发送到CPU接口端。 主要工作全局中断使能控制、控制每一个中断的使能或关闭、设置每个中断的优先级、设置每个中断的目标处理器列表、设置每个外部中断的触发模式电平触发或边沿触发、设置每个中断属于组0还是组1。分发器端相关寄存器相对于 GIC 基地址偏移为 0x1000 CPU接口端 和 CPU Core 相连每个 CPU Core 都可以在GIC中找到一个对应的CPU Interface。主要工作使能或关闭发送到CPU Core 的中断请求信号、应答中断、通知中断处理完成、设置优先级掩码并通过掩码来设置哪些中断不需要上报给CPU Core、定义抢占策略、当多个中断到来时选择优先级最高的。接口端相关寄存器相对于 GIC 基地址偏移为 0x2000。 GIC 基地址就需要用到 CP15 协处理器获取。
CP15协处理器 CP15协处理器一般用于存储系统管理但是在中断中也会使用一共有16个32位寄存器该处理器的访问通过如下指令完成
MRC将CP15协处理器中的寄存器数据读到ARM寄存器中MCR将ARM寄存器的数据写入到CP15中
MCR{cond} p15,opc1,Rt,CRn,CRm,opc2
/** cond: 指令执行的条件码如果忽略就表示无条件执行* opc1: 协处理器要执行的操作码* Rt: ARM源寄存器要写入到CP15寄存器的数据* CRn: CP15的目标寄存器* CRm: 协处理器中附加的目标寄存器或源操作数寄存器如果不需要附加信息就将CRm设置为C0否则表示不可预测* opc2: 可选的协处理器特定操作码不需要的时候要设置为0
*/
// MRC 指令格式和 MCR 一样
// 例如将 CP15 中 C0 寄存器的值读取到 R0 中
MRC p15, 0, r0, c0, c0, 0c0寄存器 上图是c0寄存器不同搭配的含义。当指令中的 CRnc0opc10CRmc0opc20 的时候就表示此时的 c0 就是 MIDR 寄存器也就是主 ID 寄存器。
c1寄存器 SCTLR是c1寄存器的基本作用也就是系统控制寄存器主要是完成控制功能位结构图如下 bit13 V中断向量表基地址选择位为0的话就是0x00000000软件可使用VBAR来重映射此基地址为1的话就是0xFFFF0000此及地址不能被重新映射 bit12 II Cache使能位为0就关闭为1就使能 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 的话就可以使用如下命令
MRC p15, 0, Rt, c1, c0, 0 // 读取 SCTLR 寄存器数据保存到 Rt 中。
MCR p15, 0, Rt, c1, c0, 0 // 将 Rt 中的数据写到 SCTLR(c1)寄存器中。c12寄存器 设置中断向量表偏移的时候就需要将新的中断向量表基地址写入VBAR中也就是向量表基地址寄存器。
ldr r0, 0x87800000
MCR p15,0,r0,c12,c0,0 // c120x87800000c15寄存器 需要c15作为CBAR寄存器因为 GIC 的基地址就保存在 CBAR 中
MRC p15,4,r1,c15,c0,0 // 获取基地址保存在r1中获取到基地址后就可以设置相关寄存器比如获取当前中断ID中断ID保存在GICC_IAR中属于CPU接口寄存器
MRC p15,4,r1,c15,c0,0 // 获取基地址
ADD r1,r1,#0x2000 // 基地址加0x2000得到CPU接口寄存器起始地址
LDR r0,[r1,#0xC] // 读取CPU接口端起始地址0xC处的寄存器值也就是寄存器GIC_IAR的值中断使能
中断使能包括两部分一个是IRQ或FIQ总中断使能另一个就是 ID0 ~ ID1020 中断源的使能
IRQ和FIQ总中断使能 要想使用I.MX6U上的外设时钟就必须先打开IRQ中断。CPSR的I1禁止IRQI0使能IRQF1禁止FIQF0使能FIQ。也可以使用指令 ID0~ID1019中断使能和禁止 GIC寄存器 GICD_ISENABLERn 和 GICD_ICENABLERn 用来完成外部中断的使能和禁止。对于 Cortex-A7 内核来说中断 ID 只使用了 512 个。一个 bit 控制一个中断 ID 的使能那么就需要 512/3216 个 GICD_ISENABLER 寄存器来完成中断的使能。同理也需要 16 个GICD_ICENABLER 寄存器来完成中断的禁止。其中 GICD_ISENABLER0 的 bit[15:0] 对应ID15 ~ 0 的 SGI 中断 GICD_ISENABLER0 的 bit[31:16] 对应 ID31 ~ 16 的 PPI 中断。剩下的 GICD_ISENABLER1 ~ GICD_ISENABLER15 就是控制 SPI 中断的。
中断优先级设置
优先级数配置 GICC_PMR
中断优先级可以分为抢占优先级和子优先级。GIC 控制器最多可以支持 256 个优先级数字越小优先级越高 Cortex-A7 选择了 32 个优先级。在使用中断的时候需要初始化 GICC_PMR 寄存器此寄存器用来决定使用几级优先级
抢占优先级和子优先级位数设置 GICC_BPR
也就是设置抢占优先级和子优先级各占多少位 一般将所有的中断优先级位配置为抢占优先级比如I.MX6U的优先级位数为532个优先级所以可以设置Binary point 为2
优先级设置 D_IPRIORITYR
某个中断 ID 的中断优先级是由寄存器 D_IPRIORITYR 来设置的一共有512个如果优先级个数为32使用寄存器 bit7:4 来设置也就是实际的优先级要左移 3 位
GICD_IPRIORITYR[40]53; // 设置ID40 中断优先级为5代码实例
移植 SDK 包中断相关文件
将 core_ca7.h 拷贝到 imx6ul 文件夹中主要留下 GIC 相关的内容重点是10个接口函数
修改start.s 添加中断向量表编写复位中断服务函数和IRQ中断服务函数
.global _start_start:ldr pc, Reset_Handler // 复位中断ldr pc, Undefined_Handler // 未定义指令中断ldr pc, SVC_Handler // SVC 中断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/D Cache 和 MMU采取读-改-写的方式bic 指令就是清除特定位mrc p15, 0, r0, c1, c0, 0 // 读取 CP15 的 C1 寄存器到 R0 中bic r0, r0, #(0x1 12) // 清除 C1 的 I 位关闭 I Cache清除 r0 位并将结果保存到 r0 中bic r0, r0, #(0x1 2) // 清除 C1 的 C 位关闭 D Cachebic r0, r0, #0x2 // 清除 C1 的 A 位关闭对齐检查bic r0, r0, #(0x1 11) // 清除 C1 的 Z 位关闭分支预测bic r0, r0, #0x1 // 关闭 C1 的 M 位关闭MMUmcr p15, 0, r0, c1, c0, 0 // 将 r0 的值写入到 CP15 的 C1 中// 设置各个模式下的栈指针// IMX6U 的堆栈是向下增长的也要注意4字节内存对齐// DDR 的范围是 0x80000000 ~ 0x9FFFFFFF 或者 0x8FFFFFFF// 进入 IRQ 模式mrs r0, cpsrbic r0, r0, #0x1f // 将 r0 的低五位清零orr r0, r0, #0x12 // r0 | 0x12表示使用 IRQ 模式msr cpsr, r0 // 将 r0 的数据写入到 cpsr 中ldr sp, 0x80600000 // IRQ 模式栈首地址为 0x80600000大小为 2MB// 进入 SYS 模式mrs r0, cpsrbic r0, r0, #0x1f // 将 r0 的低五位清零orr r0, r0, #0x1f // r0 | 0x1f表示使用 SYS 模式msr cpsr, r0 // 将 r0 的数据写入到 cpsr 中ldr sp, 0x80600000 // SYS 模式栈首地址为 0x80400000大小为 2MB// 进入 SVC 模式mrs r0, cpsrbic r0, r0, #0x1f // 将 r0 的低五位清零orr r0, r0, #0x13 // r0 | 0x13表示使用 SVC 模式msr cpsr, r0 // 将 r0 的数据写入到 cpsr 中ldr sp, 0x80200000 // SVC 模式栈首地址为 0x80600000大小为 2MB cpsie i // 打开全局中断b main // 跳转到 main 函数
Undefined_Handler:ldr r0, Undefined_Handlerbx r0 // 跳转到未定义中断服务函数位置执行SVC_Handler:ldr r0, SVC_Handler bx r0PrefAbort_Handler:ldr r0, PrefAbort_Handler bx r0DataAbort_Handler:ldr r0, DataAbort_Handler bx r0NotUsed_Handler:ldr r0, NotUsed_Handler bx r0IRQ_Handler:push {lr} // 保存 lr 地址push {r0-r3, r12} // 保存 r0-r3r12 寄存器mrs r0, spsr // 读取 spsr 寄存器push {r0} // 保存 spsr 寄存器// 以上是保护现场方便回到触发中断的位置继续执行mrc p15, 4, r1, c15, c0, 0 // 将 CP15 的 C0 的值读到 r1 寄存器中add r1, r1, #0x2000 // GIC 基地址加 0x2000得到 CPU 接口地址ldr r0, [r1, #0xC] // CPU 接口端基地址加 0x0c 就是 GICC_IAR 寄存器保存着当前发生中断的中断号要根据中断号来判断调用哪个中断服务函数push {r0, r1} // 保存 r0, r1cps #0x13 // 进入 SVC 模式允许其他中断再次进去push {lr} // 保存 SVC 模式的 lr 寄存器ldr r2, system_irqhandler // 加载C语言中断处理函数到r2寄存器中blx r2 // 运行C语言中断处理函数带有一个参数pop {lr} // 执行完C语言中断服务函数lr 出栈cps #0x12 // 进入 IRQ 模式pop {r0, r1} str r0, [r1, #0x10] // 中断执行完成向 EOIR 寄存器写入刚刚处理完成的中断号pop {r0}msr spsr_cxsf, r0 // 恢复 spsrpop {r0-r3, r12} // r0-r3r12 出栈pop {lr} // lr 出栈subs pc, lr, #4 // 将 lr-4 赋给 pc// 为什么要将 pc-4 后再赋值ARM 指令是三级流水线分别是取指、译指、执行pc指向的是正在取值的地址
FIQ_Handler:ldr r0, FIQ_Handlerbx r0编写复位中断服务函数
IRQ中断服务函数 MRC p15, 4, r1, c15, c0, 0读取CP15的CBAR寄存器CBAR寄存器保存了GIC控制器的寄存器组的首地址。0x1000 ~ 0x1ff是GIC的分发器0x2000 ~ 0x3fff是CPU接口端。代码中R1寄存器保存着GIC控制器的CPU接口端基地址读取CPU接口端的GICC_IAR 寄存器的值保存到R0寄存器里面。可以从GICC_IAR的bit9 ~ 0读取中断ID也就可以判断出是什么中断就可以执行对应的中断处理函数。system_irqhandler就是具体的中断服务函数只有一个参数就是GICC_IAR的值。处理完具体的中断后需要将对应的中断ID值写入到GICC_EOIR
通用中断处理函数编写 bsp_int.h 不同的中断源对应不同的中断处理函数IMX6U有160个中断源可以将这些中断处理函数放在一个数组中对应的下标就是中断号。
#pragma once
#include imx6ul.h// 定义中断处理函数
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 ini_init();
void system_irqtable_init();
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam);// IRQn_Type 是 NXP 官方 SDK中的文件 MCIMX6Y2C.h 中定义的一个枚举类型包含了IMX6U的所有中断
void system_irqhandler(unsigned int giccIar);
void default_irqhandler(unsigned int giccIar, void *userParam);bsp_int.c #include bsp_int.h
static unsigned int irqNesting; // 统计中断个数
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS]; // 中断处理函数表这个宏是在MCIMX6Y2.h中定义的void int_init()
{GIC_init(); // 初始化 GICsystem_irqtable_init(); // 初始化中断表__set_VBAR((uint32_t)0x87800000); // 中断向量表偏移
}
// 初始化中断处理函数表
void system_irqtable_init()
{unsigned int i 0;irqNesting0;// 先将所有的中断服务函数设置为默认值for(i0; iNUMBER_OF_INT_VECTORS;i){system_register_irqhandler((IRQn_Type)i, default_irqhandler, NULL);}
}
// 注册中断处理函数
void system_register_irqhandler(IRQn_Yype irq, system_irq_handler_t handler, void *userParam)
{irqTable[irq].irqHandler handler;irqTable[irq].userParam userParam;
}// 具体的中断处理函数IRQ_Handler 会调用此函数
void system_irqhandler(unsigned int gicciar)
{uint32_t intNum giccier 0x3ff;// 检查中断IDif((intNum 1020) || (intNum NUMBER_OF_INT_VECTORS)){return;}irqNesting;irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);// 执行具体的中断处理函数irqNesting--;
}
// 默认中断处理函数
void default_irqhandler(unsigned int gicciar, void *UserParam)
{while(1){}
}修改GPIO驱动文件
首先要设置GPIO中断触发方式也就是GPIO_ICR1 或 GPIO_ICR2 寄存器。触发方式有0低电平、1高电平、2上升沿和3下降沿触发方式。对于本例程设置KEY0也就是UART1_CTS为下降沿触发。使能GPIO对应的中断设置GPIO_IMR 寄存器处理完中断后需要清除中断标志位也就是GPIO_ISR寄存器相应的位是通过写1清零
#pragma once
#define _BSP_KEY_H
#include imx6ul.h
// 枚举类型和结构体定义
typedef enum _gpio_pin_direction
{kGPIO_DigitalInput 0U, /* 输入 */kGPIO_DigitalOutput 1U, /* 输出 */
} gpio_pin_direction_t;// 触发中断类型枚举
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);#include bsp_gpio.h
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);
}按键中断驱动文件
#pragma once
#include imx6ul.h
void exit_init(void); // 中断初始化
void gpio1_io18_irqhandler(void); // 中断处理函数按键所使用的是 UART_CTS 这个IO设置复用为 GPIO1_IO18设置为下降沿中断
#include bsp_exit.h
#include bsp_gpio.h
#include bsp_int.h
#include bsp_delay.h
#include bsp_beep.hvoid 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的中断功能 */
}
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); /* 清除中断标志位 */
}主函数
#include bsp_clk.h
#include bsp_delay.h
#include bsp_led.h
#include bsp_beep.h
#include bsp_key.h
#include bsp_int.h
#include bsp_exit.hint main(void)
{unsigned char state OFF;int_init(); imx6u_clkinit(); clk_enable(); led_init(); beep_init(); key_init(); exit_init(); while(1) { state !state;led_switch(LED0, state);delay(500);}return 0;
}