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

程序中断(1)

这一部分用的都是基于PS的中断。
该部分中断分为两部分,

  1. 基于PL外设的中断,以方便之后PS与PL之后的同步使用。
  2. 基于PS内部的中断,比如定时器触发中断等等。

基于三种触发方式的流水灯

该部分最后会基于之前流水灯的实验继续展开:

  1. 利用PS内部的定时器触发中断完成流水灯流水的过程。
  2. 利用PL的按钮触发中断完成流水灯功能的开关。
  3. 利用PS的按钮触发中断完成流水灯方向的调整。

需要使用的GPIO口有以下4组:

  1. PL端LED: A5、A6、A7、B8、T16
  2. PS端LED:C19、G17
  3. PL端KEY:AB12
  4. PS端KEY:B19

和上一次的实验一样:由A5、A6、A7、B8、C19组成一组流水灯;由T16、G17组成另外一组。
并且C19 对应的是MIO9、G17对应的是MIO0、B19对应的是MIO11。

这里主要是先完成第一部分,即完成一个计时器控制一组流水灯。

1. 创建Block

1. 创建一个ZYNQ Processing System

这里需要修改的设置如下:

  1. Peripheral I/O Pins 使能GPIO MIO。基于ZYNQ-7000开发板的调试系列(4)_第1张图片
  2. DDR Configuration 此处按照板卡的类型选择自己DDR的型号。基于ZYNQ-7000开发板的调试系列(4)_第2张图片
  3. Interrupts 使能中断。
    基于ZYNQ-7000开发板的调试系列(4)_第3张图片
    这里针对中断还需要一点说明,具体提供的中断如下图所示。基于ZYNQ-7000开发板的调试系列(4)_第4张图片
    主要分为3类:
    1 Software Interrupts(软件中断)。
    2 Private Peripheral Interrupts(私有外设中断)。PPI
    3 Shared Peripheral Interrupts(共享外设中断)。SPI

该3类中断均由Generic Interrupt Controller(中断控制器)进行控制,
[参考ZYNQ7000 TRM (Page.255-256)]
[参考Zynq中 PS接收PL中断]

2. 创建一个AXI GPIO

这里主要由两个部分需要修改:

  1. 使能6个引脚(A5、A6、A7、B8、T16、AB12)。
  2. 使能中断。
    实际上没有必要,但是这部分我还是打算按照两个通道去使能GPIO口。基于ZYNQ-7000开发板的调试系列(4)_第5张图片
    自动连接后,生成HDL Wrapper 文件,具体大概是这个样子。这里把GPIO口命名了一下,否则有点小乱。
    基于ZYNQ-7000开发板的调试系列(4)_第6张图片

2. 建立管脚约束文件

这一部分需要6个GPIO口。
具体文件如下:

set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports key_tri_i]

set_property PACKAGE_PIN A5 [get_ports {leds_tri_o[0]}]
set_property PACKAGE_PIN A7 [get_ports {leds_tri_o[1]}]
set_property PACKAGE_PIN A6 [get_ports {leds_tri_o[2]}]
set_property PACKAGE_PIN B8 [get_ports {leds_tri_o[3]}]
set_property PACKAGE_PIN T16 [get_ports {leds_tri_o[4]}]
set_property PACKAGE_PIN AB12 [get_ports key_tri_i]

之后生成Bitstream文件后,导出硬件平台信息,开始编写ps程序。
基于ZYNQ-7000开发板的调试系列(4)_第7张图片

3. PS程序编写

这里首先还是应该先叙述一下中断需要使用的资源与具体的流程,主要有:

  1. 初始化Xilinx中断
  2. 配置中断控制器
  3. 配置中断触发信号
  4. 配置中断操作函数
  5. 连接中断函数与触发信号

首先先引用一下必须引入的模块

#include "xparameters.h" // 常数模块
#include "xgpio.h" // PL GPIO模块
#include "xgpiops.h" // PS GPIO模块
#include "xscutimer.h" // 计时器模块
#include "xscugic.h" // 中断控制器模块
#include "xil_exception.h" // 中断模块

1. 内部定时器中断(1组)

由于有两组流水灯,这一部分需要两个定时器,两组中断。所以这里没有办法使用单独的29号中断,因为需要不止一个中断信号。这里仅仅是略微修改一下下xscutimer_intr_example的例程,首先完成一组流水灯。
基于ZYNQ-7000开发板的调试系列(4)_第8张图片
[参考ZYNQ7000 TRM (Page.229)]
具体需要用的函数比较多,一股脑列出来会比较乱,这里分一下类:

  1. 与GPIO相关的:
    初始化相关:
// PL GPIO口初始化
XGpio_Config *XGpio_LookupConfig(
	u16 DeviceId
); 
int XGpio_CfgInitialize(
	XGpio * InstancePtr, 
	XGpio_Config * Config,
	UINTPTR EffectiveAddr
);

//PS GPIO口初始化
XGpioPs_Config *XGpioPs_LookupConfig(
	u16 DeviceId
);
s32 XGpioPs_CfgInitialize(
	XGpioPs *InstancePtr, 
	XGpioPs_Config *ConfigPtr,
	u32 EffectiveAddr
);

GPIO口配置

//PL GPIO口配置
void XGpio_SetDataDirection(
	XGpio *InstancePtr, 
	unsigned Channel,
	u32 DirectionMask
);

//PS GPIO口配置
void XGpioPs_SetDirection(
	XGpioPs *InstancePtr, 
	u8 Bank, 
	u32 Direction
);
void XGpioPs_SetOutputEnable(
	XGpioPs *InstancePtr, 
	u8 Bank, 
	u32 OpEnable
);

