ZYNQ之GPIO机制

目录

一、简介

二、MIO

三、EMIO

四、AXI GPIO

五、相关函数介绍

1.MIO / EMIO

① GPIO配置以及初始化

 ②GPIO操作函数集

2.AXI GPIO

① GPIO配置以及初始化


一、简介

ZYNQ芯片的的总统框图如下: 

ZYNQ之GPIO机制_第1张图片

ZYNQ 分为 PS 和 PL 两部分,那么器件的引脚资源同样也分成了两部分。 ZYNQ PS 中的外设
可以通过 MIO( Multiuse I/O,多用输入/输出)模块连接到 PS 端的引脚上。同时也可以通过 EMIO 连接到 PL端的引脚

如图GPIO框图:

ZYNQ之GPIO机制_第2张图片

 

GPIO 分为 4 个 Bank,其中 Bank0和 Bank1 连接到 MIO;而 Bank2 和 Bank3 连接到 EMIO。除 Bank1 之外的 Bank 都具有 32bit, Bank1 只具有 22bit, 所以总共 54 个 MIO。Bank2 和 Bank3 用于控制扩展的 MIO 即EMIO,总共64 个 EMIO。

所以控制GPIO的方式可以通过MIO和EMIO,除此之外从框图中还可看出还可通过AXI GPIO控制GPIO。

GPIO控制:

①MIO

②EMIO

③AXI GPIO

二、MIO

PS 所有的外设都可以通过 MIO 访问,这些外设也是与 MIO 进行连接,每个 MIO 可以独立控制,以及独立驱动单个引脚的外设,但是MIO 一但选定,引脚位置就已经确定下来了,不需要添加引脚约束。一般厂商会给MIO与外设的连接情况。PYNQ-Z2的MIO对应表目前我没有找到,查看原理图大多数的外设都是PL的,所以PS可操作外设少。

ZYNQ之GPIO机制_第3张图片

 PS 通过 APB 总线对控制、状态寄存器的读写实现对 GPIO 的驱动。MIO的作用有点像Linux驱动中的Pinctrl子系统的功能:引脚的功能复用

ZYNQ之GPIO机制_第4张图片

 通过如上红色框相关寄存器的操作实现GPIO的驱动。

DATA_RO:数据只读寄存器,通过该寄存器能够观察器件引脚上的值。如果 GPIO 信号配置为输出,则通常会反映输出上驱动的值,写入此寄存器将被忽略

MASK_DATA_LSW、MASK_DATA_MSW:数据掩码寄存器,该寄存器使软件能够有选择地一次更改所需的的输出值,该寄存器避免了对未更改位的读-修改-写序列的需要
 

DATA:数据寄存器,该寄存器控制 GPIO 信号配置为输出时要输出的值。该寄存器的所有 32 位都是一次写入的。读取该寄存器返回写入 DATA 或 MASK_DATA_ {LSW, MSW}的先前值,它不会返回器件引脚上的当前值。        

DIRM:方向模式寄存器,用于控制 I/O 引脚是用作输入还是输出。当 DIRM [x] == 0 时,输出驱动器被禁用,该引脚作为输入引脚使用。

OEN:使能输出寄存器当I/O 配置为输出时,该寄存器控制是否启用输出。禁用输出时,引脚为 3态。当 OEN [x] == 0 时,输出被禁用
 

从这些寄存器中我们可以看到,如果配置引脚为输出,不仅需要设置方向,还要使能输出。但是编程的时候不必在意这些寄存器,直接调用 Xilinx 官方提供的函数即可。

小结:

MIO(multiuse I/O):属于Zynq的PS部分,Zynq7000 系列芯片有 54 个 MIO。它们分配在 GPIO 的 Bank0 和 Bank1 上,这些引脚可以用在GPIO、SPI、UART、TIMER、Ethernet、USB等功能上,每个引脚都同时具有多种功能。

这些 IO 与 PS 直接相连,不需要添加引脚约束。对 MIO 的操作可以看作是纯 PS 的操作。
 

三、EMIO

PS 和外设之间的通信主要是通过MIO实现的,除此之外,PS 还可以通过EMIO( Extended MIO, EMIO)来实现与外部设备的连接 EMIO 使用了 PL 的I/O 资源, 当 PS 需要扩展超过 54 个引脚的时可以用 EMIO, 也可以用它来连接 PL 中实现的 IP 模块。

ZYNQ之GPIO机制_第5张图片 

PS 端经由 EMIO 引出的接口会直接连接到 PL 端的器件引脚上通过 IO 管脚约束来指定所连接 PL 引脚的位置。 通过这种方式, EMIO 可以为 PS 端实现额外的 64 个输入引脚或 64 个带有输
出使能的输出引脚。EMIO 还有一种使用方式,就是连接 PL 内实现的功能模块( IP 核) , 此时 PL 端的 IP 作为 PS 端的一个外部设备。下面会介绍。

注意:MIO 的 SPI, I2C 等资源对应固定的管脚,不能用于所有的MIO口上,但是EMIO映射出的 SPI 和I2C可以拓展到任意PL的IO上。

