3 - ZYNQ GPIO

1 GPIO基本概念

在ZYNQ中,GPIO(General Purpose Input/Output,通用输入输出)接口是非常重要的一个部分,它提供了与外部设备交互的能力。

在ZYNQ中,GPIO接口主要分为MIO(Multiplexed I/O,多路复用I/O)和EMIO(Extended Multiplexed I/O,扩展多路复用I/O)两种类型,共118个引脚,分布在4个Bank(0-3)中,如下图所示:

在这里插入图片描述

1.1 MIO-EMIO简介

在ZYNQ中,MIO-EMIO所处位置及连接关系如下图所示:

在这里插入图片描述

  • MIO基本介绍

    • MIO是ZYNQ PS提供的一组引脚,主要用于连接各种标准外设,如SPI、UART、I2C、SD卡等
    • MIO引脚分布在GPIO的Bank 0和Bank 1上,共54个引脚
      • Bank 0包含32个引脚
      • Bank 1包含22个引脚
    • MIO与外设直接连接,PS可直接操作,无需PL参与
  • EMIO基本介绍

    • EMIO是ZYNQ PS提供的一组引脚,主要用于对MIO进行扩展以连接更多外设
    • EMIO引脚分布在GPIO的Bank 2和Bank 3上,共64个引脚
      • Bank 2包含32个引脚
      • Bank 3包含32个引脚
    • EMIO通过PL进行扩展
      • 可以根据需要扩展出1-64个EMIO
      • 可以根据需要将EMIO与PL的任意可用User IO相绑定
    • EMIO通过PL扩展后与外设连接,需要PL对EMIO进行引脚约束,并生成比特流文件烧写PL后,PS方可通过EMIO对外设进行操作

1.2 MIO-EMIO连接

MIO-EMIO在PS内部的连接情况如下图所示:

在这里插入图片描述

I/O Peripherals和MIO-EMIO的连接关系:

序号名称MIOEMIO数量
1Quad SPI FlashYesNo1
2SRAM/NOR FlashYesNo1
3NAND FlashYesNo1
4USBYesNo1
5GigEYesYes2
6SD SDIOYesYes2
7UARTYesYes2
8CANYesYes2
9I2CYesYes2
10SPIYesYes2
11TTCYesYes2
12SWDTYesYes1
13PJTAGYesYes1
14TPIUYesYes1
15GPIOYesYesN/A

特别说明

  • Yes表示可与MIO或EMIO连接
  • No表示不可与MIO或EMIO连接

1.3 MIO-EMIO路由

I/O Peripherals与MIO-EMIO的路由关系如下图所示:

在这里插入图片描述

特别说明

  • 在ug585中只提到SPI连接到EMIO时最高时钟为25MHz,对于SDIO未发现相关限制(初步猜测,当把外设连接到EMIO时,最高时钟频率为25MHz)
  • Ethernet接口连接到EMIO时,只能是MII和GMII接口,需要在PL内部使用桥接电路转换为RMII、RGMII或SGMII接口,如使用Xilinx提供的Ethernet PHY MII to Reduced MII核
  • MIO-EMIO路由再次体现出有些外设可以连接到MIO和EMIO,而有些外设只能连接到MIO

1.4 MIO-EMIO配置

在ug585中,MIO与外设的配置关系如下图所示:

在这里插入图片描述

在Vivado ZYNQ7 Processing System IP中,MIO-EMIO与外设的配置关系如下图所示:

在这里插入图片描述

特别说明

  • MIO是多路复用的,即一个MIO可以被不同外设使用,但同时只能被一个外设使用
  • MIO与外设的对应关系不是任意的,是有一定的配置规则的,一种外设只能使用一些特定位置的MIO
  • MIO-EMIO配置再次体现出有些外设可以连接到MIO和EMIO,而有些外设只能连接到MIO

2 GPIO控制寄存器

GPIO的功能有三种:输入、输出和中断。MIO和EMIO在功能上几乎相同,唯一的区别是EMIO通过PL扩展后与外设连接,而MIO直接与外设连接。需要特别注意的是,MIO7和MIO8只能做输出,这在ZYNQ7000-MIO与EMIO详解中有说明。

GPIO引脚的功能是通过PS侧的寄存器来控制,寄存器为32位,控制一个GPIO Bank的32个MIO/EMIO引脚(Bank 1除外)。

在这里插入图片描述

