学会Zynq(5)GPIO中EMIO的使用方法

时间:2024-03-30 07:46:35

之前的Hello World和MIO使用都算是纯PS部分,也就是把Zynq单纯地当作ARM使用。很多人都是因为FPGA+ARM架构才使用的Zynq,有两个关键问题容易引起初学者的兴趣:(1).如何用PS控制PL;(2).如何完成PS与PL之间的数据通信。本系列会介绍解决这两个问题的各种方法。

EMIO就是PS控制PL资源的简单例子。EMIO就是可扩展的MIO,当与PS直接相连的MIO不够用时,可以使用EMIO做“扩展”。使用体会上,感觉就是ARM直接控制了PL部分的管脚。GPIO的bank2和bank3就是通过EMIO接口与PL相连的,本文将先通过PS控制PL部分流水灯的实例感受下EMIO的使用,然后再介绍EMIO相关的基本概念。


Zynq设计与代码详解

建立一个工程,配置好Zynq的时钟和DDR后,需要在MIO Configuration->I/O Peripherals->GPIO中选中EMIO GPIO。控制4个LED的流水,则EMIO GPIO(Width)选择4,相当于扩展了4个GPIO。

学会Zynq(5)GPIO中EMIO的使用方法
IP Integrator中选中GPIO_0,右键->Make External创建端口,如下图。
学会Zynq(5)GPIO中EMIO的使用方法
如果想修改添加端口名称,单击选中,在External Interface Properties窗口中修改(白板的中是不能修改的):
学会Zynq(5)GPIO中EMIO的使用方法
由于我们使用了PL中的管脚,所以必须要进行管脚约束。打开任一设计阶段的I/O Planning视图,在I/O Ports窗口中可以看到4个EMIO端口(都是inout双向端口)。根据硬件情况设置管脚号和电平标准,保存为XDC文件(当然也可以直接编辑XDC文件)。
学会Zynq(5)GPIO中EMIO的使用方法
生成bit流后导入到SDK中。SDK中新建工程,添加源文件,代码清单如下:

#include "xgpiops.h"
#include "sleep.h"

XGpioPs GpioPs_Init()
{
	XGpioPs_Config* GpioConfigPtr;
	XGpioPs psGpioInstancePtr;

	GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
	XGpioPs_CfgInitialize(&psGpioInstancePtr, GpioConfigPtr, GpioConfigPtr->BaseAddr);

	return psGpioInstancePtr;
}

int main()
{
	static XGpioPs psGpioInstancePtr;
	psGpioInstancePtr = GpioPs_Init(psGpioInstancePtr);   //GPIO初始化

	//EMIO配置为输出
	XGpioPs_SetDirectionPin(&psGpioInstancePtr, 54,1);
    XGpioPs_SetDirectionPin(&psGpioInstancePtr, 55,1);
    XGpioPs_SetDirectionPin(&psGpioInstancePtr, 56,1);
    XGpioPs_SetDirectionPin(&psGpioInstancePtr, 57,1);

    //使能EMIO输出
    XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 54,1);
    XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 55,1);
    XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 56,1);
    XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, 57,1);

	while(1)
	{
		XGpioPs_WritePin(&psGpioInstancePtr, 54, 1);//EMIO的第0位输出1
		usleep(200000);	//延时
		XGpioPs_WritePin(&psGpioInstancePtr, 54, 0);//EMIO的第0位输出0
		usleep(200000);	//延时
		XGpioPs_WritePin(&psGpioInstancePtr, 55, 1);//EMIO的第1位输出1
		usleep(200000);	//延时
		XGpioPs_WritePin(&psGpioInstancePtr, 55, 0);//EMIO的第1位输出0
		usleep(200000);	//延时
		XGpioPs_WritePin(&psGpioInstancePtr, 56, 1);//EMIO的第2位输出1
		usleep(200000);	//延时
		XGpioPs_WritePin(&psGpioInstancePtr, 56, 0);//EMIO的第2位输出0
		usleep(200000);	//延时
		XGpioPs_WritePin(&psGpioInstancePtr, 57, 1);//EMIO的第3位输出1
		usleep(200000);	//延时
		XGpioPs_WritePin(&psGpioInstancePtr, 57, 0);//EMIO的第3位输出0
		usleep(200000);	//延时
	}
    return 0;
}

用到的API函数与上一篇MIO的完全相同,不再赘述。不过注意54个MIO占用的管脚号为053;64个EMIO占用的管脚号为54117。这次用的延时函数为usleep,该函数输入参数为long型,延时以微妙为单位。


EMIO介绍

学会Zynq(5)GPIO中EMIO的使用方法
关于GPIO的相关概念已经在上一篇中讲述。EMIO只是GPIO信号的bank2、bank3与PL部分连接的接口,使用的寄存器接口与MIO的完全相同。这里对有差别的相关操作进行补充说明。

  • 输入是来自PL的连线,与输出值或OEN寄存器无关。若DIRM设置为0,可以从DATA_RO寄存器中读取输入值。
  • 输出不支持三态,因此不受OEN寄存器的控制。输出时将DIRM设置为1,使用DATA、MASK_DATA_LSW、MASK_DATA_MSK寄存器配置输出值。
  • 输出使能线由PS输出到PL,这些使能线受到DIRM和OEN寄存器的控制,如EMIOGPIOTN[x]=DIRM[X]&OEN[X]。

EMIO的I/O不能与MIO的I/O连接在一起的:EMIO的输入不能与MIO的输出接在一起;MIO的输入也不能与EMIO的输出结在一起。这是因为它们对应的GPIO属于不同的bank,每个bank都是独立的。


总结

本系列4-5篇介绍了Zynq中GPIO的使用,包括MIO和EMIO。UG585中还详细介绍了GPIO的各种操作流程和示例,但在SDK中实际编程时,已经有了封装好的库函数,一般学习这些函数的用法即可。此外,每个GPIO都可以使用中断功能,这部分内容在后面用到时再做介绍。