ZYNQ7000-GPIO详解

article/2025/11/8 10:00:34

摘要

本文介绍了ZYNQ7000芯片中GPIO的基本概念,分组、功能、控制寄存器、中断设置以及如何在Vitis中配置GPIO。

本文参考:UG585 - Zynq-7000 SoC Technical Reference Manual (v1.12.2) 385~394页–Ch14: General Purpose I/O(GPIO)

关键词:ZYNQGPIOMIOEMIOVitis

一. GPIO的基本概念

GPIO,General Purpose I/O,通用输入/输出,是ZYNQ的外设之一。ZYNQ的架构图如下图所示。

与非SOC不同的是,ZYNQ的GPIO引脚由PS侧的MIO引脚和PL侧的EMIO引脚构成(见上图)。

关于MIO和EMIO的详细介绍参见我的另一篇博客:传送门:ZYNQ7000-MIO与EMIO详解

二. GPIO框图与分组

ZYNQ的GPIO框图如下图所示。ZYNQ的GPIO引脚分为4个Bank即4组,其中,

118个GPIO = 32个MIO(Bank0) + 22个MIO(Bank1)+ 32个EMIO(Bank2)+ 32个EMIO(Bank3)

可见基本都是32个IO引脚一组,这是因为GPIO的控制寄存器是32位的,每一位对应一个IO引脚的话,一组寄存器就对应32个引脚,所以,4组基本相同的寄存器分别控制4组Bank,Bank1对应的也是一组32位寄存器,只是因为MIO引脚总共就54个,54-32=22,所以这组寄存器只控制22个引脚。

三. GPIO的功能与控制寄存器

GPIO的功能有三种:输入,输出,中断使用MIO和EMIO在功能上几乎相同,唯一的区别是EMIO因为是PL侧引脚,可以与PL部分进行通讯,而MIO对PL侧是透明的。

特别的,MIO7,MIO8只能做输出,这在ZYNQ7000-MIO与EMIO详解中有说明。

GPIO的功能在芯片内部通过一组寄存器来控制,如下图所示。注意,一组寄存器同时控制一个GPIO Bank的所有引脚

了解GPIO的控制器寄存器能帮助我们更深入的理解软件中的相关库函数,对编程有些帮助,当然,不了解也行,只要熟悉库函数即可。

输入/输出控制寄存器:

寄存器名称说明
DATA_ROdata read only(RO大概是这两个单词的缩写吧),
GPIO引脚的值存储在此寄存器中,无论GPIO被配置为输入或输出,都可以通过读此寄存器得到GPIO引脚的值。
因为是只读寄存器(对软件来说),软件向此寄存器的写入操作将被忽略。
DATA输出数据寄存器,当GPIO被配置为输出才起作用,此寄存器中的值就是输出到引脚的值。
向此寄存器写入就是在设置GPIO的输出值,
读此寄存器将返回GPIO前一时刻的输出值,而不是现在的值。
MASK_DATA_LSWMask Data Least Significant Words,输出数据低16位掩码寄存器,此寄存器只有低16位有效,
对应位为1表示DATA寄存器低16位中对应位的值可以更改,
若不为1,则表示DATA寄存器低16位中对应位保持原值
MASK_DATA_MSWMask Data Most Significant Words,输出数据高16位掩码寄存器,
功能同MASK_DATA_LSW,只是它对应DATA寄存器高16位
DIRMDirection Memory,方向寄存器,默认为0表示输入,设为1表示输出
注意,即使DIRM为1,软件也可以像输入一样去读此引脚的电平。
OENOutput Enable,输出使能寄存器,
仅当DIRM为0时有效,为1表示输出使能,
为0表示输出不使能,此时对应引脚上的值为三态值

中断控制寄存器:

