IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> ARM裸机开发:输入中断 -> 正文阅读

[嵌入式]ARM裸机开发:输入中断

ARM裸机开发:输入中断

一、硬件平台:

正点原子I.MX6U阿尔法开发板

_533488159_IMG_20210803_235508_1628006109000_xg_0

二、原理图分析

输入中断是配置GPIO作为输入IO口,检测按键引脚电平,当目标电平来到时产生中断,进入中断服务函数处理程序,I.MX6U的按键引脚如下:

20211101202031

20211101202058

20211101202909

可以看到按键引脚接到 GPIO1_IO18 口,按键的原理就是默认接一个上拉电阻,按键按下接地,可以有效控制 IO 电平

三、程序编写

程序编写前先复制上一节按键输入的工程作为本小节的开始工程

3.1 移植相关文件

在 NXP 提供的 SDK 包内 core_ca7.h 有相关的定义文件,为了节省开发时间,我们将其移植到本地工程目录;注意该文件要做一些修改,删除一些不必要的内容,不然会保存,这里我直接复制正点原子修改后的文件到工程目录下:

20211116103443

该文件下面我们只需要注意 10 个API函数,函数如下:

函数描述
GIC_Init初始化 GIC
GIC_EnableIRQ使能指定的外设中断
GIC_DisableIRQ关闭指定的外设中断
GIC_AcknowledgeIRQ返回中断号
GIC_DeactivateIRQ无效化指定中断
GIC_GetRunningPriority获取当前正在运行的中断优先级
GIC_SetPriorityGrouping设置抢占优先级位数
GIC_GetPriorityGrouping获取抢占优先级位数
GIC_SetPriority设置指定中断的优先级
GIC_GetPriority获取指定中断的优先级

文件添加后使用如下头文件调用

#include "core_ca7.h"

3.2 编写启动文件

SDK 添加完成之后就是修改启动文件,定义系统中断服务函数,修改 IRQ 中断,判断中断类型,进入不同的中断服务函数,启动文件编写如下:

首先编写全局标号,进入 _start 函数,在里面创建中断向量表

.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 和 IRQ_Handler 我们需要关注一下,其他的都暂时先编写为死循环:

/* 未定义中断 */
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
/* FIQ中断 */
FIQ_Handler:
	ldr r0, =FIQ_Handler	
	bx r0									

这些中断服务函数是可以编写一些处理代码,方便用户判断错误的来源的,暂时先不研究

下面编写复位中断服务函数:

/* 复位中断 */	
Reset_Handler:
	/* 关闭全局中断 */
	cpsid i						
	/* 关闭I、DCache和MMU 采取读-改-写的方式*/
    /* 读取CP15的C1寄存器到R0中*/
	mrc     p15, 0, r0, c1, c0, 0    
	/* 清除C1寄存器的bit12位(I位),关闭I Cache*/
    bic     r0,  r0, #(0x1 << 12)     
    /* 清除C1寄存器的bit2(C位),关闭D Cache*/
    bic     r0,  r0, #(0x1 <<  2)     
	/* 清除C1寄存器的bit1(A位),关闭对齐*/
    bic     r0,  r0, #0x2             
	/* 清除C1寄存器的bit11(Z位),关闭分支预测*/
    bic     r0,  r0, #(0x1 << 11)     
	/* 清除C1寄存器的bit0(M位),关闭MMU*/
    bic     r0,  r0, #0x1             
	/* 将r0寄存器中的值写入到CP15的C1寄存器中*/
    mcr     p15, 0, r0, c1, c0, 0     
#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函数 			 	*/

IRQ 中断服务函数,进入中断服务函数后,先进行现场保护,然后获取 GIC 的基地址,偏移后操作其寄存器,获取当前中断号,保存到寄存器 r0 和 r1,接着调用一个c语言中断处理函数,将参数从 r0-r3 四个寄存器传入函数

汇编调用 C 函数的时候建议形参不要超过 4 个,形参可以由 r0~r3 这四个寄存器来传递,如果形参大于 4 个,那么大于 4 个的部分要使用堆栈进行传递。

