本篇主要讲一下 AXI GPIO 中断,AXI GPIO 中断也是共享外设中断的一种。顾名思义,AXI GPIO 中断需要 AXI GPIO 核。
使用 PL 逻辑产生一组方波信号来模拟中断信号,方波的周期也是 2 秒。如下图所示。

产生的中断信号涌进 AXI_GPIO0,然后输入到 ZYNQ 中。同时将 AXI_GPIO0 的中断信号连接到 ZYNQ 的中断输入端口。ZYNQ 对中断做出响应,在中断处理函数里面读取 AXI_GPIO0 的值,再通过 AXI_GPIO1 写到 LED 灯上,通过 LED 灯来观察中断响应。同时也可以通过串口来打印响应的信息来观察中断响应的情况。
一、创建 Block Design 模块
整体结构如下:

说明:
- 通过 PL 逻辑产生中断方波信号,输入到 sws_8bits
- 从 FCLK_CLK0 引出 CLK_100M 信号
- 从 FCLK_RESET0_N 引出 FCLK_RESET0_N 信号
1、配置 ZYNQ
勾选 PL 到 PS 的中断

2、引出两路时钟

3、添加 AXI_GPIO0,按如下配置(注意使能中断)

配置 SW GPIO 的位宽

4、添加 AXI_GPIO1,按如下配置

配置 LED GPIO 的位宽

5、点击自动连接,(如果有需要可以选中 Block design 中的信号线,然后右键 debug 添加 ila)
6、Generate Output Product
7、创建顶层
8、生成 Bit 文件
9、导入到 SDK
二、在 SDK 中进行编程
1、创建一个 HelloWorld 工程
2、按照上一讲说的中断设置流程编写程序
3、程序分析
主函数:
#include <stdio.h> #include "xparameters.h" #include "xgpio.h" #include "sleep.h" #include "xscugic.h" #include "xil_exception.h" //使用全局变量定义 AXI_GPIO0(SW)、AXI_GPIO1(LED)结构体 XGpio LED; XGpio SW; static u32 num=0; //中断函数声明为 static,可以加快速度 static void handler_btn(void* Callback) { //在 XScuGic_Connect 设置 XGpio 对象指针作为参数送入 XGpio* g = (XGpio*)Callback; XGpio_InterruptClear(g, 0xFF);//进入中处理函数先清除中断标志,否则可能有意外的结果。我就是因为把清除中断标志放在最后导致上升沿触发时两次进入中断 XGpio_InterruptDisable(&SW, 0xFF);//失能全部 8 个按钮的中断 //读出按钮状态 u8 r; r = XGpio_DiscreteRead(g, 1); printf(" sw is %d\n\r", r);//打印 AXOI_GPIO0 的当前值 XGpio_DiscreteWrite(&LED,1,r);//将 AXOI_GPIO1 的值写到 AXI_GPIO1 printf("num is %lu\n\r",num);//每进入一次中断就加一,通过串口观察 num++; XGpio_InterruptEnable(&SW, 0xFF);//使能全部 8 个按钮的中断 } int main(void) { int sta = -1; //初始化 GPIO0,按键初始化 //ID 在 xparameters.h 中定义,查找/* Definitions for driver GPIO */ sta = XGpio_Initialize(&SW, XPAR_AXI_GPIO_0_DEVICE_ID); if (sta != XST_SUCCESS) { return XST_FAILURE; } //设置 GPIO0,1 通道每一位的传输方向为输入 //在 vivado 中已设置为 8 位 SW 输入 XGpio_SetDataDirection(&SW, 1, 0xFF); //初始化 GPIO1,LED //ID 在 xparameters.h 中定义,查找/* Definitions for driver GPIO */ sta = XGpio_Initialize(&LED, XPAR_AXI_GPIO_1_DEVICE_ID); if (sta != XST_SUCCESS) { return XST_FAILURE; } //设置 GPIO1,1 通道每一位的传输方向为输出 //在 vivado 中已设置为 8 位按钮输出 XGpio_SetDataDirection(&LED, 1, 0x00); /***********************************中断相关函数***********************************/ /***********************************中断相关函数***********************************/ //设置 GPIO0 的中断 XScuGic intc; XScuGic_Config* intc_conf; //查找中断设置 intc_conf = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID); if (intc_conf == NULL) { printf("config fail"); return XST_FAILURE; } //中断初始化 sta = XScuGic_CfgInitialize(&intc, intc_conf, intc_conf->CpuBaseAddress); if (sta != XST_SUCCESS) { printf("config initialize fail"); return XST_FAILURE; } //由 PS 模块的 PL 中断输入口接入的中断必须设置优先级与中断响应模式 //0x3 表示设置为上升沿触发 XScuGic_SetPriorityTriggerType(&intc, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR, 0xA0, 0x3); //连接中断控制函数 //中断序号在 xparameters.h 中查找/* Definitions for Fabric interrupts connected to psu_acpu_gic */ sta = XScuGic_Connect(&intc, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR, (Xil_ExceptionHandler)handler_btn, &SW); if (sta != XST_SUCCESS) { printf("connect fail"); return XST_FAILURE; } //中断控制器中使能 GPIO 中断 XScuGic_Enable(&intc, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR); //使能硬件中断 Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &intc); Xil_ExceptionEnable(); //axi gpio 使能中断 //必须使用下方 2 个语句 XGpio_InterruptEnable(&SW, 0xFF);//使能全部 8 个按钮的中断 XGpio_InterruptGlobalEnable(&SW); //printf("Numb %d\n\r", num); while (1) { } //禁用 GPIO 中断 XGpio_InterruptDisable(&SW, 0xFF); //关闭中断响应 XScuGic_Disable(&intc, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR); XScuGic_Disconnect(&intc, XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR); return 0; }
三、现象分析
在说中断现象之前,我们来分析一下 PL 逻辑产生的中断信号。
PL 产生中断信号——>AXI_GPIO0 产生中断——>输入到 ZYNQ 中
根据查询 AXI_GPIO 手册得知,当输入发生变化时就会产生一个高电平的中断信号,不论是由 0->1 或者 1->0 都会产生一个电平信号,经过我使用 ila 分析,AXI_GPIO0 产生的中断信号持续的时间是 100 个周期左右。使用波形来说明的话就是如下图:

也就在 PL 逻辑的上升沿核下降沿都会产生一个高电平信号,然后送入 ZYNQ 作为中断信号,也就是在 PL 的上升沿和下降沿都会产生中断。
但是由于产生的中断信号只有 100 个时钟周期,所以使用电平触发时,也只能产生一次中断,看起来就和上升沿触发的一样,都是一次。在做实验的时候,我在这里就卡了挺久,想着电平触发为什么只响应一次中断,后来慢慢才摸索到是因为高电平时间太少了的原因。
扫码关注尚为网微信公众号

原创文章,作者:sunev,如若转载,请注明出处:https://www.sunev.cn/embedded/1149.html