基于ZYNQ-7000开发板的调试系列(6)

程序中断(3)

该部分是基于基于ZYNQ-7000开发板的调试系列(5)继续展开的。这一部分主要是完成两个外部中断,一个是基于MIO的外部中断,另一个是基于EMIO的外部中断。其中EMIO的外部中断完成整个程序的开关,MIO的外部中断完成流水灯方向的变换。这两个外部中断均由开关触发。相关的资源可以参考基于ZYNQ-7000开发板的调试系列(4)。

这一部分内容也需要重新修改原理图,这里的主要问题是,尽管EMIO的通道不同,但是仍然无法完成中断。我也说不清楚这里到底发生了什么问题。
所以为了保险起见,之后还是需要再多添加一个GPIO口的IP核。

1. 修改Block

这里主要就是把一个GPIO核分为两个,并且为了避免之后出现混淆GPIO0和GPIO1的情况,将无需使能中断的,LED的GPIO口的IP核命名为axi_leds_5bits;将需要使能中断的,KEY的GPIO口的IP核命名为axi_key_1bit。当然还需要注意一点,由于需要使能GPIO的中断,需要将axi_key_1bit的中断的引脚与ZYNQ_PROCESSING_SYSTEM的中断引脚相连,这样才能完成中断的连接。
具体修改完成的Diagram图大概是这个样子的。
基于ZYNQ-7000开发板的调试系列(6)_第1张图片
然后这里就没有其他需要修改的部分了,这里生成Bitstream即可。

2. 编写PS程序

这一部分的程序实际上和上一部分也较为一致,主要就是增多了两个GPIO口的中断,实际上方式和之前的中断都比较类似。

1. EMIO的中断

这一部分我们可以看到,这里的一共有16路中断可以使用,中断需要分别是从61-68,84-91。
[参考自Zynq中 PS接收PL中断]
这篇文章将这一部分的具体操作写得比较详细了,实际上中断序号的查找可以直接在TRM里找到,这里使用XPS_FPGA0_INT_ID,即61号中断。
需要使用到的函数如下:

void XGpio_InterruptEnable(
	XGpio *InstancePtr, 
	u32 Mask
); // GPIO口使能中断
void XGpio_InterruptGlobalEnable(
	XGpio *InstancePtr
); // 使能所有的GPIO口中断,如果该部分不被使能,则所有的GPIO中断均无法使用
void XScuGic_SetPriorityTriggerType(
	XScuGic *InstancePtr, 
	u32 Int_Id,
	u8 Priority, // 优先级分为32档分别是0x00、0x08、0x10、....0xF8,其中0x00优先级最高
	u8 Trigger // 0x01表示为高电平触发、0x03表示为上升沿触发,有且只有这两种触发方式
); // 设置中断的优先级以及触发条件

中断内函数:

void XGpio_InterruptClear(
	XGpio *InstancePtr, 
	u32 Mask
); // 清除GPIO中断标志位
u32 XGpio_DiscreteRead(
	XGpio *InstancePtr, 
	unsigned Channel
); // 读取GPIO的值

具体的实现主要如下:

int Pl_intr_init(){
	XGpio_InterruptEnable(&plKey, PL_KEY_CHANNEL);
	XGpio_InterruptGlobalEnable(&plKey);
	XScuGic_SetPriorityTriggerType(&psGic, PL_KEY_INTR_ID, 0xA0, 0x3);
	status = XScuGic_Connect(&psGic, PL_KEY_INTR_ID, (Xil_ExceptionHandler)psIntrGpioPlHandler, (void *)&plKey);
	if(status != XST_SUCCESS) return XST_FAILURE;
	XScuGic_Enable(&psGic, PL_KEY_INTR_ID);
}

void psIntrGpioPlHandler(void* ref){
	XGpio* ptr = (XGpio*) ref;
	XGpio_InterruptClear(ptr, PL_KEY_CHANNEL);
	usleep(20000);
	if(XGpio_DiscreteRead(&plKey, PL_KEY_CHANNEL) & 0x01){
		if(state0 == 0) state0 = 1;
		else state0 = 0;
	}
}

