热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ARM裸机开发:输入中断

文章目录ARM裸机开发:输入中断一、硬件平台:二、原理图分析三、程序编写3.1移植相关文件3.2编写启动文件3.3中断处理程序3.4开启输入中断3.5按

文章目录

  • ARM裸机开发:输入中断
    • 一、硬件平台:
    • 二、原理图分析
    • 三、程序编写
      • 3.1 移植相关文件
      • 3.2 编写启动文件
      • 3.3 中断处理程序
      • 3.4 开启输入中断
      • 3.5 按键中断编写
      • 3.6 编写Makefile脚本
      • 四、实验现象


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_Handlerbx r0
/* SVC中断 */
SVC_Handler:ldr r0, =SVC_Handlerbx r0
/* 预取终止中断 */
PrefAbort_Handler:ldr r0, =PrefAbort_Handler bx r0
/* 数据终止中断 */
DataAbort_Handler:ldr r0, =DataAbort_Handlerbx r0
/* 未使用的中断 */
NotUsed_Handler:ldr r0, =NotUsed_Handlerbx 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位)&#xff0c;关闭I Cache*/bic r0, r0, #(0x1 <<12) /* 清除C1寄存器的bit2(C位)&#xff0c;关闭D Cache*/bic r0, r0, #(0x1 <<2) /* 清除C1寄存器的bit1(A位)&#xff0c;关闭对齐*/bic r0, r0, #0x2 /* 清除C1寄存器的bit11(Z位)&#xff0c;关闭分支预测*/bic r0, r0, #(0x1 <<11) /* 清除C1寄存器的bit0(M位)&#xff0c;关闭MMU*/bic r0, r0, #0x1 /* 将r0寄存器中的值写入到CP15的C1寄存器中*/mcr p15, 0, r0, c1, c0, 0
#if 0/* 汇编版本设置中断向量表偏移 */ldr r0, &#61;0X87800000dsbisbmcr p15, 0, r0, c12, c0, 0dsbisb
#endif/* 设置各个模式下的栈指针&#xff0c;* 注意&#xff1a;IMX6UL的堆栈是向下增长的&#xff01;* 堆栈指针地址一定要是4字节地址对齐的&#xff01;&#xff01;&#xff01;* DDR范围:0X80000000~0X9FFFFFFF*//* 进入IRQ模式 */mrs r0, cpsrbic r0, r0, #0x1f /* 将r0寄存器中的低5位清零&#xff0c;也就是cpsr的M0~M4 */orr r0, r0, #0x12 /* r0或上0x13,表示使用IRQ模式 */msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ldr sp, &#61;0x80600000 /* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB *//* 进入SYS模式 */mrs r0, cpsrbic r0, r0, #0x1f /* 将r0寄存器中的低5位清零&#xff0c;也就是cpsr的M0~M4 */orr r0, r0, #0x1f /* r0或上0x13,表示使用SYS模式 */msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ldr sp, &#61;0x80400000 /* 设置SYS模式下的栈首地址为0X80400000,大小为2MB *//* 进入SVC模式 */mrs r0, cpsrbic r0, r0, #0x1f /* 将r0寄存器中的低5位清零&#xff0c;也就是cpsr的M0~M4 */orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ldr sp, &#61;0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */cpsie i /* 打开全局中断 */
#if 0/* 使能IRQ中断 */mrs r0, cpsr /* 读取cpsr寄存器值到r0中 */bic r0, r0, #0x80 /* 将r0寄存器中bit7清零&#xff0c;也就是CPSR中的I位清零&#xff0c;表示允许IRQ中断 */msr cpsr, r0 /* 将r0重新写入到cpsr中 */
#endifb main /* 跳转到main函数 */

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

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

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

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

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

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

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

3.3 中断处理程序

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

新建一个新的模块文件

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

代码解释&#xff1a;

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

创建一个函数指针&#xff0c;用 typedef 定义修饰别名为 system_irq_handler_t

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

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

其他的就是一些函数声明了&#xff1a;

// 中断系统&#xff08;GIC&#xff09;初始化
void int_init(void);
// 中断信息结构体数组初始化
void system_irqtable_init(void);
// 注册中断&#xff0c;修改目标中断的结构体的信息
//要使用某个外设中断&#xff0c;那就必须调用此函数来给这个中断注册一个中断处理函数
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 模块文件代码如下&#xff0c;具体功能注释写在代码中&#xff1a;

