ZYNQ之嵌入式开发03——按键中断实验

文章目录

  • 按键中断控制LED的状态
  • AXI GPIO实现按键中断
  • 使用多个AXI GPIO实现按键中断

GPIO的简图如下图所示。
在这里插入图片描述
GPIO对应的中断ID是52。
在这里插入图片描述

按键中断控制LED的状态

前面实验中已经做了按键控制LED状态的实验,但是LED的状态分为按键按下时和按键松开时的两种状态,并不是按键按一次,LED的状态就翻转一次,要实现这样的功能,需要引入中断。
在这里导入中断的示例代码。
在这里插入图片描述
参考导入的代码示例,按键中断控制LED状态的代码如下。

#include "stdio.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#include "xscugic.h"
#include "xil_exception.h"

#define GPIO_INTERRUPT_ID	XPAR_XGPIOPS_0_INTR  //GPIO的中断号 52U
#define GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID  //GPIO1设备ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
static XGpioPs Gpio;  //结构体类型,GPIO操作实例
static XScuGic Intc;  //结构体类型
static XGpioPs_Config *ConfigPtr;  //结构体类型,成员有设备ID和寄存器地址
static XScuGic_Config *IntcConfig;  //中断控制器的实例
static u32 PS_LED1 = 0;  //PS端LED1连接的输出引脚是0号
static u32 PS_LED2 = 13;  //PS端LED2连接的输出引脚是13号
static u32 PS_KEY1 = 50;  //PS端KEY1连接的输入引脚是50号
static u32 PS_KEY2 = 51;  //PS端KEY2连接的输入引脚是51号
static u32 key_press1 = 0;
static u32 key_press2 = 0;
static u32 key_value1 = 1;
static u32 key_value2 = 1;

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId, u32 Out_pin);
static void IntrHandler()
{
	if(!XGpioPs_ReadPin(&Gpio, PS_KEY1))
	{
		printf("PS_KEY1 Interrupt detected!\n");
		key_press1 = 1;
		XGpioPs_IntrDisablePin(&Gpio,PS_KEY1);
	}
	if(!XGpioPs_ReadPin(&Gpio, PS_KEY2))
	{
		printf("PS_KEY2 Interrupt detected!\n");
		key_press2 = 1;
		XGpioPs_IntrDisablePin(&Gpio,PS_KEY2);
	}
}

int main()
{
	printf("gpio interrupt test!\n");
	//查找器件的配置信息
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	//初始化GPIO的驱动
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
	//设置LED的GPIO方向为输出
	XGpioPs_SetDirectionPin(&Gpio, PS_LED1, 1);  //0-input,1-output
	XGpioPs_SetDirectionPin(&Gpio, PS_LED2, 1);
	//设置KEY的GPIO方向为输入
	XGpioPs_SetDirectionPin(&Gpio, PS_KEY1, 0);  //0-input,1-output
	XGpioPs_SetDirectionPin(&Gpio, PS_KEY2, 0);
	//设置输出使能
	XGpioPs_SetOutputEnablePin(&Gpio, PS_LED1, 1);  //0-disable,1-enable
	XGpioPs_SetOutputEnablePin(&Gpio, PS_LED2, 1);

	//查找配置信息,初始化中断控制器驱动
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	XScuGic_CfgInitialize(&Intc, IntcConfig, IntcConfig->CpuBaseAddress);
	//初始化ARM处理器异常句柄
	Xil_ExceptionInit();
	//给IRQ异常注册处理程序
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&Intc);
	//使能处理器中断
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
	//配置中断
	SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID, PS_KEY1);
	SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID, PS_KEY2);
	while(1)
	{
		if(key_press1)
		{
			key_value1 = ~key_value1;
			key_press1 = 0;
			XGpioPs_IntrClearPin(&Gpio,PS_KEY1); //清除中断状态,以便读取下次中断
			XGpioPs_WritePin(&Gpio, PS_LED1, key_value1);  //改变PS LED1的状态
			usleep(200000);   //延时消抖
			XGpioPs_IntrEnablePin(&Gpio, PS_KEY1);  //再使能中断
		}
		if(key_press2)
		{
			key_value2 = ~key_value2;
			key_press2 = 0;
			XGpioPs_IntrClearPin(&Gpio,PS_KEY2); //清除中断状态,以便读取下次中断
			XGpioPs_WritePin(&Gpio, PS_LED2, key_value2);  //改变PS LED2的状态
			usleep(200000);   //延时消抖
			XGpioPs_IntrEnablePin(&Gpio, PS_KEY2);  //再使能中断
		}
	}
	return 0;
}

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId, u32 Out_pin)
{
	//关联中断处理函数
	XScuGic_Connect(GicInstancePtr, GpioIntrId,(Xil_ExceptionHandler)IntrHandler,(void *)Gpio);
	//设置引脚中断触发类型为下降沿
	XGpioPs_SetIntrTypePin(Gpio, Out_pin, XGPIOPS_IRQ_TYPE_EDGE_FALLING);  //设置一个GPIO  0-上升沿  1-下降沿
	//打开引脚中断使能信号
	XGpioPs_IntrEnablePin(Gpio, Out_pin);
	//为GPIO器件使能中断
	XScuGic_Enable(GicInstancePtr, GpioIntrId);
}

