日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

ZYNQ7000-GPIO详解

發(fā)布時間:2025/3/13 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ZYNQ7000-GPIO详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

ZYNQ7000-GPIO詳解

參考:UG585 - Zynq-7000 SoC Technical Reference Manual (v1.12.2) 385~394頁–Ch14: General Purpose I/O(GPIO)

一. GPIO的基本概念

GPIO,General Purpose I/O,通用輸入/輸出,是ZYNQ的外設(shè)之一。ZYNQ的架構(gòu)圖如下圖所示。

與非SOC不同的是,ZYNQ的GPIO引腳由PS側(cè)的MIO引腳和PL側(cè)的EMIO引腳構(gòu)成(見上圖)。

關(guān)于MIO和EMIO的詳細介紹參見我的另一篇博客:傳送門:ZYNQ7000-MIO與EMIO詳解

二. GPIO框圖與分組

ZYNQ的GPIO框圖如下圖所示。ZYNQ的GPIO引腳分為4個Bank即4組,其中,

118個GPIO = 32個MIO(Bank0) + 22個MIO(Bank1)+ 32個EMIO(Bank2)+ 32個EMIO(Bank3)

可見基本都是32個IO引腳一組,這是因為GPIO的控制寄存器是32位的,每一位對應(yīng)一個IO引腳的話,一組寄存器就對應(yīng)32個引腳,所以,4組基本相同的寄存器分別控制4組Bank,Bank1對應(yīng)的也是一組32位寄存器,只是因為MIO引腳總共就54個,54-32=22,所以這組寄存器只控制22個引腳。

三. GPIO的功能與控制寄存器

GPIO的功能有三種:輸入,輸出,中斷使用MIO和EMIO在功能上幾乎相同,唯一的區(qū)別是EMIO因為是PL側(cè)引腳,可以與PL部分進行通訊,而MIO對PL側(cè)是透明的。

特別的,MIO7,MIO8只能做輸出,這在ZYNQ7000-MIO與EMIO詳解中有說明。

GPIO的功能在芯片內(nèi)部通過一組寄存器來控制,如下圖所示。注意,一組寄存器同時控制一個GPIO Bank的所有引腳

了解GPIO的控制器寄存器能幫助我們更深入的理解軟件中的相關(guān)庫函數(shù),對編程有些幫助,當(dāng)然,不了解也行,只要熟悉庫函數(shù)即可。

輸入/輸出控制寄存器:

寄存器名稱說明
DATA_ROdata read only(RO大概是這兩個單詞的縮寫吧),
GPIO引腳的值存儲在此寄存器中,無論GPIO被配置為輸入或輸出,都可以通過讀此寄存器得到GPIO引腳的值。
因為是只讀寄存器(對軟件來說),軟件向此寄存器的寫入操作將被忽略。
DATA輸出數(shù)據(jù)寄存器,當(dāng)GPIO被配置為輸出才起作用,此寄存器中的值就是輸出到引腳的值。
向此寄存器寫入就是在設(shè)置GPIO的輸出值,
讀此寄存器將返回GPIO前一時刻的輸出值,而不是現(xiàn)在的值。
MASK_DATA_LSWMask Data Least Significant Words,輸出數(shù)據(jù)低16位掩碼寄存器,此寄存器只有低16位有效,
對應(yīng)位為1表示DATA寄存器低16位中對應(yīng)位的值可以更改,
若不為1,則表示DATA寄存器低16位中對應(yīng)位保持原值
MASK_DATA_MSWMask Data Most Significant Words,輸出數(shù)據(jù)高16位掩碼寄存器,
功能同MASK_DATA_LSW,只是它對應(yīng)DATA寄存器高16位
DIRMDirection Memory,方向寄存器,默認為0表示輸入,設(shè)為1表示輸出
注意,即使DIRM為1,軟件也可以像輸入一樣去讀此引腳的電平。
OENOutput Enable,輸出使能寄存器,
僅當(dāng)DIRM為0時有效,為1表示輸出使能,
為0表示輸出不使能,此時對應(yīng)引腳上的值為三態(tài)值

中斷控制寄存器:

