Zynq(3):AXI GPIO按键控制LED

AXI GPIO按键控制LED

上文中是通过EMIO实现PS端与PL端的交互,而PS与PL最主要的连接方式则是由一组AXI接口。AXI 互联接口作为 ZYNQ PS 和 PL 之间的桥梁,能够使两者协同工作,进而形成一个完整的、高度集成的系统。

简介

AXI GPIO IP 核为 AXI 接口提供了一个通用的输入/输出接口。与 PS 端的 GPIO 不同,AXI GPIO 是一个软核(Soft IP),即 ZYNQ 芯片在出厂时并不存在这样的一个硬件电路,而是由用户通过配置 PL 端的逻辑资源来实现的一个功能模块。而 PS 端的 GPIO 是一个硬核(Hard IP),它是一个生产时在硅片中实现的功能电路。AXI GPIO 可以配置成单通道或者双通道,每个通道的位宽可以单独设置。另外通过打开或者关闭三态缓冲器,AXI GPIO 的端口还可以被动态地配置成输入或者输出接口。其顶层模块的框图如下所示:
Zynq(3):AXI GPIO按键控制LED_第1张图片

从上图可以看到,模块的左侧实现了一个 32 位的 AXI4-Lite 从接口,用于主机访问 AXI GPIO 内部各通道的寄存器。当右侧接口输入的信号发生变化时,模块还能向主机产生中断信号。不过只有在配置IP 核时选择“使能中断”,才会启用模块的中断控制功能。

Block Design介绍

Zynq(3):AXI GPIO按键控制LED_第2张图片

ZYNQ7ProcessingSystem中一些接口信号是用于PS使用AXI接口与PL端进行通信是所需要的信号。

  1. M_AXI_GP0是通用(General Purpose)AXI接口,它包含了一组信号。首字母 M 表示 PS 作为主机(Master),PL 中的外设作为从机(Slave).而左侧的M_AXI_GP0_ACLK 是这个接口的全局时钟信号,它是一个输入信号,M_AXI_GP0 接口的所有信号都是在这个全局时钟的上升沿采样的。
  2. FCLK_CLK0是 PS 输出的时钟信号,它将作为 PL 中外设模块的时钟源。这个时钟由 PS 中的 IO PLL产生,频率范围可以从 0.1 到 250MHz,在配置 ZYNQ7 PS 的时候,该时钟默认为 50MHz。
  3. FCLK_RESET0_N 是由 PS 输出到 PL 的全局复位信号,低电平有效。
  4. IRQ_F2P[0:0]是由 PL 输出到 PS 的中断信号。
    在Block Design中添加PL端的AXI GPIO IP 核,并对其进行配置(本文想实现PL端按键控制PS端LED,选择数据位宽为1位),勾选中断。
    Zynq(3):AXI GPIO按键控制LED_第3张图片

进行Run Connection Automation操作。
Zynq(3):AXI GPIO按键控制LED_第4张图片

此时Vivado自动添加了两个IP核,分别是 AXI 互联( AXI Interconnect)和处理器系统复位(Processor System Reseet)。

  1. AXI Interconnect IP 核用于将一个(或多个)AXI 存储器映射的主器件连接到一个(或多个)存储器映射的从器件。在这里我们解释一下这个术语——互联(Interconnect):互联实际上是一个开关,它管理并指挥所连接的 AXI 接口之间的通信。图 5.3.8 中橙色高亮的两组信号线表明,在这个设计中,AXI 互联实现了由主器件(ZYNQ7 PS)到从器件(AXI GPIO)一对一的连接。它也可以实现一对多、多对一以及多对多的 AXI 接口连接。
  2. Processor System Reseet IP 核为整个处理器系统提供复位信号。它会处理输入端的各种复位条件,并在输出端产生相应的复位信号。Processor System Reseet 接收 ZYNQ7 PS 输出的异步复位信号FCLK_RESET0_N,然后产生一个同步到 PL 时钟源 FCLK_CLK0 的复位信号 peripheral_aresetn,用于复位PL 端的各外设模块。

PL 端所有外设模块的时钟接口都连接到了 ZYNQ7 PS 输出的时钟信号FCLK_CLK0 上。需要注意的是,该时钟同样连接到了 PS 端 M_AXI_GP0_ACLK 端口,作为 AXI GP 接口的全局时钟信号。并将AXI GPIO IP的中断和ZYNQ7 Processing System的中断连接起来。

由于AXI GPIO按键中断和EMIO按键其实都差不多。这里就不过多的介绍,笔者也是按照官方的Example和EMIO按键中断完成程序实验。

Example:

#include "stdio.h"
#include "xscugic.h"
#include "xgpiops.h"
#include "xgpio.h"
#include "xparameters.h"
#include "sleep.h"
#include "xil_exception.h"



#define AXI_GPIO_DEVICE_ID 	XPAR_GPIO_0_DEVICE_ID
#define PS_GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID
#define SCUGIC_DEVICE_ID	XPAR_SCUGIC_0_DEVICE_ID
#define AXI_GPIO_INTR_ID  	XPAR_FABRIC_GPIO_0_VEC_ID

#define PS_LED0_MIO0			0
#define KEY_CHANNEL             1
#define KEY_MASK                XGPIO_IR_CH1_MASK

int PerInstInit(XGpioPs *PsGpioInst,XGpio *AxiGpioInst,XScuGic *AxiIntr,u16 PsGpioDeviceId,u16 AxiGpioDeviceId,u16 ScugicDeviceId);
int SetupInterruptSystem(XScuGic *AxiIntr,XGpio *AxiGpio,u16 AxiGpioIntrId);
void AxiGpioHandler(void *CallBackRef);