寄存器名称说明
INT_TYPEInterrupt Type 中断类型寄存器,
控制GPIO中断是电平触发还是边缘触发
INT_POLARITYInterrupt Polarity 中断极性寄存器
控制GPIO中断是低电平/下降沿有效,还是高电平/上升沿有效
INT_ANYInterrupt Any,双边沿寄存器,
仅当INT_TYPE为边沿触发时,此寄存器才有效,控制是否双沿均可触发中断
INT_STATInterrupt State,中断状态寄存器,
此寄存器的值会被与之相连的INT State D触发器读取
D触发器存储中断状态,软件通过读此D触发器输出来判断中断是否发生,
清除此D触发器来清除中断状态
INT_MASKInterrupt Mask,中断掩码寄存器,
显示当前哪些位被屏蔽,哪些位启用
INT_DISInterrupt Disable,中断失效寄存器,
向该寄存器的任何位写入 1 都会屏蔽该中断信号。
从该寄存器读取会返回不可预测的值
INT_ENInterrupt Enable,中断使能寄存器
向该寄存器的任何位写入 1,可以启用/解除中断信号的掩码。
从该寄存器读取将返回一个不不可预测的值

四. GPIO中断设置与说明

GPIO中断号为52。此中断的优先级芯片内部已经固定好了,所以在软件中配置GPIO中断时,不需要指定GPIO中断的优先级。

GPIO所有引脚都是共享一个中断的,这意味着如果两个引脚的中断都使能了,如果不去读取具体引脚的电平,软件无法判断中断具体来自哪个引脚。

中断触发类型设置:

五. 在Vitis中配置GPIO

我自建了GPIO相关库函数,将相关GPIO功能写在一起。Xxk_PsGpio.c 与 Xxk_PsGpio.h。这里并没有使用处理整个Bank的函数,因为实际应用时很少需要处理整个Bank,都是单独处理某个Pin。

Xxk_PsGpio.h如下:

#ifndef XXK_PSGPIO_H
#define XXK_PSGPIO_H// 包含xilinx库中头文件
#include "xil_printf.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"// 宏定义
#define __weak __attribute__((weak))// 与PS GPIO相关的宏定义
// 引脚宏定义
#define MIO12 12U#define EMIO0 54U
#define EMIO1 55U
#define EMIO2 56U
#define EMIO3 57U
#define EMIO4 58U// GPIO指的就是PS侧的GPIO硬核,对于ZYNQ7,只有一个GPIO硬核
#define PSGPIO_INPUT 0U
#define PSGPIO_OUTPUT 1U
#define PSGPIO_OUTPUT_ENABLE 1U
#define PSGPIO_OUTPUT_DISABLE 0U/* 中断类型,已在xgpiops.h中定义,放在这里方便找到
#define XGPIOPS_IRQ_TYPE_EDGE_RISING	0x00U
#define XGPIOPS_IRQ_TYPE_EDGE_FALLING	0x01U
#define XGPIOPS_IRQ_TYPE_EDGE_BOTH	    0x02U
#define XGPIOPS_IRQ_TYPE_LEVEL_HIGH	    0x03U
#define XGPIOPS_IRQ_TYPE_LEVEL_LOW	    0x04U
*/// PS GPIO相关函数
// 初始化psGpio
int psGpioInti(XGpioPs *psGpioPtr, u16 psGpio_deviceId);
//psGpioInti(&psGpio, XPAR_XGPIOPS_0_DEVICE_ID);// 设置psGpio某引脚为输出并使能
void psGpio_SetPinOutputAndEnbale(const XGpioPs *psGpioPtr, u32 Pin);
//psGpio_SetPinOutputAndEnbale(&psGpio, EMIO0);// 设置psGpio某引脚为输入,输入无需使能
void psGpio_SetPinInput(const XGpioPs *psGpioPtr, u32 Pin);
//psGpio_SetPinInput(&psGpio, EMIO0);// 向psGpio某引脚写入0或1
extern void XGpioPs_WritePin(const XGpioPs *psGpioPtr, u32 Pin, u32 Data);
//XGpioPs_WritePin(&psGpio, EMIO0, 1);// 读取psGpio某引脚的电平,得到0或1
extern u32 XGpioPs_ReadPin(const XGpioPs *psGpioPtr, u32 Pin);
// EMIO0_pinData = XGpioPs_ReadPin(&psGpio, EMIO0);// 中断相关函数
int scuGic_Inti(XScuGic *scuGicPtr, u16 scuGicID); //初始化中断控制器
//scuGic_Inti(&scuGic, XPAR_XSCUTIMER_0_DEVICE_ID);void psGpio_PinIntr_SetAndEnable(XScuGic *scuGicPtr, XGpioPs *psGpioPtr, u32 psGpio_intrId,Xil_ExceptionHandler psGpio_Handler, u32 Pin, u8 IrqType);
//psGpio_PinIntr_SetAndEnable(&scuGic, &psGpio, XPAR_XGPIOPS_0_INTR,
//                            psGpio_Handler, EMIO0, XGPIOPS_IRQ_TYPE_EDGE_RISING);void psGpio_Handler(void *CallBackRef); // 需在main中重写此弱函数#endif