2. MIO中断

这一部分和刚刚一部分类似,但是需要注意的是,ZYNQ仅仅支持一个MIO的GPIO中断,即52号中断。基于ZYNQ-7000开发板的调试系列(6)_第2张图片
[参考自ZYNQ7000 TRM (Page.230)]
需要使用的函数如下:

void XGpioPs_SetIntrType(
	XGpioPs *InstancePtr, 
	u8 Bank, 
	u32 IntrType, // 0代表电位触发,1代表跳变沿触发
  	u32 IntrPolarity, // 0代表低电平或下降沿有效,1代表高电平或上升沿有效
  	u32 IntrOnAny // 0代表仅对单一的跳变沿触发,1代表所有跳变沿均触发
); // 设置每一位的GPIO口的中断要求,每一位代表一个MIO
void XGpioPs_IntrEnable(
	XGpioPs *InstancePtr, 
	u8 Bank, 
	u32 Mask
); // GPIO口中断使能
void XGpioPs_SetCallbackHandler(
	XGpioPs *InstancePtr, 
	void *CallBackRef,
 	XGpioPs_Handler FuncPointer
 ); // 这里注册中断函数

这里需要注意的是这个函数:

s32  XScuGic_Connect(
	XScuGic *InstancePtr, 
	u32 Int_Id,
	Xil_InterruptHandler Handler, 
	void *CallBackRef
); 

这个函数里面本来是用来注册自己写入的中断函数的,这里需要写入系统自带的中断函数,故而用上面的XGpioPs_SetCallbackHandler函数代替这一函数。

中断内函数:

void XGpioPs_IntrClearPin(
	XGpioPs *InstancePtr, 
	u32 Pin
); // 消除中断标志位
u32 XGpioPs_ReadPin(
	XGpioPs *InstancePtr, 
	u32 Pin
); // 读取一位GPIO口数据

具体实现如下:

void mio_intr_init(){
	XGpioPs_SetIntrType(&psGpio, PS_BANK, 0x800, 0x800, 0x000);
	XGpioPs_IntrEnable(&psGpio, PS_BANK, (1 << PS_KEY));
	status = XScuGic_Connect(&psGic, PS_KEY_INTR_ID,
						(Xil_ExceptionHandler)XGpioPs_IntrHandler,
						(void *)&psGpio);
	if(status != XST_SUCCESS) return XST_FAILURE;
	XGpioPs_SetCallbackHandler(&psGpio, (void *)&psGpio, psIntrGpioPsHandler);
	XScuGic_Enable(&psGic, PS_KEY_INTR_ID);
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}

void psIntrGpioPsHandler(void* ref, u32 bank, u32 status){
	XGpioPs_IntrClearPin(&psGpio, PS_KEY);

	if(XGpioPs_ReadPin(&psGpio, PS_KEY)){
		if(stateA == 0) stateA = 1;
		else stateA = 0;
	}
	usleep(100000);
}

这里面中断函数主要是加了一个按键防抖的机制,用起来其实非常不好用,勉强凑合能用。而且我的PS_KEY的按键有点坏了,这个真的是用点尴尬…

最后是整个项目的所有代码:

#include "xparameters.h"
#include "xgpio.h"
#include "xgpiops.h"

#include "xttcps.h"
#include "xstatus.h"
#include "xscugic.h"
#include "xil_exception.h"

#include "sleep.h"

#define PS_GIC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID

#define LEDG1_INTR_ID XPAR_XTTCPS_0_INTR
#define LEDG2_INTR_ID XPAR_XTTCPS_3_INTR
#define PS_KEY_INTR_ID	XPAR_XGPIOPS_0_INTR
#define PL_KEY_INTR_ID	XPS_FPGA0_INT_ID
#define PS_KEY_INTR_ID 	XPAR_XGPIOPS_0_INTR

#define LED1_INTR_DEVICE_ID XPAR_XTTCPS_0_DEVICE_ID
#define LED2_INTR_DEVICE_ID XPAR_XTTCPS_3_DEVICE_ID

#define LED1_FREQ_HZ	2
#define LED2_FREQ_HZ 	3