所以 r0 寄存器写入中断号就可以了传入到函数 system_irqhandler;接着该函数进行对应中断的调用和处理,处理完成后向 GICC_EOIR 寄存器写入其中断号表示中断处理完成

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

	# 现场保护
	push {lr}					/* 保存lr地址 */
	push {r0-r3, r12}			/* 保存r0-r3,r12寄存器 */
	mrs r0, spsr				/* 读取spsr寄存器 */
	push {r0}					/* 保存spsr寄存器 */
	
	
	mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中*/						
	add r1, r1, #0X2000			/* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
	ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,*/
								/* GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据*/
								/* 这个中断号来绝对调用哪个中断服务函数*/
								
	push {r0, r1}				/* 保存r0,r1 */
	cps #0x13					/* 进入SVC模式,允许其他中断再次进去 */
	push {lr}					/* 保存SVC模式的lr寄存器 */
	ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/
	blx r2						/* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */
	pop {lr}					/* 执行完C语言中断服务函数,lr出栈 */
	
	cps #0x12					/* 进入IRQ模式 */
	pop {r0, r1}		
    # 向 GICC_EOIR 寄存器写入刚刚处理完成的中断号,
    # 当一个中断处理完成以后必须向 GICC_EOIR 寄存器
    # 写入其中断号表示中断处理完成
	str r0, [r1, #0X10]			/* 中断执行完成,写EOIR */
	pop {r0}						
	msr spsr_cxsf, r0			/* 恢复spsr */
	pop {r0-r3, r12}			/* r0-r3,r12出栈 */
	pop {lr}					/* lr出栈 */
	subs pc, lr, #4				/* 将lr-4赋给pc */

之后就是进行现场恢复,返回到中断位置!注意,此处恢复现场传递的是 lr - 4 的寄存器值,而不是pc,因为 ARM 的指令是三级流水线:取指、译指、执 行pc 指向的是正在取值的地址,比如下面一段代码

0X2000 MOV R1, R0 ;执行
0X2004 MOV R2, R3 ;译指
0X2008 MOV R4, R5 ;取值 PC

当前正在执行 0X2000 地址处的指令 “MOV R1, R0” ,但 PC 里面已经保存了 0X2008 地址处的指令“MOV R4, R5”。若发生中断,中断发生的时候保存在 lr 中的是 pc 的值,即地址 0X2008。当中断处理完成如果直接跳转到 lr 里面保存的地址处(0X2008) 开始运行,那么就有一个指令没有执行,所以就需要将 lr-4 赋值给 pc,即 pc=0X2004,从第二级正在译指的指令 “MOV R2, R3” 开始执行

3.3 中断处理程序

我们在中断服务函数 IRQ_Handler 中调用了 C 函数 system_irqhandler 来处理具体的中断,该函数的具体细节需要我们自己实现,所以要编写中断处理程序来实现,同时因为中断数量较多,所以我们引入一些其他的数据结构单元辅助管理中断服务函数,编写如下:

新建一个新的模块文件

20211116141327

头文件插入如下代码

#ifndef __BSP_INT_H
#define __BSP_INT_H
#include "imx6ul.h"

typedef void (* system_irq_handler_t) (unsigned int giccIar,void *param);

typedef struct _sys_irq_handle
{
    /* data */
    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

代码解释:

typedef void (* system_irq_handler_t) (unsigned int giccIar,void *param);

创建一个函数指针,用 typedef 定义修饰别名为 system_irq_handler_t

typedef struct _sys_irq_handle
{
    /* data */
    system_irq_handler_t irqHandler;
    void *userParam;
} sys_irq_handle_t;

创建一个结构体,其有两个参数,一个是函数指针的入口指针,另外一个则是一个用户参数,创建这个结构体用于保存中断的信息,保存其中断处理函数入口因为有160个中断源,所以我们在.c文件中可以定义一个结构体数组用于存储所有中断的信息

其他的就是一些函数声明了:

// 中断系统(GIC)初始化
void int_init(void);
// 中断信息结构体数组初始化
void system_irqtable_init(void);
// 注册中断,修改目标中断的结构体的信息
//要使用某个外设中断,那就必须调用此函数来给这个中断注册一个中断处理函数
void system_register_irqhandler(IRQn_Type irq,
                                system_irq_handler_t handler,
                                void *userParam);
// _start 文件中调用的的中断号处理函数
void system_irqhandler(unsigned int giccIar);
// 默认中断处理函数
void default_irqhandler(unsigned int giccIar,void *userParam);

.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) ;
}

