ZYNQ7000中,GPIO的使用可以分为三种,即MIO、EMIO以及GPIO IP方式。其中MIO和EMIO方式是使用PS部分的GPIO硬件模块来实现GPIO功能,由于MIO是直接连接在硬核A9之上,它们可以输出三态(处理MIO7, MIO8外),并且支持IO复用,MIO共54个,引脚固定,大部分MIO用来作为外设(如ethernet, usb, qspi等)的引脚,因此MIO是比较稀缺的资源;相比之下EMIO则比较多,共64个,MIO与EMIO在硬件中按顺序编号,MIO为0~53, EMIO为54~117。GPIO IP方式则与前面两方式不同,IP方式占用PL资源,PS通过AXI总线来访问PL的IP核,从而实现GPIO功能,这个方式最大的优点是引脚资源多,PL部分有多少引脚能使用就有多少GPIO。
MIO的使用
开发板的原理图,PS端MIO0和MIO9连接在LED上,因此只使用这两个IIO进行试验。在之前的模板工程基础上进行开发,双击ZYNQ7 IP, 选择Peripheral I/O Pins,勾选GPIO MIO的0号和9号,在最小系统中没有使用的IO只有0, 7~15(使用的是QSPI Flash)。
编译->综合->生成bit流->导出硬件到SDK,在Xilinx SDK软件中,在原来的test工程(以Hello world为模板的工程,未修改)中添加MIO的配置文件。
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xgpiops.h" #include "sleep.h" //此宏定义对应于MIO的编号 #define LED1 0 #define LED2 9 XGpioPs gpio_mio; int MIO_Config(void); void MIO_LED(int led, int status); int main() { init_platform(); xil_printf("test for GPIO MIO\n\r"); MIO_Config(); while(1) { MIO_LED(LED1, 0); MIO_LED(LED2, 1); sleep(1); MIO_LED(LED1, 1); MIO_LED(LED2, 0); sleep(1); } cleanup_platform(); return 0; } int MIO_Config(void) { XGpioPs_Config *gpioPtr; int status; //每个外设都有一个ID,GPIO的ID在xparameters.h中定义 gpioPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); status = XGpioPs_CfgInitialize(&gpio_mio, gpioPtr, gpioPtr->BaseAddr); if(status != XST_SUCCESS) { xil_printf("can't config gpio\n\r"); return XST_FAILURE; } //配置GPIO为输出模式 XGpioPs_SetDirectionPin(&gpio_mio, LED1, 1); XGpioPs_SetDirectionPin(&gpio_mio, LED2, 1); //使能输出 XGpioPs_SetOutputEnablePin(&gpio_mio, LED1, 1); XGpioPs_SetOutputEnablePin(&gpio_mio, LED2, 1); return XST_SUCCESS; } void MIO_LED(int led, int status) { //写GPIO的输出值 XGpioPs_WritePin(&gpio_mio, led, status); }
编译运行,在终端打印了"test for GPIO MIO",开发板上的两个绿色LED交替闪烁,间隔为1秒。
二. EMIO的使用
三色LED连接的是PL端的IO,因此使用三个EMIO来点亮三色LED。
2.1 在ZYNQ7 IP核配置中,选择Peripheral I/O Pins, 勾选GPIO EMIO, 在MIO Configuration中选择GPIO->EMIO GPIO(width),选择位宽为3
2.2 编译,综合工程,打开引脚配置界面,配置EMIO的三个引脚对应为三色LED
bit流文件,导出硬件设计,开始编写EMIO的驱动。
2.3 新建一个应用程序,仍然以hello world为模板,但不重新生成bsp工程,添加以下程序
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xgpiops.h" #include "sleep.h" //此宏定义对应于EMIO的编号 #define LEDR 54 #define LEDG 55 #define LEDB 56 #define LED_ON 0 #define LED_OFF 1 XGpioPs gpio_emio; int EMIO_Config(void); void EMIO_LED(int led, int status); int main() { init_platform(); xil_printf("test for GPIO EMIO\n\r"); EMIO_Config(); while(1) { EMIO_LED(LEDR, 0); EMIO_LED(LEDG, 1); EMIO_LED(LEDB, 1); sleep(1); EMIO_LED(LEDR, 1); EMIO_LED(LEDG, 0); EMIO_LED(LEDB, 1); sleep(1); EMIO_LED(LEDR, 1); EMIO_LED(LEDG, 1); EMIO_LED(LEDB, 0); sleep(1); } cleanup_platform(); return 0; } int EMIO_Config(void) { XGpioPs_Config *gpioPtr; int status; //每个外设都有一个ID,GPIO的ID在xparameters.h中定义 gpioPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); status = XGpioPs_CfgInitialize(&gpio_emio, gpioPtr, gpioPtr->BaseAddr); if(status != XST_SUCCESS) { xil_printf("can't config gpio\n\r"); return XST_FAILURE; } //配置GPIO为输出模式 XGpioPs_SetDirectionPin(&gpio_emio, LEDR, 1); XGpioPs_SetDirectionPin(&gpio_emio, LEDG, 1); XGpioPs_SetDirectionPin(&gpio_emio, LEDB, 1); //使能输出 XGpioPs_SetOutputEnablePin(&gpio_emio, LEDR, 1); XGpioPs_SetOutputEnablePin(&gpio_emio, LEDG, 1); XGpioPs_SetOutputEnablePin(&gpio_emio, LEDB, 1); return XST_SUCCESS; } void EMIO_LED(int led, int status) { //写GPIO的输出值 XGpioPs_WritePin(&gpio_emio, led, status); }
重新下载bit流文件,运行应用程序,可以看到三色灯按红->绿->蓝顺序循环闪烁。
对比MIO的工程可以发现,两个工程的驱动配置几乎一模一样,唯一需要注意的是使用EMIO时GPIO的编号从54开始,54对应EMIO的0号,依次类推。
三. AXI GPIO IP的使用
3.1 取消之前的EMIO,然后配置GP0端口,PS-PL Configuration->AXI Non Secure Enablement->GP Master AXI Interface, 勾选GP0,注意不要勾选成了GP Slave的端口。
3.2 在原理图中添加AXI GPIO的IP,自动连接信号线
3.3双击axi_gpio_0,配置GPIO为3bit,全部设置为输出模式
3.4 编译综合,配置gpio_led的引脚为三色LED对应的引脚,然后在SDK中设计软件驱动
3.5 新建一个应用程序,以Hello world为模板,编写AXI GPIO驱动代码如下:
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "sleep.h" #include "xgpio.h" #define AXI_GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID #define LEDR 1 #define LEDG 2 #define LEDB 4 #define LED_ON 1 #define LED_OFF 0 XGpio gpio; int AXIGPIO_Config(void); void AXIGPIO_LED(int led, int status); int main() { init_platform(); print("AXI GPIO test!\n\r"); AXIGPIO_Config(); while(1) { AXIGPIO_LED(LEDR, LED_ON); AXIGPIO_LED(LEDG, LED_OFF); AXIGPIO_LED(LEDB, LED_OFF); sleep(1); AXIGPIO_LED(LEDR, LED_OFF); AXIGPIO_LED(LEDG, LED_ON); AXIGPIO_LED(LEDB, LED_OFF); sleep(1); AXIGPIO_LED(LEDR, LED_OFF); AXIGPIO_LED(LEDG, LED_OFF); AXIGPIO_LED(LEDB, LED_ON); sleep(1); } cleanup_platform(); return 0; } int AXIGPIO_Config(void) { XGpio_Config *XGpioCfg; int status; XGpioCfg = XGpio_LookupConfig(AXI_GPIO_DEVICE_ID); status = XGpio_CfgInitialize(&gpio, XGpioCfg, XGpioCfg->BaseAddress); if(status != XST_SUCCESS){ print("can't config axi gpio\n\r"); return XST_FAILURE; } //设置IO方向为输出 XGpio_SetDataDirection(&gpio, 1, ~(LEDR | LEDG | LEDB)); //初始写1 XGpio_DiscreteWrite(&gpio, 1, LEDR | LEDG | LEDB); return XST_SUCCESS; } void AXIGPIO_LED(int led, int status) { u32 temp; temp = XGpio_DiscreteRead(&gpio, 1); if(status) XGpio_DiscreteWrite(&gpio, 1, temp & (~led)); else XGpio_DiscreteWrite(&gpio, 1, temp | led); }
下载bit流文件,运行应用程序,则与EMIO工程表现出一样的效果。
四. 总结
介绍了ZYNQ的三种GPIO的使用方法,各有各的优点,下表列出了它们的却别对别:
GPIO方式 | MIO | EMIO | AXI GPIO |
---|---|---|---|
硬件实现方式 | 硬核资源 | 硬核资源 | |
引脚 | |||
功能 | 输入,输出,三态 | ||
驱动库函数 | xgpiops.h | xgpiops.h | xgpio.h |
BANK区 | BANK0,1 | BANK2,3 | PL |
特点 | 使用PL的引脚PS的硬件 |