int led_value = 1;
XGpioPs PsGpio;
XGpio AxiGpio;
XScuGic AxiIntr;
int main(void)
{
	int status;

	status = PerInstInit(&PsGpio, &AxiGpio, &AxiIntr, PS_GPIO_DEVICE_ID, AXI_GPIO_DEVICE_ID, SCUGIC_DEVICE_ID);
	if(status != XST_SUCCESS)
	{
		printf("Initialize Failure!\n");
		return XST_FAILURE;
	}
	XGpioPs_SetDirectionPin(&PsGpio,PS_LED0_MIO0, 0x1);
	XGpioPs_SetOutputEnablePin(&PsGpio,PS_LED0_MIO0,0x1);
	XGpioPs_WritePin(&PsGpio,PS_LED0_MIO0,led_value);

	XGpio_SetDataDirection(&AxiGpio, KEY_CHANNEL, 0x1);
	SetupInterruptSystem(&AxiIntr,&AxiGpio,AXI_GPIO_INTR_ID);

	while(1);
	return XST_SUCCESS;

}


int PerInstInit(XGpioPs *PsGpioInst,XGpio *AxiGpioInst,XScuGic *AxiIntr,u16 PsGpioDeviceId,u16 AxiGpioDeviceId,u16 ScugicDeviceId)
{
	int status;
	XScuGic_Config *ScugicConfigPtr;
	XGpioPs_Config *PsGpioConfigPtr;
	XGpio_Config *AxiGpioConfigPtr;
	PsGpioConfigPtr  = XGpioPs_LookupConfig(PsGpioDeviceId);
	if(NULL == PsGpioConfigPtr)
		return XST_FAILURE;
	AxiGpioConfigPtr = XGpio_LookupConfig(AxiGpioDeviceId);
	if(NULL == AxiGpioConfigPtr)
		return XST_FAILURE;
	ScugicConfigPtr  = XScuGic_LookupConfig(ScugicDeviceId);
	if(NULL == ScugicConfigPtr)
		return XST_FAILURE;
	status = XGpioPs_CfgInitialize(PsGpioInst, PsGpioConfigPtr, PsGpioConfigPtr->BaseAddr);
	if(status != XST_SUCCESS)
		return XST_FAILURE;
	status = XGpio_CfgInitialize(AxiGpioInst,AxiGpioConfigPtr,AxiGpioConfigPtr->BaseAddress);
	if(status != XST_SUCCESS)
		return XST_FAILURE;
	status = XScuGic_CfgInitialize(AxiIntr, ScugicConfigPtr, ScugicConfigPtr->CpuBaseAddress);
	if(status != XST_SUCCESS)
		return XST_FAILURE;
	return XST_SUCCESS;
}


int SetupInterruptSystem(XScuGic *AxiIntr,XGpio *AxiGpio,u16 AxiGpioIntrId)
{
	int status;
//	1.
	Xil_ExceptionInit();
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler)XScuGic_InterruptHandler,	AxiIntr);
//	2.
	status = XScuGic_Connect(AxiIntr, AxiGpioIntrId, AxiGpioHandler, (void*)AxiGpio);
	if(status != XST_SUCCESS)
		return XST_FAILURE;
//	3.
	XScuGic_Enable(AxiIntr, AxiGpioIntrId);
//  4.
	XGpio_InterruptEnable(AxiGpio,KEY_MASK);
	XGpio_InterruptGlobalEnable(AxiGpio);
	XScuGic_SetPriorityTriggerType(AxiIntr, AxiGpioIntrId, 0,1);
	return XST_SUCCESS;
}



void AxiGpioHandler(void *CallBackRef)
{
	int key_value = 1;
	XGpio *GpioPtr = (XGpio *)CallBackRef;
	printf("Interrupt Detected!\n");
	XGpio_InterruptDisable(GpioPtr, KEY_MASK);
	key_value = XGpio_DiscreteRead(GpioPtr, KEY_CHANNEL);
	if(key_value == 0)
	{
		led_value = ~led_value;
		XGpioPs_WritePin(&PsGpio,PS_LED0_MIO0,led_value);
	}
	usleep(200000);
	XGpio_InterruptClear(GpioPtr, KEY_MASK);
	XGpio_InterruptEnable(GpioPtr, KEY_MASK);

}

由于笔者之前并没有学过单片机或者是ARM,所以笔者学到这里已经逐渐产生了疑问,这里先记录下来。看看之后有没有办法搞清楚,也欢迎网友给予我想要的答案,大家一起进步。

Q:**s32 XScuGic_Connect(XScuGic InstancePtr, u32 Int_Id,Xil_InterruptHandler Handler, void CallBackRef)
该函数的工作机制到底是什么,也就是中断在操作系统中是如何返会原程序的。Handler和CallBackRef这两者是怎么联系的?

Q:**void XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,u8 Priority, u8 Trigger)**在本次实验中,由于AXI中断属于共享外设中断(SPI),
中断类型设置的High level sensitive.而硬件电路中按键连接方式如下图:
Zynq(3):AXI GPIO按键控制LED_第5张图片

英文解释是高电平敏感(好像并不是我想的只要检测的IO口电平是高电平就产生一个中断),我实际去观察只有我按下按键(IO电平由高至低),才会产生中断。

Date : 2022-1-5
Author : QImua
City : Wuxi

你可能感兴趣的:(#,ZYNQ7020,ARM-A9,fpga开发)