开发板上的验证结果如下动图所示,按键每按下一次,LED的状态就翻转一次。
请添加图片描述
在终端中,按键每按下一次,就打印一句话。
请添加图片描述


AXI GPIO实现按键中断

AXI全称Advanced eXtensible lnterface,是Xilinx从6系列的FPGA开始引入的一个接口协议,主要描述了主设备和从设备之间的数据传输方式,在ZYNQ中继续使用,版本是AXI4,ZYNQ内部设备都有AXI接口。AXI是一种高性能、高带宽、低延迟的片内总线,是ARM公司提出的AMBA(Advanced Microcontroller Bus Architecture)的一个部分,也用来替代以前的AHB和APB总线。
AXI协议主要描述了主设备和从设备之间的数据传输方式,主设备和从设备之间通过握手信号建立连接。当从设备准备好接收数据时,会发出READY信号,当主设备的数据准备好时,会发出和维持VALID信号,表示数据有效。数据只有在VALID和READY信号都有效的时候才开始传输。当这两个信号持续保持有效,主设备会继续传输下一个数据。主设备可以撤销VALID信号,或者从设备撤销READY信号终止传输。
在ZYNQ中,支持以下三种总线。

接口协议特性应用场合
AXI4地址、突发数据传输地址的批量传输
AXI4-Lite地址、单数据传输低速外设或控制
AXI4-Stream仅数据传输,突发传输数据流和媒体流传输

AXI4-Lite具有轻量级,结构简单的特点,适合小批量数据、简单控制场合,不支持批量传输,读写时一次只能读写一个字,主要用于访问一些低速外设和外设的控制。
AXI4接口和AXI4-Lite差不多,增加了批量传输,可以连续对一片地址进行一次性读写,就是说具有数据读写的突发功能。上面两种均采用内存映射控制方式,即ARM将用户自定义IP编入某一地址进行访问,读写时就像在读写自己的片内RAM,编程也很方便,开发难度较低。代价就是资源占用过多,需要额外的读地址线、写地址线、读数据线、写数据线、写应答线这些信号线。
AXI4-Stream是一种连续流接口,不需要地址线,很像FIFO,一直读或一直写就行。对于这类IP,ARM不能通过上面的内存映射方式控制(FIFO根本没有地址的概念),必须有一个转换装置,例如AXI-DMA模块来实现内存映射到流式接口的转换。AXI-Stream适用的场合有很多,适合做实时信号处理,如视频流处理、通信协议转换、数字信号处理、无线通信等,其本质都是针对数值流构建的数据通路,从信源到信宿构建起连续的数据流。
前面使用的GPIO是硬核GPIO,在电路中是实际存在的,而AXI GPIO是一种软核GPIO,不是实际存在的,在使用的时候进行搭建。本实验将通过调用AXI GPIO的IP核,使用中断机制,实现底板上PL端按键控制核心板上PS端LED的功能,简单的系统框图如下。
在这里插入图片描述
新建Vivado工程,点击Create Block Design,然后添加AXI GPIO核,如下图所示。
在这里插入图片描述
找相应IP核的手册,需要双击打开IP核后点击Documentation—>Product Guide,下载即可获取相应的文档。
在这里插入图片描述
然后就打开了Xilinx Documentation Navigator,在里面可以搜索并下载自己需要的文档。
在这里插入图片描述
AXI_GPIO文档中关于AXI GPIO的图示如下。
在这里插入图片描述
添加ZYNQ IP核,勾选UART外设,配置DDR。
在这里插入图片描述
然后进行MIO的配置,如下图所示,完成后点击OK。
在这里插入图片描述
点击自动运行。
在这里插入图片描述
点击自动将两个IP核进行连接。
在这里插入图片描述
自动连接完成后可以点击重新布局,效果如下图所示。
在这里插入图片描述
AXI Interconnect IP核的功能是将ZYNQ和AXI GPIO进行互联,Processor System Reset IP核为整个处理器系统提供复位信号。
双击ZYNQ IP核,将其中断接口打开,本实验中使用的是第一个中断,即61。
在这里插入图片描述
再手动将两个IP核的中断接口连接起来,如下图所示。
在这里插入图片描述
点击生成输出产品,然后再生成.v文件。
在这里插入图片描述
将PL KEY1对应的引脚进行分配,如下图所示,然后生成比特流文件。
在这里插入图片描述
导出硬件这里要包括比特流文件。
在这里插入图片描述
打开SDK,新建工程,然后导入axi_gpio的中断示例代码。
在这里插入图片描述
使用AXI_GPIO让PL端按键控制PS端LED的代码如下。

