zynq中三种GPIO的介绍和实现方式

转载链接:https://blog.csdn.net/husipeng86/article/details/52123465

三种GPIO的介绍

学了zynq一段时间,一上来的时候就被zynq的GPIO唬住了,实在没搞清楚zynq的GPIO怎么回事,一会这样,一会那样,最后才慢慢发现zynq至少有3种GPIO可以调用。难怪我觉得每篇介绍GPIO的博客说的有一些不一样呢。

我们先看有哪三种GPIO:MIO、EMIO、AXI_GPIO。其中MIO和EMIO是直接挂在PS上的GPIO。而AXI_GPIO是通过AXI总线挂在PS上的GPIO上。

我们先看一下MIO和EMIO:下图EMIO和MIO的结构。其中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"。

最后、在设计好bd文件后、系统会自动在路径:..\standalone_bsp_0\ps7_cortexa9_0\include生成 xparameters.h文件.我们可以在

xparameters.h文件中查看我们在bd设计时添加的外设ID。例如我们添加了EMIO,可以查到到该IO的地址和ID号。
  1. #define XPAR_PS7_GPIO_0_DEVICE_ID 0  
  2. #define XPAR_PS7_GPIO_0_BASEADDR 0xE000A000  
  3. #define XPAR_PS7_GPIO_0_HIGHADDR 0xE000AFFF  
再举例添加了两个AXI_GPIO,例化为BTNS_4BIT和SW_4BIT
  1. /* Definitions for driver GPIO */  
  2. #define XPAR_XGPIO_NUM_INSTANCES 2  
  3.   
  4. /* Definitions for peripheral BTNS_4BIT */  
  5. #define XPAR_BTNS_4BIT_BASEADDR 0x41210000  
  6. #define XPAR_BTNS_4BIT_HIGHADDR 0x4121FFFF  
  7. #define XPAR_BTNS_4BIT_DEVICE_ID 0  
  8. #define XPAR_BTNS_4BIT_INTERRUPT_PRESENT 0  
  9. #define XPAR_BTNS_4BIT_IS_DUAL 0  
  10.   
  11.   
  12. /* Definitions for peripheral SW_4BIT */  
  13. #define XPAR_SW_4BIT_BASEADDR 0x41200000  
  14. #define XPAR_SW_4BIT_HIGHADDR 0x4120FFFF  
  15. #define XPAR_SW_4BIT_DEVICE_ID 1  
  16. #define XPAR_SW_4BIT_INTERRUPT_PRESENT 0  
  17. #define XPAR_SW_4BIT_IS_DUAL 0  


再来看一下,AXI_GPIO相当于GPIO的IP核,我们调用时是占用相应AXI总线地址空间,如下图,占用地址为0x41200000和0x41210000



MIO和EMIO方式是使用PS部分的GPIO模块来实现GPIO功能的,支持54个MIO(可输出三态)、64个输入和128个输出(64个输出和64个输出使能)EMIO

而IP方式是在PL部分实现 GPIO功能,PS部分通过M_AXI_GP接口来控制该GPIO IP模块;另外EMIO模块虽然使用PS部分GPIO但也使用了PL部分的管脚资源。

MIO方式实现GPIO

vivado中zynq设置如下图

mio_vivado中配置

由图中可见要选中打开GPIO,其下自动显示可用于GPIO的MIO(当MIO作为其他功能时就不能作为GPIO使用了),其中MIO 7、MIO 8只能作为输出使用,因为它们用于VMODE管脚(参考UG585第14章:14.2.3)

软件部分如下

#include <stdio.h>
#include "platform.h"
#include "xgpiops.h"

#define LED1    0
#define LED2    9

static void delay(int dly)
{
    int i, j;
    for (i = 0; i < dly; i++) {
        for (j = 0; j < 0xffff; j++) {
            ;
        }
    }
}