Xxk_PsGpio.c 如下:

#include "Xxk_PsGpio.h"// 初始化PS侧GPIO,包括MIO和EMIO
int psGpioInti(XGpioPs *psGpioPtr, u16 psGpio_deviceId)
{XGpioPs_Config *psGpio_configPtr;psGpio_configPtr = XGpioPs_LookupConfig(psGpio_deviceId); // 根据器件ID查找配置// 配置初始化配置int status;status = XGpioPs_CfgInitialize(psGpioPtr, psGpio_configPtr, psGpio_configPtr->BaseAddr);if (status != XST_SUCCESS){xil_printf("PsGpio %d Initialization Failed\r\n", psGpio_deviceId);return status;}status = XGpioPs_SelfTest(psGpioPtr);if (status != XST_SUCCESS){xil_printf("PsGpio %d SelfTest Failed\r\n", psGpio_deviceId);return status;}xil_printf("PsGpio %d Initialization Succeed\r\n", psGpio_deviceId);return status;
}// 设置psGpio某引脚为输入,输入无需使能
void psGpio_SetPinInput(const XGpioPs *psGpioPtr, u32 Pin)
{XGpioPs_SetDirectionPin(psGpioPtr, Pin, PSGPIO_INPUT);
}// 设置psGpio某引脚为输出并使能
void psGpio_SetPinOutputAndEnbale(const XGpioPs *psGpioPtr, u32 Pin)
{XGpioPs_SetDirectionPin(psGpioPtr, Pin, PSGPIO_OUTPUT);XGpioPs_SetOutputEnablePin(psGpioPtr, Pin, PSGPIO_OUTPUT_ENABLE);
}// 设置psGpio某引脚输出不使能
void psGpio_SetPinOutputDisbale(const XGpioPs *psGpioPtr, u32 Pin)
{XGpioPs_SetOutputEnablePin(psGpioPtr, Pin, PSGPIO_OUTPUT_DISABLE);
}// PS GPIO PIN中断使能
void psGpio_PinIntr_SetAndEnable(XScuGic *scuGicPtr, XGpioPs *psGpioPtr, u32 psGpio_intrId,Xil_ExceptionHandler psGpio_Handler, u32 Pin, u8 IrqType)
{// 连接中断ID与固定服务函数XScuGic_Connect(scuGicPtr, psGpio_intrId,(Xil_ExceptionHandler)psGpio_Handler,(void *)psGpioPtr);// 启用对应中断ID的中断源XScuGic_Enable(scuGicPtr, psGpio_intrId);// 注意psGPIO中断不需要设置中断优先级// 设置psGPIO中断类型XGpioPs_SetIntrTypePin(psGpioPtr, Pin, IrqType);// 在使能前先清除一次中断,否则之前的中断状态可能残留,导致烧写后马上进一次中断XGpioPs_IntrClearPin(psGpioPtr, Pin);// 使能psGPIO中断XGpioPs_IntrEnablePin(psGpioPtr, Pin);
}// psGpio中断处理弱函数
__weak void psGpio_Handler(void *CallBackRef)
{XGpioPs *psGpioPtr = (XGpioPs *)CallBackRef;//	psGpio_intrFlag = 1;if (XGpioPs_IntrGetStatusPin(psGpioPtr, MIO12) == TRUE){XGpioPs_IntrDisablePin(psGpioPtr, MIO12);XGpioPs_IntrClearPin(psGpioPtr, MIO12);xil_printf("This is psGpio_Handler - MIO12\r\n");}else if (XGpioPs_IntrGetStatusPin(psGpioPtr, EMIO4) == TRUE){XGpioPs_IntrDisablePin(psGpioPtr, EMIO4);XGpioPs_IntrClearPin(psGpioPtr, EMIO4);xil_printf("This is psGpio_Handler - EMIO4\r\n");}
}// 初始化中断控制器ScuGic
int scuGic_Inti(XScuGic *scuGicPtr, u16 scuGicID)
{// 打开系统的中断处理功能Xil_ExceptionInit(); // 初始化异常句柄,只在很早版本的bsp中需要,此处为了兼容性保留Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,scuGicPtr); // 为IRQ注册中断处理程序Xil_ExceptionEnable();									 // 使能系统中断功能XScuGic_Config *scuGicConfig;scuGicConfig = XScuGic_LookupConfig(scuGicID); // 根据器件ID查找配置int status;status = XScuGic_CfgInitialize(scuGicPtr, scuGicConfig, scuGicConfig->CpuBaseAddress);if (status != XST_SUCCESS){xil_printf("ScuGic Initialization Failed\r\n");return XST_FAILURE;}xil_printf("ScuGic Initialization Succeed\r\n");return status;
}