#include "stdio.h"
#include "xparameters.h"
#include "xgpio.h"    //axi_gpio对应的头文件
#include "xgpiops.h"   //PS端gpio对应的头文件
#include "sleep.h"
#include "xscugic.h"
#include "xil_exception.h"

#define GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID   //GPIO设备ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID  //中断控制器的设备ID
#define AXI_GPIO_DEVICE_ID		XPAR_GPIO_0_DEVICE_ID  //AXI GPIO设备ID
#define AXI_GPIO_INTERRUPT_ID	XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR    //PL-PS GPIO的中断号 61
#define GPIO_CHANNEL1  1  //AXI GPIO通道1
static XGpio AXI_Gpio;
static XGpioPs Gpio;  //结构体类型,GPIO操作实例
static XScuGic Intc;  //结构体类型
static XGpioPs_Config *ConfigPtr;  //结构体类型,成员有设备ID和寄存器地址
static XScuGic_Config *IntcConfig;  //中断控制器的实例
static u32 PS_LED1 = 0;  //PS端LED1连接的输出引脚是0号
static u32 PS_LED2 = 13;  //PS端LED2连接的输出引脚是13号
static u32 key_press = 0;
static u32 key_value = 1;

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId);
static void IntrHandler()
{
	if(!XGpio_DiscreteRead(&AXI_Gpio, GPIO_CHANNEL1))  //按键按下改变状态,抬起不改变
	{
		printf("PL_KEY1 Interrupt detected!\n");
		key_press = 1;
		XGpio_InterruptDisable(&AXI_Gpio,1);
	}
}

int main()
{
	printf("axi_gpio interrupt test!\n");
	//查找PS端GPIO的配置并初始化
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
	//对AXI GPIO进行初始化
	XGpio_Initialize(&AXI_Gpio,AXI_GPIO_DEVICE_ID);
	//设置PS LED的GPIO方向为输出,并设置输出使能
	XGpioPs_SetDirectionPin(&Gpio, PS_LED1, 1);  //0-input,1-output
	XGpioPs_SetDirectionPin(&Gpio, PS_LED2, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, PS_LED1, 1);  //0-disable,1-enable
	XGpioPs_SetOutputEnablePin(&Gpio, PS_LED2, 1);
	//配置AXI GPIO
	XGpio_SetDataDirection(&AXI_Gpio, GPIO_CHANNEL1, 1);  //将最低位设置为输入   1-输入   0-输出
	//配置中断
	SetupInterruptSystem(&Intc, &AXI_Gpio, AXI_GPIO_INTERRUPT_ID);

	while(1)
	{
		if(key_press)
		{
			key_value = ~key_value;
			key_press = 0;
			XGpio_InterruptClear(&AXI_Gpio, 1);  //清除中断状态,以便读取下次中断
			XGpioPs_WritePin(&Gpio, PS_LED1, key_value);  //改变PS LED1的状态
			//XGpioPs_WritePin(&Gpio, PS_LED2, key_value);  //改变PS LED2的状态
			usleep(200000);   //延时按键消抖
			XGpio_InterruptEnable(&AXI_Gpio, 1);  //再使能中断
		}
	}
	return 0;
}

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId)
{
	//查找配置信息,初始化中断控制器驱动
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
	//初始化ARM处理器异常句柄
	Xil_ExceptionInit();
	//给IRQ异常注册处理程序
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,GicInstancePtr);
	//使能处理器中断
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
	//0xA0:中断源的优先级;0x1:中断类型为高有效电平敏感类型
	XScuGic_SetPriorityTriggerType(GicInstancePtr, AXI_GpioIntrId, 0xA0, 0x1);
	//关联中断处理函数
	XScuGic_Connect(GicInstancePtr, AXI_GpioIntrId,(Xil_ExceptionHandler)IntrHandler,(void *)AXI_Gpio);
	//为GPIO器件使能中断
	XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);
	//打开全局中断
	XGpio_InterruptGlobalEnable(AXI_Gpio);
	//打开通道中信号对应的中断使能
	XGpio_InterruptEnable(AXI_Gpio, 1);
}

