【ZYNQ入门】第二篇、ZYNQ的中断系统及常用中断初始化程序

目录

第一部分、Zynq的中断系统及架构

1、中断系统结构

第二部分、各类中断对应的ID

1、软中断SGI的ID

2、私有中断PPI的ID

3、共享SPI的ID(最常用)

第三部分、常用中断初始化程序

1、UART0中断配置代码(SPI)

1.1、串口的工作模式

1.1.1、Normal Mode模式

1.1.2、Automatic Echo Mode模式

1.1.4、Remote Loopback Mode模式

1.2、串口初始化

1.3、中断初始化代码        

2、PL中断PS的配置代码(SPI)

2.1、硬中断的基础知识

2.2、硬中断实验内容

2.3、中断初始化代码如下

3、GPIO中断配置代码

4、AMP模式下软中断配置代码

4.1、SGI中断配置要点

4.2、AMP中断配置完整代码


第一部分、Zynq的中断系统及架构

        刚入手ZYNQ,对SDK的编程风格真的是不太习惯,不过适应一段时间后,会发现SDK还是挺好用的。这篇文章,主要是将最近学的中断系统进行一次整理,让自己的遗忘速度稍微变慢一点。

 1、中断系统结构

        想要完全认识ZYNQ的中断,下面这张图真的要非常熟悉。主干是通用中断控制器GIC)。GIC的左边包含三部分,分别是软中断SGI私有中断PPI以及共享中断SPI。最常用的就是共享中断,只要把其中一种中断的初始化步骤弄清楚,那么其它理解起来就会变很简单。

第二部分、各类中断对应的ID

        每个中断都对应有自己的ID,这里ID的作用主要是在初始化中断的时候,为某些中断初始化的入口参数。目的是告诉中断系统我用的哪一个中断,名字告诉你了,你别搞错了。

1、软中断SGI的ID

2、私有中断PPI的ID

3、共享SPI的ID(最常用)

第三部分、常用中断初始化程序

        中断的程序流程如下图所示,其中前四步为固定流程,因为这四步主要是用来初始化通用中断控制器,也就是第一张图的主干部分的初始化。因为无论是SGI、PPI还是SPI,这三个中断类型都连接着GIC,因此前四步初始化的便可以固定下来。

        前四步的配置代码如下:

    /*step1、初始化异常处理*/
    Xil_ExceptionInit();
    /*step2、初始化中断控制器*/
    ScuGicCfgPtr = XScuGic_LookupConfig(GIC_DEV_ID);
    status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
        printf("Initial Gic Failed!");
        return status;
    }
    /*step3、注册异常处理回调函数*/
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
    /*step4、异常处理使能*/
    Xil_ExceptionEnable();

1、UART0中断配置代码(SPI)

1.1、串口的工作模式

        Zynq内的串口一共有四种工作模式,分别是普通模式自动回声模式本地回环模式远程回环模式

1.1.1、Normal Mode模式

        用于标准的UART操作模式。正常情况下都是用的这个模式。

1.1.2、Automatic Echo Mode模式

        这个模式下RXD引脚接收的数据,会自动路由到TXD引脚上。一边接收数据一边发送数据。

1.1.3、Local Loopback Mode模式

        本地回环模式,相当于自发自收,数据不经过TXD和RXD这两个管脚。

1.1.4、Remote Loopback Mode模式

        这个模式下,CPU在TXD上不能发送任何内容,在RXD上不能接收任何内容。

1.2、串口初始化

        配置串口中断之前,必须要对串口进行初始化,然后再去配置串口中断。初始化步骤包括:连接串口的ID、设置波特率、设置串口工作模式、设置中断间隔时间、启动中断监听。代码如下:

//串口初始化函数
void Uart0_Init()
{
    int state = 0;
    //寻找ID,初始化设备
    UartPsCfgPtr = XUartPs_LookupConfig(XPAR_PS7_UART_0_DEVICE_ID);//Device id
    state = XUartPs_CfgInitialize(&Uart0,UartPsCfgPtr,UartPsCfgPtr->BaseAddress);
    if(state != XST_SUCCESS)
    {
        printf("Uart initial failed");
        return state;
    }
    //设置波特率(应该与硬件相关,在设置硬件的时候就设置串口为115200)
    XUartPs_SetBaudRate(&Uart0,115200);
    //设置串口的工作模式
    XUartPs_SetOperMode(&Uart0,XUARTPS_OPER_MODE_NORMAL);//设置为普通模式
    //设置中断间隔时间(由于初始化中断时,设置为时间间隔中断)
    XUartPs_SetRecvTimeout(&Uart0,8);//8*4*1/115200 间隔这么久没有数据,就算中断
    //启动监听
    XUartPs_Recv(&Uart0,uart_rx_data,32);// 启动监听,设置接收buf的预计的字节数
}

        需要注意的就是串口中断触发方式,串口的触发方式有多种,本次实验选用的是接收时间间隔中断,当超过一段时间RXD没有接收到数据,便产生中断。具体参照xuartps_hw.h头文件。

