ZYNQ可以提供多种方式提供GPIO的能力,早上到公司就想应该先搞清楚里面的各种区别,因为我自己不自然就只会用自己的最熟悉的方案来实现,所以在此总结一下;
很多帖子讨论这个,当然是因为简单了;但是好像都没有整理完整
ZYNQ中GPIO有四种,其中PS中MIO/EMIO两种,而PL中同样有两种情况,AXI_GPIO和AXI_LITE自定义的GPIO;下面就这四种情况进行说明;
第一种 PS的MIO实现的GPIO
MIO实现的GPIO需要在做PCB板卡之前就对功能有所限制;
Zynq7000 系列芯片有 54 个 MIO(multiuse I/O) , 它们分配在 GPIO 的 Bank0 和Bank1 隶属于 PS 部分, 这些 IO 与 PS 直接相连。 不需要添加引 脚约束, MIO 信号对 PL部分是透明的, 不可见。 所以对 MIO 的操作可以看作是纯 PS 的操作。GPIO 的控制和状态寄存器基地址为: 0xE000_A000, 我们 SDK 下软件操作底层都是对于内存地址空间的操作。
Bank0:MI0[31:0]
Bank1:MI0[53:32]
Bank2:EMI0[31:0]
Bank3:EMI0[63:32]
其中MIO和EMIO是直接挂在PS上的GPIO。而AXI_GPIO是通过AXI总线挂在PS上的GPIO上。
其中MIO分布在BANK0,BANK1,而EMIO则分布在BANK2、BANK3。注意一下几项:
首先、MIO在zynq上的管脚是固定的,而EMIO,是通过PL部分扩展的,所以使用EMIO时候需要在约束文件中分配管脚,所以设计EMIO的程序时,需要生成PL部分的bit文件,烧写到FPGA中。
其次、MIO共占54bit,而EMIO占64bit。其中MIO占用IO号为0-53。而EMIO占用IO号为54-117。
无论是EMIO还是MIO都属于PS上的IO,直接由PS操作。在调用头文件,
只调用#include "xgpiops.h"即可,
而在调用AXI_GPIO时,则需要#include "xgpio.h"。
第二种 PS的EMIO实现GPIO
![](https://i-blog.csdnimg.cn/blog_migrate/bb7763cdca976c205d3df9e8927e3932.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6cb32620ce774dd4785833222a3d740b.png)
- module ad_iobuf
- (
- input [1:0] dio_t,
- input [1:0] dio_i,
- output [1:0] dio_o,
- inout [1:0] dio_p
- );
- genvar n;
- generate
- for (n = 0; n < 2; n = n + 1)
- begin: g_iobuf
- assign dio_o[n] = dio_p[n];
- assign dio_p[n] = (dio_t[n] == 1'b1) ? 1'bz : dio_i[n];
- end
- endgenerate
- endmodule
static XGpioPs psGpioInstancePtr; //定义PS的GPIO指针,如果用到MIO和EMIO也只要定义这一个就行
XGpioPs_Config *GpioConfigPtr; //XGpioPs结构体中还包含一个结构体,查bsp中的h文件
int xStatus;
GpioConfigPtr =XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
if(GpioConfigPtr == NULL)
returnXST_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);
XGpioPs_WritePin(&psGpioInstancePtr,iPinNumber,0); //这里是写某一个pin,也有写一个bank的,陆书上P111就是写bank的。
EMIO占用IO号为54-117
static XGpioPs psGpioInstancePtr; //定义PS的GPIO指针,如果用到MIO和EMIO也只要定义这一个就行
XGpioPs_Config *GpioConfigPtr; //XGpioPs 结构体中还包含一个结构体,查bsp中的h文件
int xStatus;
static int iPinNumber = 7; /*Led LD9 isconnected to MIO pin 7*/
static int iPinNumberEMIO = 54;
GpioConfigPtr =XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
if(GpioConfigPtr == NULL)
returnXST_FAILURE;
xStatus =XGpioPs_CfgInitialize(&psGpioInstancePtr,GpioConfigPtr,GpioConfigPtr->BaseAddr);
if(XST_SUCCESS != xStatus)
print("PS GPIO INIT FAILED \n\r");
XGpioPs_SetDirectionPin(&psGpioInstancePtr,7,1); 0输入,1输出
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,7,1); 0 为dis,1为enable
XGpioPs_WritePin(&psGpioInstancePtr,7,0);
XGpioPs_SetDirectionPin(&psGpioInstancePtr,54,0); 0输入,1输出
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,54,0); 0 为dis,1为enable
注明:7可以用iPinNumber代替,54可以用iPinNumberEMIO代替。
第三种 PL的AXI_GPIO实现GPIO
vivado中zynq设置如下图
图中可知GPIO中MIO和EMIO都不选择,但要打开M_AXI_GP接口(这里选择M_AXI_GP0)和复位管脚,如下图
当然用到了PL部分逻辑则至少需要一个时钟输出到PL部分,这里选择FCLK_CLK0输出50MHz,如下图
推荐加入zynq后,不要自动连接,再加入gpio并位宽设置为3,具体设置如下图
GPIO设置好后,再点击上面的蓝色字体的自动连接,即可得到上面的连接,这样可以减少手动连接量。
最后vivado中连接如下图
与EMIO类似需要将顶层三个GPIO管脚要绑定到芯片对应管脚上。
软件部分如下
#include <stdio.h>
#include "platform.h"
#include "xgpio.h"
#define AXI_GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
#define XGPIO_BANK1 1
#define XGPIO_BANK2 2
#define LED34_R_PIN 0x01
#define LED34_G_PIN 0x02
#define LED34_B_PIN 0x04
static void delay(int dly)
{
int i, j;
for (i = 0; i < dly; i++) {
for (j = 0; j < 0xffff; j++) {
;
}
}
}
int main()
{
XGpio_Config *XGpioCfg;
XGpio XGpio;
int Status;
init_platform();
XGpioCfg = XGpio_LookupConfig(AXI_GPIO_DEVICE_ID);
Status = XGpio_CfgInitialize(&XGpio, XGpioCfg, XGpioCfg->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XGpio_SetDataDirection(&XGpio, XGPIO_BANK1, ~(LED34_R_PIN | LED34_G_PIN | LED34_B_PIN));
XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, LED34_R_PIN | LED34_G_PIN | LED34_B_PIN);
while (1) {
XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~LED34_R_PIN);
delay(1000);
XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_R_PIN | LED34_G_PIN));
delay(1000);
XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_R_PIN | LED34_G_PIN | LED34_B_PIN));
delay(1000);
XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_G_PIN | LED34_B_PIN));
delay(1000);
XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_B_PIN));
delay(1000);
XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, LED34_R_PIN | LED34_G_PIN | LED34_B_PIN);
delay(1000);
}
cleanup_platform();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
这里实现的功能与EMIO方式中功能相同,当时IP方式中为PL部分实现的GPIO,所以调用的函数与前面两种GPIO实现函数不同,注意包含的GPIO头文件,前两种是#include "xgpiops.h"
而这最后一种为#include "xgpio.h"
第四种 PL的AXI_LITE自定义组件实现GPIO
待补充
总结
MIO和EMIO方式使用PS部分的GPIO模块,其中MIO方式不占用PL部分资源,其输出管脚只能为固定的54个(而且要在未被其它外设使用的情况下),EMIO方式会占用PL的管脚资源,其管脚可在PL部分任意选择(除特殊功能管脚),IP方式除了占用PL部分管脚资源外还会占用PL部分逻辑资源,所以其GPIO功能在PL部分实现其调用函数也和前两种不同,最后EMIO和IP方式在vivado都需要绑定管脚。