文章目录
- 1 GPIO基本概念
- 1.1 MIO-EMIO简介
- 1.2 MIO-EMIO连接
- 1.3 MIO-EMIO路由
- 1.4 MIO-EMIO配置
- 2 GPIO控制寄存器
- 2.1 输入/输出控制寄存器
- 2.2 中断控制寄存器
- 2.3 中断触发设置
- 3 GPIO在Vivado SDK中的使用
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内部的连接情况如下图所示:
MIO-EMIO在PS内部的连接情况决定了MIO-EMIO对外可连接的外设类型和数量:
序号 | 名称 | MIO | EMIO | 数量 |
---|---|---|---|---|
1 | Quad SPI Flash | Yes | No | 1 |
2 | SRAM/NOR Flash | Yes | No | 1 |
3 | NAND Flash | Yes | No | 1 |
4 | USB | Yes | No | 1 |
5 | GigE | Yes | Yes | 2 |
6 | SD SDIO | Yes | Yes | 2 |
7 | UART | Yes | Yes | 2 |
8 | CAN | Yes | Yes | 2 |
9 | I2C | Yes | Yes | 2 |
10 | SPI | Yes | Yes | 2 |
11 | TTC | Yes | Yes | 2 |
12 | SWDT | Yes | Yes | 1 |
13 | PJTAG | Yes | Yes | 1 |
14 | TPIU | Yes | Yes | 1 |
15 | GPIO-MIO | Yes | No | N/A |
16 | GPIO-EMIO | No | Yes | N/A |
特别说明
- Yes表示可与MIO或EMIO连接
- No表示不可与MIO或EMIO连接
- GPIO-MIO是指未与任何外设连接的MIO引脚
- GPIO-EMIO是指未与任何外设连接的EMIO引脚
1.3 MIO-EMIO路由
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 deviceStatus = XGpioPs_SelfTest(GpioPtr);if (Status != XST_SUCCESS){return XST_FAILURE;}// Set Pin DirectionXGpioPs_SetDirectionPin(GpioPtr, EMIO_LED, GPIO_DIR_OUTPUT);XGpioPs_SetDirectionPin(GpioPtr, EMIO_KEY, GPIO_DIR_INPUT);// Enable Pin OutputXGpioPs_SetOutputEnablePin(GpioPtr, EMIO_LED, GPIO_OUTPUT_ENABLE);// Set Pin Interrupt TypesXGpioPs_SetIntrTypePin(GpioPtr, EMIO_KEY, XGPIOPS_IRQ_TYPE_EDGE_RISING);// Enable Pin InterruptXGpioPs_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 StatusInt_val = XGpioPs_IntrGetStatusPin(GpioInstancePtr, EMIO_KEY);// Clear Interrupt StatusXGpioPs_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;
}