本文介绍在zynq中三种实现GPIO的方式,分别为MIO、EMIO和IP方式。
而IP方式是在PL部分实现 GPIO功能,PS部分通过M_AXI_GP接口来控制该GPIO IP模块;另外EMIO模块虽然使用PS部分GPIO但也使用了PL部分的管脚资源。
MIO方式实现GPIO
vivado中zynq设置如下图
由图中可见要选中打开GPIO,其下自动显示可用于GPIO的MIO(当MIO作为其他功能时就不能作为GPIO使用了),其中MIO 7、MIO 8只能作为输出使用,因为它们用于VMODE管脚(参考UG585第14章:14.2.3)
软件部分如下
1 #include <stdio.h> 2 #include "platform.h" 3 #include "xgpiops.h" 4 5 #define LED1 0 6 #define LED2 9 7 8 static void delay(int dly) 9 { 10 int i, j; 11 for (i = 0; i < dly; i++) { 12 for (j = 0; j < 0xffff; j++) { 13 ; 14 } 15 } 16 } 17 18 int main() 19 { 20 int Status; 21 XGpioPs_Config *ConfigPtr; 22 XGpioPs Gpio; 23 24 init_platform(); 25 26 ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); 27 Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, 28 ConfigPtr->BaseAddr); 29 if (Status != XST_SUCCESS){ 30 return XST_FAILURE; 31 } 32 33 XGpioPs_SetDirectionPin(&Gpio, LED1, 1); 34 XGpioPs_SetDirectionPin(&Gpio, LED2, 1); 35 XGpioPs_SetOutputEnablePin(&Gpio, LED1, 1); 36 XGpioPs_SetOutputEnablePin(&Gpio, LED2, 1); 37 38 while (1) { 39 XGpioPs_WritePin(&Gpio, LED1, 0); 40 XGpioPs_WritePin(&Gpio, LED2, 1); 41 delay(1000); 42 XGpioPs_WritePin(&Gpio, LED1, 1); 43 XGpioPs_WritePin(&Gpio, LED2, 0); 44 delay(1000); 45 } 46 cleanup_platform(); 47 }
EMIO方式实现GPIO
vivado中zynq设置如下图
图中可知GPIO中选择使用EMIO,并选择位宽(这里例子中选择3);其vivado中连接如下图
上图可知除了FIXED IO和DDR接口外,还多了3个3对(一个输入,一个输出和一个输出使能)GPIO管脚。
不同于MIO,这里三个IO管脚(一个输入,一个输出和一个输出使能在自动生成的顶层模块中合并为一个IO)要绑定到芯片对应管脚上
软件部分如下
1 #include <stdio.h> 2 #include "platform.h" 3 #include "xgpiops.h" 4 5 #define LED_R 54 6 #define LED_G 55 7 #define LED_B 56 8 #define LED_ON 0 9 #define LED_OFF 1 10 11 static void delay(int dly) 12 { 13 int i, j; 14 for (i = 0; i < dly; i++) { 15 for (j = 0; j < 0xffff; j++) { 16 ; 17 } 18 } 19 } 20 21 int main() 22 { 23 int Status; 24 XGpioPs_Config *ConfigPtr; 25 XGpioPs Gpio; 26 27 init_platform(); 28 29 ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); 30 Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, 31 ConfigPtr->BaseAddr); 32 if (Status != XST_SUCCESS) { 33 print("cfg init err "); 34 return XST_FAILURE; 35 } 36 XGpioPs_SetDirectionPin(&Gpio, LED_R, 1); 37 XGpioPs_SetOutputEnablePin(&Gpio, LED_R, 1); 38 XGpioPs_SetDirectionPin(&Gpio, LED_G, 1); 39 XGpioPs_SetOutputEnablePin(&Gpio, LED_G, 1); 40 XGpioPs_SetDirectionPin(&Gpio, LED_B, 1); 41 XGpioPs_SetOutputEnablePin(&Gpio, LED_B, 1); 42 43 while (1) { 44 XGpioPs_WritePin(&Gpio, LED_R, LED_ON); 45 delay(1000); 46 XGpioPs_WritePin(&Gpio, LED_G, LED_ON); 47 delay(1000); 48 XGpioPs_WritePin(&Gpio, LED_B, LED_ON); 49 delay(1000); 50 XGpioPs_WritePin(&Gpio, LED_R, LED_OFF); 51 delay(1000); 52 XGpioPs_WritePin(&Gpio, LED_G, LED_OFF); 53 delay(1000); 54 XGpioPs_WritePin(&Gpio, LED_B, LED_OFF); 55 delay(1000); 56 } 57 cleanup_platform(); 58 }
类似MIO方式(都为PS部分GPIO操作),设置为输出并设置输出使能,但要注意这里的GPIO号是从54开始的3个。
IP方式实现GPIO
vivado中zynq设置如下图
图中可知GPIO中MIO和EMIO都不选择,但要打开M_AXI_GP接口(这里选择M_AXI_GP0)和复位管脚,如下图
当然用到了PL部分逻辑则至少需要一个时钟输出到PL部分,这里选择FCLK_CLK0输出50MHz,如下图
推荐加入zynq后,不要自动连接,再加入gpio并位宽设置为3,具体设置如下图
GPIO设置好后,再点击上面的蓝色字体的自动连接,即可得到上面的连接,这样可以减少手动连接量。
最后vivado中连接如下图
与EMIO类似需要将顶层三个GPIO管脚要绑定到芯片对应管脚上。
软件部分如下
1 #include <stdio.h> 2 #include "platform.h" 3 #include "xgpio.h" 4 5 #define AXI_GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID 6 #define XGPIO_BANK1 1 7 #define XGPIO_BANK2 2 8 9 #define LED34_R_PIN 0x01 10 #define LED34_G_PIN 0x02 11 #define LED34_B_PIN 0x04 12 13 static void delay(int dly) 14 { 15 int i, j; 16 for (i = 0; i < dly; i++) { 17 for (j = 0; j < 0xffff; j++) { 18 ; 19 } 20 } 21 } 22 23 int main() 24 { 25 XGpio_Config *XGpioCfg; 26 XGpio XGpio; 27 int Status; 28 29 init_platform(); 30 31 XGpioCfg = XGpio_LookupConfig(AXI_GPIO_DEVICE_ID); 32 Status = XGpio_CfgInitialize(&XGpio, XGpioCfg, XGpioCfg->BaseAddress); 33 if (Status != XST_SUCCESS) { 34 return XST_FAILURE; 35 } 36 37 XGpio_SetDataDirection(&XGpio, XGPIO_BANK1, ~(LED34_R_PIN | LED34_G_PIN | LED34_B_PIN)); 38 XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, LED34_R_PIN | LED34_G_PIN | LED34_B_PIN); 39 while (1) { 40 XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~LED34_R_PIN); 41 delay(1000); 42 XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_R_PIN | LED34_G_PIN)); 43 delay(1000); 44 XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_R_PIN | LED34_G_PIN | LED34_B_PIN)); 45 delay(1000); 46 XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_G_PIN | LED34_B_PIN)); 47 delay(1000); 48 XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, ~(LED34_B_PIN)); 49 delay(1000); 50 XGpio_DiscreteWrite(&XGpio, XGPIO_BANK1, LED34_R_PIN | LED34_G_PIN | LED34_B_PIN); 51 delay(1000); 52 } 53 cleanup_platform(); 54 return 0; 55 }
这里实现的功能与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都需要绑定管脚。