寄存器名稱說明
INT_TYPEInterrupt Type 中斷類型寄存器,
控制GPIO中斷是電平觸發(fā)還是邊緣觸發(fā)
INT_POLARITYInterrupt Polarity 中斷極性寄存器
控制GPIO中斷是低電平/下降沿有效,還是高電平/上升沿有效
INT_ANYInterrupt Any,雙邊沿寄存器,
僅當(dāng)INT_TYPE為邊沿觸發(fā)時,此寄存器才有效,控制是否雙沿均可觸發(fā)中斷
INT_STATInterrupt State,中斷狀態(tài)寄存器,
此寄存器的值會被與之相連的INT State D觸發(fā)器讀取
D觸發(fā)器存儲中斷狀態(tài),軟件通過讀此D觸發(fā)器輸出來判斷中斷是否發(fā)生,
清除此D觸發(fā)器來清除中斷狀態(tài)
INT_MASKInterrupt Mask,中斷掩碼寄存器,
顯示當(dāng)前哪些位被屏蔽,哪些位啟用
INT_DISInterrupt Disable,中斷失效寄存器,
向該寄存器的任何位寫入 1 都會屏蔽該中斷信號。
從該寄存器讀取會返回不可預(yù)測的值
INT_ENInterrupt Enable,中斷使能寄存器
向該寄存器的任何位寫入 1,可以啟用/解除中斷信號的掩碼。
從該寄存器讀取將返回一個不不可預(yù)測的值

四. GPIO中斷設(shè)置與說明

GPIO中斷號為52。此中斷的優(yōu)先級芯片內(nèi)部已經(jīng)固定好了,所以在軟件中配置GPIO中斷時,不需要指定GPIO中斷的優(yōu)先級。

GPIO所有引腳都是共享一個中斷的,這意味著如果兩個引腳的中斷都使能了,如果不去讀取具體引腳的電平,軟件無法判斷中斷具體來自哪個引腳。

中斷觸發(fā)類型設(shè)置:

五. 在Vitis中配置GPIO

我自建了GPIO相關(guān)庫函數(shù),將相關(guān)GPIO功能寫在一起。Xxk_PsGpio.c 與 Xxk_PsGpio.h。這里并沒有使用處理整個Bank的函數(shù),因為實際應(yīng)用時很好需要處理整個Bank,都是單獨處理某個Pin。

Xxk_PsGpio.h如下:

#ifndef XXK_PSGPIO_H #define XXK_PSGPIO_H// 包含xilinx庫中頭文件 #include "xil_printf.h" #include "xgpiops.h" #include "xscugic.h" #include "xil_exception.h"// 宏定義 #define __weak __attribute__((weak))// 與PS GPIO相關(guān)的宏定義 // 引腳宏定義 #define MIO12 12U#define EMIO0 54U #define EMIO1 55U #define EMIO2 56U #define EMIO3 57U #define EMIO4 58U// GPIO指的就是PS側(cè)的GPIO硬核,對于ZYNQ7,只有一個GPIO硬核 #define PSGPIO_INPUT 0U #define PSGPIO_OUTPUT 1U #define PSGPIO_OUTPUT_ENABLE 1U #define PSGPIO_OUTPUT_DISABLE 0U/* 中斷類型,已在xgpiops.h中定義,放在這里方便找到 #define XGPIOPS_IRQ_TYPE_EDGE_RISING 0x00U #define XGPIOPS_IRQ_TYPE_EDGE_FALLING 0x01U #define XGPIOPS_IRQ_TYPE_EDGE_BOTH 0x02U #define XGPIOPS_IRQ_TYPE_LEVEL_HIGH 0x03U #define XGPIOPS_IRQ_TYPE_LEVEL_LOW 0x04U */// PS GPIO相關(guān)函數(shù) // 初始化psGpio int psGpioInti(XGpioPs *psGpioPtr, u16 psGpio_deviceId); //psGpioInti(&psGpio, XPAR_XGPIOPS_0_DEVICE_ID);// 設(shè)置psGpio某引腳為輸出并使能 void psGpio_SetPinOutputAndEnbale(const XGpioPs *psGpioPtr, u32 Pin); //psGpio_SetPinOutputAndEnbale(&psGpio, EMIO0);// 設(shè)置psGpio某引腳為輸入,輸入無需使能 void psGpio_SetPinInput(const XGpioPs *psGpioPtr, u32 Pin); //psGpio_SetPinInput(&psGpio, EMIO0);// 向psGpio某引腳寫入0或1 extern void XGpioPs_WritePin(const XGpioPs *psGpioPtr, u32 Pin, u32 Data); //XGpioPs_WritePin(&psGpio, EMIO0, 1);// 讀取psGpio某引腳的電平,得到0或1 extern u32 XGpioPs_ReadPin(const XGpioPs *psGpioPtr, u32 Pin); // EMIO0_pinData = XGpioPs_ReadPin(&psGpio, EMIO0);// 中斷相關(guān)函數(shù) int scuGic_Inti(XScuGic *scuGicPtr, u16 scuGicID); //初始化中斷控制器 //scuGic_Inti(&scuGic, XPAR_XSCUTIMER_0_DEVICE_ID);void psGpio_PinIntr_SetAndEnable(XScuGic *scuGicPtr, XGpioPs *psGpioPtr, u32 psGpio_intrId,Xil_ExceptionHandler psGpio_Handler, u32 Pin, u8 IrqType); //psGpio_PinIntr_SetAndEnable(&scuGic, &psGpio, XPAR_XGPIOPS_0_INTR, // psGpio_Handler, EMIO0, XGPIOPS_IRQ_TYPE_EDGE_RISING);void psGpio_Handler(void *CallBackRef); // 需在main中重寫此弱函數(shù)#endif