#define XUARTPS_IXR_RBRK	0x00002000U /**< Rx FIFO break detect interrupt */
#define XUARTPS_IXR_TOVR	0x00001000U /**< Tx FIFO Overflow interrupt */
#define XUARTPS_IXR_TNFUL	0x00000800U /**< Tx FIFO Nearly Full interrupt */
#define XUARTPS_IXR_TTRIG	0x00000400U /**< Tx Trig interrupt */
#define XUARTPS_IXR_DMS		0x00000200U /**< Modem status change interrupt */
#define XUARTPS_IXR_TOUT	0x00000100U /**< Timeout error interrupt */
#define XUARTPS_IXR_PARITY 	0x00000080U /**< Parity error interrupt */
#define XUARTPS_IXR_FRAMING	0x00000040U /**< Framing error interrupt */
#define XUARTPS_IXR_OVER	0x00000020U /**< Overrun error interrupt */
#define XUARTPS_IXR_TXFULL 	0x00000010U /**< TX FIFO full interrupt. */
#define XUARTPS_IXR_TXEMPTY	0x00000008U /**< TX FIFO empty interrupt. */
#define XUARTPS_IXR_RXFULL 	0x00000004U /**< RX FIFO full interrupt. */
#define XUARTPS_IXR_RXEMPTY	0x00000002U /**< RX FIFO empty interrupt. */
#define XUARTPS_IXR_RXOVR  	0x00000001U /**< RX FIFO trigger interrupt. */
#define XUARTPS_IXR_MASK	0x00003FFFU /**< Valid bit mask */

1.3、中断初始化代码        

        串口中断初始化剩下的步骤如下

        main.c代码如下,需要注意的是串口初始化要放在中断初始化之前,不然程序会卡死,原因:串口的初始化中,包含了寻找串口的设备ID,和初始化变量赋值的过程。

#include <stdio.h>      //printf函数
#include "xparameters.h"
#include "xil_printf.h" //printf打印函数文件
#include "sleep.h"      //延迟头文件
#include "xscugic.h"    //通用中断控制器头文件
#include "xuartps.h"    //串口中断的处理函数
#include "xuartps_hw.h" //串口中断模式设置


/*重定义*/
#define GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID//gic device id
#define UART0_INTR_ID XPAR_XUARTPS_0_INTR     //UART0 interrupt id,not device id,the id from "xparameters_ps.h"

/*必要变量定义*/
static XScuGic ScuGic;//初始化中断控制器变量
static XScuGic_Config * ScuGicCfgPtr;

static XUartPs Uart0;//串口初始化中断控制器
static XUartPs_Config * UartPsCfgPtr;

/*全局变量*/
unsigned char uart_rx_data[32];//存储串口接收的数据

//函数初始化
int Init_Gic(void);//通用中断控制器初始化
void Uart0_Handler(void *CallBackRef, u32 Event,u32 EventData);//串口中断处理函数
void Uart0_Init();//串口初始化函数(包含中断模式)


int main()
{
    int status = 0;
    Uart0_Init();//串口初始化放到中断控制器前面
    status = Init_Gic();
    if(status != XST_SUCCESS){
        return status;
    }

    while(1)
    {

    }
    return 0;
}


//串口中断处理函数
void Uart0_Handler(void *CallBackRef, u32 Event,u32 EventData)
{
    u32 revcnt = 0;
    if(Event = XUARTPS_IXR_TOUT)//判断触发中断的事件是哪一个
    {
        revcnt = EventData;
        if(revcnt == 8 && uart_rx_data[0] == 0x55 && uart_rx_data[1] == 0x55)
        {
            printf("frame1\n\r");
        }
        printf("串口中断成功,frame1\n\r");
        XUartPs_Recv(&Uart0,uart_rx_data,32);// 启动监听,设置接收buf的预计的字节数
    }

}


