自学内容网 自学内容网

Zynq(3)使用外设MIO/EMIO

在这里插入图片描述

前言:在上一篇文章中,介绍了soc芯片的开发环境,简要说明了工程目录,以helloworld实验为例说明了soc芯片开发的大致流程。本文承接上文,以MIO、EMIO实验即将连接在PS端的LED与连接在PL端的LED灯组合起来实现流水灯的效果为例。介绍soc开发中的一些细节。主要包括, 介绍MIO与EMIO的区别;熟悉Zynq IP配置;掌握如何通过分析硬件原理图完成必要的工程配置;Vitis代码分析; 读者通过本文可以进一步熟悉SOC开发流程,可以通过分析硬件原理图完成ip配置,工程搭建,使用各种不同外设。

1.什么是MIO/EMIO

MIO(Multiuse I/O),即多用输入/输出,SOC芯片通过MIO与外设交互。MPSoC系列芯片一般有 78 个 MIO分布在bank0bank2,每个bank有26个;有96个EMIO,分布在bank3bank5。与单片机一样,GPIO也是复用的,即一个MIO可以作为哪些外设是提前定义好的,换句话说,想要用UART外设,必须要找到可以复用为UART的那几个MIO。即IO 所能支持的接口功能和对应的管脚是固定的,这是由于内部是固化电路的原因。因此在硬件设计的时候要特别注意。
如何确定被操作的IO的位号?所谓位号就是在PS端的程序里如何标识MIO和EMIO。MIO在bank上已经标出了序号,在原理图找见对应引脚即可找见对应的位号;由于MIO“占据”了前78个位号(0-77)所以EMIO 从第 79 个信号开始,编号为 78(从 0 开始编号)。由于EMIO用的是PL端的资源,因此通过设置约束的方式指定管脚位置。在vivado中block design配置好之后要右键选择create HDL Wrapper, 这时会把IP配置中勾选上的EMIO引出来(要先在IP配置上引出的管脚右键选择make external),如下图所示,其中接到外部的信号ps_emio_tri_io就是emio,这个信号在vivado中通过约束指定了管脚,在vitis中,ps_emio_tri_io[0]对应78,ps_emio_tri_io[1]对应79,依次类推。
在这里插入图片描述

EMIO和MIO的区别?从应用上都是可以连接IO类型的外设,一个在PL端一个在PS端。但是去搞清本质上的区别对于认识zynq这个芯片非常有帮助。如下图所示,红色部分是我自己添加的,可以看到左边一竖排矩形区域对应的是控制这个引脚特性的寄存器电路在PS端,再往后不同的复用单元,通过MIO的路由到PS上的引脚,通过EMIO的路由到了PL上的引脚。因此本例使用EMIO对于寄存器的设置全部都在PS端。
在这里插入图片描述

2.工程搭建

vivado在soc开发中有两个作用最为突出,其一是通过对zynq IP核的配置确定PS端外设的复用的连接情况;其二是将PL端电路定制成一个PS端的特定外设。zynq IP可以理解为是放在PL端的PS黑箱,代表PS端,用以完成上述两个vivado实现的功能。
创建工程,完成实验目标,即将连接在PS端的LED与连接在PL端的LED灯组合起来实现流水灯的效果。soc开发的流程见Zynq(2)从Hello World熟悉Zynq开发流程,这里从vivado工程创建完毕,添加zynq IP核后说起。双击IP核进入IP配置界面。
在这里插入图片描述
PS UltraScale+ Block Design是IP配置的一个整体概览,其中绿色部分为所有可配置的部分,点击即可进入相应的子界面,下面的四个栏目中的配置内容均可从此进入。
1.I/O配置
IO配置主要作用就是结合原理图为硬件设计分配管脚及其所在bank对应的电平标准。低速存储接口包括QSPI、NAND、SD卡,IO外设包括常见的SPI、IIC、GPIO等。高速接口包括GEM、USB、PCIe、DP、SATA等。这些配置主要是勾选数量、选择对应的MIO、以及指定对应bank的电平标准。本例中要勾选GPIO即MIO和EMIO,如下图。我用的板子上bank电压全部都是1.8V,将这个里bank电压设置为LVCMOS18。
在这里插入图片描述

2.参考时钟配置
Clock Configuration 页面分为 Input Clocks和 Output Clocks两个标签页,用来配置 PS 输入时钟、外设时钟等。设置PS端的输入参考时钟为33.3333MHz,PCIE DP USB的参考时钟在IO勾选了相应的外设之后会显示,本例不配置。可以配置PS端的时钟输出,用于与PL保持时钟同步,本例用不到。
3.DDR Configuration
DDR的配置是必要的,根据板贴的DDR的型号及相关时序参数填入下表即可。
在这里插入图片描述