GPIO口写入

// PL GPIO写入
void XGpio_DiscreteWrite(
	XGpio *InstancePtr, 
	unsigned Channel, 
	u32 Mask
);

// PS GPIO口写入
void XGpioPs_WritePin(
	XGpioPs *InstancePtr, 
	u32 Pin, 
	u32 Data
);
  1. 与定时器相关的:
    定时器初始化
XScuTimer_Config *XScuTimer_LookupConfig(
	u16 DeviceId
);
s32 XScuTimer_CfgInitialize(
	XScuTimer *InstancePtr,
	XScuTimer_Config *ConfigPtr, 
	u32 EffectiveAddress
);

定时器配置

void XScuTimer_EnableAutoReload(
	XScuTimer *InstancePtr
); // 设定定时器是否在一次计时完成后自动重新装载
void XScuTimer_LoadTimer(
	XScuTimer *InstancePtr,
	u32 value
); // 设定计时器时间
void XScuTimer_EnableInterrupt(
	XScuTimer *InstancePtr
); // 设定计时器支持中断

这三个函数的本质就是往寄存器里直接写入数值,但是经过层层包装。

static INLINE void Xil_Out32(
	UINTPTR Addr, 
	u32 Value
);

定时器启动

void XScuTimer_Start(
	XScuTimer *InstancePtr
);
  1. 中断控制器相关的:
    中断控制器初始化:
XScuGic_Config *XScuGic_LookupConfig(
	u16 DeviceId
);
s32  XScuGic_CfgInitialize(
	XScuGic *InstancePtr, 
	XScuGic_Config *ConfigPtr,
	u32 EffectiveAddr
);

中断控制器

extern void Xil_ExceptionRegisterHandler(
	u32 Exception_id,
	Xil_ExceptionHandler Handler,
	void *Data
); // 注册中断
s32  XScuGic_Connect(
	XScuGic *InstancePtr,
	u32 Int_Id,
	Xil_InterruptHandler Handler,
	void *CallBackRef
); // 将中断与中断回调函数连接起来
void XScuGic_Enable(
	XScuGic *InstancePtr, 
	u32 Int_Id
); // 使能中断控制器
void Xil_ExceptionEnable(
); // 使能中断

其中,void Xil_ExceptionEnable() 本质上是将通用寄存器中的中断参量传到状态寄存器上。

程序编写:

#include "xparameters.h"
#include "xgpio.h"
#include "xgpiops.h"
#include "xscutimer.h"
#include "xscugic.h"
#include "xil_exception.h"

#define PS_GIC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR

#define PS_TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_LOAD_VALUE (XPAR_PS7_CORTEXA9_0_CPU_CLK_FREQ_HZ/2 - 1)

#define PL_GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
#define PS_GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID

#define PL_LED_CHANNEL 1
#define PL_KEY_CHANNEL 2

#define PS_BANK XGPIOPS_BANK0

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

#define PS_LED0 0x09

volatile u8 led_status = 0x00;
volatile u8 state1;

XGpio plGpio;
XGpioPs psGpio;
XScuTimer psTimer;
XScuGic	psGic;

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

void psIntrTimerHandler(void*);


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

// 1
int init(){
	int status;
	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(){
	XScuTimer_Start(&psTimer);
	while(1){
		switch(state1){
		case 1: {
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0E);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 2: {
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0D);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 3: {
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0B);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 4: {
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x07);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		}break;
		case 0: {
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0F);
			XGpioPs_WritePin(&psGpio, PS_LED0, 0x00);
		}break;
		default:{
			XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0F);
			XGpioPs_WritePin(&psGpio, PS_LED0, 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(&plGpio, 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;
	status = init_Exti_Timer();
	if(status != XST_SUCCESS) return XST_FAILURE;
	status = init_Exti_Gic();
	if(status != XST_SUCCESS) return XST_FAILURE;

	// Timer //
	XScuTimer_EnableAutoReload(&psTimer);
	XScuTimer_LoadTimer(&psTimer, TIMER_LOAD_VALUE);
	XScuTimer_EnableInterrupt(&psTimer);

	// Gic
	XScuGic* ptrGic = &psGic;
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				&psGic);
	status = XScuGic_Connect(ptrGic, TIMER_IRPT_INTR,
						(Xil_ExceptionHandler)psIntrTimerHandler,
						(void *)ptrGic);
	if(status != XST_SUCCESS) return XST_FAILURE;
	XScuGic_Enable(&psGic, TIMER_IRPT_INTR);
	Xil_ExceptionEnable();
	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;
	return XGpio_CfgInitialize(&plGpio, 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_Timer(){
	XScuTimer_Config *xGpioCfg;
	xGpioCfg = XScuTimer_LookupConfig(PS_TIMER_DEVICE_ID);
	if(xGpioCfg == (XScuTimer_Config *)NULL) return XST_FAILURE;
	return XScuTimer_CfgInitialize(&psTimer, xGpioCfg, xGpioCfg->BaseAddr);
} //

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 psIntrTimerHandler(void *ref){
	XScuTimer *ptr = (XScuTimer *) ref;
	if(XScuTimer_IsExpired(ptr)){
		XScuTimer_ClearInterruptStatus(ptr);
		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;
		}
	}
}//

其实就是xscutimer_intr_example.c,只是重新整理修改了里面一丢丢的东西。
下一部分主要是如何使用两组定时器控制两组中断。

你可能感兴趣的:(FPGA)