//串口初始化函数
void Uart0_Init()
{
    int state = 0;
    //寻找ID,初始化设备
    UartPsCfgPtr = XUartPs_LookupConfig(XPAR_PS7_UART_0_DEVICE_ID);//Device id
    state = XUartPs_CfgInitialize(&Uart0,UartPsCfgPtr,UartPsCfgPtr->BaseAddress);
    if(state != XST_SUCCESS)
    {
        printf("Uart initial failed");
        return state;
    }
    //设置波特率(应该与硬件相关,在设置硬件的时候就设置串口为115200)
    XUartPs_SetBaudRate(&Uart0,115200);
    //设置串口的工作模式
    XUartPs_SetOperMode(&Uart0,XUARTPS_OPER_MODE_NORMAL);//设置为普通模式
    //设置中断间隔时间
    XUartPs_SetRecvTimeout(&Uart0,8);//8*4*1/115200 间隔这么久没有数据,就算中断
    //启动监听
    XUartPs_Recv(&Uart0,uart_rx_data,32);// 启动监听,设置接收buf的预计的字节数
}
//初始化通用中断控制器
int Init_Gic(void)
{
    int status;
    //step1、初始化异常处理
    Xil_ExceptionInit();
    //step2、初始化中断控制器
    ScuGicCfgPtr = XScuGic_LookupConfig(GIC_DEV_ID);
    status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
        printf("Initial Gic Failed!");
        return status;
    }

    //step3、注册异常处理回调函数
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
    //step4、异常处理使能
    Xil_ExceptionEnable();

    /******配置区域*******/
    //step5、在gic中使能uart0中断
    XScuGic_Enable(&ScuGic,UART0_INTR_ID);
    //step6、在gic中连接uart0对应的中断ID
    status = XScuGic_Connect(&ScuGic,UART0_INTR_ID,(Xil_ExceptionHandler)XUartPs_InterruptHandler,&Uart0);
    if(status != XST_SUCCESS)
    {
        printf("Connect Gic Failed!");
        return status;
    }
    //step7、设置uart0中断处理函数
    XUartPs_SetHandler(&Uart0,(XUartPs_Handler)Uart0_Handler,&Uart0);
    //step8、设置uart0中断触发方式(这里为间隔时间中断)
    XUartPs_SetInterruptMask(&Uart0,XUARTPS_IXR_TOUT);//多事件相或

    return XST_SUCCESS;
}

2、PL中断PS的配置代码(SPI)

2.1、硬中断的基础知识

        PL到PS的中断,也属于共享中断,一般都称做硬中断硬中断一共有16个ID分别是61-68和84-91,系统默认从61开始分配ID号。如下图IRQ_F2P[0] = 61,IRQ_F2P[1] = 62,依次类推,需要注意的是:IRQ_F2P的数据位宽是自动分配,不要自己去分配。

        其次,由上面可知,硬中断的触发方式可分为上升沿触发和高电平触发,因此在配置时,需要说明清楚,我一般使用都配置为边沿触发。

        注意:高电平触表示在高电平期间触发中断,但并不是说在高电平期间的每一个时刻都会触发中断。

2.2、硬中断实验内容

       本次实验,由FPGA给CPU0产生一个1s中断,FPGA每次计数到1s就产生一个1us的中断信号给CPU0。

        Verilog代码如下:

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : BigFartPeach
// 优快云   : 大屁桃
// E-mail : 2624507313@qq.com
// File   : intr1s.v
// Create : 2023-11-04 14:33:39
// -----------------------------------------------------------------------------
module intr1s(
	input wire clk,	//50MHz
	input wire rst,
	output wire intr1sflag
	);

//常量定义
parameter CNT_END = 49999999;//50,000,000 - 1 
parameter CNT_1US_END = 49;

//1s计数器
reg [25:0] cnt_1s;
reg flag_1s;
assign intr1sflag = flag_1s;