Xxk_PsGpio.c 如下:

#include "Xxk_PsGpio.h"// 初始化PS側(cè)GPIO,包括MIO和EMIO int psGpioInti(XGpioPs *psGpioPtr, u16 psGpio_deviceId) {XGpioPs_Config *psGpio_configPtr;psGpio_configPtr = XGpioPs_LookupConfig(psGpio_deviceId); // 根據(jù)器件ID查找配置// 配置初始化配置int status;status = XGpioPs_CfgInitialize(psGpioPtr, psGpio_configPtr, psGpio_configPtr->BaseAddr);if (status != XST_SUCCESS){xil_printf("PsGpio %d Initialization Failed\r\n", psGpio_deviceId);return status;}status = XGpioPs_SelfTest(psGpioPtr);if (status != XST_SUCCESS){xil_printf("PsGpio %d SelfTest Failed\r\n", psGpio_deviceId);return status;}xil_printf("PsGpio %d Initialization Succeed\r\n", psGpio_deviceId);return status; }// 設(shè)置psGpio某引腳為輸入,輸入無需使能 void psGpio_SetPinInput(const XGpioPs *psGpioPtr, u32 Pin) {XGpioPs_SetDirectionPin(psGpioPtr, Pin, PSGPIO_INPUT); }// 設(shè)置psGpio某引腳為輸出并使能 void psGpio_SetPinOutputAndEnbale(const XGpioPs *psGpioPtr, u32 Pin) {XGpioPs_SetDirectionPin(psGpioPtr, Pin, PSGPIO_OUTPUT);XGpioPs_SetOutputEnablePin(psGpioPtr, Pin, PSGPIO_OUTPUT_ENABLE); }// 設(shè)置psGpio某引腳輸出不使能 void psGpio_SetPinOutputDisbale(const XGpioPs *psGpioPtr, u32 Pin) {XGpioPs_SetOutputEnablePin(psGpioPtr, Pin, PSGPIO_OUTPUT_DISABLE); }// PS GPIO PIN中斷使能 void psGpio_PinIntr_SetAndEnable(XScuGic *scuGicPtr, XGpioPs *psGpioPtr, u32 psGpio_intrId,Xil_ExceptionHandler psGpio_Handler, u32 Pin, u8 IrqType) {// 連接中斷ID與固定服務(wù)函數(shù)XScuGic_Connect(scuGicPtr, psGpio_intrId, (Xil_ExceptionHandler)psGpio_Handler, (void *)psGpioPtr);// 啟用對應(yīng)中斷ID的中斷源XScuGic_Enable(scuGicPtr, psGpio_intrId);// 注意psGPIO中斷不需要設(shè)置中斷優(yōu)先級// 設(shè)置psGPIO中斷類型XGpioPs_SetIntrTypePin(psGpioPtr, Pin, IrqType);// 在使能前先清除一次中斷,否則之前的中斷狀態(tài)可能殘留,導(dǎo)致燒寫后馬上進一次中斷XGpioPs_IntrClearPin(psGpioPtr, Pin);// 使能psGPIO中斷XGpioPs_IntrEnablePin(psGpioPtr, Pin); }// psGpio中斷處理弱函數(shù) __weak void psGpio_Handler(void *CallBackRef) {XGpioPs *psGpioPtr = (XGpioPs *)CallBackRef;// psGpio_intrFlag = 1;if (XGpioPs_IntrGetStatusPin(psGpioPtr, MIO12) == TRUE){XGpioPs_IntrDisablePin(psGpioPtr, MIO12);XGpioPs_IntrClearPin(psGpioPtr, MIO12);xil_printf("This is psGpio_Handler - MIO12\r\n");}else if (XGpioPs_IntrGetStatusPin(psGpioPtr, EMIO4) == TRUE){XGpioPs_IntrDisablePin(psGpioPtr, EMIO4);XGpioPs_IntrClearPin(psGpioPtr, EMIO4);xil_printf("This is psGpio_Handler - EMIO4\r\n");} }// 初始化中斷控制器ScuGic int scuGic_Inti(XScuGic *scuGicPtr, u16 scuGicID) {// 打開系統(tǒng)的中斷處理功能Xil_ExceptionInit(); // 初始化異常句柄,只在很早版本的bsp中需要,此處為了兼容性保留Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler,scuGicPtr); // 為IRQ注冊中斷處理程序Xil_ExceptionEnable(); // 使能系統(tǒng)中斷功能XScuGic_Config *scuGicConfig;scuGicConfig = XScuGic_LookupConfig(scuGicID); // 根據(jù)器件ID查找配置int status;status = XScuGic_CfgInitialize(scuGicPtr, scuGicConfig, scuGicConfig->CpuBaseAddress);if (status != XST_SUCCESS){xil_printf("ScuGic Initialization Failed\r\n");return XST_FAILURE;}xil_printf("ScuGic Initialization Succeed\r\n");return status; }

