【正点原子ZYNQ领航者7020】AXI GPIO与EMIO联合自行编写的verilog模块完成按键控制LED以及LED闪烁

文章目录

前言

无,仅作记录,不具有参考价值。

1.创建Vivado工程

1.创建XC7Z020CLG400-2项目

略。

2.PS端硬件设置
AXI GPIO信号线连接以及端口连接

双击PL_CLK端口:

EMIO端口引出
3.编写PL端verilog模块

创建sys_top.v,key_debounce.v,led_drive,led_flash_drive.v,并编写对应的代码,如下:

sys_top.v:

c 复制代码
module sys_top(
    input [1:0] key,
    output [2:0] led,

    inout [14:0]DDR_addr,
    inout [2:0]DDR_ba,
    inout DDR_cas_n,
    inout DDR_ck_n,
    inout DDR_ck_p,
    inout DDR_cke,
    inout DDR_cs_n,
    inout [3:0]DDR_dm,
    inout [31:0]DDR_dq,
    inout [3:0]DDR_dqs_n,
    inout [3:0]DDR_dqs_p,
    inout DDR_odt,
    inout DDR_ras_n,
    inout DDR_reset_n,
    inout DDR_we_n,
    inout FIXED_IO_ddr_vrn,
    inout FIXED_IO_ddr_vrp,
    inout [53:0]FIXED_IO_mio,
    inout FIXED_IO_ps_clk,
    inout FIXED_IO_ps_porb,
    inout FIXED_IO_ps_srstb
);

wire PL_CLK;
wire PL_RST_N;
wire [1:0] AXI_GPIO_I_PL_tri_i;
wire [1:0] AXI_GPIO_O_PL_tri_o;
wire [1:0] EMIO_I;
wire [1:0] EMIO_O;

system_wrapper u_system_wrapper(
    .AXI_GPIO_I_PL_tri_i (AXI_GPIO_I_PL_tri_i ),
    .AXI_GPIO_O_PL_tri_o (AXI_GPIO_O_PL_tri_o ),
    .DDR_addr            (DDR_addr            ),
    .DDR_ba              (DDR_ba              ),
    .DDR_cas_n           (DDR_cas_n           ),
    .DDR_ck_n            (DDR_ck_n            ),
    .DDR_ck_p            (DDR_ck_p            ),
    .DDR_cke             (DDR_cke             ),
    .DDR_cs_n            (DDR_cs_n            ),
    .DDR_dm              (DDR_dm              ),
    .DDR_dq              (DDR_dq              ),
    .DDR_dqs_n           (DDR_dqs_n           ),
    .DDR_dqs_p           (DDR_dqs_p           ),
    .DDR_odt             (DDR_odt             ),
    .DDR_ras_n           (DDR_ras_n           ),
    .DDR_reset_n         (DDR_reset_n         ),
    .DDR_we_n            (DDR_we_n            ),
    .EMIO_I              (EMIO_I              ),
    .EMIO_O              (EMIO_O              ),
    .FIXED_IO_ddr_vrn    (FIXED_IO_ddr_vrn    ),
    .FIXED_IO_ddr_vrp    (FIXED_IO_ddr_vrp    ),
    .FIXED_IO_mio        (FIXED_IO_mio        ),
    .FIXED_IO_ps_clk     (FIXED_IO_ps_clk     ),
    .FIXED_IO_ps_porb    (FIXED_IO_ps_porb    ),
    .FIXED_IO_ps_srstb   (FIXED_IO_ps_srstb   ),
    .PL_CLK              (PL_CLK              ),
    .PL_RST_N            (PL_RST_N            )
);


key_debounce u_key0_debounce(
    .sys_clk    (PL_CLK    ),
    .sys_rst_n  (PL_RST_N  ),
    .key        (key[0]        ),
    .key_filter (AXI_GPIO_I_PL_tri_i[0])
);

key_debounce u_key1_debounce(
    .sys_clk    (PL_CLK    ),
    .sys_rst_n  (PL_RST_N  ),
    .key        (key[1]        ),
    .key_filter (AXI_GPIO_I_PL_tri_i[1])
);

led_flash_drive u_led_flash_drive(
    .sys_clk    (PL_CLK    ),
    .sys_rst_n  (PL_RST_N  ),
    .trig_500ms (EMIO_I[0] )
);

led_drive u_led0_drive(
    .sys_clk     (PL_CLK     ),
    .sys_rst_n   (PL_RST_N   ),
    .gpio_output (AXI_GPIO_O_PL_tri_o[0]),
    .led         (led[0]         )
);

