在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。
本文基于前面的工程建立:ZYNQ最小Linux系统硬件设计
下面介绍着三种方式下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 |
硬件实现 | 硬核资源 | 硬核资源 | PL资源 |
引脚 | PS固定引脚 | PL部分引脚 | PL部分引脚 |
功能 | 输入,输出,三态 | 输入,输出 | 输入,输出 |
驱动库函数 | xgpiops.h | xgpiops.h | xgpio.h |
BANK区 | BANK0,1 | BANK2,3 | PL |
特点 | 资源少,大部分MIO作为特殊功能 使用 | 占用PL的引脚 | 占用PL的逻辑资源 |
欢迎关注亦梦云烟的微信公众号: 亦梦智能计算