使用自建库,GPIO输入,输出及中断均有使用的main.c如下:

/*功能:控制EMIO0~2为输出,EMIO4为输入,EMIO3为中断
*/#include "Xxk_PsGpio.h"
#include "sleep.h"// 全局变量
XGpioPs psGpio;
XScuGic scuGic;int psGpio_EMIO_intrFlag = 0;
int psGpio_MIO_intrFlag = 0;int main(int argc, char const *argv[])
{xil_printf("begin\r\n");// 初始化psGpiopsGpioInti(&psGpio, XPAR_XGPIOPS_0_DEVICE_ID);// 设置psGpio引脚方向psGpio_SetPinOutputAndEnbale(&psGpio, EMIO0);psGpio_SetPinOutputAndEnbale(&psGpio, EMIO1);psGpio_SetPinOutputAndEnbale(&psGpio, EMIO2);psGpio_SetPinInput(&psGpio, EMIO3);// 初始化中断控制器scuGic_Inti(&scuGic, XPAR_XSCUTIMER_0_DEVICE_ID);// 设置并使能psGpio某引脚中断psGpio_PinIntr_SetAndEnable(&scuGic, &psGpio, XPAR_XGPIOPS_0_INTR,psGpio_Handler, EMIO4, XGPIOPS_IRQ_TYPE_EDGE_FALLING);psGpio_PinIntr_SetAndEnable(&scuGic, &psGpio, XPAR_XGPIOPS_0_INTR,psGpio_Handler, MIO12, XGPIOPS_IRQ_TYPE_EDGE_FALLING);while (1){sleep(1);xil_printf("EMIO3 value: %d\r\n", XGpioPs_ReadPin(&psGpio, EMIO3));XGpioPs_WritePin(&psGpio, EMIO0, 1);XGpioPs_WritePin(&psGpio, EMIO1, 1);XGpioPs_WritePin(&psGpio, EMIO2, 0);if (psGpio_EMIO_intrFlag){xil_printf("This is psGpio_Handler - EMIO4\r\n");psGpio_EMIO_intrFlag = 0;XGpioPs_IntrEnablePin(&psGpio, EMIO4);}if (psGpio_MIO_intrFlag){xil_printf("This is psGpio_Handler - MIO12\r\n");psGpio_MIO_intrFlag = 0;XGpioPs_IntrEnablePin(&psGpio, MIO12);}sleep(1);XGpioPs_WritePin(&psGpio, EMIO0, 0);XGpioPs_WritePin(&psGpio, EMIO2, 1);}return 0;
}void psGpio_Handler(void *CallBackRef) // 重写弱函数
{XGpioPs *psGpioPtr = (XGpioPs *)CallBackRef;if (XGpioPs_IntrGetStatusPin(psGpioPtr, MIO12) == TRUE){psGpio_MIO_intrFlag = 1;XGpioPs_IntrDisablePin(psGpioPtr, MIO12);XGpioPs_IntrClearPin(psGpioPtr, MIO12);}if (XGpioPs_IntrGetStatusPin(psGpioPtr, EMIO4) == TRUE){psGpio_EMIO_intrFlag = 1;XGpioPs_IntrDisablePin(psGpioPtr, EMIO4);XGpioPs_IntrClearPin(psGpioPtr, EMIO4);}
}

