软件版本:VIVADO2017.4 操作系统:WIN10 64bit 硬件平台:适用米联客 ZYNQ系列开发板 米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!! 4.1 概述本课对ZYNQ芯片的GPIO进行介绍,通过设计PS端点亮LED的功能,讲解了PS端MIO使用方法。 4.2 GPIO简介Zynq7000系列芯片有54个MIO(multiuse I/O),它们分配在 GPIO 的Bank0 和Bank1隶属于PS部分,这些IO与PS直接相连。不需要添加引脚约束,MIO信号对PL部分是透明的,不可见。所以对MIO的操作可以看作是纯PS的操作。 GPIO的控制和状态寄存器基地址为:0xE000_A000,SDK软件底层操作是对于内存地址空间的操作。 Bank0:MIO [31:0] GPIO PIN脚号:0~31 Bank1:MIO[32:53] GPIO PIN脚号:32~53 以上描述和我们原理图中和VIVADO ZYNQ IP中定义的有冲突,这个留给大家去讨论,下图中,是ZYNQ IP部分MIO电压分配,从这个软件上看,BANK0 IO 是0~15,BANK1 IO是16~53,这个是奇怪的事情,笔者现在还没搞懂。实际应用以软件设定和原理图为准。 Bank2:EMIO [31 : 0] GPIO PIN脚号:54~85 Bank3:EMIO [63:32] GPIO PIN脚号:86~ 117 4.2.1 GPIO的控制寄存器地址空间SDK软件下的底层操作是对这些寄存器的操作,具体的相关参数请参考技术手册ug585-Zynq-7000-TRM.pdf 4.2.2 MIO内部构造分析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 4.2.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 4.3 电路分析及实验预期开发板上有一个MIO是与开发板上的一个LD9相连的,这个MIO就是MIO7。实验通过操作该MIO来实现LD9的闪烁。 原理图 4.4 搭建BD工程Step1:新建一个名为为Miz_sys的工程。 Step2:创建一个BD文件,并命名为system,添加并且配置好ZYNQ IP。读者需要根据自己的硬件类型配置好输入时钟频率、内存型号、串口,连接时钟等。新手不清楚这些内容个,请参考“CH01 HelloWold/DDR/网口测试及固化”这一节课。 Step3:ZYNQ IP MIO配置 MIO部分其他功能的配置可以参考参考“CH01 HelloWold/DDR/网口测试及固化”这一节课 Step2:建立一个空的工程 Step3: 在我们提供例程的文件夹中找到main.c文件,并进行复制。 Step4: 点击MIO_Test旁边的箭头使其展开,然后选中src,按下Ctrl+V快捷键,粘贴main.c文件 Step5:右击工程,选择Debug as ->Debug configurations。 Step6:选中system Debugger,双击创建一个系统调试,点击Apply,点击Debug。 Step7:单击窗口上的运行按钮,运行程序 4.7 测试结果可看到核心板LD9闪烁 4.8 程序分析接下来对程序进行分析。 分析1 语句:static XGpioPs psGpioInstancePtr; 含义:这是一个指针实例,指向添加的GPIO端口。 具体分析: XGpiops:绿色标识的一个结构体。SDK中结构体都用绿色标识。 将鼠标停留在XGpiops上或右键Open Declaration,查看这个结构体所包含的内容。 分析2 语句:XGpioPs_Config* GpioConfigPtr 含义:这是一个指针实例。 具体分析: XGpioPs_Config:绿色标识的一个结构体。 将鼠标停留在XGpiops上或右键Open Declaration,查看这个结构体所包含的内容。 此结构体存放的是GPIO的设备地址和基地址。 分析3 语句:int iPinNumber= 7; 含义:参数 具体分析:iPinNumber这个参数,是告知程序,操作的MIO是哪一个,因为我们要操作的是MIO7,所以这里所以这里的iPinNumber等于7。(在第三章EMIO中也使用了这个参数,具体请参看下一节内容,这里仅作铺垫) 分析4 语句:GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); if(GpioConfigPtr == NULL) return XST_FAILURE; 含义:查找GPIO配置程序。此处用到xparameters.h中XPAR_PS7_GPIO_0_DEVICE_ID。这段话的整体意思为查找GPIO的配置,判断其是否为空,若为空则返回查找失败。 具体分析: 分析5 语句: xStatus=XGpioPs_CfgInitialize(&psGpioInstancePtr,GpioConfigPtr,GpioConfigPtr->BaseAddr); if(XST_SUCCESS != xStatus) print(" PS GPIO INIT FAILED \n\r"); 含义: 完成gpio配置的初始化工作,如果初始化不成功,将通过串口打印出初始化失败的信息。 分析6 语句:XGpioPs_SetDirectionPin(&psGpioInstancePtr, iPinNumber,uPinDirection) 含义:指定pin脚的方向设置。 具体分析: 程序完成指定pin脚的方向设置。程序首先读取bank号,对应的子程序: /* Get the Bank number and Pin number within the bank. */ XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber); 将鼠标停留在XGpioPs_GetBankPin函数上,按F3查看下其功能。 图中画红框部分为程序查找bank号对应代码。程序首先判断ZYNQ的类型,本课第一节GPIO简介介绍7010和7020有四个bank,因此当程序执行后,程序首先执行else部分的程序。此时看else部分程序。程序给出了四个bank的bank号的最大值,初始化bank号为0,while语句限制了bank的最大数量为4。然后用pin的序号从bank0到bank3逐个比对,若是此时pin的序号小于或等于当前bank的最大值,则可以判断出pin是属于这个bank的,跳出while语句,否则bank号进行自加操作直到得出符合的bank号。接下来if语句,判断bank号是否为bank0,若是则将PinNumber直接赋值,否则经过计算一段公式得出PinNumber。 接下来回到XGpioPs_SetDirectionPin函数分析其他的子程序。获取了bank号后,开始读取寄存器,程序如下: DirModeReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr, ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_DIRM_OFFSET); 这里重点观察第二个参数,这是一个任务寄存器偏移+DIRM_OFFSET的参数。此时打开xilinx的编程手册ug585-zynq-7000-TRM(接下来的内容中我们将将其简称为ug585),来具体看看这个参数含义。 复制DIRM,在ug585中查找到这么一段话: 此时得知这是一个方向寄存器,当它等于0的时候输出被禁止,只有输入进行,即此时为输入功能。等于1时做输出使用。在GPIO的通道示意图中也能发现有这个部分构成。 回到XGpioPs_SetDirectionPin的分析,再得到了bank号与要写哪个寄存器的地址后,接下来的if else语句就是对这对pinbumer这一位单独做一些操作,最后把方向寄存器的值写入到读出的那个寄存器当中。 分析7 语句:XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, iPinNumber,1); 含义:配置输出 分析8 语句:XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 1) 含义:写数据到pin脚 具体分析: XGpioPs_WritePin的参数分别为gpio的基地址、要操作的MIO号和写入的数据。定义如下: 观察图中方框圈起来部分,此处观测到有两个陌生的偏移,此时可在ug585中查看其具体含义。 此时可以得知,语句是要写入数据的高16位偏移量和低16位偏移量。程序是通过判断PinNumber的值来决定寄存器偏移量是用高16位偏移量还是低16位偏移量。 此时再看XGpioPs_WritePin函数的接下来的这段程序: 这段程序完成向指定MIO写入某个值的操作。分析这段程序,如果要向MIO7写入1,程序一开始已经把要写入的值赋值给了DataVar,此段程序又将DataVar与0x01与操作,操作后DataVar的值还是为1。 接下来的value就是要写入寄存器的值。~ ((u32)1 << (u32)PinNumber):表示把PinNumber加上16(也就是把pinNumber移到高16位)赋值为1,然后再取反,执行完后这一段的值为~(80000)h,也就是(FFF7FFFF)h。 ((DataVar << PinNumber) | 0xFFFF0000U):已经得到DataVar的值为1,因此这里的意思为把pinNumber位赋值为1,再与FFFF0000或操作,执行完这一段的值为(80)h | (FFFF0000)h,也就是(FFFF0080)h,整句执行完之后就是(FFF7FFF)h & (FFFF0080)h=(FFF70080)h。也就是此时Value的值为FFF70080。 XGpioPs_WriteReg这个函数功能是往寄存器中写入数据。 从图上可知,第一个参数为设备的基地址, 第二个参数为偏移量,此处为0,第三个参数为要写入寄存器的数据。另外程序还可直接使用寄存器函数对MIO进行操作,其用法参照之前的分析,寄存器函数操作如下所示:
按照之前讲解的方法,开发人员可自行对库函数进行学习分析。 |
扫描关注,了解最新资讯