2.1 输入/输出控制寄存器

  • DATA_RO(Data Read Only,数据只读寄存器)
    • 读取GPIO引脚上的当前值
    • 不论GPIO引脚被配置为输入还是输出,DATA_RO寄存器都能正确反映该引脚上的电平状态
  • DATA
    • 当GPIO引脚被配置为输出模式时,DATA寄存器用于设置该引脚的电平状态
    • 通过向DATA寄存器写入相应的值,可以控制GPIO引脚输出高电平或低电平,从而实现对外部设备的控制
  • MASK_DATA_LSW(MASK DATA Low Significant Word,数据掩码低16位寄存器)
    • 选择性更改:允许有选择地对GPIO的低16位引脚进行输出值的设置和屏蔽;这意味着,开发者可以精确地控制哪些引脚需要更改输出,而无需重新写入整个Bank的输出值
    • 屏蔽功能:对于那些不需要更改的位,允许它们保持其先前的值不变,这避免了不必要的读-修改-写序列,提高了系统效率和响应速度
    • 提高灵活性:通过与DATA和MASK_DATA_MSW寄存器的结合使用,开发者可以根据需要,灵活地设置和屏蔽GPIO引脚的输出值
  • MASK_DATA_MSW(MASK DATA Most Significant Word,数据掩码高16位寄存器)
    • 与MASK_DATA_LSW类似,但MASK_DATA_MSW控制的是GPIO Bank的高16位
  • DIRM(Direction Mode Register,方向模式寄存器)
    • 控制GPIO引脚的方向,即决定引脚是作为输入引脚还是输出引脚
    • 设置为1时,对应的GPIO引脚被配置为输出模式
    • 设置为0时,对应的GPIO引脚被配置为输入模式
  • OEN(Output Enable Register,输出使能寄存器)
    • 当GPIO引脚被配置为输出引脚时,OEN寄存器控制该引脚是否输出信号
    • 设置为1时,表示对应的GPIO引脚输出使能,即该引脚可以输出信号
    • 设置为0时,表示对应的GPIO引脚输出关闭,即该引脚处于三态

2.2 中断控制寄存器

  • INT_TYPE(Interrupt Type,中断类型寄存器)
    • 用于控制GPIO中断是边沿敏感还是电平敏感
  • INT_POLARITY(Interrupt Polarity,中断极性寄存器)
    • 用于控制中断是低电平有效还是高电平有效(或下降沿敏感或上升沿敏感)
  • INT_ANY(Interrupt Any,双边沿寄存器)
    • 当INT_TYPE寄存器被设置为边沿敏感时(无论是上升沿还是下降沿),用于控制中断是否对GPIO信号的上升沿和下降沿都敏感
  • INT_EN(Interrupt Enable,中断使能寄存器)
    • 启用(或取消屏蔽)GPIO引脚的中断
    • GPIO引脚引脚上的信号满足中断触发条件时,能够产生中断请求并触发相应的中断服务例程(ISR)
  • INT_DIS(Interrupt Disable,中断禁用寄存器)
    • 禁用(或屏蔽)GPIO引脚的中断
    • GPIO引脚的中断信号将被屏蔽,即使该引脚上的信号满足中断触发条件,也不会产生中断请求,从而防止中断服务例程(ISR)被不必要地触发
  • INT_MASK(Interrupt Mask,中断屏蔽寄存器)
    • 只读寄存器,用于显示GPIO引脚的中断是否被屏蔽
    • GPIO引脚的中断屏蔽状态由INT_EN和INT_DIS寄存器控制
      • INT_EN寄存器写1,启用(或取消屏蔽)GPIO引脚的中断
      • INT_DIS寄存器写1,禁用(或屏蔽)GPIO引脚的中断
  • INT_STAT(Interrupt Status,中断状态寄存器)
    • 中断状态指示:INT_STAT寄存器用于指示哪些GPIO引脚已经触发中断; GPIO引脚引脚上的信号满足中断触发条件时,能够触发中断,同时相应的中断状态位会被置位
    • 中断状态查询:通过读取INT_STAT寄存器的值,可以查询哪些GPIO引脚的中断请求已被触发,从而进行相应的中断处理
    • 中断状态清除:通常在进入中断服务例程(ISR)后,向INT_STAT寄存器相应的位写入1清除中断状态,以确保中断状态被正确清除,从而允许系统响应后续的中断事件

2.3 中断触发设置

GPIO引脚的中断触发方式是由INT_TYPE、INT_POLARITY和INT_ANY三个寄存器共同决定的,如下图所示:

在这里插入图片描述

特别说明

  • GPIO所有引脚共享一个中断,中断号是52

3 GPIO在Vivado SDK中的使用

实验内容

  • 按下PL侧按键,通过EMIO向PS发送中断
  • PS收到中断后,通过EMIO控制PL侧LED灯亮灭
/***************************** Include Files ********************************/

#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include <xil_printf.h>

/************************** Constant Definitions ****************************/
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID
#define GPIO_INTR_ID		XPAR_XGPIOPS_0_INTR

#define EMIO_LED			54U
#define EMIO_KEY			58U

#define GPIO_DIR_INPUT      0U
#define GPIO_DIR_OUTPUT     1U
#define GPIO_OUTPUT_ENABLE 	1U

/**************************** Type Definitions ******************************/

/***************** Macros (Inline Functions) Definitions *******************/

/************************** Function Prototypes ****************************/