//1s计数器
always @(posedge clk) begin
	if (rst == 1'b1) begin
		cnt_1s <= 'd0;
	end
	else if (cnt_1s == CNT_END) begin
		cnt_1s <= 'd0;
	end
	else begin
		cnt_1s <= cnt_1s + 1'b1;
	end
end

//1us的标志信号
always @(posedge clk) begin
	if (rst == 1'b1) begin
		flag_1s <= 1'b0;
	end
	else if (cnt_1s == CNT_1US_END) begin
		flag_1s <= 1'b0;
	end
	else if(cnt_1s == CNT_END) begin
		flag_1s <= 1'b1;
	end
end

endmodule

  2.3、中断初始化代码如下

        注意:

        1、AMP模式下,需要对硬中断的ID进行绑定CPU。

        2、AMP模式下,对CPU0和CPU1同时初始化时,要有先后顺序,一般情况下先给CPU0初始化,再给CPU1初始化。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xscugic.h" //中断头文件
#include "xparameters.h"//设备ID

/*step1 重定义*/
#define GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define GIC_BASEADDR XPAR_PS7_SCUGIC_0_DIST_BASEADDR
#define F2P_DEV_INTR0 61 //1s中断源对应的ID
#define INTR_PORI 32 //F2P中断优先级
#define TRIG_TYPE 3  //F2P中断触发类型 2'b01 高电平触发,2'b11 = 3为上升沿触发


/*step2 变量定义*/
XScuGic ScuGic;
XScuGic_Config *ScuGicCfgPtr;

/*step3 函数声明*/
int Init_Gic(void);
void F2P0_Handler(void *data);//1s中断处理函数

int main()
{
    Init_Gic();
    while(1)
    {

    }
    return 0;
}


int Init_Gic(void)
{
    int status = 0;
    /*step1、初始化异常处理*/
    Xil_ExceptionInit();
    /*step2、初始化中断控制器*/
    ScuGicCfgPtr = XScuGic_LookupConfig(GIC_DEV_ID);
    status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
        printf("Initial Gic Failed!");
        return status;
    }
    /*step3、注册异常处理回调函数*/
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
    /*step4.异常处理使能*/
    Xil_ExceptionEnable();

    /******配置区域*******/
    /*step5、在gic中使能(被中断的设备ID)*/
    XScuGic_Enable(&ScuGic,F2P_DEV_INTR0);
    /*step6、在gic中连接被中断的设备ID,并注册回调函数*/
    status = XScuGic_Connect(&ScuGic,F2P_DEV_INTR0,(Xil_ExceptionHandler)F2P0_Handler,&ScuGic);
    if(status != XST_SUCCESS)
    {
        printf("Connect Gic Failed!");
        return status;
    }
    /*step7、设置F2P中断源优先级和触发类型*///设置触发类型是因为F2P有两种触发方式让你选
    XScuGic_SetPriTrigTypeByDistAddr(GIC_BASEADDR,F2P_DEV_INTR0,INTR_PORI,TRIG_TYPE);
    /*step8、在双核架构下需要,将硬件ID与对应的CPU相连接*/
//    XScuGic_InterruptMaptoCpu(&ScuGic,0,F2P_DEV_INTR0);

    return XST_SUCCESS;
}

void F2P0_Handler(void *data)
{
    printf("come from PL 1s 999!\n\r");
}

3、GPIO中断配置代码

        GPIO和串口中断比较类似,在中断初始化之前首先要对GPIO进行初始化,只有在GPIO初始化结束后再去配置GPIO的中断。

        需要注意的是:要区分开设备ID中断号ID这两者的区别。同时关于MIO和EMIO的设备ID如下图。MIO和EMIO的ID顺序都是按照初始化时数组的低位到高位排列,对应于不同bank的低位到高位。

        具体代码如下:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h" //设备ID号
#include "xgpiops.h"     //GPIO设置头文件
#include "xscugic.h"     //通用中断控制器头文件
#include "sleep.h"       //延时函数头文件
//全局变量
u32  led_value = 1;

/*1.重定义*/
#define GPIO_DEV_ID XPAR_PS7_GPIO_0_DEVICE_ID  //GPIO device ID
#define GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID //GIC devive ID

#define LED0 54
#define LED1 55
#define SW0  56

#define GPIO_INTR_ID 52//GPIO的中断ID,来源于表格

#define SW_BANK_ID 2   //连接按键的管脚属于EMIO,属于GPIO BANK 2
/*2.变量定义*/
XScuGic ScuGic;
XScuGic_Config * ScuGicCfgPtr;

XGpioPs GpioPs;
XGpioPs_Config * GpioPsCfPtr;

/*3.函数初始化*/
int Init_GPIO(void);
int Init_Gic(void);
void GPIO_Handler(void *CallBackRef, u32 Bank, u32 Status);

int main()
{
	int status = 0;
//	printf("GPIO interrupt !\r\n");
	Init_GPIO();
	status = Init_Gic();
	if(status != XST_SUCCESS)
	{
		return	status;
	}
	while(1)
	{
		printf("GPIO interrupt ! led_value = %d\r\n",led_value);
		sleep(1);
	}
    return 0;
}

int Init_GPIO(void)
{
	int status = 0;
	GpioPsCfPtr = XGpioPs_LookupConfig(GPIO_DEV_ID);
	status = XGpioPs_CfgInitialize(&GpioPs,GpioPsCfPtr,GpioPsCfPtr->BaseAddr);
	if(status != XST_SUCCESS){
		return	status;
	}
	//设置GPIO的方向
	XGpioPs_SetDirectionPin(&GpioPs,LED0,0x01);
	XGpioPs_SetDirectionPin(&GpioPs,LED1,0x01);
	XGpioPs_SetDirectionPin(&GpioPs,SW0,0x00);//input for button
	//设置输出使能
	XGpioPs_SetOutputEnablePin(&GpioPs,LED0,0x01);
	XGpioPs_SetOutputEnablePin(&GpioPs,LED1,0x01);

	return	XST_SUCCESS;
}

int Init_Gic(void)
{
	int status = 0;
	/*step1、初始化异常处理*/
	Xil_ExceptionInit();
	/*step2、初始化中断控制器*/
	ScuGicCfgPtr = XScuGic_LookupConfig(GIC_DEV_ID);
	status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
	if(status != XST_SUCCESS)
	{
		printf("Initial Gic Failed!");
		return status;
	}
	/*step3、注册异常处理回调函数*/
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
	/*step4、使能异常处理*/
	Xil_ExceptionEnable();

	/***********配置区域***********/
    //step5、在GIC中使能GPIO中断
	XScuGic_Enable(&ScuGic,GPIO_INTR_ID);
	//step6、在GIC中连接GPIO对应的中断ID
	status = XScuGic_Connect(&ScuGic,GPIO_INTR_ID,(Xil_InterruptHandler)XGpioPs_IntrHandler,&GpioPs);
	if(status != XST_SUCCESS){
			return status;
	}
	//step7、设置GPIO中断处理函数
	XGpioPs_SetCallbackHandler(&GpioPs,(void*) &GpioPs,(XGpioPs_Handler)GPIO_Handler);
	//step8、使能GPIO对应的bank上的具体地址
	XGpioPs_IntrEnable(&GpioPs,SW_BANK_ID,1<<(SW0-54));

	return	XST_SUCCESS;
}


void GPIO_Handler(void *CallBackRef, u32 Bank, u32 Status)
{
	XGpioPs *GpioPtr;
	GpioPtr = (XGpioPs *)CallBackRef;
	//读取中断是否被触发
	u32 intrstatus;
	intrstatus = XGpioPs_IntrGetStatusPin(GpioPtr,SW0);
	if(intrstatus == 1)
	{
		//清除按键中断使能
		XGpioPs_IntrClearPin(GpioPtr,SW0);
		XGpioPs_IntrDisablePin(GpioPtr,SW0);
		//等待按键抖动结束,100ms高电平
		u32 readSW=0;
		int cnt;
		while(cnt <100)
		{
			readSW= XGpioPs_ReadPin(GpioPtr,SW0);
			if(readSW == 1)
			{
				cnt ++;
			}
			else
			{
				cnt =0;
			}
			usleep(1000);
		}
		//打开对应GPIO的中断使能
		XGpioPs_IntrEnablePin(GpioPtr,SW0);

	}
	/*中断处理*/
	led_value = ~led_value;
	XGpioPs_WritePin(GpioPtr,LED1,led_value);
	XGpioPs_WritePin(GpioPtr,LED0,led_value);

}

4、AMP模式下软中断配置代码

        软中断的中断ID为0~15,并且全部是上升沿触发,主要用于核间中断或者CPU自己中断自己。关于AMP模式下的工程构建,请参考博客:

4.1、SGI中断配置要点

        CPU0和CPU1的中断ID固定如下

#define CPU0_INTR_DEV_ID 0x0D//CPU0 interrupt ID
#define CPU1_INTR_DEV_ID 0x0E//CPU1 interrupt ID

        触发中断的方式:在中断配置完成后,CP0可以自己中断自己,也可以中断CPU1

//XSCUGIC_SPI_CPU0_MASK和XSCUGIC_SPI_CPU1_MASK为系统自带
status = XScuGic_SoftwareIntr(&ScuGic,CPU0_INTR_DEV_ID,XSCUGIC_SPI_CPU0_MASK);//CPU0自己中断自己
status = XScuGic_SoftwareIntr(&ScuGic,CPU1_INTR_DEV_ID,XSCUGIC_SPI_CPU1_MASK);//CPU0中断CPU1

4.2、AMP中断配置完整代码

        CPU0的配置代码如下:

//cpu0 main.c
#include <stdio.h>
#include "sleep.h"
#include "xparameters.h"
#include "xscugic.h"    //通用中断控制器头文件

/*1.重定义*/
#define GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID //GIC devive ID
#define CPU0_INTR_DEV_ID 0x0D//CPU0 interrupt ID(被中断的设备ID)
#define CPU1_INTR_DEV_ID 0x0E//CPU1 interrupt ID(被中断的设备ID)

/*2.变量定义*/
XScuGic ScuGic;
XScuGic_Config * ScuGicCfgPtr;

/*3.函数初始化*/
int Init_GIC_SGI(void);
void CPU0_Handler(void * CallBackRef);

int main()
{
    int status = 0;
    status = Init_GIC_SGI();
    if(status != XST_SUCCESS)
    {
        printf("Initial GIC and SGI Failed!");
        return status;
    }
    while(1)
    {
        status = XScuGic_SoftwareIntr(&ScuGic,CPU0_INTR_DEV_ID,XSCUGIC_SPI_CPU0_MASK);//自己中断自己
        if (status != XST_SUCCESS) {
            return XST_FAILURE;
        }
        sleep(5);
    }
    return 0;
}

int Init_GIC_SGI(void)
{
    int status = 0;
    /******中断控制器初始化*******/
    //step1.初始化异常处理
    Xil_ExceptionInit();
    //step2.初始化中断控制器
    ScuGicCfgPtr = XScuGic_LookupConfig(GIC_DEV_ID);
    status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
        printf("Initial Gic Failed!");
        return status;
    }
    //step3.注册异常处理回调函数
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
    //step4.异常处理使能
    Xil_ExceptionEnable();

    /******SGI配置区域*******/
    //step5.在gic中使能(被中断的设备ID)
    XScuGic_Enable(&ScuGic,CPU0_INTR_DEV_ID);
    //step6.在gic中连接被中断的设备ID,并注册回调函数
    status = XScuGic_Connect(&ScuGic,CPU0_INTR_DEV_ID,(Xil_ExceptionHandler)CPU0_Handler,&ScuGic);
    if(status != XST_SUCCESS)
    {
        printf("Connect Gic Failed!");
        return status;
    }
    return XST_SUCCESS;
}