3.4 开启输入中断

这里 GPIO 配置代码直接使用正点原子的驱动方案,有关的注释我写在代码内

bsp_gpio.h

#ifndef _BSP_GPIO_H
#define _BSP_GPIO_H
#define _BSP_KEY_H
#include "imx6ul.h"
/***************************************************************
Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_gpio.h
作者	   : 左忠凯
版本	   : V1.0
描述	   : GPIO操作文件头文件。
其他	   : 无
论坛 	   : www.openedv.com
日志	   : 初版V1.0 2019/1/4 左忠凯创建
	 	 V2.0 2019/1/4 左忠凯修改
	 	 添加GPIO中断相关定义

***************************************************************/

/* 枚举类型和结构体定义 */
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;

/* 函数声明 */
// GPIO 初始化
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
// GPIO 读IO电平
int gpio_pinread(GPIO_Type *base, int pin);
// GPIO 写GPIO电平
void gpio_pinwrite(GPIO_Type *base, int pin, int value);
// GPIO 中断配置
void gpio_intconfig(GPIO_Type* base, unsigned int pin, 
                    gpio_interrupt_mode_t pinInterruptMode);
// 使能 GPIO 中断
void gpio_enableint(GPIO_Type* base, unsigned int pin);
// 失能 GPIO 中断
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"
/***************************************************************
Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_gpio.h
作者	   : 左忠凯
版本	   : V1.0
描述	   : GPIO操作文件。
其他	   : 无
论坛 	   : www.openedv.com
日志	   : 初版V1.0 2019/1/4 左忠凯创建
		 V2.0 2019/1/4 左忠凯修改:
		 修改gpio_init()函数,支持中断配置.
		 添加gpio_intconfig()函数,初始化中断
		 添加gpio_enableint()函数,使能中断
		 添加gpio_clearintflags()函数,清除中断标志位
		 
***************************************************************/

/*
 * @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);
}

3.5 按键中断编写

有了 GPIO 驱动代码后,我们就可以新建一个新的模块代码,用于配置外部触发中断,新建模块如下:

20211116150340

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"

void exit_init(void)
{
    //设定GPIO模式
    gpio_pin_config_t key_config;
    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);
	//设定按键中断
    key_config.direction=kGPIO_DigitalInput;
    key_config.interruptMode=kGPIO_IntFallingEdge;
    key_config.outputLogic=1;
    gpio_init(GPIO1,18,&key_config);
	//使能GIC中断,注册按键触发中断
    GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
    system_register_irqhandler(GPIO1_Combined_16_31_IRQn,
                               (system_irq_handler_t)gpio1_io18_irqhandler,
                               NULL);
    //使能按键触发中断
    gpio_enableint(GPIO1, 18);
}

void gpio1_io18_irqhandler(void)
{
    static unsigned char state = 0;
    //延时消抖(中断中严禁使用死延时,这里是为了IO稳定)
    delay(10);
    if(gpio_pinread(GPIO1,18) == 0)
    {
        state = !state;
        beep_switch(state);
    }
    //清除中断标志
    gpio_clearintflags(GPIO1,18);
}

以上代码准备完成后,我们在 main.c 中分别调用代码进行初始化

20211116155803

3.6 编写Makefile脚本

在 Makefile 里面添加上对应文件的文件夹就可以完成编译,添加位置如下:

20211116155909
编译一下,成功通过:

20211116155930

四、实验现象

按下按键 LED 的灯光效果切换

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-17 12:54:59  更:2021-11-17 12:55:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:50:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码