可以在Run As—>Run Configurations进行下面的配置,勾选编程FPGA,当然也可以在菜单栏下点击Xilinx—>Program FPGA编程FPGA再运行。
在这里插入图片描述
在开发板上执行的结果是,按下PL KEY1一次,PS LED1的状态就翻转一次。


使用多个AXI GPIO实现按键中断

本实验中采用PL端的两个按键分别控制PS端的两个LED,具体为PL KEY1控制PS LED1的亮灭,PL KEY2控制PS LED2的亮灭。
框图如下图所示。
在这里插入图片描述
需要两个AXI_GPIO的IP核,另外还需要添加一个Concat,用来连接各AXI_GPIO和ZYNQ的IP核。配置完成之后要分配管脚,然后生成比特流文件,然后导出SDK文件,再启动SDK进行编码。
本实验的代码如下。

#include "stdio.h"
#include "xparameters.h"
#include "xgpio.h"    //axi_gpio对应的头文件
#include "xgpiops.h"   //PS端gpio对应的头文件
#include "sleep.h"
#include "xscugic.h"
#include "xil_exception.h"

#define GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID   //GPIO设备ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID  //中断控制器的设备ID
#define AXI_GPIO1_DEVICE_ID		XPAR_GPIO_0_DEVICE_ID  //AXI GPIO1设备ID
#define AXI_GPIO2_DEVICE_ID		XPAR_GPIO_1_DEVICE_ID  //AXI GPIO2设备ID
#define AXI_GPIO1_INTERRUPT_ID	62U    //PL-PS GPIO1的中断号 61
#define AXI_GPIO2_INTERRUPT_ID	61U    //PL-PS GPIO2的中断号 62
#define GPIO_CHANNEL1  1  //AXI GPIO通道1
static XGpio AXI_Gpio1;
static XGpio AXI_Gpio2;
static XGpioPs Gpio;  //结构体类型,GPIO操作实例
static XScuGic Intc;  //结构体类型
static XGpioPs_Config *ConfigPtr;  //结构体类型,成员有设备ID和寄存器地址
static XScuGic_Config *IntcConfig;  //中断控制器的实例
static u32 PS_LED1 = 0;  //PS端LED1连接的输出引脚是0号
static u32 PS_LED2 = 13;  //PS端LED2连接的输出引脚是13号
static u32 key_press1 = 0;
static u32 key_value1 = 1;
static u32 key_press2 = 0;
static u32 key_value2 = 1;

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId);
static void IntrHandler()
{
	if(!XGpio_DiscreteRead(&AXI_Gpio1, GPIO_CHANNEL1))  //按键按下改变状态,抬起不改变
	{
		XGpio_InterruptDisable(&AXI_Gpio2,1);    //先关掉另一个中断
		printf("PL_KEY1 Interrupt detected!\n");
		key_press1 = 1;
		XGpio_InterruptDisable(&AXI_Gpio1,1);
	}
	if(!XGpio_DiscreteRead(&AXI_Gpio2, GPIO_CHANNEL1))  //按键按下改变状态,抬起不改变
	{
		XGpio_InterruptDisable(&AXI_Gpio1,1);   //先关掉另一个中断
		printf("PL_KEY2 Interrupt detected!\n");
		key_press2 = 1;
		XGpio_InterruptDisable(&AXI_Gpio2,1);
	}
}