void CPU0_Handler(void * CallBackRef)
{
    printf("i am cpu000 intr !\n\r");
}

         CPU1的配置代码如下:

//cpu1 main.c
#include <stdio.h>
#include "sleep.h"
#include "xparameters.h"
#include "xscugic.h"    //通用中断控制器头文件

/*1.重定义*/
#define GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID //GIC devive ID
#define CPU0_INTR_DEV_ID 0x0D//CPU0 interrupt ID(被中断的设备ID)
#define CPU1_INTR_DEV_ID 0x0E//CPU1 interrupt ID(被中断的设备ID)

/*2.变量定义*/
XScuGic ScuGic;
XScuGic_Config * ScuGicCfgPtr;

/*3.函数初始化*/
int Init_GIC_SGI(void);
void CPU1_Handler(void * CallBackRef);

int main()
{
	int status = 0;
    sleep(2);//让cpu0先初始化
	status = Init_GIC_SGI();
    if(status != XST_SUCCESS)
    {
        printf("Initial GIC and SGI Failed!");
        return status;
    }
	while(1)
	{
//		sleep(3);
//		printf("this is cpu1 main\n\r");
		sleep(2);
		status = XScuGic_SoftwareIntr(&ScuGic,CPU0_INTR_DEV_ID,XSCUGIC_SPI_CPU0_MASK);//中断CPU0
		if (status != XST_SUCCESS) {
			return XST_FAILURE;
		}

	}
    return 0;
}