小结

 EMIO:属于Zynq的PS部分,只是连接到了PL上,再从PL的引脚连到芯片外面实现数据输入输出。Zynq7000 系列芯片有 64 个 EMIO,它们分配在 GPIO 的 Bank2 和 Bank3 上,当 MIO 不够用时,PS 可以通过驱动 EMIO 控制 PL 部分的引脚,EMIO 的使用相当于PS + PL 的结合使用。所以,EMIO 需要分配引脚以及编译综合生成 bit文件。
 

四、AXI GPIO

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

每个AXI接口包含多个AXI通道,九个PL接口是用了上千个信号来实现的。

ZYNQ之GPIO机制_第6张图片

 AXI全称Advanced eXtensible Interface 是Xilinx从6系列的FPGA开始引入的一个接口协议,主要描述了主设备和从设备之间的数据传输方式。在zYNQ中继续使用,版本是AXI4,所以经常会看到AXI4.0,ZYNQ内部设备都有AXI接口。

在ZYNQ中,支持AXI-Lite,AXI4和AXIl-Stream三种总线

ZYNQ之GPIO机制_第7张图片

AXI4-Lite:   具有轻量级,结构简单的特点,适合小批量数据、简单控制场合。不支持批量传输,读写时一次只能读写一个字( 32bit )。主要用于访问一些低速外设和外设的控制。 

AXI4: 在AXI4-Lite上增加了批量传输功能,可以连续对一片地址进行一次性读写。

上面两种均采用内存映射控制方式,即ARM将用户自定义IP编入某一地址进行访问,读写时就像在读自己的片内RAM,编程简便,但是资源占用过多,需要额外的读地址线、写地址线、读数据线、写数据线、写应答线这些信号线。



AXI4-Stream:  连续流接口,不需要地址线(像FIFO,一直读或一直写就行)。对于这类IP,ARM不能通过内存映射方式控制,必须有一个转换装置。AXI-DMA模块来实现内存映射到流式接口的转换。

AXl-Stream适用的场合有很多:视频流处理;通信协议转换;数字信号处理;无线通信等。其本质是针对数值流构建的数据通路,从信源(例如ARM内存、DMA、无线接收前端)到信宿(例如HDMI显示器、高速AD音频输出)构建起连续的数据流,这种接口适合做实时信号处理。

AXI GPIO IP 核为 AXI 接口提供了一个通用的输入/输出接口。 与 PS 端的 GPIO 不同, AXI GPIO 是一个软核( Soft IP),即 ZYNQ 芯片在出厂时并不存在这样的一个硬件电路, 是由用户通过配置 PL 端的逻辑资源来实现的一个功能模块PS 端的 GPIO 是一个硬核( Hard IP) ,它是一个生产时在硅片中实现的功能电路

ZYNQ之GPIO机制_第8张图片

AXI GPIO 可以配置成单通道或者双通道, 每个通道的位宽可以单独设置。 另外通过打开或者关闭三态缓冲器, AXI GPIO 的端口还可以被动态地配置成输入或者输出接口。其顶层模块的框图如上所示。

模块的左侧实现了一个 32 位的 AXI4-Lite 从接口, 用于主机访问 AXI GPIO 内部各通道的寄存器。 当右侧接口输入的信号发生变化时,模块还能向主机产生中断信号。
 

小结

AXIGPIO: 由FPGA的PL逻辑核功能实现,相当于GPIO的IP核,是通过AXI总线挂在PS上的GPIO上。使用FPGA的逻辑资源生成的GPIO硬件电路,占用较多的PL端的资源,所以用的是PL资源,而非PS硬件资源。所以AXI GPIO也需要分配引脚以及编译综合生成 bit文件

五、相关函数介绍

1.MIO / EMIO

GPIO相关头文件介绍

xparameters:定义了各个外设的基地址、器件ID、中断

xgpiops:包含 PS GPIO 的函数声明
xil_printf:中断输出相关函数
sleep:延时相关函数

xstatus:包含 XST_FAILURE 和 XST_SUCCESS 的宏定义,返回值宏定义
 

重要结构体:XGpioPs和XGpioPs_Config

typedef struct {
	XGpioPs_Config GpioConfig;	/**< Device configuration */
	u32 IsReady;			/**< Device is initialized and ready */
	XGpioPs_Handler Handler;	/**< Status handlers for all banks */
	void *CallBackRef; 		/**< Callback ref for bank handlers */
	u32 Platform;			/**< Platform data */
	u32 MaxPinNum;			/**< Max pins in the GPIO device */
	u8 MaxBanks;			/**< Max banks in a GPIO device */
} XGpioPs;

typedef struct {
	u16 DeviceId;		/**< Unique ID of device */
	u32 BaseAddr;		/**< Register base address */
} XGpioPs_Config;

XGpioPs 结构体里面各个成员变量的大致作用:

GpioConfig 是XGpioPs_Config结构体变量,其内部成员有DeviceId和BaseAddr,保存gpio这个设备的Id和基地址,在zynq中任何外设都有一个相应的id和基地址用来识别。 

IsReady,当设备初始化完成,设备已经准备好

Handler ,中断句柄,用于中断读取和写入

