zynq中各种GPIO方式的区别:MIO,EMIO,AXI_GPIO 核

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

EMIO实现GPIO也使用PS,但是有FPGA的灵活

EMIO实现是在平台设置



EMIO设置之后如果实现为IO方式就需要添加一个自定义的iobuf如图



IOBUF的实现代码如下
[plain]  view plain  copy
  1. module ad_iobuf  
  2.     (  
  3.     input   [1:0]  dio_t,  
  4.     input   [1:0]  dio_i,  
  5.     output  [1:0]  dio_o,  
  6.     inout   [1:0]  dio_p  
  7.     );  
  8.       
  9.   genvar n;  
  10.   
  11.   generate  
  12.     for (n = 0; n < 2; n = n + 1)   
  13.         begin: g_iobuf  
  14.             assign dio_o[n] = dio_p[n];  
  15.             assign dio_p[n] = (dio_t[n] == 1'b1) ? 1'bz : dio_i[n];  
  16.         end  
  17.   endgenerate  
  18.   
  19. 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设置如下图

axi_gpio_vivado中配置

图中可知GPIO中MIO和EMIO都不选择,但要打开M_AXI_GP接口(这里选择M_AXI_GP0)和复位管脚,如下图

axi_gpio_vivado中配置_GP和复位

当然用到了PL部分逻辑则至少需要一个时钟输出到PL部分,这里选择FCLK_CLK0输出50MHz,如下图

axi_gpio_vivado中配置_时钟

推荐加入zynq后,不要自动连接,再加入gpio并位宽设置为3,具体设置如下图

axi_gpio_ip设置

GPIO设置好后,再点击上面的蓝色字体的自动连接,即可得到上面的连接,这样可以减少手动连接量。

最后vivado中连接如下图

axi_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都需要绑定管脚。

猜你喜欢

转载自blog.csdn.net/ningjinghai11/article/details/80440683