int main()
{
	printf("axi_gpio interrupt test!\n");
	//查找PS端GPIO的配置并初始化
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
	//对AXI GPIO进行初始化
	XGpio_Initialize(&AXI_Gpio1,AXI_GPIO1_DEVICE_ID);
	XGpio_Initialize(&AXI_Gpio2,AXI_GPIO2_DEVICE_ID);
	//设置PS LED的GPIO方向为输出,并设置输出使能
	XGpioPs_SetDirectionPin(&Gpio, PS_LED1, 1);  //0-input,1-output
	XGpioPs_SetDirectionPin(&Gpio, PS_LED2, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, PS_LED1, 1);  //0-disable,1-enable
	XGpioPs_SetOutputEnablePin(&Gpio, PS_LED2, 1);
	//配置AXI GPIO
	XGpio_SetDataDirection(&AXI_Gpio1, GPIO_CHANNEL1, 1);  //将最低位设置为输入   1-输入   0-输出
	XGpio_SetDataDirection(&AXI_Gpio2, GPIO_CHANNEL1, 1);

	//查找配置信息,初始化中断控制器驱动
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	XScuGic_CfgInitialize(&Intc, IntcConfig, IntcConfig->CpuBaseAddress);
	//初始化ARM处理器异常句柄
	Xil_ExceptionInit();
	//给IRQ异常注册处理程序
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&Intc);
	//使能处理器中断
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

	//配置中断
	SetupInterruptSystem(&Intc, &AXI_Gpio1, AXI_GPIO1_INTERRUPT_ID);
	SetupInterruptSystem(&Intc, &AXI_Gpio2, AXI_GPIO2_INTERRUPT_ID);

	while(1)
	{
		if(key_press1)
		{
			key_value1 = ~key_value1;
			key_press1 = 0;
			XGpio_InterruptClear(&AXI_Gpio1, 1);  //清除中断状态,以便读取下次中断
			XGpio_InterruptClear(&AXI_Gpio2, 1);
			XGpioPs_WritePin(&Gpio, PS_LED1, key_value1);  //改变PS LED1的状态
			usleep(200000);   //延时按键消抖
			XGpio_InterruptEnable(&AXI_Gpio1, 1);  //再使能中断
		}
		if(key_press2)
		{
			key_value2 = ~key_value2;
			key_press2 = 0;
			XGpio_InterruptClear(&AXI_Gpio1, 1);
			XGpio_InterruptClear(&AXI_Gpio2, 1);  //清除中断状态,以便读取下次中断
			XGpioPs_WritePin(&Gpio, PS_LED2, key_value2);  //改变PS LED2的状态
			usleep(200000);   //延时按键消抖
			XGpio_InterruptEnable(&AXI_Gpio2, 1);  //再使能中断
		}
	}
	return 0;
}

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId)
{
	//0xA0:中断源的优先级;0x1:中断类型为高有效电平敏感类型
	XScuGic_SetPriorityTriggerType(GicInstancePtr, AXI_GpioIntrId, 0xA0, 0x1);
	//关联中断处理函数
	XScuGic_Connect(GicInstancePtr, AXI_GpioIntrId,(Xil_ExceptionHandler)IntrHandler,(void *)AXI_Gpio);
	//为GPIO器件使能中断
	XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);
	//打开全局中断
	XGpio_InterruptGlobalEnable(AXI_Gpio);
	//打开通道中信号对应的中断使能
	XGpio_InterruptEnable(AXI_Gpio, 1);
}

实验结果如下动图所示。
请添加图片描述
再加入两个AXI_GPIO核,使用PL端的两个按键分别控制PL端的两个LED,对应的框图如下图所示。
在这里插入图片描述
该实验的代码如下。

#include "stdio.h"
#include "xparameters.h"
#include "xgpio.h"    //axi_gpio对应的头文件
#include "sleep.h"
#include "xscugic.h"
#include "xil_exception.h"

