文章目录
- 按键中断控制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之嵌入式开发