zynq中断:共享外设中断(SPI)

    xiaoxiao2022-07-13  139

    摘要: 关于zynq的中断说明网上有很多的帖子,比如说一下的帖子就写的很不错。 https://blog.csdn.net/shangguanyunlan/article/details/53147587

    关于中断的说明不想在多说,详细的可以看上面的博客。我这里主要将如何编程实现。关于中断的说明将会分两篇来说明,第一篇是直接在PL端通过逻辑来产生中断,第二篇是通过按键来产生中断。

    本篇通过逻辑来产生中断信号,并将其接入ZYNQ的中断输入。产生的中断信号是一个周期方波,周期为2秒。 共享外设中断的触发方式有上升沿和高电平两种方式。上升沿触发,顾名思义就是在上升沿的时候触发中断。高电平触发就是在高电平期间触发中断,但并不是说在高电平期间的每一个时刻都会触发中断。比如说高电平的占空比是1秒的话,并不是在1秒内都会产生中断。为什么呢?我想主要的原因就是,当中断相应进入中断处理函数后,会关闭中断,然后再进行处理,处理完后才再次打开中断,因此并不是每一个时刻都会响应中断。

    高电平触发的应用场景主要用于类似于报警系统,当报警没有接触的时候就一直给高电平,产生警报。当警报解除之后就可以拉低信号。

    一、在vivado 中创建一个Block Design

    Block Design的完整配置如下 1、配置ZYNQ a、中断设置 b、时钟设置 2、创建三个端口 分别创建PL_intr、CLK_100M和FCLK_RESET_N端口,并把这两个端口分别连接到IRQ_F2P、FCLK_CLK0和FCLK_RESET_N。 3、选中中断信号线, 并debug。 3、按照第一张图完成连接,如下图。 4、Generate output product 5、创建一个顶层文件,选择可以修改顶层文件 6、修改顶层文件如下

    `timescale 1 ps / 1 ps module design_1_wrapper ( //CLK100M, DDR_addr, DDR_ba, DDR_cas_n, DDR_ck_n, DDR_ck_p, DDR_cke, DDR_cs_n, DDR_dm, DDR_dq, DDR_dqs_n, DDR_dqs_p, DDR_odt, DDR_ras_n, DDR_reset_n, DDR_we_n, //FCLK_RESET0_N, FIXED_IO_ddr_vrn, FIXED_IO_ddr_vrp, FIXED_IO_mio, FIXED_IO_ps_clk, FIXED_IO_ps_porb, FIXED_IO_ps_srstb, // key_in, led // PL_intr ); // output CLK100M; inout [14:0]DDR_addr; inout [2:0]DDR_ba; inout DDR_cas_n; inout DDR_ck_n; inout DDR_ck_p; inout DDR_cke; inout DDR_cs_n; inout [3:0]DDR_dm; inout [31:0]DDR_dq; inout [3:0]DDR_dqs_n; inout [3:0]DDR_dqs_p; inout DDR_odt; inout DDR_ras_n; inout DDR_reset_n; inout DDR_we_n; //output FCLK_RESET0_N; inout FIXED_IO_ddr_vrn; inout FIXED_IO_ddr_vrp; inout [53:0]FIXED_IO_mio; inout FIXED_IO_ps_clk; inout FIXED_IO_ps_porb; inout FIXED_IO_ps_srstb; // input key_in; output led; //input [0:0]PL_intr; wire CLK_100M; wire [14:0]DDR_addr; wire [2:0]DDR_ba; wire DDR_cas_n; wire DDR_ck_n; wire DDR_ck_p; wire DDR_cke; wire DDR_cs_n; wire [3:0]DDR_dm; wire [31:0]DDR_dq; wire [3:0]DDR_dqs_n; wire [3:0]DDR_dqs_p; wire DDR_odt; wire DDR_ras_n; wire DDR_reset_n; wire DDR_we_n; wire FCLK_RESET0_N; wire FIXED_IO_ddr_vrn; wire FIXED_IO_ddr_vrp; wire [53:0]FIXED_IO_mio; wire FIXED_IO_ps_clk; wire FIXED_IO_ps_porb; wire FIXED_IO_ps_srstb; wire [0:0]PL_intr; design_1 design_1_i (.CLK_100M(CLK_100M), .DDR_addr(DDR_addr), .DDR_ba(DDR_ba), .DDR_cas_n(DDR_cas_n), .DDR_ck_n(DDR_ck_n), .DDR_ck_p(DDR_ck_p), .DDR_cke(DDR_cke), .DDR_cs_n(DDR_cs_n), .DDR_dm(DDR_dm), .DDR_dq(DDR_dq), .DDR_dqs_n(DDR_dqs_n), .DDR_dqs_p(DDR_dqs_p), .DDR_odt(DDR_odt), .DDR_ras_n(DDR_ras_n), .DDR_reset_n(DDR_reset_n), .DDR_we_n(DDR_we_n), .FCLK_RESET0_N(FCLK_RESET0_N), .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn), .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp), .FIXED_IO_mio(FIXED_IO_mio), .FIXED_IO_ps_clk(FIXED_IO_ps_clk), .FIXED_IO_ps_porb(FIXED_IO_ps_porb), .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb), .PL_intr(PL_intr)); //产生中断信号逻辑 reg [31:0] count=0; reg [3:0] count1=0; (*mark_debug="true"*)reg start; always@(posedge CLK_100M or negedge FCLK_RESET0_N)begin if(!FCLK_RESET0_N) count<=0; else if(count<(400000000-1)) count<=count+1; else count<=0; end //start 就是中断信号,是周期为2秒的方波 always@(posedge CLK_100M or negedge FCLK_RESET0_N)begin if(!FCLK_RESET0_N) start<=0; else if(count<800) start<=1; else start<=0; end assign PL_intr = start; reg led_wire; assign led=led_wire; always@(posedge CLK_100M or negedge FCLK_RESET0_N )begin if(!FCLK_RESET0_N) led_wire<=0; else if(count==(400000000-1)) led_wire<=~led_wire; else led_wire<=led_wire; end wire key_out; endmodule

    7、添加XDC文件

    set_property iostandard LVCMOS33 [get_ports led] set_property PACKAGE_PIN T22 [get_ports led]

    8、生成比特文件

    二、导入到SDK,编写C代码

    1、导入到SDK,创建一个HlloWorld应用

    2、C代码编写流程

    基本套路就是按照以下的方式进行初始化和设置就可以了

    根据中断ID查找GIC中断配置——>初始化GIC中断控制器——>异常初始化——>将中断注册到异常处理——>使能异常——>将中断处理函数与中断异常连接起来——>设置中断优先级触发方式——>GIC中断使能

    3、删除到HelloWorld.c

    4、编写函数

    main.c

    #include "PL0_intr.h" #include "sys_intr.h" static XScuGic GIC; //GIC int main(void) { Init_Intr_System(&GIC); PL0_Setup_Intr_System( &GIC ); while(1) ; }

    sys_intr.c

    #include "sys_intr.h" int Init_Intr_System(XScuGic * IntcInstancePtr) //Ò»°ã²»ÐèÒªÐÞ¸Ä { int Status; XScuGic_Config *IntcConfig; //根据中断ID查找GIC中断配置 IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); if (NULL == IntcConfig) { return XST_FAILURE; } //初始化GIC中断控制器 Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* Enable interrupts from the hardware */ //异常初始化——>将中断注册到异常处理——>使能异常 Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, (void *)IntcInstancePtr); Xil_ExceptionEnable(); return XST_SUCCESS; }

    sys_intr.h

    #ifndef SYS_INTR_H_ #define SYS_INTR_H_ #include "xparameters.h" #include "xil_exception.h" #include "xdebug.h" #include "xscugic.h" #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID int Init_Intr_System(XScuGic * IntcInstancePtr); #endif /* SYS_INTR_H_ */

    PL0_intr.c

    #include <stdio.h> #include "xadcps.h" #include "xil_types.h" #include "Xscugic.h" #include "Xil_exception.h" #include "xscutimer.h" //中响应响应函数 static void PL0_IntrHandler(void *CallBackRef) { static int i = 0 ; printf("the %d 次中断\n\r",i); i++; } void PL0_Setup_Intr_System(XScuGic *GicInstancePtr) { //将中断处理函数与中断异常连接起来——>设置中断优先级触发方式——>GIC中断使能** XScuGic_Connect(GicInstancePtr, 61, (Xil_ExceptionHandler)PL0_IntrHandler,//set up the timer interrupt (void *)PL0_msg); XScuGic_SetPriorityTriggerType(GicInstancePtr, 61, 0X90 , 0x3) ;//最后一个函数是设置触发方式:0x1:电平 ,0x3: XScuGic_Enable(GicInstancePtr, 61);//enable the interrupt for the Timer at GIC }

    三、现象

    根据设置的触发方式的不同有不同的现象。 1、如果设置为上升沿触发,就会在每一个上升沿产生一个中断 2、如果设置为高电平触发就会在高电平期间触发多次中断。但是这里有一个值得注意的就是,如果高电平持续时间较短,就只能触发一次中断,会让人误以为是上升沿触发,因此我这里设置高电平是2秒,时间充足,就可以触发多次中断。然后经过我摸索,高电平至少持续1000个时钟周期,才会触发多次中断。

    注意:如果用按键触发中断的话,可能会由于按键抖动导致多次中断。因为之前用的就是按键中断,导致多次触发中断,当时ila有没有抓到多次触发的信号,就以为有其他的原因。实际上就是按键抖动多次中断的原因。

    最新回复(0)