#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID  //中断控制器的设备ID
#define AXI_GPIO1_DEVICE_ID		XPAR_GPIO_0_DEVICE_ID  //AXI GPIO1设备ID  PL KEY1
#define AXI_GPIO2_DEVICE_ID		XPAR_GPIO_1_DEVICE_ID  //AXI GPIO2设备ID  PL KEY2
#define AXI_GPIO3_DEVICE_ID		XPAR_GPIO_2_DEVICE_ID  //AXI GPIO3设备ID  PL LED1
#define AXI_GPIO4_DEVICE_ID		XPAR_GPIO_3_DEVICE_ID  //AXI GPIO4设备ID  PL LED2
#define AXI_GPIO1_INTERRUPT_ID	62U    //PL-PS GPIO1的中断号 61
#define AXI_GPIO2_INTERRUPT_ID	61U    //PL-PS GPIO2的中断号 62
#define GPIO_CHANNEL1  1  //AXI GPIO通道1
static XGpio AXI_Gpio1;
static XGpio AXI_Gpio2;
static XGpio AXI_Gpio3;
static XGpio AXI_Gpio4;
static XScuGic Intc;  //结构体类型
static XScuGic_Config *IntcConfig;  //中断控制器的实例
static u32 key_press1 = 0;
static u32 key_value1 = 1;
static u32 key_press2 = 0;
static u32 key_value2 = 1;

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId);
static void IntrHandler()
{
	if(!XGpio_DiscreteRead(&AXI_Gpio1, GPIO_CHANNEL1))  //按键按下改变状态,抬起不改变
	{
		XGpio_InterruptDisable(&AXI_Gpio2,1);
		printf("PL_KEY1 Interrupt detected!\n");
		key_press1 = 1;
		XGpio_InterruptDisable(&AXI_Gpio1,1);
	}
	if(!XGpio_DiscreteRead(&AXI_Gpio2, GPIO_CHANNEL1))  //按键按下改变状态,抬起不改变
	{
		XGpio_InterruptDisable(&AXI_Gpio1,1);
		printf("PL_KEY2 Interrupt detected!\n");
		key_press2 = 1;
		XGpio_InterruptDisable(&AXI_Gpio2,1);
	}
}

int main()
{
	printf("axi_gpio4 interrupt test!\n");
	//对AXI GPIO进行初始化
	XGpio_Initialize(&AXI_Gpio1,AXI_GPIO1_DEVICE_ID);
	XGpio_Initialize(&AXI_Gpio2,AXI_GPIO2_DEVICE_ID);
	XGpio_Initialize(&AXI_Gpio3,AXI_GPIO3_DEVICE_ID);
	XGpio_Initialize(&AXI_Gpio4,AXI_GPIO4_DEVICE_ID);
	//配置AXI GPIO
	XGpio_SetDataDirection(&AXI_Gpio1, GPIO_CHANNEL1, 1);  //将最低位设置为输入   1-输入   0-输出
	XGpio_SetDataDirection(&AXI_Gpio2, GPIO_CHANNEL1, 1);
	XGpio_SetDataDirection(&AXI_Gpio3, GPIO_CHANNEL1, 0);  //将最低位设置为输出   1-输入   0-输出
	XGpio_SetDataDirection(&AXI_Gpio4, GPIO_CHANNEL1, 0);

	//查找配置信息,初始化中断控制器驱动
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	XScuGic_CfgInitialize(&Intc, IntcConfig, IntcConfig->CpuBaseAddress);
	//初始化ARM处理器异常句柄
	Xil_ExceptionInit();
	//给IRQ异常注册处理程序
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&Intc);
	//使能处理器中断
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

	//写入初值,熄灭LED
	XGpio_DiscreteWrite(&AXI_Gpio3, GPIO_CHANNEL1, key_value1);
	XGpio_DiscreteWrite(&AXI_Gpio4, GPIO_CHANNEL1, key_value2);

	//配置中断
	SetupInterruptSystem(&Intc, &AXI_Gpio1, AXI_GPIO1_INTERRUPT_ID);
	SetupInterruptSystem(&Intc, &AXI_Gpio2, AXI_GPIO2_INTERRUPT_ID);

	while(1)
	{
		if(key_press1)
		{
			key_value1 = ~key_value1;
			key_press1 = 0;
			XGpio_InterruptClear(&AXI_Gpio1, 1);  //清除中断状态,以便读取下次中断
			XGpio_DiscreteWrite(&AXI_Gpio3, GPIO_CHANNEL1, key_value1);  //改变PS LED1的状态
			usleep(200000);   //延时按键消抖
			XGpio_InterruptEnable(&AXI_Gpio1, 1);  //再使能中断
		}
		if(key_press2)
		{
			key_value2 = ~key_value2;
			key_press2 = 0;
			XGpio_InterruptClear(&AXI_Gpio2, 1);  //清除中断状态,以便读取下次中断
			XGpio_DiscreteWrite(&AXI_Gpio4, GPIO_CHANNEL1, key_value2);  //改变PS LED2的状态
			usleep(200000);   //延时按键消抖
			XGpio_InterruptEnable(&AXI_Gpio2, 1);  //再使能中断
		}
	}
	return 0;
}

