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

PS和PL之间的交互,怎么都逃不过中断,稍微大型的数字系统,PS和PL之间配合使用就需要中断作为桥梁。

本文通过按键发起中断请求尝试学习PL请求中断的处理机制。

板子用的是zc702。

ZYNQ的中断

ZYNQ是中断类系统框图:

20180114205851676

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

1.软件中断(SGISoftware generatedinterrupts,中断号0-15)(16–26 reserved) :

被路由到一个或者两个CPU上,通过写ICDSGIR寄存器产生SGI.

2.私有外设中断(PPIprivate peripheralinterrupts ,中断号27-31):

每个CPU都有一组PPI,包括全局定时器、私有看门狗定时器、私有定时器和来自PL的FIQ/IRQ.

3.共享外设中断(SPIshared peripheralinterrupts,中断号32-95):

由PS和PL上的各种I/O控制器和存储器控制器产生,这些中断信号被路由到相应的CPU.


中断控制器****(GIC,generic interrupt controller )

用于集中管理从PS和PL产生的中断信号的资源集合。

控制器可以使能、关使能、屏蔽中断源和改变中断源的优先级,并且会将中断送到对应的CPU中,CPU通过私有总线访问这些寄存器。

PL和PS之间的中断有:

20180114211504706

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

20180114211616090

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

20180114211852004

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

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

20180114212330234

按键中断硬件部分

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

20180114213415401

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

20180114213736015

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

20180114213914944

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

20180114214208608

连接完是这样的:

20180114214233074

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

添加约束如下:

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 <stdio.h>
#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是个啥:

20180114220727528

配置软件中断触发方式的寄存器0的相对地址,这个地址+中断号/16×4变为配置这个中断的寄存器的地址,

我们这里是61,62,就跳到共享中断的配置寄存器了:

20180114221153825

可以看到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);  
}  

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

posted @ 2024-03-28 16:26  L707  阅读(130)  评论(0编辑  收藏  举报