學會Zynq(5)GPIO中EMIO的使用方法

FPGADesigner發表於2019-03-11

之前的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。

在這裡插入圖片描述
IP Integrator中選中GPIO_0,右鍵->Make External建立埠,如下圖。
在這裡插入圖片描述
如果想修改新增埠名稱,單擊選中,在External Interface Properties視窗中修改(白板的中是不能修改的):
在這裡插入圖片描述
由於我們使用了PL中的管腳,所以必須要進行管腳約束。開啟任一設計階段的I/O Planning檢視,在I/O Ports視窗中可以看到4個EMIO埠(都是inout雙向埠)。根據硬體情況設定管腳號和電平標準,儲存為XDC檔案(當然也可以直接編輯XDC檔案)。
在這裡插入圖片描述
生成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介紹

在這裡插入圖片描述
關於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都可以使用中斷功能,這部分內容在後面用到時再做介紹。

相關文章