【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
实际开发中,我们还是希望有一部分工作是放到PL上面去做的,比如说低效率的io工作,或者是算法类的工作。但是这类工作做完之后,怎么告诉ps呢?这里面就有两种办法,一种是寄存器读取,也就是ps轮询的办法;还有一种就是中断的方法,整理上来说,中断的方法要比轮询的方法效率高很多,但前提是中断也不能过于频繁。
1、创建好工程,准备好block design
创建工程还是和之前一样,选择好020c'lg400-1即可。随后就是创建block design,添加一个zynq ps。添加之后不需要删除三个线,即clk、reset和gp0,因为它们今天会被用到。当然串口和ddr3的配置还是需要,ddr还是要注意16bit和芯片类型的选择。
2、添加gpio
这里pl,没有自己写,就用一个axi gpio作为测试用例。测试的axi gpio,我们就设置了一位,并且全部都是输入。当然,最重要的,就是把中断开关打开。
3、自动连接框图
依次自动设置ps连接、block连接之后,我们就可以看到这样的连接图。需要注意的是,我们需要手动打开pl-ps的中断开关。它位于ps的设置里面,双击ps,选择“Interrupts”,然后是“Fabric Interrupts”、“PL-PS Interrupt Ports”,把第一个选项“IRQ_F2P[15:0]”选上。
接着,就是把ps的中断和pl的中断连接上,中断号就是61,不出意外就是这样的框图。
4、完成剩下来的vivado工作
剩下来的vivado工作,和之前一样,就是“Generate output products”和“Create Wrapper”。因为这里使用到了外部ip,而且有外部的pin脚,我们在添加好了pin脚约束之后,就可以直接单击“Generate bitstream”了。
set_property PACKAGE_PIN P16 [get_ports {gpio_rtl_0_tri_i}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_rtl_0_tri_i}]
完成之后,就是导出硬件,注意选择include bitstream。接着,就是启动sdk软件。
5、启动sdk软件,开发和调试代码
sdk开发相对比较简单。启动之后,直接创建一个hello world工程即可。我们也是基于这个hello world工程进行修改的。其实关于中断的代码,之前也编写过。不过当时,我们编写的是定时器代码,只是这一次换成了gpio。整个中断也是采用注册+回调的方式进行的。
#include "xgpio.h"
#include "xscugic.h"
#include "xparameters.h"
#include "xstatus.h"
#define GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTR_CHANNEL XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR
XGpio Gpio;
XScuGic Intc;
// 中断处理程序
static void GpioIntrHandler(void *CallbackRef) {XGpio_InterruptClear(&Gpio, 1);xil_printf("clicked\n");
}
// 设置中断
int SetupInterruptSystem(XScuGic *IntcInstancePtr) {int Status;XScuGic_Config *IntcConfig;// 获取中断控制器配置IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);if (IntcConfig == NULL) {return XST_FAILURE;}// 初始化中断控制器Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);if (Status != XST_SUCCESS) {return XST_FAILURE;}Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,IntcInstancePtr);// 使能异常Xil_ExceptionEnable();return XST_SUCCESS;
}
int main() {int Status;// 初始化中断控制器(使用 XScuGic_CfgInitialize)Status = SetupInterruptSystem(&Intc);if (Status != XST_SUCCESS) {return XST_FAILURE;}// 初始化 GPIOStatus = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);if (Status != XST_SUCCESS) {return XST_FAILURE;}// 设置 GPIO 为输入方向,并使能中断XGpio_SetDataDirection(&Gpio, 1, 0xFF); // 设置为输入模式// 连接中断处理程序Status = XScuGic_Connect(&Intc, GPIO_INTR_CHANNEL,(Xil_ExceptionHandler)GpioIntrHandler, &Gpio);if (Status != XST_SUCCESS) {return XST_FAILURE;}// 启用中断XScuGic_Enable(&Intc, GPIO_INTR_CHANNEL);XGpio_InterruptEnable(&Gpio, 1); // 使能 GPIO 中断XGpio_InterruptGlobalEnable(&Gpio); // 使能全局中断while (1) {// 主循环可以做其他任务}return XST_SUCCESS;
}
我们实际开发调试的时候,发现一个细节蛮有趣。那就是在中断函数的时候,只有插拔type c、烧入fpga之后,第一次free run,或者是第一次debug才有效果。如果第一次debug之后,我们想再次debug一下,看看中断函数是不是可以再次进去,大概率就进不了的。这个时候,能做的,就是重新插拔type c、烧入fpga,把所有动作再来一遍。
测试的时候,还有一个小窍门,那就是大家如果找不到free run,可以一直按step return也可以。这样也可以实现free run的效果。直到有按键按下去的时候,中断再次被触发。