led_drive u_led1_drive(
    .sys_clk     (PL_CLK     ),
    .sys_rst_n   (PL_RST_N   ),
    .gpio_output (AXI_GPIO_O_PL_tri_o[1]),
    .led         (led[1]         )
);

led_drive u_led2_drive(
    .sys_clk     (PL_CLK     ),
    .sys_rst_n   (PL_RST_N   ),
    .gpio_output (EMIO_O[1]),
    .led         (led[2]         )
);




endmodule

key_debounce.v:

c 复制代码
module key_debounce(
    input sys_clk,
    input sys_rst_n,
    input key,
    output reg key_filter
);
parameter T = 20; //ms
parameter CNT_MAX = 20'd50_000 * T;
reg key_d0;
reg key_d1;
reg [19:0] cnt;

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        key_d0 <= 1;
        key_d1 <= 1;
    end
    else begin
        key_d0 <= key;
        key_d1 <= key_d0;
    end
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt <= 20'd0;
    else if(key_d0 != key_d1)
        cnt <= CNT_MAX;
    else if(cnt > 0)
        cnt <= cnt - 1;
    else
        cnt <= 0;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        key_filter <= 1;
    else if(cnt == 20'd1)
        key_filter <= key_d1;
    else
        key_filter <= key_filter;
end

endmodule

led_drive.v:

c 复制代码
module led_drive(
    input sys_clk,
    input sys_rst_n,
    input gpio_output,
    output led
);

assign led = gpio_output;

endmodule

led_flash_drive.v:

c 复制代码
module led_flash_drive(
    input sys_clk,
    input sys_rst_n,
    output trig_500ms
);

reg [24:0] cnt_500ms;

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_500ms <= 0;
    else if(cnt_500ms < 25_000_000 - 1)
        cnt_500ms <= cnt_500ms + 1;
    else
        cnt_500ms <= 0;
end

assign trig_500ms = (cnt_500ms == 25_000_000 - 1) ? 1 : 0;

endmodule

模块结构如下:

4.生成bit流文件以及xsa文件

添加并编写pin.xdc:

c 复制代码
#----------------------PL_KEY---------------------------
set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports {key[0]}]
set_property -dict {PACKAGE_PIN K16 IOSTANDARD LVCMOS33} [get_ports {key[1]}]

#----------------------PL_LED---------------------------
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports {led[0]}]
set_property -dict {PACKAGE_PIN L15 IOSTANDARD LVCMOS33} [get_ports {led[1]}]
set_property -dict {PACKAGE_PIN J16 IOSTANDARD LVCMOS33} [get_ports {led[2]}]

bit流生成之后:

导出xsa文件之后:

打开vitis并创建应用工程以及main.c文件。

2.Vitis软件设计

1.在 Vitis 中创建应用工程

略。

2.编写代码

在创建main.c之后编写以下代码:

c 复制代码
#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "xgpio.h"
#include "sleep.h"
#include "xscugic.h"
#include "xil_exception.h"

#define INTC_DEVICE_ID				XPAR_SCUGIC_SINGLE_DEVICE_ID	//中断控制器器件ID
#define AXI_GPIO_DEVICE_ID			XPAR_GPIO_0_DEVICE_ID			//AXI GPIO的器件ID
#define INTC_GPIO_INTERRUPT_ID		XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR //AXI GPIO的中断ID号
#define AXI_GPIO_CHANNEL1			0x1								//AXI GPIO的通道1
#define AXI_GPIO_CHANNEL2			0x2								//AXI GPIO的通道2


#define GPIO_DEVICE_ID 				XPAR_XGPIOPS_0_DEVICE_ID		//GPIO器件ID
#define GPIO_INTERRUPT_ID			XPAR_XGPIOPS_0_INTR				//GPIO的中断ID号
#define EMIO_PL_FLASH_DRIVE			54								//EMIO0引脚编号
#define EMIO_PL_LED2				55								//EMIO1引脚编号

void SetupInterruptSystem_AxiGpio(XScuGic *GicInstancePtr, XGpio *Axi_Gpio, u16 Axi_GpioIntrId);
void IntrHandler_AxiGpio(void *CallBackRef);
void SetupInterruptSystem_Gpio(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId);
void IntrHandler_Gpio(void *CallBackRef);