static void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId)
{
	//0xA0:中断源的优先级;0x1:中断类型为高有效电平敏感类型
	XScuGic_SetPriorityTriggerType(GicInstancePtr, AXI_GpioIntrId, 0xA0, 0x1);
	//关联中断处理函数
	XScuGic_Connect(GicInstancePtr, AXI_GpioIntrId,(Xil_ExceptionHandler)IntrHandler,(void *)AXI_Gpio);
	//为GPIO器件使能中断
	XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);
	//打开全局中断
	XGpio_InterruptGlobalEnable(AXI_Gpio);
	//打开通道中信号对应的中断使能
	XGpio_InterruptEnable(AXI_Gpio, 1);
}

上面程序在开发板上测试时,PL KEY1按下一次,PL LED1的状态就翻转一次,同样的,PL KEY2按下一次,PL LED2的状态就翻转一次。


参考视频:
正点原子手把手教你学ZYNQ之嵌入式开发

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

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

相关文章

00_STM32CubeMX如何新建一个工程

STM32CubeMX如何新建一个工程 STM32CubeMX如何新建一个工程以使用PA1口点亮LED为例子 STM32CubeMX如何新建一个工程 以使用PA1口点亮LED为例子 1.创建一个新工程 2.搜索芯片,然后双击 3.点击PA1引脚,设置为输出口 4.文件一定要保存到英文路径&#xff…

4. WPF应用程序中的未捕获异常处理

文章目录 一. 目标二. 技能介绍① UI未捕获异常的处理方式② 全局程序域抛出的未处理异常的捕获③ 异步Task任务中的异常捕获 三. 总结 一. 目标 理解和使用UI未捕获异常DispatcherUnhandledException的使用方法和触发方式.理解和使用程序域未捕获异常AppDomain.CurrentDomain.…

vs2022断点调试怎么看堆栈帧,找异常位置

打一个断点以后,会出现如图报错 我们要怎么找到报错的语句?鼠标点击->堆栈帧->上一行运行的位置->直到找到错误出错如图所示: 跳转到,我们手写的代码,执行出错的位置

【第十五届】蓝桥杯省赛C++b组

今年的蓝桥杯省赛已经结束了,与以往不同,今年又回到了8道题,而22,23年出现了10道题 大家觉得难度怎么样,欢迎进来讨论,博主今年没参加哈,大家聊聊,我听听大家的意见和看法哈 试题A:…

DSP笔记13-时间基准子模块Time base(TB)比较子模块Counter cpmpare(CC)

时间基准子模块Time base(TB) 同步,计数 CTR计数寄存器 PRD周期寄存器 CMP比较寄存器,占空比 EPWMA, EPWMB,两个比较寄存器,但只有以及计数寄存器以及一个周期寄存器 计数模式 计数时钟TBCLK HSPCLKDIVx x0,分…

HBuilderX 中开发vue,引入百度地图获取当前ip地址定位

实现功能&#xff1a;使用百度地图获取IP地址&#xff0c;定位到当前位置 参考文档地址&#xff1a;MapVGL | 快速入门 一、在有外网的情况下&#xff0c;常规引入百度地图的方法如下&#xff1a; 1、在index.html中引入 <script src"//api.map.baidu.com/api?v1.…

element问题总结之el-table使用fixed固定列后滚动条滑动到底部或者最右侧的时候错位问题

el-table使用fixed固定列后滚动条滑动到底部或者最右侧的时候错位 效果图前言解决方案纵向滑动滚动条滑动到底部的错位解决横向滚动条滑动到最右侧的错位解决 效果图 前言 在使用el-table固定行的时候移动滚动条会发现移动到底部或者移动到最右侧的时候会出现表头和内容错位或…

在 Flutter App 中使用 GPS 定位

现代手机上&#xff0c;不论是苹果 iPhone 还是安卓 Android&#xff0c;都配备了强大的定位能力。 定位主要通过卫星和地面基站提供的信号&#xff0c;获得不同精度的定位信息。 通过手机的操作系统&#xff0c;可以获取这些定位信息。这是手机操作系统给应用层提供的能力。…

Udio——革命性的AI音乐生成软件

Udio是一款革命性的AI音乐生成软件&#xff0c;由前谷歌DeepMind的顶尖AI研究人员和工程师共同创立&#xff0c;得到著名风险投资公司a16z的支持。它旨在为音乐爱好者和专业人士提供一个全新的音乐创作和分享平台。用户可以通过文本提示来生成音乐&#xff0c;支持广泛的音乐风…

