1 ZYNQ-7000 GPIO介绍
- GPIO(Generous Purpose Input Output)是指CPU引出的,可以配置为输入或输出的端口,用于CPU与外界进行数据的传输。
- ZYNQ-7000 架构由 PL+PS 组成,所以它的 GPIO 与一般的 ARM 不同。ZYNQ 的 GPIO,分为两种,MIO(multiuse I/O)和 EMIO(extendable multiuse I/O)。
- MIO 有54个,分配在 GPIO 的 Bank0 和 Bank1 隶属于 PS 部分,这些 IO 与 PS 直接相连。不需要添加引脚约束,MIO 信号对 PL 部分是透明的,不可见。
- EMIO有64个,EMIO连接PS到PL,作为ZYNQ的拓展MIO,当 MIO 不够用时,PS 可以通过驱动 EMIO 控制 PL 部分的引脚。因此使用PS的EMIO时,需要添加PL引脚约束。
- Bank0:MIO [31:0] GPIO PIN 脚号:0~31
Bank1:MIO [32:53] GPIO PIN 脚号:32~53
Bank2:EMIO [31:0] GPIO PIN 脚号:54~85
Bank3:EMIO [63:32] GPIO PIN脚号:86~ 117 - 因此我们要注意,在分配外设控制器管脚时,EMIO的那些要在PL端进行约束,MIO也要仔细阅读米联开发板相关文档。注意引脚的连接是否正确。
- SD卡控制器,串口控制器和网口控制器直接通过MIO与外设互联,不需要PL约束。
- SPI控制器通过EMIO引出到PL,并在PL进行管脚约束。
2 ZYNQ-7000 GPIO控制器介绍
- MIO和EMIO的内部构造如下图所示
- 主要的配置寄存器有下列几个:
DATA_RO:通过这个寄存器获取GPIO的输入值,当GPIO被配置为输出时,这个寄存器的值会反应输出的 PIN 脚的情况。
DATA:此寄存器控制输出到 GPIO 的值,读这个寄存器的值可以读到最后一次写入该寄存器的值。
MASK_DATA_LSW:低16位根据高16位的掩码情况写入DATA的低16位,被掩盖的位保持之前的值。
MASK_DATA_MSW:低16位根据高16位的掩码情况写入DATA的高16位,被掩盖的位保持之前的值。
DIRM:此寄存器控制GPIO为输入或输出,输入总是使能的,所以它只控制是否输出,DIRM[x]==0时输出被禁止。
OEN:输出使能控制寄存器,当 OEN[x]==0 的时候输出关闭,PIN 脚处于三态。 - 因此 ,如果要读IO状态就得读DATA_RO的值,如果是对某一位进行操作就是写 MASK_DATA_LSW、MASK_DATA_MSW。
- EMIO与 MIO 大部分类似,输出不能设置成三态,EMIO 在 PL 部分,输入与 OEN 寄存器无关。
3 MIO的使用
3.1 PL逻辑设计
- 米联MZ7100FA开发板上有一个 MIO 是与开发板上的一个 LD9 相连的,这个 MIO 就是 MIO7。实验通过操作该 MIO 来实现LD9 的闪烁。输出为高电平时该LED灯亮。
- 搭建zynq最小系统后,勾选GPIO MIO,会自动将未用到的MIO管脚选中。
- 生成bitstream后导出到SDK,在SDK中创建裸机PS工程。
3.2 PS程序设计
#include "xgpiops.h"
#include "sleep.h"
int main()
{
static XGpioPs psGpioInstancePtr;
XGpioPs_Config* GpioConfigPtr;
int iPinNumber= 7;
u32 uPinDirection = 0x1;
int xStatus;
GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
if(GpioConfigPtr == NULL)
return XST_FAILURE;
xStatus = XGpioPs_CfgInitialize(&psGpioInstancePtr,GpioConfigPtr, GpioConfigPtr->BaseAddr);
if(XST_SUCCESS != xStatus)
print(" PS GPIO INIT FAILED \n\r");
XGpioPs_SetDirectionPin(&psGpioInstancePtr, iPinNumber,uPinDirection);
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, iPinNumber,1);
while(1)
{
XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 1);
sleep(1);
XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 0);
sleep(1);
}
return 0;
}
- XGpioPs是一个局部的静态结构体,用户操作的每一个GPIO端口都要分配一个这样的结构体。
XGpioPs_LookupConfig
- 根据device_id在SDK导出的bsp库中查找相关板级信息,并返回指向该数据的指针。
XGpioPs_CfgInitialize
- 根据指向板级信息的指针初始化用户的XGpioPs结构体。
- 根据芯片的不同类型初始化Bank和MaxPinNum。
- 关闭所有Banks的中断。
XGpioPs_SetDirectionPin
- 设置某一pin脚的方向。
- XGpioPs_GetBankPin函数用于计算PinNumber在哪一个Bank的第几号位置。
- XGpioPs_ReadReg读取特定地址的寄存器值,在这里读取XGPIOPS_DIRM_OFFSET寄存器的值。根据用户输入输出配置进行相关设置后,再用XGpioPs_WriteReg写回到该寄存器中。
XGpioPs_SetOutputEnablePin
- 设置特定管脚的输出使能。
- 读取XGPIOPS_OUTEN_OFFSET寄存器后,进行相关设置后写回。
XGpioPs_WritePin
- 向特定的GPIO管脚写数据,注意一次写1bit,但形参是unsigned int,有效的是最低位。
XGpioPs_ReadPin
- 读取特定GPIO管脚的数据,注意一次读取1bit,但返回类型是unsigned int,有效的是最低位。注意最后和1进行了按位与。
4 EMIO的使用
4.1 PL逻辑设计
- PS通过EMIO连接到了PL端,所以使用EMIO需要在PL端进行FPGA引脚约束,在米联MZ7100FA开发板上PL端连接了4个LED灯,在PS端通过EMIO控制它们实现流水灯。
- 选择使用4bit的EMIO,然后引出至PL,并添加xdc引脚约束。
set_property PACKAGE_PIN E13 [get_ports {EMIO_0_tri_io[0]}]
set_property IOSTANDARD LVCMOS15 [get_ports {EMIO_0_tri_io[0]}]
set_property PACKAGE_PIN E12 [get_ports {EMIO_0_tri_io[1]}]
set_property IOSTANDARD LVCMOS15 [get_ports {EMIO_0_tri_io[1]}]
set_property PACKAGE_PIN F13 [get_ports {EMIO_0_tri_io[2]}]
set_property IOSTANDARD LVCMOS15 [get_ports {EMIO_0_tri_io[2]}]
set_property PACKAGE_PIN H13 [get_ports {EMIO_0_tri_io[3]}]
set_property IOSTANDARD LVCMOS15 [get_ports {EMIO_0_tri_io[3]}]
4.2 PS程序设计
- 程序逻辑与MIO基本相同,但要注意,EMIO的起始编号从54开始,所以我们使用的4bit GPIO实际编号是54-57。
#include "xgpiops.h"
#include "sleep.h"
int main()
{
static XGpioPs psGpioInstancePtr;
XGpioPs_Config* GpioConfigPtr;
int xStatus;
GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
if(GpioConfigPtr == NULL)
return XST_FAILURE;
xStatus = XGpioPs_CfgInitialize(&psGpioInstancePtr,GpioConfigPtr, GpioConfigPtr->BaseAddr);
if(XST_SUCCESS != xStatus)
print(" PS GPIO INIT FAILED \n\r");
XGpioPs_SetDirectionPin(&psGpioInstancePtr, 54,1);
XGpioPs_SetDirectionPin(&psGpioInstancePtr, 55,1);
XGpioPs_SetDirectionPin(&psGpioInstancePtr, 56,1);
XGpioPs_SetDirectionPin(&psGpioInstancePtr, 57,1);
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 54,1);
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 55,1);
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 56,1);
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 57,1);
while(1)
{
XGpioPs_WritePin(&psGpioInstancePtr, 54, 1);
usleep(200000);
XGpioPs_WritePin(&psGpioInstancePtr, 54, 0);
usleep(200000);
XGpioPs_WritePin(&psGpioInstancePtr, 55, 1);
usleep(200000);
XGpioPs_WritePin(&psGpioInstancePtr, 55, 0);
usleep(200000);
XGpioPs_WritePin(&psGpioInstancePtr, 56, 1);
usleep(200000);
XGpioPs_WritePin(&psGpioInstancePtr, 56, 0);
usleep(200000);
XGpioPs_WritePin(&psGpioInstancePtr, 57, 1);
usleep(200000);
XGpioPs_WritePin(&psGpioInstancePtr, 57, 0);
usleep(200000);
}
return 0;
}