ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)

PS和PL之间的交互,怎么都逃不过中断,稍微大型的数字系统,PS和PL之间配合使用就需要中断作为桥梁。本文通过按键发起中断请求尝试学习PL请求中断的处理机制。

板子用的是zc702。

ZYNQ是中断类系统框图:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第1张图片

由上图可知,zynq的中断分为三种:

1.软件中断SGISoftware generatedinterrupts,中断号0-15)(16–26 reserved) :被路由到一个或者两个CPU上,通过写ICDSGIR寄存器产生SGI.

2.私有外设中断PPIprivate peripheralinterrupts ,中断号27-31:每个CPU都有一组PPI,包括全局定时器、私有看门狗定时器、私有定时器和来自PLFIQ/IRQ.

3.共享外设中断SPIshared peripheralinterrupts,中断号32-95:PSPL上的各种I/O控制器和存储器控制器产生,这些中断信号被路由到相应的CPU. 


中断控制器(GIC,generic interrupt controller :用于集中管理从PSPL产生的中断信号的资源集合。控制器可以使能、关使能、屏蔽中断源和改变中断源的优先级,并且会将中断送到对应的CPU中,CPU通过私有总线访问这些寄存器。 

PL和PS之间的中断有:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第2张图片

两个CPU都具有各自16个软件中断,CPU可以中断自己,也可以中断其他CPU,上升沿触发,不可修改

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第3张图片

CPU的私有中断,这些中断都是固定死的,不能修改:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第4张图片

从PL来的中断先反转然后送到中断控制器,所以触发类型变成active low了。

共享中断就是一些端口共用一个中断请求线, PL部分有16个共享中断,他们的触发方式可以设置:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第5张图片


按键中断硬件部分

新建一个Vivado工程,添加zynq核,勾选中断:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第6张图片

添加两个逻辑门IP,设为非门:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第7张图片

再添加一个合并多路信号到一路的IP,我们这里要合并两路信号:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第8张图片

这里可以修改输出引脚的名称:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第9张图片

连接完是这样的:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第10张图片

常规操作,生成顶层文件。

添加约束如下:

set_property PACKAGE_PIN G19 [get_ports {SW1[0]}]
set_property IOSTANDARD LVCMOS25 [get_ports {SW1[0]}]
set_property PACKAGE_PIN F19 [get_ports {SW2[0]}]
set_property IOSTANDARD LVCMOS25 [get_ports {SW2[0]}]
生成bit文件,导入到SDK。


软件部分

新建一个应用工程:

#include 
#include "xscugic.h"
#include "xil_exception.h"

#define INT_CFG0_OFFSET 0x00000C00
#define SW1_INT_ID 61
#define SW2_INT_ID 62
#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define INT_TYPE_RISING_EDGE 0x03
#define INT_TYPE_HIGHLEVEL 0x01
#define INT_MASK 0x03

static XScuGic INTCInst;
static void SW_intr_Handler(void *param);
static int IntcInitFunction(u16 DeviceId);

static void SW_intr_Handler(void *param){
	int sw_id = (int)param;
	printf("SW%d int\n\r", sw_id);
}

void IntcTypeSetup(XScuGic *InstancePtr, int intId, int intType){//设置触发方式
	int mask;
	intType &= INT_MASK;
	mask = XScuGic_DistReadReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4);
	mask &= ~(INT_MASK << (intId%16)*2);
	mask |= intType << ((intId%16)*2);
	XScuGic_DistWriteReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4, mask);
}

int IntcInitFunction(u16 DeviceId){
	XScuGic_Config *IntcConfig;
	int status;
	// Interrupt controller initialisation
	IntcConfig = XScuGic_LookupConfig(DeviceId);
	status = XScuGic_CfgInitialize(&INTCInst, IntcConfig,
	IntcConfig->CpuBaseAddress);
	if(status != XST_SUCCESS) return XST_FAILURE;
	// Call to interrupt setup
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
	(Xil_ExceptionHandler)XScuGic_InterruptHandler,
	&INTCInst);
	Xil_ExceptionEnable();
	// Connect SW1~SW3 interrupt to handler
	status = XScuGic_Connect(&INTCInst,
	SW1_INT_ID,
	(Xil_ExceptionHandler)SW_intr_Handler,
	(void *)1);
	if(status != XST_SUCCESS) return XST_FAILURE;
	status = XScuGic_Connect(&INTCInst,
	SW2_INT_ID,
	(Xil_ExceptionHandler)SW_intr_Handler,
	(void *)2);
	if(status != XST_SUCCESS) return XST_FAILURE;
	// Set interrupt type of SW1~SW3 to rising edge
	IntcTypeSetup(&INTCInst, SW1_INT_ID, INT_TYPE_RISING_EDGE);
	IntcTypeSetup(&INTCInst, SW2_INT_ID, INT_TYPE_RISING_EDGE);
	// Enable SW1~SW3 interrupts in the controller
	XScuGic_Enable(&INTCInst, SW1_INT_ID);
	XScuGic_Enable(&INTCInst, SW2_INT_ID);
	return XST_SUCCESS;
}
int main(void){
	printf("PL int test\n\r");
	IntcInitFunction(INTC_DEVICE_ID);
	while(1);
	return 0;
}
首先解释一下#define INT_CFG0_OFFSET 0x00000C00是个啥:

ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第11张图片

配置软件中断触发方式的寄存器0的相对地址,这个地址+中断号/16×4变为配置这个中断的寄存器的地址,我们这里是61,62,就跳到共享中断的配置寄存器了:
ZYNQ+Vivado2015.2系列(十二)按键中断(PL中断请求)_第12张图片

可以看到11代表上升沿触发:#define INT_TYPE_RISING_EDGE 0x03

中断触发有固定的处理顺序,现阶段我们能看懂函数功能,会改就可以了。

XScuGic  //产生一个中断控制器实例

XScuGic_Config //中断控制器配置实例

XScuGic_LookupConfig  //找到scugic实体

XScuGic_CfgInitialize //初始化scugic

Xil_ExceptionRegisterHandler //Xilinx提供的通用异常处理程序,中断触发之后统一由XScuGic_InterruptHandler先处理,然后在HandlerTable中查找相应的处理函数

Xil_ExceptionEnable //使能异常处理

XScuGic_Connect  //连接到我们自己定义的中断处理函数

XScuGic_Enable //使能我们设立的中断实例


这里我们设置的中断处理函数是:一旦按键按下,串口打印出相关信息:

static void SW_intr_Handler(void *param){
int sw_id = (int)param;
printf("SW%d int\n\r", sw_id);
}

重点:函数的参数是一个回调信息,可以将中断实例信息带给我们的处理函数。


你可能感兴趣的:(ZYNQ)