#define PL_GPIO_DEVICE_ID XPAR_AXI_LEDS_5BITS_DEVICE_ID
#define PL_KEY_DEVICE_ID XPAR_AXI_KEY_1BIT_DEVICE_ID
#define PS_GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID

#define PL_LED_CHANNEL 1
#define PL_KEY_CHANNEL 1

#define PS_BANK XGPIOPS_BANK0

#define PL_LED0 0x01
#define PL_LED1 0x02
#define PL_LED2 0x04
#define PL_LED3 0x08
#define PL_LED4 0x10

#define PS_LED0 0x09
#define PS_LED1 0x00
#define PS_KEY	0x0B

typedef struct {
	u32 OutputHz;	/* Output frequency */
	XInterval Interval;	/* Interval value */
	u8 Prescaler;	/* Prescaler value */
	u16 Options;	/* Option settings */
} TmrCntrSetup;

volatile u8 led_status = 0x00;
volatile u8 stateA;
volatile u8 state0;
volatile u8 state1;
volatile u8 state2;

volatile u8 plLed;
volatile u8 psLed;

XGpio plGpio;
XGpioPs psGpio;
XGpio plKey;

XTtcPs psTimer1;
XTtcPs psTimer2;

XScuGic	psGic;

static TmrCntrSetup timer1Setup = {LED1_FREQ_HZ, 0, 0, 0};
static TmrCntrSetup timer2Setup = {LED2_FREQ_HZ, 0, 0, 0};


int init();
void run();
int init_Gpio();
int init_Exti();
int init_Gpio_Pl();
int init_Gpio_Ps();
int init_Exti_Timer1();
int init_Exti_Timer2();
int init_Exti_Gic();

void psIntrTimer1Handler(void*);
void psIntrTimer2Handler(void*);
void psIntrGpioPlHandler(void*);
void psIntrGpioPsHandler(void*, u32, u32);

// 0
int main(){
	int status;
	status = init();
	if(status != XST_SUCCESS) return XST_FAILURE;
	run();
	return 0;
}

// 1
int init(){
	int status;
	stateA = 1;
	state0 = 1;
	status = init_Gpio();
	if(status != XST_SUCCESS) return XST_FAILURE;
	status = init_Exti();
	if(status != XST_SUCCESS) return XST_FAILURE;
	return XST_SUCCESS;
}