使用自建庫,GPIO輸入,輸出及中斷均有使用的main.c如下:

/*功能:控制EMIO0~2為輸出,EMIO4為輸入,EMIO3為中斷 */#include "Xxk_PsGpio.h" #include "sleep.h"// 全局變量 XGpioPs psGpio; XScuGic scuGic;int psGpio_EMIO_intrFlag = 0; int psGpio_MIO_intrFlag = 0;int main(int argc, char const *argv[]) {xil_printf("begin\r\n");// 初始化psGpiopsGpioInti(&psGpio, XPAR_XGPIOPS_0_DEVICE_ID);// 設(shè)置psGpio引腳方向psGpio_SetPinOutputAndEnbale(&psGpio, EMIO0);psGpio_SetPinOutputAndEnbale(&psGpio, EMIO1);psGpio_SetPinOutputAndEnbale(&psGpio, EMIO2);psGpio_SetPinInput(&psGpio, EMIO3);// 初始化中斷控制器scuGic_Inti(&scuGic, XPAR_XSCUTIMER_0_DEVICE_ID);// 設(shè)置并使能psGpio某引腳中斷psGpio_PinIntr_SetAndEnable(&scuGic, &psGpio, XPAR_XGPIOPS_0_INTR,psGpio_Handler, EMIO4, XGPIOPS_IRQ_TYPE_EDGE_FALLING);psGpio_PinIntr_SetAndEnable(&scuGic, &psGpio, XPAR_XGPIOPS_0_INTR,psGpio_Handler, MIO12, XGPIOPS_IRQ_TYPE_EDGE_FALLING);while (1){sleep(1);xil_printf("EMIO3 value: %d\r\n", XGpioPs_ReadPin(&psGpio, EMIO3));XGpioPs_WritePin(&psGpio, EMIO0, 1);XGpioPs_WritePin(&psGpio, EMIO1, 1);XGpioPs_WritePin(&psGpio, EMIO2, 0);if (psGpio_EMIO_intrFlag){xil_printf("This is psGpio_Handler - EMIO4\r\n");psGpio_EMIO_intrFlag = 0;XGpioPs_IntrEnablePin(&psGpio, EMIO4);}if (psGpio_MIO_intrFlag){xil_printf("This is psGpio_Handler - MIO12\r\n");psGpio_MIO_intrFlag = 0;XGpioPs_IntrEnablePin(&psGpio, MIO12);}sleep(1);XGpioPs_WritePin(&psGpio, EMIO0, 0);XGpioPs_WritePin(&psGpio, EMIO2, 1);}return 0; }void psGpio_Handler(void *CallBackRef) // 重寫弱函數(shù) {XGpioPs *psGpioPtr = (XGpioPs *)CallBackRef;if (XGpioPs_IntrGetStatusPin(psGpioPtr, MIO12) == TRUE){psGpio_MIO_intrFlag = 1;XGpioPs_IntrDisablePin(psGpioPtr, MIO12);XGpioPs_IntrClearPin(psGpioPtr, MIO12);}if (XGpioPs_IntrGetStatusPin(psGpioPtr, EMIO4) == TRUE){psGpio_EMIO_intrFlag = 1;XGpioPs_IntrDisablePin(psGpioPtr, EMIO4);XGpioPs_IntrClearPin(psGpioPtr, EMIO4);} }

六. GPIO的另一種實現(xiàn)方式 - AXI GPIO

本文介紹了如何使用MIO和EMIO實現(xiàn)GPIO,而對于ZYNQ來說,在PL中使用AXI GPIO IP核也可以實現(xiàn)GPIO功能,具體介紹參見我的另一篇博文。

傳送門:ZYNQ7000-AXI GPIO詳解

七. 總結(jié)

本文介紹了ZYNQ7000中GPIO的基本概念,GPIO是ZYNQ PS側(cè)最簡單的一種外設(shè),它可以由MIO或EMIO實現(xiàn)。以32為界,118個GPIO被分為了4個Bank,每個Bank對應(yīng)一組控制寄存器,然后簡單介紹了每個寄存器的名稱含義和功能,最后附上了通過自建GPIO函數(shù)庫,便捷操作GPIO的軟件代碼。

對GPIO理解還不深,如有疏漏,歡迎評論指出!

總結(jié)

以上是生活随笔為你收集整理的ZYNQ7000-GPIO详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。