#include "bsp_int.h"/* 中断嵌套计数器,计算中断嵌套信息 */
static unsigned int irqNesting;/* 中断服务函数表&#xff0c; 用于存放中断的信息*/
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];/** &#64;description : 中断初始化函数* &#64;param : 无* &#64;return : 无*/
void int_init(void)
{GIC_Init(); /* 初始化GIC*/system_irqtable_init(); /* 初始化中断表*/__set_VBAR((uint32_t)0x87800000); /* 中断向量表偏移&#xff0c;偏移到起始地址*/
}/** &#64;description : 初始化中断服务函数表 * &#64;param : 无* &#64;return : 无*/
void system_irqtable_init(void)
{unsigned int i &#61; 0;irqNesting &#61; 0;/* 先将所有的中断服务函数设置为默认值 */for(i &#61; 0; i < NUMBER_OF_INT_VECTORS; i&#43;&#43;){//给每个中断的数组改变传入参数和数值system_register_irqhandler((IRQn_Type)i,default_irqhandler, NULL);}
}/** &#64;description : 给指定的中断号注册中断服务函数 * &#64;param - irq : 要注册的中断号* &#64;param - handler : 要注册的中断处理函数* &#64;param - usrParam : 中断服务处理函数参数* &#64;return : 无*/
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam)
{irqTable[irq].irqHandler &#61; handler;irqTable[irq].userParam &#61; userParam;
}/** &#64;description : C语言中断服务函数&#xff0c;irq汇编中断服务函数会调用此函数&#xff0c;此函数通过在中断服务列表中查找指定中断号所对应的中断处理函数并执行。* &#64;param - giccIar : 中断号* &#64;return : 无*/
void system_irqhandler(unsigned int giccIar)
{uint32_t intNum &#61; giccIar & 0x3FFUL;/* 检查中断号是否符合要求 */if ((intNum &#61;&#61; 1023) || (intNum >&#61; NUMBER_OF_INT_VECTORS)){return;}irqNesting&#43;&#43;; /* 中断嵌套计数器加一 *//* 根据传递进来的中断号&#xff0c;在irqTable中调用确定的中断服务函数 */irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);irqNesting--; /* 中断执行完成&#xff0c;中断嵌套寄存器减一 */}/** &#64;description : 默认中断服务函数* &#64;param - giccIar : 中断号* &#64;param - usrParam : 中断服务处理函数参数* &#64;return : 无*/
void default_irqhandler(unsigned int giccIar, void *userParam)
{while(1) ;
}

3.4 开启输入中断

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

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 &#61; 0U, /* 输入 */kGPIO_DigitalOutput &#61; 1U, /* 输出 */
} gpio_pin_direction_t;/* GPIO中断触发类型枚举 */
typedef enum _gpio_interrupt_mode
{kGPIO_NoIntmode &#61; 0U, /* 无中断功能 */kGPIO_IntLowLevel &#61; 1U, /* 低电平触发 */kGPIO_IntHighLevel &#61; 2U, /* 高电平触发 */kGPIO_IntRisingEdge &#61; 3U, /* 上升沿触发 */kGPIO_IntFallingEdge &#61; 4U, /* 下降沿触发 */kGPIO_IntRisingOrFallingEdge &#61; 5U, /* 上升沿和下降沿都触发 */
} gpio_interrupt_mode_t; /* GPIO配置结构体 */
typedef struct _gpio_pin_config
{gpio_pin_direction_t direction; /* GPIO方向:输入还是输出 */uint8_t outputLogic; /* 如果是输出的话&#xff0c;默认输出电平 */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()函数&#xff0c;支持中断配置.添加gpio_intconfig()函数&#xff0c;初始化中断添加gpio_enableint()函数&#xff0c;使能中断添加gpio_clearintflags()函数&#xff0c;清除中断标志位***************************************************************/
/** &#64;description : GPIO初始化。* &#64;param - base : 要初始化的GPIO组。* &#64;param - pin : 要初始化GPIO在组内的编号。* &#64;param - config : GPIO配置结构体。* &#64;return : 无*/
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{base->IMR &&#61; ~(1U << pin);if(config->direction &#61;&#61; kGPIO_DigitalInput) /* GPIO作为输入 */{base->GDIR &&#61; ~( 1 << pin);}else /* 输出 */{base->GDIR |&#61; 1 << pin;gpio_pinwrite(base,pin, config->outputLogic); /* 设置默认输出电平 */}gpio_intconfig(base, pin, config->interruptMode); /* 中断功能配置 */
}/** &#64;description : 读取指定GPIO的电平值 。* &#64;param - base : 要读取的GPIO组。* &#64;param - pin : 要读取的GPIO脚号。* &#64;return : 无*/int gpio_pinread(GPIO_Type *base, int pin){return (((base->DR) >> pin) & 0x1);}/** &#64;description : 指定GPIO输出高或者低电平 。* &#64;param - base : 要输出的的GPIO组。* &#64;param - pin : 要输出的GPIO脚号。* &#64;param - value : 要输出的电平&#xff0c;1 输出高电平&#xff0c; 0 输出低低电平* &#64;return : 无*/
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{if (value &#61;&#61; 0U){base->DR &&#61; ~(1U << pin); /* 输出低电平 */}else{base->DR |&#61; (1U << pin); /* 输出高电平 */}
}/** &#64;description : 设置GPIO的中断配置功能* &#64;param - base : 要配置的IO所在的GPIO组。* &#64;param - pin : 要配置的GPIO脚号。* &#64;param - pinInterruptMode: 中断模式&#xff0c;参考枚举类型gpio_interrupt_mode_t* &#64;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 &#61; pin;base->EDGE_SEL &&#61; ~(1U << pin);if(pin < 16) /* 低16位 */{icr &#61; &(base->ICR1);}else /* 高16位 */{icr &#61; &(base->ICR2);icrShift -&#61; 16;}switch(pin_int_mode){case(kGPIO_IntLowLevel):*icr &&#61; ~(3U << (2 * icrShift));break;case(kGPIO_IntHighLevel):*icr &#61; (*icr & (~(3U << (2 * icrShift)))) | (1U << (2 * icrShift));break;case(kGPIO_IntRisingEdge):*icr &#61; (*icr & (~(3U << (2 * icrShift)))) | (2U << (2 * icrShift));break;case(kGPIO_IntFallingEdge):*icr |&#61; (3U << (2 * icrShift));break;case(kGPIO_IntRisingOrFallingEdge):base->EDGE_SEL |&#61; (1U << pin);break;default:break;}
}/** &#64;description : 使能GPIO的中断功能* &#64;param - base : 要使能的IO所在的GPIO组。* &#64;param - pin : 要使能的GPIO在组内的编号。* &#64;return : 无*/
void gpio_enableint(GPIO_Type* base, unsigned int pin)
{ base->IMR |&#61; (1 << pin);
}/** &#64;description : 禁止GPIO的中断功能* &#64;param - base : 要禁止的IO所在的GPIO组。* &#64;param - pin : 要禁止的GPIO在组内的编号。* &#64;return : 无*/
void gpio_disableint(GPIO_Type* base, unsigned int pin)
{ base->IMR &&#61; ~(1 << pin);
}/** &#64;description : 清除中断标志位(写1清除)* &#64;param - base : 要清除的IO所在的GPIO组。* &#64;param - pin : 要清除的GPIO掩码。* &#64;return : 无*/
void gpio_clearintflags(GPIO_Type* base, unsigned int pin)
{base->ISR |&#61; (1 << pin);
}

3.5 按键中断编写

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

20211116150340

bsp_exit.h 代码&#xff1a;

#ifndef __BSP_EXIT_H
#define __BSP_EXIT_H
#include "imx6ul.h"
// 外部中断初始化
void exit_init(void);
// 外部中断回调函数
void gpio1_io18_irqhandler(void);
#endif

bsp_exit.c 代码如下&#xff1a;

#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&#61;kGPIO_DigitalInput;key_config.interruptMode&#61;kGPIO_IntFallingEdge;key_config.outputLogic&#61;1;gpio_init(GPIO1,18,&key_config);//使能GIC中断&#xff0c;注册按键触发中断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 &#61; 0;//延时消抖&#xff08;中断中严禁使用死延时&#xff0c;这里是为了IO稳定&#xff09;delay(10);if(gpio_pinread(GPIO1,18) &#61;&#61; 0){state &#61; !state;beep_switch(state);}//清除中断标志gpio_clearintflags(GPIO1,18);
}

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

20211116155803

3.6 编写Makefile脚本

在 Makefile 里面添加上对应文件的文件夹就可以完成编译&#xff0c;添加位置如下&#xff1a;

20211116155909
编译一下&#xff0c;成功通过&#xff1a;

20211116155930

四、实验现象

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


推荐阅读
  • 在进行网络编程时,准确获取本地主机的IP地址是一项基本但重要的任务。Winsock作为20世纪90年代初由Microsoft与多家公司共同制定的Windows平台网络编程接口,为开发者提供了一套高效且易用的工具。通过Winsock,开发者可以轻松实现网络通信功能,并准确获取本地主机的IP地址,从而确保应用程序在网络环境中的稳定运行。此外,了解Winsock的工作原理及其API函数的使用方法,有助于提高开发效率和代码质量。 ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • BZOJ4240 Gym 102082G:贪心算法与树状数组的综合应用
    BZOJ4240 Gym 102082G 题目 "有趣的家庭菜园" 结合了贪心算法和树状数组的应用,旨在解决在有限时间和内存限制下高效处理复杂数据结构的问题。通过巧妙地运用贪心策略和树状数组,该题目能够在 10 秒的时间限制和 256MB 的内存限制内,有效处理大量输入数据,实现高性能的解决方案。提交次数为 756 次,成功解决次数为 349 次,体现了该题目的挑战性和实际应用价值。 ... [详细]
  • 本文将详细介绍在Android应用中添加自定义返回按钮的方法,帮助开发者更好地理解和实现这一功能。通过具体的代码示例和步骤说明,本文旨在为初学者提供清晰的指导,确保他们在开发过程中能够顺利集成返回按钮,提升用户体验。 ... [详细]
  • 结语 | 《探索二进制世界:软件安全与逆向分析》读书笔记:深入理解二进制代码的逆向工程方法
    结语 | 《探索二进制世界:软件安全与逆向分析》读书笔记:深入理解二进制代码的逆向工程方法 ... [详细]
  • 在Ubuntu系统中,由于预装了MySQL,因此无需额外安装。通过命令行登录MySQL时,可使用 `mysql -u root -p` 命令,并按提示输入密码。常见问题包括:1. 错误 1045 (28000):访问被拒绝,这通常是由于用户名或密码错误导致。为确保顺利连接,建议检查MySQL服务是否已启动,并确认用户名和密码的正确性。此外,还可以通过配置文件调整权限设置,以增强安全性。 ... [详细]
  • C++ 进阶:类的内存布局与虚函数类的实现细节
    C++ 进阶:类的内存布局与虚函数类的实现细节 ... [详细]
  • 本文详细探讨了C语言中`extern`关键字的简易编译方法,并深入解析了预编译、`static`和`extern`的综合应用。通过具体的代码示例,介绍了如何在不同的文件之间共享变量和函数声明,以及这些关键字在编译过程中的作用和影响。文章还讨论了预编译过程中宏定义的使用,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • Go语言实现Redis客户端与服务器的交互机制深入解析
    在前文对Godis v1.0版本的基础功能进行了详细介绍后,本文将重点探讨如何实现客户端与服务器之间的交互机制。通过具体代码实现,使客户端与服务器能够顺利通信,赋予项目实际运行的能力。本文将详细解析Go语言在实现这一过程中的关键技术和实现细节,帮助读者深入了解Redis客户端与服务器的交互原理。 ... [详细]
  • CodeForces 722C 数组破坏算法解析与优化策略 ... [详细]
  • JVM参数设置与命令行工具详解
    JVM参数配置与命令行工具的深入解析旨在优化系统性能,通过合理设置JVM参数,确保在高吞吐量的前提下,有效减少垃圾回收(GC)的频率,进而降低系统停顿时间,提升服务的稳定性和响应速度。此外,本文还将详细介绍常用的JVM命令行工具,帮助开发者更好地监控和调优JVM运行状态。 ... [详细]
  • 深入解析 C 语言与 C++ 之间的差异及关联
    深入解析 C 语言与 C++ 之间的差异及关联 ... [详细]
  • 技术日志:深入探讨Spark Streaming与Spark SQL的融合应用
    技术日志:深入探讨Spark Streaming与Spark SQL的融合应用 ... [详细]
  • Go语言中的高效排序与搜索算法解析
    在探讨Go语言中高效的排序与搜索算法时,本文深入分析了Go语言提供的内置排序功能及其优化策略。通过实例代码,详细讲解了如何利用Go语言的标准库实现快速、高效的排序和搜索操作,为开发者提供了实用的编程指导。 ... [详细]
  • 深入解析 iOS Objective-C 中的对象内存对齐规则及其优化策略
    深入解析 iOS Objective-C 中的对象内存对齐规则及其优化策略 ... [详细]
author-avatar
lisen0001
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有