void run(){
	XTtcPs_Start(&psTimer1);
	XTtcPs_Start(&psTimer2);
	while(1){
		switch(state1){
		case 1: {
			plLed = (plLed & 0x10) + 0x0E;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 2: {
			plLed = (plLed & 0x10) + 0x0D;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 3: {
			plLed = (plLed & 0x10) + 0x0B;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 4: {
			plLed = (plLed & 0x10) + 0x07;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 0: {
			plLed = (plLed & 0x10) + 0x0F;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x00);
		}break;
		default:{
			plLed = (plLed & 0x10) + 0x0F;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}
		}
		switch(state2){
		case 0:{
			plLed = (plLed & 0x0F) + 0x00;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		} break;
		case 1:{
			plLed = (plLed & 0x0F) + 0x10;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
		} break;
		default:{
			plLed = (plLed & 0x0F) + 0x10;
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
			XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		}
		}
	}
}

// 2
int init_Gpio(){
	int status;
	status = init_Gpio_Pl();
	if(status != XST_SUCCESS) return XST_FAILURE;
	status = init_Gpio_Ps();
	if(status != XST_SUCCESS) return XST_FAILURE;

	// PL
	XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL, 0x00);
	XGpio_SetDataDirection(&plKey, PL_KEY_CHANNEL, 0x01);

	// PS
	XGpioPs_SetDirection(&psGpio, PS_BANK, 0x201);
	XGpioPs_SetOutputEnable(&psGpio, PS_BANK, 0x201);

	return XST_SUCCESS;
}

int init_Exti(){
	int status;
	TmrCntrSetup *t1;
	TmrCntrSetup *t2;
	status = init_Exti_Timer1();
	if(status != XST_SUCCESS) return XST_FAILURE;
	status = init_Exti_Timer2();
	if(status != XST_SUCCESS) return XST_FAILURE;
	status = init_Exti_Gic();
	if(status != XST_SUCCESS) return XST_FAILURE;

	// Timer //
	t1 = &timer1Setup;
	t2 = &timer2Setup;
	t1->Options |= (XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE);
	t2->Options |= (XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE);

	XTtcPs_SetOptions(&psTimer1, t1->Options);
	XTtcPs_CalcIntervalFromFreq(&psTimer1, t1->OutputHz, &(t1->Interval), &(t1->Prescaler));
	XTtcPs_SetInterval(&psTimer1, t1->Interval);
	XTtcPs_SetPrescaler(&psTimer1, t1->Prescaler);
	XTtcPs_EnableInterrupts(&psTimer1, XTTCPS_IXR_INTERVAL_MASK);

	XTtcPs_SetOptions(&psTimer2, t2->Options);
	XTtcPs_CalcIntervalFromFreq(&psTimer2, t2->OutputHz, &(t2->Interval), &(t2->Prescaler));
	XTtcPs_SetInterval(&psTimer2, t2->Interval);
	XTtcPs_SetPrescaler(&psTimer2, t2->Prescaler);
	XTtcPs_EnableInterrupts(&psTimer2, XTTCPS_IXR_INTERVAL_MASK);

	// Key Pl
	XGpio_InterruptEnable(&plKey, PL_KEY_CHANNEL);
	XGpio_InterruptGlobalEnable(&plKey);
	XScuGic_SetPriorityTriggerType(&psGic, PL_KEY_INTR_ID, 0xA0, 0x3);

	// Key Ps
	XGpioPs_SetIntrType(&psGpio, PS_BANK, 0x800, 0x800, 0x000);
	XGpioPs_IntrEnable(&psGpio, PS_BANK, (1 << PS_KEY));


	// Gic
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				&psGic);

	status = XScuGic_Connect(&psGic, LEDG1_INTR_ID,
						(Xil_ExceptionHandler)psIntrTimer1Handler,
						(void *)&psTimer1);
	if(status != XST_SUCCESS) return XST_FAILURE;
	XScuGic_Enable(&psGic, LEDG1_INTR_ID);

	status = XScuGic_Connect(&psGic, LEDG2_INTR_ID,
						(Xil_ExceptionHandler)psIntrTimer2Handler,
						(void *)&psTimer2);
	if(status != XST_SUCCESS) return XST_FAILURE;
	XScuGic_Enable(&psGic, LEDG2_INTR_ID);

	status = XScuGic_Connect(&psGic, PL_KEY_INTR_ID,
						(Xil_ExceptionHandler)psIntrGpioPlHandler,
						(void *)&plKey);
	if(status != XST_SUCCESS) return XST_FAILURE;
	XScuGic_Enable(&psGic, PL_KEY_INTR_ID);

	status = XScuGic_Connect(&psGic, PS_KEY_INTR_ID,
						(Xil_ExceptionHandler)XGpioPs_IntrHandler,
						(void *)&psGpio);
	if(status != XST_SUCCESS) return XST_FAILURE;
	XGpioPs_SetCallbackHandler(&psGpio, (void *)&psGpio, psIntrGpioPsHandler);
	XScuGic_Enable(&psGic, PS_KEY_INTR_ID);

	Xil_ExceptionEnable();
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
	return XST_SUCCESS;

}

// 3
int init_Gpio_Pl(){
	XGpio_Config *xGpioCfg;
	xGpioCfg = XGpio_LookupConfig(PL_GPIO_DEVICE_ID);
	if(xGpioCfg == (XGpio_Config *)NULL) return XST_FAILURE;
	XGpio_CfgInitialize(&plGpio, xGpioCfg, xGpioCfg->BaseAddress);

	xGpioCfg = XGpio_LookupConfig(PL_KEY_DEVICE_ID);
	if(xGpioCfg == (XGpio_Config *)NULL) return XST_FAILURE;
	return XGpio_CfgInitialize(&plKey, xGpioCfg, xGpioCfg->BaseAddress);
}

int init_Gpio_Ps(){
	XGpioPs_Config *xGpioCfg;
	xGpioCfg = XGpioPs_LookupConfig(PS_GPIO_DEVICE_ID);
	if(xGpioCfg == (XGpioPs_Config *)NULL) return XST_FAILURE;
	return XGpioPs_CfgInitialize(&psGpio, xGpioCfg, xGpioCfg->BaseAddr);
}

int init_Exti_Timer1(){
	XTtcPs_Config *xGpioCfg;
	xGpioCfg = XTtcPs_LookupConfig(LED1_INTR_DEVICE_ID);
	if(xGpioCfg == (XTtcPs_Config *)NULL) return XST_FAILURE;
	return XTtcPs_CfgInitialize(&psTimer1, xGpioCfg, xGpioCfg->BaseAddress);
} //

int init_Exti_Timer2(){
	XTtcPs_Config *xGpioCfg;
	xGpioCfg = XTtcPs_LookupConfig(LED2_INTR_DEVICE_ID);
	if(xGpioCfg == (XTtcPs_Config *)NULL) return XST_FAILURE;
	return XTtcPs_CfgInitialize(&psTimer2, xGpioCfg, xGpioCfg->BaseAddress);
} //

int init_Exti_Gic(){
	XScuGic_Config *xGpioCfg;
	xGpioCfg = XScuGic_LookupConfig(PS_GIC_DEVICE_ID);
	if(xGpioCfg == (XScuGic_Config *)NULL) return XST_FAILURE;
	return XScuGic_CfgInitialize(&psGic, xGpioCfg, xGpioCfg->CpuBaseAddress);
} //

void psIntrTimer1Handler(void *ref){
	u32 statusEvent;

	statusEvent = XTtcPs_GetInterruptStatus(&psTimer1);
	XTtcPs_ClearInterruptStatus(&psTimer1, statusEvent);

	if(0 != (XTTCPS_IXR_INTERVAL_MASK & statusEvent)){
		if (stateA){
			if(state0){
				switch(state1){
				case 0: state1 = 1; break;
				case 1: state1 = 2; break;
				case 2: state1 = 3; break;
				case 3: state1 = 4; break;
				case 4: state1 = 0; break;
				default: state1 = 0;
				}
			}
			else{
				switch(state1){
				case 0: state1 = 4; break;
				case 1: state1 = 0; break;
				case 2: state1 = 1; break;
				case 3: state1 = 2; break;
				case 4: state1 = 3; break;
				default: state1 = 0;
				}
			}
		}
		else{
			state1 = 0xFF;
		}
	}
}//

void psIntrTimer2Handler(void *ref){
	u32 statusEvent;

	statusEvent = XTtcPs_GetInterruptStatus(&psTimer2);
	XTtcPs_ClearInterruptStatus(&psTimer2, statusEvent);

	if(0 != (XTTCPS_IXR_INTERVAL_MASK & statusEvent)){
		if (stateA){
			switch(state2){
			case 0: state2 = 1; break;
			case 1: state2 = 0; break;
			default: state2 = 0;
			}
		}
		else{
			state2 = 0xFF;
		}
	}
}//

void psIntrGpioPlHandler(void* ref){
	XGpio* ptr = (XGpio*) ref;
	XGpio_InterruptClear(ptr, PL_KEY_CHANNEL);
	usleep(20000);
	if(XGpio_DiscreteRead(&plKey, PL_KEY_CHANNEL) & 0x01){
		if(state0 == 0) state0 = 1;
		else state0 = 0;
	}
}

void psIntrGpioPsHandler(void* ref, u32 bank, u32 status){
	XGpioPs_IntrClearPin(&psGpio, PS_KEY);

	if(XGpioPs_ReadPin(&psGpio, PS_KEY)){
		if(stateA == 0) stateA = 1;
		else stateA = 0;
	}
	usleep(100000);
}

该部分代码和之前没有太大的出入,主要是又修改了一下LED函数,使得其能够配合中断函数使用。至此,关于ZYNQ的中断部分全部完成。

你可能感兴趣的:(FPGA,fpga)