學會Zynq(4)GPIO中MIO的使用方法
本文將介紹PS部分GPIO中MIO的使用。本文先通過一個控制LED閃爍的例項體會MIO的用法,學習GPIO相關結構體與API函式的使用;然後再系統講解GPIO的相關概念。
Zynq設計與程式碼詳解
與第1篇相似,建立一個工程,配置好Zynq的時鐘和DDR後,需要在MIO Configuration->I/O Peripherals->GPIO中選中GPIO MIO。一般設計中配置的UART、乙太網等外設會佔用一部分MIO,這裡列表中會顯示剩餘可用的MIO。
配置完成後按流程匯入到SDK中。SDK中新建一個空白工程,src目錄上右鍵->New->File,彈出視窗中填寫帶字尾的完整檔名,如“main.c”。
開發板的LED與MIO7相連,控制其閃爍的程式碼清單如下:
#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;
int iPinNumber = 7; //MIO7,與LED相連
u32 uPinDirection = 0x1; //1表示輸出,0表示輸入
psGpioInstancePtr = GpioPs_Init(psGpioInstancePtr); //GPIO初始化
XGpioPs_SetDirectionPin(&psGpioInstancePtr, iPinNumber, uPinDirection); //MIO7配置為輸出
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, iPinNumber, 1); //使能MIO7
while(1)
{
XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 1); //點亮
sleep(1); //延時
XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 0); //熄滅
sleep(1); //延時
}
return 0;
}
程式和STM32很相似,由於這是第一次接觸Zynq的應用程式程式碼,我們深入瞭解一下其中的細節。程式中有兩個綠色的結構體,XGpioPs_Config儲存了器件的配置資訊;使用者需要為系統中的GPIO裝置分配一個XGpioPs型別的變數,GPIO相關的API函式都需要一個指向該變數的指標。SDK中滑鼠移動到結構體上即可檢視其原型:
typedef struct {
u16 DeviceId; /* 每個裝置都有一個單獨的ID */
u32 BaseAddr; /* GPIO的暫存器基地址 */
} XGpioPs_Config;
typedef struct {
XGpioPs_Config GpioConfig; /* 裝置配置 */
u32 IsReady; /* 裝置是否例項化與準備 */
XGpioPs_Handler Handler; /* 所有bank的狀態處理 */
void *CallBackRef; /* bank處理的回撥參考 */
u32 Platform; /* 平臺資料 */
u32 MaxPinNum; /* GPIO的最大管教號 */
u8 MaxBanks; /* GPIO中的最大bank號 */
} XGpioPs;
GPIO初始化函式中首先用到了XGpioPs_LookupConfig函式,右鍵->Open Declaration即可檢視函式原型。檢視原始檔我們可以知道,這個函式是根據唯一的裝置ID來查詢裝置配置,其輸入引數為要查詢的裝置ID號,返回值為一個XGpioPs_Config型別的指標,如果沒有找到則返回NULL。上面程式中裝置ID使用了巨集定義XPAR_PS7_GPIO_0_DEVICE_ID,同樣可以通過Open Declaration檢視源定義。
接下來使用XGpioPs_CfgInitialize函式完成對XGpioPs裝置的初始化,第一個引數為待例項化的XGpioPs裝置的指標(所以上面程式中加了取地址符&);第二個引數為指向XGpioPs裝置的配置結構體;第三個地址為裝置在虛擬記憶體空間中的基地址,上面程式中通過訪問XGpioPs_Config結構體的成員來獲取基地址。
//兩個函式的介面
XGpioPs_Config *XGpioPs_LookupConfig(u16 DeviceId){}
s32 XGpioPs_CfgInitialize(XGpioPs *InstancePtr, XGpioPs_Config *ConfigPtr, u32 EffectiveAddr){}
主程式中先定義了int型的要操作的MIO管腳號,7即表示MIO7。主程式中初始化GPIO後,先用XGpioPs_SetDirectionPin函式設定指定管腳的方向,第一個引數為指向XGpioPs裝置的指標;第二個引數為操作的管腳號;第三個引數位要設定的方向,0表示輸入,1表示輸出。
接下來使用XGpioPs_SetOutputEnablePin函式設定特定管腳的輸出使能,前兩個引數的含義與XGpioPs_SetDirectionPin函式相同;第三個引數為0表示禁止輸出使能,為1表示啟用輸出使能。
//兩個函式的介面
void XGpioPs_SetDirectionPin(XGpioPs *InstancePtr, u32 Pin, u32 Direction){}
void XGpioPs_SetOutputEnablePin(XGpioPs *InstancePtr, u32 Pin, u32 OpEnable){}
while迴圈中使用XGpioPs_WritePin函式向管腳寫資料,前兩個引數的含義與上面兩個函式相同;第三個引數為要寫入的資料,0或1。while中還用到sleep延遲函式,該函式的延時單位為秒,且傳入引數為int型,因此不要希望能實現延時0.5秒、1.5秒這樣的功能。
//兩個函式的介面
void XGpioPs_WritePin(XGpioPs *InstancePtr, u32 Pin, u32 Data){}
unsigned sleep(unsigned int seconds) {}
這樣我們就對GPIO相關操作和相關API函式有了清楚的認識。SDK在儲存檔案時會自動完成構建操作(編譯與連結)。進行Debug除錯(或直接執行),可以觀察到與PS的MIO7相連的LED燈以1s頻率閃爍,功能正確。
GPIO介紹
首先要清楚:GPIO訊號≠MIO或EMIO,這在Vivado中是兩個概念,MIO和EMIO只是GPIO訊號的兩種介面,很多初學者確把這些概念混淆。GPIO即General Purpose I/O,Zynq-7000中處理器的GPIO具有如下特性:
- 54個GPIO訊號通過MIO與裝置管腳直接相連,且支援三態輸出;
- 192個GPIO訊號通過EMIO介面連線PS與PL部分,64個為PL到PS的輸入,128個為PS到PL的輸出;
- 每個GPIO可以單個或以組為單位進行動態程式設計;
- 支援使能、按bit寫資料、按bank寫資料、輸出使能、方向控制功能;
- 每個GPIO都可配置為中斷敏感,支援原始與遮蔽中斷的狀態讀取,敏感源支援電平敏感(高電平或低電平)、邊沿敏感(上升沿、下降沿或兩者同時)。
GPIO按相相關聯的介面訊號分為4個bank。GPIO控制和狀態暫存器在記憶體中對映的基地址為)0xE000_A000。GPIO的模組框圖如下所示,其中bank0和bank1是通過MIO直接與Zynq管腳相連的部分。另外注意7z007單核與7z010雙核CLG225晶片的MIO只有32個,而非其它型號的54個。
通過軟體對GPIO或bank的控制,實質上就是對一系列記憶體對映暫存器的控制,主要是slcr.MIO_PIN_xx暫存器,只不過MIO和EMIO之間會有一些差別。
器件管腳的GPIO控制
軟體可以將GPIO配置為輸出或輸入模式。很多時候,應用程式會需要同時切換多個GPIO。同時切換的GPIO必須來自同一個bank的高16bits或低16bits,使用一個儲存指令完成對MASK_DATA暫存器的寫入。從上圖可以注意到,由於MIO只有54個,因此bank1只有22位。
GPIO通道的結構框圖如下:
與MIO相關的(bank0和bank1)bank控制暫存器如下,EMIO在下一篇講述:
- DATA_RO:無論輸入、輸出模式,軟體都可通過這個暫存器來獲取器件管腳上的值。不能向該暫存器寫值。但如果MIO沒有配置為使能該GPIO管腳,從DATA_RO中讀到的值將是無法預料的。
- DATA:當GPIO訊號配置為輸出時,該暫存器控制輸出的值,一次寫入一個bank所有的32bits。讀取該暫存器可以得到前一個寫入該暫存器的值。
- MASK_DATA_LSW:控制bank的低16bits。
- MASK_DATA_MSW:控制bank的高16bits。
- DIRM:選擇管腳方向為輸入或輸出。實際上GPIO的輸入邏輯總是使能狀態,DIRM控制的其實是輸出驅動器的使能狀態。當DIRM[x]==0時,禁用輸出驅動器,則GPIO處於輸入模式。
- OEN:當GPIO配置為輸出時,OEN控制輸出是否使能。當OEN[x]==0禁用輸出時,管腳處於三態狀態。
54個與MIO相連的GPIO訊號中有兩個特例,bank0的bits[7]和bits[8]。在Vivado中配置GPIO時我們會發現,與這兩個訊號相連的MIO7和MIO8只能作為輸出out,而其它MIO都可以作為雙向inout。
其實與這兩個訊號相關的管腳在復位期間要用來控制I/O緩衝器的電壓模式,稱作VMODE管腳。這兩個管腳必須根據合適的電壓模式,由外部系統驅動,而不能有其它系統邏輯驅動,因此不能作為輸出。但是系統啟動之後,已經讀取了電壓模式,系統便可以將MIO7和MIO8作為輸出使用。
總結
最後還是要強調,GPIO才是與處理器直接相關的,分為bank0~bank3四個組。MIO和EMIO只是GPIO訊號與外界連線的介面,bank0、bank1通過MIO相連,bank2、bank3通過EMIO相連。哪怕我們在實際使用過程中就是把MIO當作GPIO來看待,沒有出現任何問題,但我認為學習應該保持嚴謹的態度,搞清這些基礎的概念,也是專業素養的一種體現。
相關文章
- 學會Zynq(5)GPIO中EMIO的使用方法
- Xilinx ZYNQ 7000+Vivado2015.2系列(四)之GPIO的三種方式:MIO、EMIO、AXI_GPIO
- ZYNQ 中PS端GPIO EMIO使用
- 學會Zynq(8)PL中斷示例(SPI)
- ZYNQ的gpio的硬體驅動庫知識
- 學會Zynq(7)中斷系統簡介
- 學會Zynq(3)Zynq的軟體開發基礎知識
- 學會Zynq(2)Zynq-7000處理器的配置詳解
- 有關GPIO的使用方法教程
- 學會Zynq(10)lwIP簡介
- 學會Zynq(1)搭建Zynq-7000 AP SoC處理器
- filezilla使用,4步學會filezilla使用方法
- 學會Zynq(12)lwIP 1.4.1庫的配置與使用
- 學會Zynq(9)定時器使用示例(PPI)定時器
- 學會Zynq(11)RAW API的TCP和UDP程式設計APITCPUDP程式設計
- FPGADesigner《學會Zynq》系列目錄與傳送門FPGA
- 學會Zynq(6)固化程式到SD卡或QSPI FlashSD卡
- baremetal GPIO中斷REM
- Xilinx-ZYNQ7000系列-學習筆記(7):解決ZYNQ IP核自動佈線後會更改原有配置的問題筆記
- 遠端會議的使用方法
- SLF4J 和 Logback 在 Maven 專案中的使用方法Maven
- 3.外設GPIO、中斷
- 立創泰山派學習03--GPIO的控制
- STM32學習筆記——GPIO筆記
- java中json的使用方法JavaJSON
- react中hooks的使用方法ReactHook
- Linux 中 ranger 的使用方法LinuxRanger
- YogaKit中 position 的使用方法
- jquery 中 $.map 的使用方法jQuery
- GPIO模式模式
- Java中try()catch{}的使用方法Java
- Go 中 io 包的使用方法Go
- SpringAop中JoinPoint物件的使用方法Spring物件
- 004:ZYNQ_AXI匯流排學習筆記(1)筆記
- 4G模組Air724UG軟體的GPIO教程 | 技術版!AI
- vlookup函式不會用?詳細使用方法就在這兒,用心學!函式
- Sql中SYSDATE函式的使用方法SQL函式
- postgresql和mysql中的limit使用方法MySqlMIT