學習內容
使用SDK和提供的API進行初始化GPIO,并驅動led和btn進行操作,實現led呼吸燈效果,串口讀取btn的值。
開發環境
vivado 18.3 && SDK 開發板 pynq-z2
原理講解
- gpio可以看做一個外設,用于對器件的引腳作觀測(input)以及控制(output)
- MIO 是將PS的外設和靜態存儲器接口的訪問多路復用到PS的引腳上。
- 當我們的PS端的MIO不夠用時候,我們可以在搭建硬件平臺時候進行設計,將我們的PL端口的引腳定義拓展為EMIO然后被PS正常驅動。
- gpio 被分為了4個bank bank0和bank1通過MIO連接到PS的引腳,bank2和bank3通過EMIO連接到PL
這里給出GPIO的一個寄存器的結構圖,方便我們從底層更加深入了解gpio的具體配置的一個狀態
圖片的上半部分表示的是和IO的中斷功能有關的寄存器,本節主要關注下面的部分。 通過閱讀UG585,我們可以知道寄存器組的不同功能: - DATA_RO:用來反映引腳的功能狀態
- DATA:當GPIO信號被配置為輸出時,這個寄存器控制要輸出的值。該寄存器的所有32位同時寫入。
- MASK_DATA_LSW: 用于屏蔽DATA的低16位
- MASK_DATA_MSW: 用于屏蔽DATA的高16位
- DIRM:用于控制IO引腳作為輸入還是輸出。0關閉輸出驅動,1打開輸出驅動
- OEN:當IO配置為輸出時,用于控制打開或者關閉輸出使能。0關閉使能,1進行使能
硬件平臺搭建
同樣和前面相似,我們先添加IP ZYNQ7,然后進行IP配置,選中uart sd gpio
EMIO的 gpio
APB AP transaction error, DAP status f0000021 問題解決方法
我們可以簡單的理解為zynq這個芯片是由這樣幾部分組成,ddr區,PS區,PL區。DDR區是存儲區,包括我們的芯片運行SDK的程序時候也是下載到這里。所以在我們沒有對應我們的板子配置好DDR時,會碰到這個錯誤信息 ==“APB AP transaction error, DAP status f0000021”== 這也是可以定位到我們在配置ddr信息時候沒有和開發板匹配,這里我進行操作的時候,我是添加了對應的板卡信息,ddr自動連線的時候會有一個加載預配置。 pynq板卡信息會自動幫你配置ddr的信息,這里我貼出pynqz2的ddr的具體信息,需要更改的地方我會標記指出:
EMIO和MIO的區別
前面提到了zynq7000的大致劃區,在ps區,也就是我們的arm核這里,一般都是引出了54個管腳進行開發,也就是我們在ZYNQ7的ip里在MIO config的可以配置的那些,這就是MIO我們可以針對這54個管腳進行配置自己想要的功能,一般不同的板卡會對應設計SD UART USB GPIO接口什么的,方便用戶進行PS端的單獨調試學習,那么,對于FPGA的PL端的引腳我們可以不可以進行PS操作調用呢?答案是肯定的!這就提到了EMIO的概念 EMIO的E就是 external的意思,也就是我們可以管腳定義了FPGA端的管腳進行擴展給PS端進行操作。 配置完成后如圖:
然后我們可以輸出生成頂層文件了
因為這里是用的EMIO,我們不能忘記新建約束文件進行管腳分配:
set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { GPIO0_tri_io[0] }]; #IO_L6N_T0_VREF_34 Sch=led[0]
set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { GPIO0_tri_io[1] }]; #IO_L4P_T0_35 Sch=btn[0]
set_property -dict { PACKAGE_PIN D20 IOSTANDARD LVCMOS33 } [get_ports { GPIO0_tri_io[2] }]; #IO_L4N_T0_35 Sch=btn[1]
最后綜合生成bit流,導出硬件,launch SDK
SDK軟件部分
運行SDK新建helloworld工程(不再多說,看前文),這里貼出SDK的main代碼:
#include
#include "platform.h"
#include "xil_printf.h"
#include "xgpiops.h"
#include "xparameters.h"
#define GPIO_ID XPAR_PS7_GPIO_0_DEVICE_ID
#define LED0 54
#define BTN0 55
#define BTN1 56
static XGpioPs GpioPs;
static XGpioPs_Config *XGpioPs_Cfg;
int initGpio();
int main()
{
u32 led = 0;
int i,j;
init_platform();
initGpio();
while(1){
for(i=0;i<1000;i++){
for(j=0;j<1000;j++){
usleep(1);
if(iBaseAddr);
if( status != XST_SUCCESS){
return XST_FAILURE;
}
XGpioPs_SetDirectionPin(&GpioPs,LED0,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED0,0x01);
XGpioPs_SetDirectionPin(&GpioPs,BTN0,0x00);
XGpioPs_SetOutputEnablePin(&GpioPs,BTN0,0x00);
XGpioPs_SetDirectionPin(&GpioPs,BTN1,0x00);
XGpioPs_SetOutputEnablePin(&GpioPs,BTN1,0x00);
}
部分代碼解釋
#include "xgpiops.h"
#include "xparameters.h"
#define GPIO_ID XPAR_PS7_GPIO_0_DEVICE_ID
#define LED0 54
#define BTN0 55
#define BTN1 56
這里的XPAR_PS7_GPIO_0_DEVICE_ID可以在 "xparameters.h" 尋到,對于LED0 BTN0 BTN1的定義,正是因為前文提到的EMIO的概念,直接PS課操作的管腳54個,是從MIO 0 -- MIO 53 所以這里的EMIO的擴展引腳編號就是54往后,從GPIO0_tri_io 0 依次往后進行編號,也可以理解為管腳號。
int initGpio(){
int status;
XGpioPs_Cfg = XGpioPs_LookupConfig(GPIO_ID);
status = XGpioPs_CfgInitialize(&GpioPs,XGpioPs_Cfg,XGpioPs_Cfg->BaseAddr);
if( status != XST_SUCCESS){
return XST_FAILURE;
}
XGpioPs_SetDirectionPin(&GpioPs,LED0,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED0,0x01);
XGpioPs_SetDirectionPin(&GpioPs,BTN0,0x00);
XGpioPs_SetOutputEnablePin(&GpioPs,BTN0,0x00);
XGpioPs_SetDirectionPin(&GpioPs,BTN1,0x00);
XGpioPs_SetOutputEnablePin(&GpioPs,BTN1,0x00);
}
初始化函數同前文iic的配置,和keil開發32的流程類似,聲明結構體進行初始化,然后設置GPIO的方向,輸入還是輸出模式。