FPGA中ARMGPIO的控制实现一、简介:在Xilinx的FPGA中集成ARM处理器的FPGA称为ZYNQ,ZYNQ中包含PL、PS两部分。本次介绍的ZYNQ主要为Xilinx的ZYNQUltraScale+XCZU3CG-SFVC784-2-E,该款ZYNQ的PS测GPIO主要由78个MIO管脚和96个EMIO管脚组成。EMIO的GPIO[92:95]通道,用于在PL中重置用户定义的逻辑。Bank0到Bank2是每个Bank26bitBank0:管理MIOpins[0:25].Bank1:管理MIOpins[26:51].Bank2:管理MIOpins[52:77].MIO这些IO与PS直接相连,使用时不需要添加引脚约束,MIO信号对PL部分是透明的,不可见。所以对MIO的操作可以看作是纯PS的操作。Bank3:管理EMIO信号设置[0:31].Bank4:管理EMIO信号设置[32:63].Bank5:管理EMIO信号设置[64:95].使用EMIO的好处就是,当MIO不够用时,PS可以通过驱动EMIO控制PL部分的引脚,EMIO的使用相当于,是一个PS+PL的结合使用的例子。所以,EMIO需要分配引脚,以及编译综合生成bit文件。GPIO控制寄存器和状态寄存器是内存映射,从基址0xFF0A_0000开始,由XPPU保护,我们SDK下软件操作底层都是对于内存地址空间的操作。二、硬件描述:硬件板卡上ZYNQPS的MIO[33:30]接有4个LED灯与MIO[26]接有1个按键,PL侧D6管脚上接有1个按键。MIO[30]:LED0;MIO[31]:LED1;MIO[32]:LED2;MIO[33]:LED3;MIO[26];PS_SW;D6:PL_SW.三、实验说明:利用板卡提供的硬件资源设计通过SDK操作MIO和EMIO的输入输出实验。在本次设计中按键作为输入信号,LED灯作为输出信号;PS的按键控制LED0、LED1两个信号灯,PL的按键控制LED2、LED3两个信号灯;当I/O的初始化完成后4个LED灯会由一次进行亮灭检测,然后进入循环等待按键按下,当检测到由按键按下后对应的LED灯会点亮。四、创建Vivado工程:Vivado版本为2017.41.新建一个工程,芯片型号为XCZU3CG-SFVC784-2-E;2.创建一个块设计:CreateBlockDesign,添加ZYNQUltraScale+MPSOC;3.双击进入添加完成的ZYNQIP进行设置:1)设置该IP的第一页是一个总体的框图,会把设置的资源打勾体现出来2)I/O配置,本次实验中会用到一个EMIO,用来接PL侧的按键,所以在EMIO处例化一个管脚;LED灯和PS侧的按键都是接在GPIO1MIO处,把这个选项也勾选上。3)输入时钟的设置,输入时钟为33.333MHZ,本次不需要输出时钟,可以取消输出时钟4)配置DDR,本次没有使用DDR,可以不用配置,如需配置按照DDR参数填写即可;5)配置PS-PL,本次实验使用不到,不需要配置,可以在该页把一些用不到的管脚关掉;6)配置完所以点击OK即可,仔细观察发现的zynq核心多出一组引脚名为GPIO_0,这个正是我们刚刚设置的一组EMIO,我们右击该引脚,选择makeexternal把GPIO_0引脚引出(或者单击该引脚处,按快捷键Ctrl+T,也可以将引脚引出)。7)单击GPIO_0,经行管脚名字更改4.经行完如上的设置,需要经行综合,点击RunSynthesis;5.综合完成后,对PL侧的按键经行管脚约束,单击OpenSynthesizedDesign,选择I/OPlanning,管脚为D6,电平为LVCOMS18;6.设置完成约束后需要生成BIT文件7.导出到硬件:File-Export-ExportHardware8.打开SDK:File-LaunchSDK;9.在SDK单击File-New-Applicationproject,选择EmptyApplication,创建一个空的工程,单击Finish完成创建10.单击工程名字右边的三角形按钮,然后右单击src,选择New-sourcefile,输入一个文件名,此处命名为main.c,单击Finish按钮完成C文件的添加。11.在main.c中添加程序如下:#includexgpiops.h#includesleep.hintmain(){staticXGpioPspsGpioInstancePtr;XGpioPs_Config*GpioConfigPtr;intxStatus;u32uPinDirection=0x1;//1代表输出,0代表输入u32PS_SW;u32PL_SW;//MIO的初始化与EMIO的初始化GpioConfigPtr=XGpioPs_LookupConfig(XPAR_PSU_GPIO_0_DEVICE_ID);if(GpioConfigPtr==NULL)returnXST_FAILURE;xStatus=XGpioPs_CfgInitialize(&psGpioInstancePtr,GpioConfigPtr,GpioConfigPtr-BaseAddr);if(XST_SUCCESS!=xStatus)print(PSGPIOINITFAILED\n\r);//EMIO输入输出选择,0是输入,1是输出XGpioPs_SetDirectionPin(&psGpioInstancePtr,78,0);//MIO的输入输出操作,配置MIO输出方向XGpioPs_SetDirectionPin(&psGpioInstancePtr,30,uPinDirection);XGpioPs_SetDirectionPin(&psGpioInstancePtr,31,uPinDirection);XGpioPs_SetDirectionPin(&psGpioInstancePtr,32,uPinDirection);XGpioPs_SetDirectionPin(&psGpioInstancePtr,33,uPinDirection);//配置MIO输入方向XGpioPs_SetDirectionPin(&psGpioInstancePtr,26,0);//配置MIO的输出XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,30,1);XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,31,1);XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,32,1);XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,33,1);usleep(500000);//延时XGpioPs_WritePin(&psGpioInstancePtr,30,1);usleep(1000000);//延时XGpioPs_WritePin(&psGpioInstancePtr,30,0);usleep(1000000);//延时XGpioPs_WritePin(&psGpioInstancePtr,31,1);usleep(1000000);//延时XGpioPs_WritePin(&psGpioInstancePtr,31,0);usleep(1000000);//延时XGpioPs_WritePin(&psGpioInstancePtr,32,1);usleep(1000000);//延时XGpioPs_WritePin(&psGpioInstancePtr,32,0);usleep(1000000);//延时XGpioPs_WritePin(&psGpioInstancePtr,33,1);usleep(1000000);//延时XGpioPs_WritePin(&psGpioInstancePtr,33,0);usleep(1000000);//延时while(1){PS_SW=XGpioPs_ReadPin(&psGpioInstancePtr,26);PL_SW=XGpioPs_ReadPin(&psGpioInstancePtr,78);if(PS_SW==0)//检测PS按键是否按下{XGpioPs_WritePin(&psGpioInstancePtr,30,1);XGpioPs_WritePin(&psGpioInstancePtr,31,1);}else{XGpioPs_WritePin(&psGpioInstancePtr,30,0);XGpioPs_WritePin(&psGpioInstancePtr,31,0);}if(PL_SW==0)//检测PL按键是否按下{XGpioPs_WritePin(&psGpioInstancePtr,32,1);XGpioPs_WritePin(&psGpioInstancePtr,33,1);}else{XGpioPs_WritePin(&psGpioInstancePtr,32,0);XGpioPs_WritePin(&psGpioInstancePtr,33,0);}}}12.添加完程序后点击保存机会对语法进行检查,然后到程序的文件夹处右键,选择RunAs-LaunchonHardware(SystemDebugger),也就是可以从ZYNQ板卡上观察到实验现象五、程序分析1.这是一个指针实例,指向的我们添加进来的GPIO端口。绿色标识的一个结构体(SDK中结构体都用绿色标识)XGpiops,我们将鼠标停留在这个结构体上面,就可以看到它里面所包含的内容。从这个图上可以看到,这个结构体上包含了GPIO的一些参数,分别是:设备的配置、设备是否初始化并准备好、所有状态的处理程序、块处理程序的回调、设备数据、GPIO的最大pin数量和GPIO的最大的bank数量。2.也是一个指针实例,此结构体存放的是GPIO的设备地址和基地址。3.查找GPIO配置程序。XGpiops_Lookupconfig()这个函数是一个xilinx官方提供的GPIO的查找配置的函数,程序的参数为要查找的GPIO的基地址。基地址可xparameters.h中查看,单击BSP支持包(此处为emio_mio_test_bsp)的小三角形,选择pus7_cortexa53_0文件夹下的include文件夹,在其中找到xparameters.h,双击打开它。若未找到,则在主界面下的System.mss界面点击,重新生成BSP支持包,此时只要耐心等待即可。如下图所示。此处我们用到的就是XPAR_PSU_GPIO_0_DEVICE_ID。这段话的整体意思就是查找GPIO的配置,然后判断其是否为空,若为空则返回查找失败。4.完成的是gpio配置的初始化工作,如果初始化不成功的话,将通过串口打印出一串初始化失败的通知信息,在此就不再去对其详细的分析。5.XGpioPs_SetDirectionPin函数中涉及到了一些ZYNQ中GPIO的硬件结构,将鼠标停留在这个函数上,按F3查看其函数定义。它完成的是指定pin脚的方向设置。EMIO的编号为MIO最后一个编号往后继续排,也就是编在MIO后。6.XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,30,1)函数设置管脚输出使能。7.usleep(500000);延时函数8.XGpioPs_WritePin(&psGpioInstancePtr,30,1);函数写管脚数据。9.函数是从管脚读数据。