int main()
{
    int Status;
    XGpioPs_Config *ConfigPtr;
    XGpioPs Gpio;

    init_platform();

    ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
    Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
                    ConfigPtr->BaseAddr);
    if (Status != XST_SUCCESS){
        return XST_FAILURE;
    }

    XGpioPs_SetDirectionPin(&Gpio, LED1, 1);
    XGpioPs_SetDirectionPin(&Gpio, LED2, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, LED1, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, LED2, 1);

    while (1) {
        XGpioPs_WritePin(&Gpio, LED1, 0);
        XGpioPs_WritePin(&Gpio, LED2, 1);
        delay(1000);
        XGpioPs_WritePin(&Gpio, LED1, 1);
        XGpioPs_WritePin(&Gpio, LED2, 0);
        delay(1000);
    }
    cleanup_platform();
}

EMIO方式实现GPIO

vivado中zynq设置如下图

emio_vivado中配置

图中可知GPIO中选择使用EMIO,并选择位宽(这里例子中选择3);其vivado中连接如下图

emio_vivado中连接

上图可知除了FIXED IO和DDR接口外,还多了3个3对(一个输入,一个输出和一个输出使能)GPIO管脚。

不同于MIO,这里三个IO管脚(一个输入,一个输出和一个输出使能在自动生成的顶层模块中合并为一个IO)要绑定到芯片对应管脚上

软件部分如下

#include <stdio.h>
#include "platform.h"
#include "xgpiops.h"

#define LED_R   54
#define LED_G   55
#define LED_B   56
#define LED_ON  0
#define LED_OFF 1

static void delay(int dly)
{
    int i, j;
    for (i = 0; i < dly; i++) {
        for (j = 0; j < 0xffff; j++) {
            ;
        }
    }
}

int main()
{
    int Status;
    XGpioPs_Config *ConfigPtr;
    XGpioPs Gpio;

    init_platform();

    ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
    Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
                    ConfigPtr->BaseAddr);
    if (Status != XST_SUCCESS) {
        print("cfg init err\n");
        return XST_FAILURE;
    }
    XGpioPs_SetDirectionPin(&Gpio, LED_R, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, LED_R, 1);
    XGpioPs_SetDirectionPin(&Gpio, LED_G, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, LED_G, 1);
    XGpioPs_SetDirectionPin(&Gpio, LED_B, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, LED_B, 1);

    while (1) {
        XGpioPs_WritePin(&Gpio, LED_R, LED_ON);
        delay(1000);
        XGpioPs_WritePin(&Gpio, LED_G, LED_ON);
        delay(1000);
        XGpioPs_WritePin(&Gpio, LED_B, LED_ON);
        delay(1000);
        XGpioPs_WritePin(&Gpio, LED_R, LED_OFF);
        delay(1000);
        XGpioPs_WritePin(&Gpio, LED_G, LED_OFF);
        delay(1000);
        XGpioPs_WritePin(&Gpio, LED_B, LED_OFF);
        delay(1000);
    }
    cleanup_platform();
}

类似MIO方式(都为PS部分GPIO操作),设置为输出并设置输出使能,但要注意这里的GPIO号是从54开始的3个。

IP方式实现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;
}

这里实现的功能与EMIO方式中功能相同,当时IP方式中为PL部分实现的GPIO,所以调用的函数与前面两种GPIO实现函数不同,注意包含的GPIO头文件,前两种是#include "xgpiops.h"而这最后一种为#include "xgpio.h"

总结

MIO和EMIO方式使用PS部分的GPIO模块,其中MIO方式不占用PL部分资源,其输出管脚只能为固定的54个(而且要在未被其它外设使用的情况下),EMIO方式会占用PL的管脚资源,其管脚可在PL部分任意选择(除特殊功能管脚),IP方式除了占用PL部分管脚资源外还会占用PL部分逻辑资源,所以其GPIO功能在PL部分实现其调用函数也和前两种不同,最后EMIO和IP方式在vivado都需要绑定管脚。

猜你喜欢

转载自blog.csdn.net/feifansong/article/details/80307687