6.0 本章難度系數★★☆☆☆☆☆
6.1 GPIO簡介
Zynq7000系列芯片有54個MIO(multiuse I/O),它們分配在 GPIO 的Bank0 和Bank1隸屬於PS部分,這些IO與PS直接相連。不需要添加引腳約束,MIO信號對PL部分是透明的,不可見。所以對MIO的操作可以看作是純PS的操作。
GPIO的控制和狀態寄存器基地址為:0xE000_A000,我們SDK下軟件操作底層都是對於內存地址空間的操作。
Bank0:MI0[31:0]
Bank1:MI0[52:53]
Bank2:EMI0[31:0]
Bank3:EMI0[63:32]
6.1.1 GPIO的控制寄存器地址空間
我們在SDK下操作的時候底層都是對這些寄存器的操作,具體的相關參數請參考技術手冊ug585-Zynq-7000-TRM.pdf
6.1.2MIO內部構造分析
DATA_RO: 此寄存器使能軟件觀察PIN腳,當GPIO被配置成輸出的時候,這個寄存器的值會反應輸出的PIN腳情況。
DATA:此寄存器控制輸出到GPIO的值,讀這個寄存器的值可以讀到最后一次寫入該寄存器的值。
MASK_DATA_LSW:位操作寄存器,寫入GPIO 低16bit 其他沒有改變的位置保存原先的狀態
MASK_DATA_MSW:位操作寄存器,寫入GPIO 高16bit 其他沒有改變的位置保存原先的狀態
DIRM:此寄存器控制輸出的開關,當DIRM[x]==0時候,禁止輸出
OEN: 輸出使能,當OEN[x]==0 的時候輸出關閉,PIN腳處於三態
因此,如果要讀IO狀態就得讀DATA_RO的值,如果是對某一位進行操作就是寫MASK_DATA_LSW/MASK_DATA_MSW
具體的相關參數請參考技術手冊ug585-Zynq-7000-TRM.pdf
6.1.3 EMIO的特性
與MIO大部分類似但是一下幾點需要注意下
• EMIO在PL部分,輸入與OEN寄存器無關,當DIRM設置為0的時候設置為輸入可以讀DATA_RO寄存器獲取數據。
• 輸出不能設置成三態,當DIRM設置為1的時候為輸出,寫入DATA寄存器或者MASK_DATA_LSW/MASK_DATA_MSW寄存器
• EMIOGPIOTN[x]=DIRM[x] & OEN[x],實現輸出的控制。
具體的相關參數請參考技術手冊ug585-Zynq-7000-TRM.pdf
6.2 電路分析及實驗預期
在MiZ702的開發板上有一個MIO是與開發板上的一個LD9相連的,這個MIO就是MIO7。實驗通過操作該MIO來實現LD9的閃爍。
6.3 ZYNQ核的添加及配置
Step1:新建一個名為為Miz701_sys的工程
Step2:選擇RTL Project 勾選Do not specify source at this time
Step3:選擇芯片xc7z010clg400-1。
Step4:單擊Finish
6.4使用IP Integrator創建硬件系統
Step1:單擊Create Block Design
Step2:輸入system
Step4:搜素單詞z選擇ZYNQ7 Processing System,然后雙擊
Step5:添加進來了ZYNQ CPU IP,雙擊ZYNQ7 Processing System。
Step6: 修改時鍾輸入為50MHZ,可以看到ARM時鍾為650MHZ DDR為525MHZ(1050MHZ),並且修改FCLK_CLK0 為100MHZ。
Step7: 修改MIO的配置 修改IO電壓,增加ENT0 和UART1接口,在這個實驗中將會使用到MIO7確保勾選(默認勾選)單擊OK退出.
Step8:修改內存型號為MT41K256M16RE-125 M。
Step9:單機Run Block Automation 進行自動連線,VIVADO軟件會根據信號的命名規則智能連線。
Step10:勾選如下,直接單機OK
Step11:在你點擊了OK后,你會發現DDR以及FICED_IO自勱的延伸出來,然后把時鍾FCLK_CLK0和M_AXI_GPI0_ACLK連接。方法:當把鼠標靠近的時候會自動連接。
Step12:為了讓以太網PHY RTL8211E-VL可以工作,必須讓其復位PIN腳設置為1,只要簡單添加一個常量IP並且映射到PL部分的PIN腳(復位腳在FPGA 部分(PL)),和添加CPU方法一樣
Step13:雙擊設置常量為1
Step14:右擊PIN腳選擇Make External
Step15:取個有意義的名字如下圖,只要單擊相應的模塊就可以在右手邊修改
Step16: 右擊 system.bd, 單擊Generate Output Products
Step17:支部操作會產生執行、仿真、綜合的文件,可以看出來最后的硬件設計步驟還是回到了我們前面的FPGA開發上來了。
Step18:右擊system.bd 選擇 Create HDL Wrapper 這步的作用是產生頂層的HDL文件
Step19:選擇Leave Let Vivado manager wrapper and auto-update 然后單擊OK
Step20:單機Add Sources。
Step21:選擇Add or create constraints
Step22:選擇Create File
Step23:輸入zynq_pin。
Step24:如圖,單擊Finish
Step25:雙擊打開zynq_pin.xdc文件,添加PIN腳約束
6.5導出SOC硬件到SDK
Step1:File->Export->Export Hardware
Step2:勾選Include bitstream 直接單擊OK
Step3:File->Launch SDK加載到SDK
Step4:單擊OK
6.6 新建LED_Flash SDK工程
Step1:在SDK界面中,新建一個名為LED_Flash的工程,單擊Next。
Step2:建立一個空的工程
Step3:新建一個C的源文件
Step4:取名為LED_Flash.c
Step5:取名為LED_Flash.c
接下來就向LED_Flash.c中添加內容了,之前講過,其中MIO7接到了LD9這個燈上,接下來我們利用程序讓他閃起來。
#include "sleep.h" int main() { static XGpioPs psGpioInstancePtr; XGpioPs_Config* GpioConfigPtr; int iPinNumber= 7; //LD9連接的是MIO7 u32 uPinDirection = 0x1; //1表示輸出,0表示輸入 int xStatus; //--MIO的初始化 GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); if(GpioConfigPtr == NULL) return XST_FAILURE; xStatus = XGpioPs_CfgInitialize(&psGpioInstancePtr,GpioConfigPtr, GpioConfigPtr->BaseAddr); if(XST_SUCCESS != xStatus) print(" PS GPIO INIT FAILED \n\r"); //--MIO的輸入輸出操作 XGpioPs_SetDirectionPin(&psGpioInstancePtr, iPinNumber,uPinDirection);//配置MIO輸出方向 XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, iPinNumber,1);//配置MIO的第7位輸出 while(1) { XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 1);//點亮MIO的第7位輸出1 sleep(1); //延時 XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 0);//熄滅MIO的第7位輸出0 sleep(1); //延時 } return 0; } |
6.7 程序分析
具體內容請對照注釋說明,這里想說明的是iPinNumber這個參數,應為我們要操作的是MIO7,所以這里所以這里的iPinNumber等於7,在后一章的EMIO中也有這個參數,具體怎么算請參看下一節內容,這里就做個鋪墊吧。
6.8 IO寄存器直接輸出
實際上以上代碼的銷量是非常低的,因為封裝好的函數,里面有很多調用開銷,還有很多自動化的代碼,特別是當要求IO輸出速度很高的時候,差距會很明顯。筆者這里把IO部分的函數做一點小的改動,改為直接控制寄存器地址空間。
while(1) { //XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 1);//點亮MIO的第7位輸出1 Value = ~((u32)1 << (iPinNumber + 16U)) & ((1 << iPinNumber) | 0xFFFF0000U); Xil_Out32(0xE000A000,Value);//直接寄存器操作 sleep(1); //延時 Value = ~((u32)1 << (iPinNumber + 16U)) & ((0 << iPinNumber) | 0xFFFF0000U); Xil_Out32(0xE000A000,Value);// //XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 0);//熄滅MIO的第7位輸出0 sleep(1); //延時 } |
這個地址0xE000A000是不是很熟悉呢?沒錯這就是前面筆者貼出的一張表格,而且正是位控制 寄存器MASK_DATA_0_LSW
6.9電平翻轉速度庫函數和寄存器對比
庫函數測試函數
XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 1);//點亮MIO的第7位輸出1 XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 0);//熄滅MIO的第7位輸出0 |
寄存器測試函數
while(1) { Value = ~((u32)1 << (iPinNumber + 16U)) & ((1 << iPinNumber) | 0xFFFF0000U); Xil_Out32(0xE000A000,Value);//直接寄存器操作 Value = ~((u32)1 << (iPinNumber + 16U)) & ((0 << iPinNumber) | 0xFFFF0000U); Xil_Out32(0xE000A000,Value);// } |
庫函數測試函數出來的頻率
寄存器測試函數出來的頻率(由於示波器帶寬太低,波形已經變形)
6.10 本章小結
本章講解了ZYNQ芯片的GPIO的一些知識,然后通過使用SDK進行編程點亮一個LED。同時分析了程序的代碼。測試結果說明了,庫函數使用方便,但是效率地下,寄存器效率高,但是使用不方便。因此在設計系統的時候如何優化是需要綜合考慮的。