int  GpioInit(XGpioPs *GpioPtr, u32 DeviceId);
int  GpioIntrSetup(XScuGic *IntcPtr, u32 DeviceId, XGpioPs *GpioPtr, u32 IntrID);
void GpioIntrHandler(void *CallbackRef);
void GpioOutputExample(XGpioPs *GpioPtr);

/************************** Variable Definitions **************************/

XGpioPs Gpio;
XScuGic Intc;

int KeyFlag;
int KeyValue;

/*****************************************************************************/

int main()
{
	//
    int Status;

    //
    Status = GpioInit(&Gpio, GPIO_DEVICE_ID);
    if (Status != XST_SUCCESS)
    {
    	xil_printf("GPIO initialization failed.\r\n");
    	return XST_FAILURE;
    }
    xil_printf("GPIO initialization success.\r\n");
    //
    Status = GpioIntrSetup(&Intc, INTC_DEVICE_ID, &Gpio, GPIO_INTR_ID);
    if (Status != XST_SUCCESS)
    {
    	xil_printf("GPIO interrupt setup failed.\r\n");
    	return XST_FAILURE;
    }
    xil_printf("GPIO interrupt setup success.\r\n");

    //
    GpioOutputExample(&Gpio);

    //
    return 0;
}

/*****************************************************************************/

int GpioInit(XGpioPs *GpioPtr, u32 DeviceId)
{
	//
	XGpioPs_Config *ConfigPtr;
	int Status;

	// Initialize the Gpio driver.
	ConfigPtr = XGpioPs_LookupConfig(DeviceId);
	if (ConfigPtr == NULL)
	{
		return XST_FAILURE;
	}

	Status = XGpioPs_CfgInitialize(GpioPtr, ConfigPtr, ConfigPtr->BaseAddr);
	if (Status != XST_SUCCESS)
	{
		return XST_FAILURE;
	}

	// Run a self-test on the GPIO device
	Status = XGpioPs_SelfTest(GpioPtr);
	if (Status != XST_SUCCESS)
	{
		return XST_FAILURE;
	}

	 // Set Pin Direction
	XGpioPs_SetDirectionPin(GpioPtr, EMIO_LED, GPIO_DIR_OUTPUT);
	XGpioPs_SetDirectionPin(GpioPtr, EMIO_KEY, GPIO_DIR_INPUT);

	 // Enable Pin Output
	XGpioPs_SetOutputEnablePin(GpioPtr, EMIO_LED, GPIO_OUTPUT_ENABLE);

	 // Set Pin Interrupt Types
	XGpioPs_SetIntrTypePin(GpioPtr, EMIO_KEY, XGPIOPS_IRQ_TYPE_EDGE_RISING);

	 // Enable Pin Interrupt
	XGpioPs_IntrEnablePin(GpioPtr, EMIO_KEY);

	//
	return XST_SUCCESS;
}

/*****************************************************************************/
int GpioIntrSetup(XScuGic *IntcPtr, u32 DeviceId, XGpioPs *GpioPtr, u32 IntrID)
{
	//
	int Status;
	XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */

	// step1. 初始化中断控制器
	IntcConfig = XScuGic_LookupConfig(DeviceId);
	if (NULL == IntcConfig)
	{
		return XST_FAILURE;
	}

	Status = XScuGic_CfgInitialize(IntcPtr, IntcConfig, IntcConfig->CpuBaseAddress);
	if (Status != XST_SUCCESS)
	{
		return XST_FAILURE;
	}

	// step2. 初始化异常处理
	Xil_ExceptionInit();

	// step3. 注册异常处理回调函数
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcPtr);

	// step4. 使能异常处理
	Xil_ExceptionEnable();

	// step5. 注册中断
	Status = XScuGic_Connect(IntcPtr, IntrID, (Xil_ExceptionHandler)GpioIntrHandler, (void *)GpioPtr);
	if (Status != XST_SUCCESS)
	{
		return XST_FAILURE;
	}

	// step.6 使能中断
	XScuGic_Enable(IntcPtr, IntrID) ;

	//
	return XST_SUCCESS;
}

/*****************************************************************************/
void GpioIntrHandler(void *CallbackRef)
{
	//
	XGpioPs *GpioInstancePtr = (XGpioPs *)CallbackRef;
	int Int_val;

	// Get Interrupt Status
	Int_val = XGpioPs_IntrGetStatusPin(GpioInstancePtr, EMIO_KEY);

	// Clear Interrupt Status
	XGpioPs_IntrClearPin(GpioInstancePtr, EMIO_KEY);

	//
	if (Int_val == 1)
	{
		KeyFlag = 1;
	}

	//
	return;
}

/*****************************************************************************/
void GpioOutputExample(XGpioPs *GpioPtr)
{
	//
	while(1)
	{
		if (KeyFlag == 1)
		{
			xil_printf("You pust the button\r\n");
			XGpioPs_WritePin(GpioPtr, EMIO_LED, KeyValue);
			KeyValue = ~ KeyValue;
			KeyFlag = 0;
		}
	}
	//
	return;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值