4.PS-PL Configuration 页面用于 PS 和 PL 交互的相关配置,包括常用的中断、复位信号和数据接口,本例不设置。
在IP配置完成后,IP界面上会出现一个引脚就是我们勾选上的EMIO,右键选择make external,如下图所示,这就是引出的EMIO,在导出xsa文件之间,应该先为这几个引脚设置约束,然后生成bit流,在导出xsa文件时应该include bitstream。接下来就进入vitis程序了。
在这里插入图片描述

3.程序解读

打开vitis,新建工程并基于以上生成的xsa文件设置好空模板后,新建一个main.c文件,输入以下代码,编译debug就可以看到流水灯的效果。下面对这段代码进行分析。

#include "stdio.h"
#include "xparameters.h"
 #include "xgpiops.h"
#include "sleep.h" //包含 sleep()函数
#define GPIOPS_ID XPAR_XGPIOPS_0_DEVICE_ID //PS 端 GPIO 器件 ID
#define MIO_LED1 42 //PS_LED1 连接到 MIO42
#define MIO_LED2 40 //PS_LED2 连接到 MIO40
#define EMIO_LED1 78 //PL_LED1 连接到 EMIO2
#define EMIO_LED2 79 //PL_LED2 连接到 EMIO3
 int main ()
 {
 XGpioPs gpiops_inst;  //PS 端 GPIO 驱动实例
 XGpioPs_Config * gpiops_cfg_ptr; //PS 端 GPIO 配置信息
 //根据器件 ID 查找配置信息
 gpiops_cfg_ptr = XGpioPs_LookupConfig( GPIOPS_ID );
 //初始化器件驱动
 XGpioPs_CfgInitialize (&gpiops_inst, gpiops_cfg_ptr,gpiops_cfg_ptr->BaseAddr );
 //设置 LED 为输出
 XGpioPs_SetDirectionPin (&gpiops_inst,  MIO_LED1,  1 );
 XGpioPs_SetDirectionPin (&gpiops_inst,  MIO_LED2,  1 );
 XGpioPs_SetDirectionPin (&gpiops_inst,  EMIO_LED1,  1 );
 XGpioPs_SetDirectionPin (&gpiops_inst,  EMIO_LED2,  1 );
 //使能 LED 输出
 XGpioPs_SetOutputEnablePin (&gpiops_inst, MIO_LED1,  1 );
 XGpioPs_SetOutputEnablePin (&gpiops_inst, MIO_LED2,  1 );
 XGpioPs_SetOutputEnablePin (&gpiops_inst, EMIO_LED1,  1 );
 XGpioPs_SetOutputEnablePin (&gpiops_inst, EMIO_LED2,  1 );
 u8 nled =0;
 //将PS PL四个LED组在一起组成流水灯
 while(1){
 XGpioPs_WritePin(&gpiops_inst, MIO_LED1, nled==0);
 XGpioPs_WritePin(&gpiops_inst, MIO_LED2, nled==1);
 XGpioPs_WritePin(&gpiops_inst, EMIO_LED1, nled==2);
 XGpioPs_WritePin(&gpiops_inst, EMIO_LED2, nled==3);
 if(nled == 3)
 nled = 0;
 else
 nled++;
 sleep(1);
 }
 return 0;
 }

首先给XGpioPs_LookupConfig函数一个标识外设的ID来返回一个记录IO配置的实例(结构体指针),GPIOPS_ID存在的原因是为了区分不同组的GPIO。在XGpioPs_LookupConfig函数中通过XGpioPs_ConfigTable(XGpioPs_Config型结构体数组)返回了一个XGpioPs_Config实例。调用XGpioPs_CfgInitialize函数,传入XGpioPs_Config实例及相关参数,完成XGpioPs instance的初始化。后面与XgpioPs相关的操作函数都要用到XGpioPs_Config实例当作参数。原因是XGpioPs_Config实例中保存了XgpioPs的ID信号和偏移地址信息,而CPU对于外设的操作都是通过向不同的地址(这里的不同地址实际上是不同的寄存器)中写入数据和读出数据完成的。因此操作函数需要知道外设的偏移地址从而让CPU与对应的寄存器执行读写操作从而完成相应操作。 如下图所示,对比了vitis程序中保存的偏移地址和mpsoc trm手册中写到的偏移地址是一致的。
在这里插入图片描述

XGpioPs_SetDirectionPin函数就是设置IO方向,XGpioPs_SetOutputEnablePin设置输出使能,XGpioPs_WritePin设置IO输出值。三个函数都是参数都是一致的,需要XGpioPs_Config实例以匹配寄存器偏移地址,需要IO的位号指定管脚,最后需要一个寄存器的写入值。

4.传送门

END

🔈文章原创,首发于CSDN论坛。
🔈欢迎点赞❤❤收藏⭐⭐打赏💴💴!
🔈欢迎评论区或私信指出错误❌,提出宝贵意见或疑问❓。


原文地址:https://blog.csdn.net/weixin_40615338/article/details/142877083

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!