摘要:
本能篇主要讲一下AXI GPIO 中断,AXI GPIO 中断也是共享外设中断的一种。本讲和上一讲说的中断很像,区别就是AXI GPIO 中断需要AXI GPIO核。
本章也是使用PL逻辑产生一组方波信号来做中断信号,方波的周期也是2秒。如下图L:
中断信号
产生的中断信号捅进AXI_GPIO0,然后输入到ZYNQ中。同时将AXI_GPIO0的中断信号连接到ZYNQ的中断输入端口。ZYNQ对中断做出响应,在中断处理函数里面读取AXI_GPIO0的值,再通过AXI_GPIO1写到LED灯上,通过LED灯来观察中断响应。同时也可以通过串口来打印响应的信息来观察中断响应的情况。
一、创建Block Design模块
说明:
a、通过PL逻辑产生中断方波信号,输入到sws_8bits.
b、从FCLK_CLK0引出CLK_100M信号
c、从FCLK_RESET0_N引出FCLK_RESET0_N信号
1、配置ZYNQ
勾选PL到PS的中断
2、引出两路时钟
3、添加AXI_GPIO0,按如下配置(注意使能中断)
4、添加AXI_GPIO1,按如下配置
5、点击自动连接,(如果有需要可以选中Block design中的信号线,然后右键debug添加ila)
6、Generate Output Product
7、创建顶层
8、生成Bit文件
9、导入到SDK
二、在SD中进行编程
1、创建一个HelloWorld工程
2、按照上一讲说的中断设置流程编写程序
3、程序分析
主函数:
#include
#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个时钟周期,所以使用电平触发时,也只能产生一次中断,看起来就和上升沿触发的一样,都是一次。在做实验的时候,我在这里就卡了挺久,想着电平触发为什么只响应一次中断,后来慢慢才摸索到是因为高电平时间太少了的原因。
本文的主要代码借鉴了这个博主的代码。
https://blog.csdn.net/botao_li/article/details/86242850#commentsedit