CallBackRef 中断回调函数,没有用到中断时可不做关心

Platform 这个是跟你的开发板硬件平台相关,不做关心

MaxPinNum ps端能使用的最大的pin数量,在zynq中为118

MaxBanks 在zynq中的io bank数目为4
 

① GPIO配置以及初始化

(1)查询函数XGpioPs_LookupConfig()

XGpioPs_Config *XGpioPs_LookupConfig(u16 DeviceId)

参数:设备id,可以在#include "xparameters_ps.h"找到id和地址

ZYNQ之GPIO机制_第9张图片

根据唯一的设备ID号DeviceID,该函数查找设备配置。根据该号,该函数返回一个配置表。 每一个设备都有一个唯一的配置表:

XGpioPs_Config XGpioPs_ConfigTable[XPAR_XGPIOPS_NUM_INSTANCES] =
{
	{
		XPAR_PS7_GPIO_0_DEVICE_ID,
		XPAR_PS7_GPIO_0_BASEADDR
	}
};

XGpioPs_LookupConfig这个函数就是根据设备id,查找配置表,然后将设备的地址等信息,通过XGpioPs_Config 指针返回。

(2)初始化函数XGpioPs_CfgInitialize()

s32 XGpioPs_CfgInitialize(XGpioPs *InstancePtr, const XGpioPs_Config *ConfigPtr,
				u32 EffectiveAddr)

InstancePtr:XGpioPs 结构体指针
ConfigPtr:指向XGpioPs_Config 设备指针
EffectiveAddr:指向设备基地址

参数2和参数3由XGpioPs_LookupConfig()返回值而来。

 该函数用于初始化一个GPiO实例,包括初始化该实例的所有成员。

配置和初始化GPIO模板

XGpioPs Gpio;	/* The driver instance for GPIO Device. */
XGpioPs_Config *ConfigPtr;
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) 
{
	return XST_FAILURE;
}

 ②GPIO操作函数集

(1)设置GPIO输入输出方向XGpioPs_SetDirectionPin()

void XGpioPs_SetDirectionPin(const XGpioPs *InstancePtr, u32 Pin, u32 Direction)

InstancePtr:XGpioPs结构体指针
Pin:要写入数据的Pin的编号,zynq中为0-117(包括MIO和EMIO)
Direction:指定引脚设置的方向,输入方向为:0输出方向为:1。 

(2)设置GPIO输出使能XGpioPs_SetOutputEnablePin

void XGpioPs_SetOutputEnablePin(const XGpioPs *InstancePtr, u32 Pin, u32 OpEnable)

InstancePtr:指向XGpioPs结构体的指针
Pin:Pin是要写入数据的Pin的编号,zynq中为0-117(包括MIO和EMIO)
OpEnable:设定引脚使能开关,0为关闭输出使能,1为打开输出使能

 这两个函数一般 和GPIO的初始化放在一起。

(3)读取GPIO引脚值XGpioPs_ReadPin()


u32 XGpioPs_ReadPin(XGpioPs *InstancePtr, u32 Pin)

InstancePtr:是指向XGpioPs结构体的指针
Pin:要读取数据的Pin编号
功能  :此函数用于读取指定对象的状态 

(4)向写入GPIO引脚值XGpioPs_WritePin()

void XGpioPs_WritePin(XGpioPs *InstancePtr, u32 Pin, u32 Data)

InstancePtr:是指向XGpioPs结构体的指针
Pin:要写入数据的Pin编号
Data:写入指定引脚的数据(0或1)

 注:尽管 MIO 和 EMIO 组之间存在功能差异,但是对每组 GPIO 的控制是相同的

2.AXI GPIO

① GPIO配置以及初始化

(1)GPIO配置和初始化XGpio_Initialize()

int XGpio_Initialize(XGpio * InstancePtr, u16 DeviceId)

InstancePtr :XGpio实例的指针

DeviceId:  设备id

函数内部其实调用了XGpio_LookupConfig和XGpio_CfgInitialize完成了GPIO的初始化。

/* GPIO driver initialisation */
Status = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) 
{
    return XST_FAILURE;
}

(2)设置GPIO输入输出方向XGpio_SetDataDirection()

void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel,u32 DirectionMask)

InstancePtr :指向GPIO实例的指针   

Channel:  待设置GPIO的通道(Vivado中设置gpio IP时的设置通道为1或2)

DirectionMask:方向设置。0:output;1:input

(3)读取GPIO值XGpio_DiscreteRead()

u32 XGpio_DiscreteRead(XGpio * InstancePtr, unsigned Channel)

InstancePtr :指向GPIO实例的指针   

Channel:  待设置GPIO的通道(Vivado中设置gpio IP时的设置通道为1或2)

返回值: GPIO状态

(4)向GPIO写入数据XGpio_DiscreteWrite()

void XGpio_DiscreteWrite(XGpio * InstancePtr, unsigned Channel, u32 Data)

InstancePtr :指向GPIO实例的指针   

Channel:  待设置GPIO的通道(Vivado中设置gpio IP时的设置通道为1或2)

Data :写入的数据(0/1)

你可能感兴趣的:(ZYNQ,fpga开发)