六. GPIO的另一种实现方式 - AXI GPIO

本文介绍了如何使用MIO和EMIO实现GPIO,而对于ZYNQ来说,在PL中使用AXI GPIO IP核也可以实现GPIO功能,具体介绍参见我的另一篇博文。

传送门:ZYNQ7000-AXI GPIO详解

七. 总结

本文介绍了ZYNQ7000中GPIO的基本概念,GPIO是ZYNQ PS侧最简单的一种外设,它可以由MIO或EMIO实现。以32为界,118个GPIO被分为了4个Bank,每个Bank对应一组控制寄存器,然后简单介绍了每个寄存器的名称含义和功能,最后附上了通过自建GPIO函数库,便捷操作GPIO的软件代码。

对GPIO理解还不深,如有疏漏,欢迎评论指出!


徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。


http://chatgpt.dhexx.cn/article/Rvj4L64P.shtml

相关文章

zynq7000中断系统

7.1环境 这一章节描述了系统级的中断环境以及中断控制器功能。PS端是基于ARM架构的,并且使用了两个Cortex-A9处理器以及型号为pl390的GIC中断控制器。整个中断结构与处理器、可编程逻辑器件(PL)、从外部IO接受到的中断请求有非常紧密的联系。本章节包含了下面这些关…

【ZYNQ】ZYNQ7000 UART 控制器及驱动应用示例

UART 简介 我们在使用 PS 的时候,通常会添加 UART 控制器,用于打印信息和调试代码。除此之外,PS 在和外部设备通信时,也会经常使用串口进行通信。 UART 控制器 UART 控制器是一个全双工异步收发控制器,ZYNQ 内部包含…

Xilinx ZYNQ 7000入门

ZYNQ是Xilinx推出的新一代全可编程片上系统,将处理器的软件可编程性与FPGA的硬件可编程性完美整合。 ZYNQ7020:xc7z020clg400-2 ZYNQ7010:xc7z010clg400-1 组合了一个双核ARM Cortex-A9(PS)处理器和一个传统的…

ZYNQ7000平台介绍

基于ZYNQ7000平台软件开发 ZYNQ7000平台简介 本文介绍ZYNQ7000平台采用Avnet推出的ZedBoard开发板进行讲解,ZedBoard开发板基于Xilinx Zynq-7000 All Programmable Soc,整合了双核Cortex-A9的PS处理单元和PL逻辑单元。 在zynq上,ARM Cortex…

【ZYNQ】ZYNQ7000 全局定时器及其驱动示例

定时器简介 在 ZYNQ 嵌入式系统中,定时器的资源是非常丰富的,每个 Cortex-A9 处理器都有各自独立的 32 位私有定时器和 32 位看门狗定时器,这两个 CPU 同时共享一个 64 位的全局定时器(GT)。 系统框图 全局定时器&am…

Xilinx ZYNQ 7000学习笔记一(复位和启动)

一、复位系统 参考文献:Zynq-7000 SoC Technical Reference Manual (UG585)-ch26 Reset System zynq7000复位信号源包括硬件复位、看门狗定时器、JTAG控制器复位信号和软件复位信号。其中,硬件复位引脚由上电复位信号PS_POR_B和系统复位信号PS_SRST_B驱…

zynq7000系列芯片介绍

ZYNQ从架构上可以划分为两大模块,一个是PS(处理器系统),另一个是PL(可编程逻辑) PS由APU、内存接口、IO外设、互连线4大模块组成。 1、APU(Application Processor Unit)应用处理单元 即PS【可编…

xilinx zynq-7000 基本知识

Zynq-7000 采用可扩展式处理平台架构(Extensible Processing Platform、EPP),是 Xilinx 用 28nm HKMG工艺制成的低功耗,高性能,高扩展性的新型芯片,这款新品里面集成了ARM CORTEX-A9 MPSOC 硬核以及相应的S…

ZYNQ-7000概述