XScuGic Intc; 		//GIC器件驱动实例
XGpio Axi_Gpio;		//AXI GPIO器件的驱动实例
XGpioPs Gpio;		//GPIO驱动实例
u32 axi_gpio_channel1_val_last;
u32 axi_gpio_channel1_val_new;
u8 led0_val = 0;
u8 led1_val = 0;
u8 led2_val = 0;
int main(void)
{
	//AXI GPIO初始化
	XGpio_Initialize(&Axi_Gpio, AXI_GPIO_DEVICE_ID);
	//设置AXI GPIO按键为输入

	//AXI GPIO中断配置
	SetupInterruptSystem_AxiGpio(&Intc, &Axi_Gpio, INTC_GPIO_INTERRUPT_ID);

	axi_gpio_channel1_val_last = XGpio_DiscreteRead(&Axi_Gpio, AXI_GPIO_CHANNEL1);
	axi_gpio_channel1_val_new = XGpio_DiscreteRead(&Axi_Gpio, AXI_GPIO_CHANNEL1);




	//对GPIO驱动进行初始化
	XGpioPs_Config *ConfigPtr;
	//根据器件ID来去查找器件的配置信息
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	//对GPIO的驱动进行初始化
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
	//设置引脚的方向 0:输入 1:输出
	XGpioPs_SetDirectionPin(&Gpio, EMIO_PL_LED2, 1);
	XGpioPs_SetDirectionPin(&Gpio, EMIO_PL_FLASH_DRIVE, 0);
	//设置输出使能 1:使能输出 0:不使能输出
	XGpioPs_SetOutputEnablePin(&Gpio, EMIO_PL_LED2, 1);
	//GPIO中断配置
	SetupInterruptSystem_Gpio(&Intc, &Gpio, GPIO_INTERRUPT_ID);
	printf("AXI GPIO Interrupt test!\n");
	while(1)
	{
		XGpio_DiscreteWrite(&Axi_Gpio, AXI_GPIO_CHANNEL2, (led0_val << 0) | (led1_val << 1));
		XGpioPs_WritePin(&Gpio, EMIO_PL_LED2, led2_val);
	}
	return 0;
}

void SetupInterruptSystem_AxiGpio(XScuGic *GicInstancePtr, XGpio *Axi_Gpio, u16 Axi_GpioIntrId)
{
	XScuGic_Config *IntcConfig; //GIC配置信息的驱动实例


	//根据中断控制器的器件ID来查找配置信息
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	//根据查找到的配置信息初始化中断控制器
	XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
	//关联AXI GPIO中断处理程序
	XScuGic_Connect(GicInstancePtr, Axi_GpioIntrId, (Xil_ExceptionHandler)IntrHandler_AxiGpio, (void *)Axi_Gpio);
	//设置AXI GPIO的中断优先级和中断触发类型
	XScuGic_SetPriorityTriggerType(GicInstancePtr, INTC_GPIO_INTERRUPT_ID, 160, 0x1);
	//使能AXI GPIO通道1的中断
	XGpio_InterruptEnable(Axi_Gpio, AXI_GPIO_CHANNEL1);
	//关闭AXI GPIO通道2的中断,防止误触
	XGpio_InterruptDisable(Axi_Gpio, AXI_GPIO_CHANNEL2);
	//使能AXI GPIO全局中断
	XGpio_InterruptGlobalEnable(Axi_Gpio);
	//使能GIC的AXI GPIO中断
	XScuGic_Enable(GicInstancePtr, Axi_GpioIntrId);

	//异常初始化
	Xil_ExceptionInit();
	//注册中断请求异常的处理程序
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				GicInstancePtr);
	//使能异常处理
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}

void IntrHandler_AxiGpio(void *CallBackRef)
{
	XGpio *Axi_Gpio = (XGpio *)CallBackRef;

	if(XGpio_InterruptGetStatus(Axi_Gpio) & XGPIO_IR_CH1_MASK)
	{

		printf("AXI GPIO CHANNEL1 Interrupt detect!\n");
		axi_gpio_channel1_val_new = XGpio_DiscreteRead(Axi_Gpio, AXI_GPIO_CHANNEL1);
		if((axi_gpio_channel1_val_new ^ axi_gpio_channel1_val_last) & (1 << 0))
		{
			printf("AXI GPIO CHANNEL1_PIN0 Interrupt detect!\n");
			if((axi_gpio_channel1_val_new & (1 << 0)) == 0)
			{
				printf("LED0 TOGGLE!\n");
				led0_val = !led0_val;
			}
		}
		if((axi_gpio_channel1_val_new ^ axi_gpio_channel1_val_last) & (1 << 1))
		{
			printf("AXI GPIO CHANNEL1_PIN1 Interrupt detect!\n");
			if((axi_gpio_channel1_val_new & (1 << 1)) == 0)
			{
				printf("LED1 TOGGLE!\n");
				led1_val = !led1_val;
			}
		}
		axi_gpio_channel1_val_last = axi_gpio_channel1_val_new;
		//清除对应AXI GPIO通道引发中断的标志位
		XGpio_InterruptClear(Axi_Gpio, AXI_GPIO_CHANNEL1);
	}
	if(XGpio_InterruptGetStatus(Axi_Gpio) & XGPIO_IR_CH2_MASK)
	{
		printf("AXI GPIO CHANNEL2 Interrupt detect!\n");
		//清除对应AXI GPIO通道引发中断的标志位
		XGpio_InterruptClear(Axi_Gpio, AXI_GPIO_CHANNEL2);
	}

}