int Init_GIC_SGI(void)
{
	int status = 0;
	/******中断控制器初始化*******/
    //step1.初始化异常处理
    Xil_ExceptionInit();
    //step2.初始化中断控制器
    ScuGicCfgPtr = XScuGic_LookupConfig(GIC_DEV_ID);
    status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
        printf("Initial Gic Failed!");
        return status;
    }
    //step3.注册异常处理回调函数
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
    //step4.异常处理使能
    Xil_ExceptionEnable();

    /******SGI配置区域*******/
    //step5.在gic中使能(被中断的设备ID)
    XScuGic_Enable(&ScuGic,CPU1_INTR_DEV_ID);
    //step6.在gic中连接被中断的设备ID,并注册回调函数
    status = XScuGic_Connect(&ScuGic,CPU1_INTR_DEV_ID,(Xil_ExceptionHandler)CPU1_Handler,&ScuGic);
    if(status != XST_SUCCESS)
    {
        printf("Connect Gic Failed!");
        return status;
    }
    return XST_SUCCESS;
}

void CPU1_Handler(void * CallBackRef)
{
	printf("i am cpu1 intr !\n\r");
}

<think>好的,用户想了解Zynq中断合成的实现方法和相关文档教程。首先我需要回忆一下Zynq中断系统结构。Zynq的PS部分有通用中断控制器(GIC),可以处理来自PL的中断请求。用户提到的中断合成可能是指将多个PL中断信号合并后再传递给PS的GIC。根据引用[4],PL到PS的中断通道包括16个IRQ和4个FIQ,所以可能需要通过Vivado配置中断控制器的连接。 接下来要考虑如何在设备树中配置中断。根据引用[2],在Xilinx SDK中添加设备树源文件地址,可能需要修改设备树源文件(dts)中的中断节点,指定中断号、触发类型等参数。同时,用户需要在内核驱动中注册中断处理函数,这部分可能需要参考Linux内核的中断系统文档。 另外,中断合成可能涉及硬件逻辑设计,比如在PL部分使用逻辑门或IP核将多个中断信号合并为一个,再连接到PS的IRQ/FIQ引脚。需要检查Vivado工程中PL部分的连接,确保中断信号正确连接到PS的中断控制器。编译步骤方面,引用[1]提到使用petalinux-build命令进行编译,生成包含设备树和内核的镜像。 用户可能需要具体的步骤,比如如何在Vivado中配置中断控制器的连接,如何编写设备树节点,以及驱动中的中断处理函数注册。还需要确保引用的相关文档如ZYNQ Linux移植说明和VxWorks的中断配置是否适用。此外,用户可能关心中断优先级、共享中断的处理方法,以及如何调试中断问题。需要综合硬件配置、设备树设置和驱动开发三个方面来回答,并提供文档和教程的参考来源。</think>### Zynq中断合成实现方法 Zynq中断合成涉及PL(可编程逻辑)与PS(处理系统)的中断交互机制,主要通过以下步骤实现: 1. **硬件配置** 在Vivado中需将PL中断信号连接到PS端的中断控制器。通过双击`processing_system7_0`组件,勾选`Interrupts`中的`IRQ_F2P[15:0]`选项,开启16个IRQ和4个FIQ通道[^4]。PL端的中断信号可通过AXI Interrupt Controller或自定义逻辑合并后连接到这些引脚。 2. **设备树配置** 在设备树(Device Tree)中需定义中断号与触发方式。例如: ```dts pl_interrupt { compatible = "generic-uio"; interrupt-parent = <&intc>; interrupts = <0 29 1>; // IRQ 29, 高电平触发 }; ``` 其中`interrupt-parent`指向PS的GIC控制器,`interrupts`参数依次为中断类型(0表示SPI)、中断号、触发方式[^2]。 3. **驱动开发** 在Linux内核驱动中需注册中断处理函数,例如: ```c irq_handler_t pl_irq_handler(int irq, void *dev_id) { // 中断处理逻辑 return IRQ_HANDLED; } request_irq(irq_num, pl_irq_handler, IRQF_TRIGGER_HIGH, "pl_irq", NULL); ``` 需注意共享中断(`IRQF_SHARED`)和中断线程化处理等优化方法[^3]。 4. **系统编译** 使用PetaLinux工具链完成配置后,执行`petalinux-build`命令编译内核、设备树及驱动模块[^1]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大屁桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值