numpy学习笔记(5),其他实用函数

8. 更多函数 8.1 随机数 8.1.1 常用随机数 8.1.1.1 numpy.random.rand(d0, d1, …, dn) 返回[0.0, 1.0)随机浮点数&#xff0c;即大于等于0.0&#xff0c;小于1.0。d0, d1, …, dn&#xff1a;返回的数组形状 # 使用numpy.random.rand函数 import numpy as np np.random.r…

百元内的运动蓝牙耳机哪个牌子好?五大高分品牌实测推荐

在追求健康生活的当下&#xff0c;运动已成为许多人日常生活的一部分&#xff0c;而音乐更是运动时的最佳伴侣&#xff0c;对于预算有限的学生党或普通消费者来说&#xff0c;如何在百元内挑选到一款性能优越、品质可靠的运动蓝牙耳机&#xff0c;确实是个不小的挑战&#xff0…

(六)C++自制植物大战僵尸游戏关卡数据讲解

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/xjvbb 游戏关卡数据文件定义了游戏中每一个关卡的数据&#xff0c;包括游戏类型、关卡通关奖励的金币数量、僵尸出现的波数、每一波出现僵尸数量、每一波僵尸出现的类型等。根据不同的游戏类型&#xff0c;定义了不同的通…

ATA-214高压放大器用在哪些实验中使用的

高压放大器在科学实验和工程应用中扮演着关键角色。它是一种能够将低电压信号放大到高电压水平的设备。这种放大器通常用于需要处理高电压信号的实验和应用中。以下是高压放大器在各种实验中的应用范围。 粒子物理实验&#xff1a;在粒子物理实验中&#xff0c;科学家使用高压放…

【分享】3种方法取消Word文档的“打开密码”

我们知道&#xff0c;Word文档可以设置“打开密码”&#xff0c;防止文档被随意打开&#xff0c;那后续不需要密码保护了&#xff0c;要怎么取消呢&#xff1f;不小心把密码忘记了还可以取消吗&#xff1f;不清楚的小伙伴一起来看看吧&#xff01; 如果是Word文档不再需要密码…

Python Flask-Security- 构建安全而强大的Web应用

Flask-Security是一个基于Flask的安全扩展&#xff0c;为开发者提供了构建安全且强大的Web应用的工具。本文将深入探讨Flask- Security的核心功能、基本用法以及在实际应用中的一些高级特性&#xff0c;通过丰富的示例代码&#xff0c;助您更全面地了解和应用这一用于Web应用安…

windows的jar包开机自启动【搬代码】

感觉最方便的就是放到启动项目里操作步骤 winR 输入&#xff1a;shell:startup回车或点击确定 3.将自己jar包右键创建快捷方式 4.然后放进去 5.重启电脑&#xff0c;浏览器输入网址&#xff0c;就可以看到重启成功了 另外一个就是放入.exe文件的快捷方式 首先&#xff0c;…

docker安装nessus服务及使用

Nessus 是目前全世界最多人使用的系统漏洞扫描与分析软件&#xff0c;现在软件服务越来越多&#xff0c;越来越复杂&#xff0c;涉及的数据也更多&#xff1b;因此系统完成后对于系统漏洞的检测并对其进行修改十分有必要&#xff0c;本文介绍通过docker安装nessus服务及简单的使…

15 Python进阶: random和pyecharts

Python random 模块主要用于生成随机数。 random 模块实现了各种分布的伪随机数生成器。 要使用 random 函数必须先导入&#xff1a; import randompython random 模块的一般用法 Python中的random模块提供了生成伪随机数的功能&#xff0c;可以用于模拟、游戏开发、密码学…

关于centos8自带的apache2.4开启https后,XP系统的IE8无法显示网页的问题

经检验&#xff0c;是因为系统的apache和openssl版本太高导致的。 禁用系统默认的apache2.4&#xff0c;自己重新源码编译安装一套openssl-1.0.1fapache2.2.23php7.1.2即可。跟update-crypto-policies没有关系&#xff0c;可保持默认的DEFAULT状态。 关于centos8自带的apache2…

多无人机集群协同避障

matlab2020a正常运行 场景1规划结果 场景2规划结果 场景3规划结果 代码地址&#xff1a; 多无人机集群协同避障效果&#xff08;5架&#xff09;资源-CSDN文库