摘要 Xilinx推出的ZYNQ-7000被称为全可编程片上系统(SOC),它由FPGA与ARM组合构成,硬件可编程,软件也可编程,在众多应用场合有一定优势。本文根据Xilinx官网的介绍并结合ZYNQ-7000的多份数据手册总结了此产品…

ZYNQ学习笔记(一)---初识ZYNQ-7000系列

前几日刚入手一款ZYNQ-7000系列的板卡,之前我也没用过FPGA,直接跨越到ZYNQ的原因主要是某宝上这款板卡相较于其他片上仅有一块FPGA的板卡性价比更高。作为入门,在学习过程中,笔者选择先单独学习ZYNQ上的FPGA部分,也就是…

ZYNQ7000 Vivado详细教学步骤

ZYNQ7000 Vivado开发 ZYNQ7000 Vivado详细教学步骤 ZYNQ7000 Vivado开发1.建立工程项目2.创建Block Design3.配置IO BANK4.配置DDR和CLOCK5.配置PS外设6.测试PS外设7.增加PL外设 1.建立工程项目 Xilinx提供了一系列开发工具,其中包括Vivado平台工具,它是…

mongodb客户端 robo 3T 查询突破50行限制

robo 3T的小bug 这个mongodb客户端,每次查询数据只有50行,虽然有向下翻页的功能 但实际上点击后会被重置,还是只有前50条 解决办法 DBQuery.shellBatchSize 500; 当前窗口最大查询数量修改到500(只有当前窗口生效)…

centos7仅安装mysql/mongodb客户端

1、仅安装MySQL客户端 # 添加rpm源 [rootk8s-master ~]# rpm -ivh https://repo.mysql.com//mysql57-community-release-el7-11.noarch.rpm [roottest1 ~]# # 通过yum搜索 [roottest1 ~]# yum search mysql-community [roottest1 ~]# # 安装x64位的 mysql客户端 [roottest1 ~]…

专业级MongoDB桌面客户端:MongoBooster for Mac

mongobooster mac破解版是适用于MongoDB的最智能IDE,也是以shell为中心的跨平台GUI工具,它提供了流畅的查询构建器,SQL查询SQL查询,就地更新,ES2017语法支持和真正的智能感知体验。MongoBooster下载可以帮助用户能够连接到选项卡式用户界面中的多个数据库,并通过shell命令…

mongodb官方客户端可视化工具Compass,免费,官方值得信赖

之前一直用的客户端是nosqlbooster4mongo,基本操作还都是挺好用的,但是有一些命令在这个客户端上运行不了,以后要慢慢的替换到compass上。 下载compass 官方下载:https://downloads.mongodb.com/compass/mongodb-compass-1.26.1…

Centos下mongodb的安装

方法一 可以使用yum命令直接安装MongoDB服务端和客户端。 sudo yum install mongodb-server # 安装MongoDB服务端 sudo yum install mongodb # 安装MongoDB客户端 sudo mongod -f /etc/mongod.conf # 加载配置项,启动mongodb服务器 mongo #启动客户端方法…

linux下MongoDB客户端shell基本操作

MongoDB 是一款NoSql数据库,没有固定的模式,即同一个集合中的不同文档结构可以不同,如:第一条记录{name:”xiaoming”},第二条记录:{name:”xiaoli”,age:15},这在关系型数据库中是无法实现的。…

MongoDB 客户端 MongoVue

直接上图片,图片是按顺序来的 软件下载地址(Windows下的MongoDB客户端MongoVUE 这是最后一个全功能的不收费的版本): http://pan.baidu.com/s/1skYIEq5

mongoDB介绍与客户端认证权限

mongoDB简介 Mongo 是 humongous 的中间部分,在英文里是“巨大无比”的意思。所以 MongoDB 可以翻译成“巨大无比的数据库”,更优雅的叫法是“海量数据库”。 Mongodb是一款非关系型数据库,说到非关系型数据库,区别于关系型数据库…

MongoDB客户端Robo 3T安装使用

MongoDB客户端Robo 3T安装使用 MongoDB安装 MacBook解压安装MongoDB Linux解压安装MongoDB Robo 3T下载和安装 官网下载Robo 3T 官网下载地址: https://robomongo.org/download 有解压版本和安装版本 下载成功后,点击安装,这里不多做说明 Robo 3T使用 打开Ro…