void SetupInterruptSystem_Gpio(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId)
{
	XScuGic_Config *IntcConfig; //GIC配置信息的驱动实例


	//根据中断控制器的器件ID来查找配置信息
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	//根据查找到的配置信息初始化中断控制器
	XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
	//关联GPIO中断处理程序
	XScuGic_Connect(GicInstancePtr, GpioIntrId, (Xil_ExceptionHandler)IntrHandler_Gpio, (void *)Gpio);
	//设置GPIO的中断优先级和中断触发类型
	XScuGic_SetPriorityTriggerType(GicInstancePtr, XPAR_XGPIOPS_0_INTR, 160, 0x1);
	//设置GPIO中断的触发类型,下降沿
	XGpioPs_SetIntrTypePin(Gpio, EMIO_PL_FLASH_DRIVE, XGPIOPS_IRQ_TYPE_EDGE_RISING);
	//使能GPIO引脚的中断
	XGpioPs_IntrEnablePin(Gpio, EMIO_PL_FLASH_DRIVE);
	//使能GIC的GPIO中断
	XScuGic_Enable(GicInstancePtr, GpioIntrId);

	//异常初始化
	Xil_ExceptionInit();
	//注册中断请求异常的处理程序
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				GicInstancePtr);
	//使能异常处理
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}

void IntrHandler_Gpio(void *CallBackRef)
{
	XGpioPs *Gpio = (XGpioPs *)CallBackRef;
	printf("GPIO Interrupt detect!\n");

	if(XGpioPs_IntrGetStatusPin(Gpio,EMIO_PL_FLASH_DRIVE))
	{
		led2_val = !led2_val;
		printf("500ms coming...\n");
		//清除对应引脚引发中断的标志位
		XGpioPs_IntrClearPin(Gpio, EMIO_PL_FLASH_DRIVE);
	}

}

build之后run as→launch on hardware(Single Application Debug),进行下载。

3.下载验证

略。

总结

这次实验打通了PL端和PS端的信号交互。在以前没学zynq的时候,用的是STM32和FPGA完成比赛项目,两者使用杜邦线连接进行信号交互,用zynq的AXI GPIO模块来进行信号交互不仅可以节省PL端的引脚资源还可以避免使用杜邦线来进行不太稳定的连接。

工程文件分享

ps_axi_gpio_emio_pra.zip

相关推荐
FPGA_小田老师1 个月前
ZYNQ7020笔记:MIO、EMIO、GPIO的区别及应用
fpga开发·gpio·zynq·外设·mio·emio
s09071362 个月前
Zynq 7000使用NAND Flash启动UBIFS文件系统全攻略(基于PetaLinux 2017.4)
zynq·petalinux·ubifs·nand启动
Soari2 个月前
Ziggo-CaaS-Switch软件配置: undefined reference to pthread_create
java·开发语言·fpga开发·tsn·zynq·交换机配置
s09071362 个月前
ZYNQ 软硬件协同踩坑日记:PS写BRAM后,PL端连续4个地址读出相同数据的原因与解决办法
fpga开发·zynq·硬件设计
weixin_450907282 个月前
[ZYNQ Linux] 端侧AI模型部署(DPU)
zynq
s09071362 个月前
ZYNQ7000 AXI DMA 接收中断(S2MM_introut)全解析:从硬件原理到Linux驱动开发
linux·驱动开发·dma·zynq
日拱一卒的小田2 个月前
ZYNQ学习笔记1-裸机-PS端中断配置、IO配置及PS/PL AXI交互
io·zynq·中断
s09071363 个月前
Zynq-7000 PetaLinux 千兆网卡 Link UP 但无法 Ping 通的终极排查与解决(以 KSZ9031 为例)
linux·skew·zynq·ksz9031·ping不通
weixin_450907283 个月前
[ZYNQ Linux] V4L2视频驱动
zynq