正点原子 发表于 2023-3-27 10:04:42

《ATK-DFPGL22G之FPGA开发指南_V1.0》第二章GPIO之MIO控制LED实验

本帖最后由 正点原子 于 2023-3-27 10:04 编辑

1)实验平台:正点原子 DFZU2EG_4EV MPSoC开发板
2)购买链接:https://item.taobao.com/item.htm?&id=692368045899
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-340252-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子FPGA交流群:994244016



第二章GPIO之MIO控制LED实验
MPSOC PS中包含一组丰富的外设,如USB控制器、UART控制器、I2C控制器以及GPIO等等。他们提供了各种工业标准的接口,用于和外部设备进行通信。其中GPIO外设一般用于控制一些简单的外设,如LED和蜂鸣器,此时GPIO用作输出;也可以用于观测一些简单外设的状态,如按键,此时GPIO用作输入。
GPIO可以通过MIO连接到PS端的引脚,也可以通过EMIO连接到PL。本章将介绍如何使用GPIO外设通过MIO控制PS端的LED。本章包括以下几个部分:
2.1简介
2.2实验任务
2.3硬件设计
2.4软件设计
2.5下载验证


2.1简介
MPSOC分为PS和PL两部分,那么器件的引脚(Pin)资源同样也分成了两部分。MPSOC PS中的外设可以通过MIO(Multiuse I/O,多用输入/输出)模块连接到PS端的引脚上,也可以通过EMIO连接到PL端的引脚。MPSOC系列芯片一般有78个MIO。
GPIO是英文“general purpose I/O”的缩写,即通用的输入/输出。它是MPSOC PS中的一个外设,用于观测和控制器件引脚的状态。图 2.1.1是GPIO的框图,从中我们可以看到GPIO分为6个Bank,其中Bank0、Bank1和Bank2连接到MIO;而Bank3、Bank4和Bank5连接到EMIO。
Bank0、Bank1和Bank2分别有26bit,总共78bit,也就是说有78个MIO。Bank3、Bank4和Bank5分别有32bit,也就是说PS端可以使用96个EMIO。

图 2.1.1 GPIO框图
PS所有的外设都可以通过MIO访问,这些外设也是与MIO进行连接,每个MIO虽然可以独立控制,以及独立驱动单个引脚的外设,但对于QSPI、USB、以太网等这些外设,对于MIO的连接有着特殊的要求,如图 2.1.2所示,对于以太网而言,要与MIO26~37、MIO38~49、MIO52~63和MIO64~75引脚连接,而且以太网与MIO26连接的引脚只能作为以太网的tx_clk使用,可见当其作为以太网的接口引脚时,相应的MIO的功能就已经确定下来了。从图 2.1.2 MIO一览表中我们可以看到MIO一但选定,引脚位置就已经确定下来了,不需要添加引脚约束。


图 2.1.2 MIO一览表
通过图 2.1.2我们了解到了MIO与外设的连接情况,例如当我们想要两个MIO作为UART的接口时,可以使用MIO2、3或者MIO6、7等引脚,如果选用MIO2、3作为UART的接口,MIO2就是UART的rx引脚,MIO3为tx引脚。那么MIO与PS是怎么连接的呢?
图 2.1.3展示PS的IO外设。PS外设的大多数I/O信号可以通过MIO路由到PS引脚,或通过EMIO路由到PL引脚。

图 2.1.3 I/O外设系统图
这里我们重点介绍图 2.1.3 I/O外设系统图中箭头所指的部分。PS通过APB总线对控制、状态寄存器的读写实现对GPIO的驱动,具体可以参见下图。

图 2.1.4 GPIO通道
图 2.1.4左边的一列是寄存器,上半部分是关于中断的,这部分我们在涉及到中断的时候会讲解,这里我们重点介绍下红色框圈出的下半部分。
DATA_RO是数据只读寄存器,通过该寄存器能够观察器件引脚上的值。如果GPIO信号配置为输出,则通常会反映输出上驱动的值,写入此寄存器将被忽略。
DATA是数据寄存器,该寄存器控制GPIO信号配置为输出时要输出的值。该寄存器的所有32位都是一次写入的。读取该寄存器返回写入DATA或MASK_DATA_ {LSW,MSW}的先前值,它不会返回器件引脚上的当前值。
MASK_DATA_LSW和MASK_DATA_MSW是数据掩码寄存器,该寄存器使软件能够有选择地一次更改所需的输出值。可以写入最多16位的任意组合,MASK_DATA_LSW控制Bank的低16位,MASK_DATA_MSW控制高16位。未写入的那些位保持不变并保持其先前的值。读取该寄存器返回写入DATA或MASK_DATA_ {LSW,MSW}的先前值;它不会返回器件引脚上的当前值。该寄存器避免了对未更改位的读-修改-写序列的需要。
DIRM是方向模式寄存器,用于控制I/O引脚是用作输入还是输出。当DIRM == 0时,输出驱动器被禁用,该引脚作为输入引脚使用。
OEN是使能输出寄存器。将I/O配置为输出时,该寄存器控制是否启用输出。禁用输出时,引脚为3态。当OEN == 0时,输出被禁用。
从这些寄存器中我们可以看到,如果配置引脚为输出,不仅需要设置方向,还要使能输出。关于这些寄存器的具体介绍,可参考ug1085手册。需要说明的是我们在程序中操作MIO时直接调用Xilinx官方提供的函数即可,无需直接操作这些寄存器。
另外需要说明的是MIO信号对PL部分是不可用的,所以对MIO的操作是纯PS的操作,且每个GPIO都可独立动态编程为输入、输出或中断检测。
2.2实验任务
本章的实验任务是使用GPIO通过MIO控制PS端LED的亮灭,实现LED闪烁的效果。
2.3硬件设计
发光二极管的原理图如图 2.3.1所示,PS_LED1发光二极管的阴极通过330欧姆的电阻连到地(GND)上,阳极与MPSOC的IO相连。LED与地之间的电阻起到限流作用。

图 2.3.1 LED灯原理图
从实验任务我们可以画出如下的系统框图,DDR4中存放和运行程序、UART打印信息、MIO驱动LED外设。虽然本实验可以不需要UART,不过为了方便打印一些信息,此处我们加上UART。

图 2.3.2 系统框图
我们本章如同《第一章 Hello World》实验一样步步为营的创建工程并完成实验。
step1:创建Vivado工程
硬件设计可以在《第一章 Hello World》实验的基础上进行,我们就没有必要重新从头创建工程了,此处顺便可以介绍下如何在先前工程的基础上继续实验而不破坏先前的工程。
1-1 我们先打开《第一章 Hello World》实验的Vivado工程,打开后选择菜单栏的File-> Project->Save As...,如下图所示:

图 2.3.3 选择另存为
1-2 在弹出的另存为界面中可以输入新的工程名或更改保存位置,此处我们输入新的工程名“gpio_mio”,工程位置保持默认即可,然后取消勾选Include run results,如下图所示,最后点击“OK”按钮。

图 2.3.4 另存为工程为gpio_mio
此时如果我们打开工程所在目录即F:\ZYNQ\Embedded_System\gpio_mio,可以看到如下的目录结构:

图 2.3.5 gpio_mio工程目录
此时我们就在原工程的基础上创建了一个新的工程,而没有破坏原来的工程,也避免了重新创建工程或复制工程后修改的麻烦。
现在我们开始第二步,在《第一章 Hello World》实验的硬件设计基础上搭建GPIO_MIO实验的硬件。
step2:使用IP Integrator创建Processing System
2-1 在Flow Navigator中,点击IP INTEGRATOR下的Open Block Design,如下图所示:

图 2.3.6 打开Block Design
2-2 在打开的下图Diagram窗口,双击打开Zynq UltraScale+ MPSOC重定义窗口。

图 2.3.7 重定义Zynq UltraScale+ MPSOC
2-3 在下图所示的重定义窗口,点击左侧的I/O Configuration,在右侧的界面中展开Low Speed,展开I/O Peripherals,展开GPIO,然后勾选GPIO0 MIO和GPIO1 MIO。另外开发板上的Bank0即原理图中的BANK500为1.8V,所以我们将5处的Bank0、6处的bank2电压设置为LVCOMS 1.8V,最后点击OK。

图 2.3.8 勾选GPIO0_MIO
需要说明的是虽然这里我们勾选GPIO0_MIO和GPIO1_MIO后有很多MIO,但我们实际用到的GPIO_MIO与原理图相关。为了方便大家的查找和使用,MPSOC PS端IO引脚分配我们都列在了资料盘开发板原理图文件夹下的IO引脚分配总表中,我们摘录部分如下图,可以看到开发板有4个GPIO_MIO连接到外设LED和KEY上,这些GPIO_MIO用来驱动外设LED和KEY。由于这些引脚都是PS的引脚,不需要在PL中进行引脚位置约束。

图 2.3.9 连接到外设LED和KEY的GPIO_MIO
2-4 由于不需要添加其它IP,按Ctrl+S快捷键保存Diagram。此时我们的第二步完成,进入第三步
step3:生成顶层HDL
3-1 在sources面板中,右键选择Design Sources下的design_1_wrapper中的design_1_i: design_1,在弹出的菜单中选择Generate Output Products...,如下图所示

图 2.3.10 选择Generate Output Products...
3-2 在弹出的下图中,Synthesis Options选择Global,Run Setings保持默认选择,然后点击 Generate。

图 2.3.11 设置Generate
等Generate完成后,在弹出的下图中,直接点击“OK”

图 2.3.12 Generate完成
3-3 创建顶层HDL Wrapper
因为我们在创建Hello World实验时创建顶层HDL Wrapper使用的是下图所示的Let Vivado manage wrapper and auto-update选项,所以此处无需再创建顶层HDL Wrapper,Vivado会自动更新顶层HDL Wrapper。此时第三步完成。

图 2.3.13 创建Hello World实验时创建顶层HDL Wrapper的选项
step4:生成Bitstream文件并导出Hardware
由于本实验未用到PL部分,所以无需生成Bitstream文件,只需导出hardware即可。如果使用到PL,则需要添加引脚约束以及对该系统进行综合、实现并生成Bitstream文件。
4-1 导出硬件。
选择 File > Export > Export hardware

图 2.3.14 导出硬件
在弹出的下图所示界面中,因为没有生成bitstream文件,所以无需勾选“Include bitstream”,直接点击“OK”按钮。

图 2.3.15 无需勾选“Include bitstream”
新建vitis文件夹,并将生成的xsa文件放入其中,如下图所示:

图 2.3.16 新建vitis文件夹
4-2 硬件导出完成后,选择菜单Tools->Launch Vitis,如下图所示,启动Vitis开发环境。

图 2.3.17 启动Vitis开发环境
在弹出的对话框中,点击Browse将工程路径指定到新建的vitis文件夹下,然后点击Launch启动Vitis开发环境,如下图所示:

图 2.3.18 启动Vitis开发环境
至此第四步完成,下面进入第五步,进入到Vitis软件中开发,也就是软件设计部分。
2.4软件设计
在硬件设计的最后,我们打开了Vitis开发环境,下面我们开始第五步——创建应用工程。
step5:在Vitis中创建应用工程
5-1 在菜单栏依次点击“File->New->Application Project”,新建一个Vitis应用工程。在弹出的对话框中,输入工程名“gpio_mio”,其他默认,点击“Next”。如下图所示:

图 2.4.1 创建应用工程
5-2 点击“Create a new platform hardware(XSA)”,软件提供了一些板卡的硬件平台以供选择,但对于我们自己的硬件平台需要手动添加,点击“+”添加。

图 2.4.2 添加硬件平台
选择vitis文件夹下的“design_1_wrapper.xsa”,如下图所示。

图 2.4.3 选择硬件平台
成功添加我们自己的硬件平台后,点击next,如下图所示:

图 2.4.4 硬件平台添加完成
5-3 弹出的页面中保持默认设置,点击next。

图 2.4.5 保持默认设置
5-4选择工程模版“Empty Application”,本章将自行创建工程文件,故选择空模板,然后点击“Finish”按钮,如下图所示:

图 2.4.6 选择工程模板
双击硬件平台目录下的platform.spr文件,找到点击板级支持包“Board_Support_Package”,点击展开“Peripheral Drivers”,右侧有相关文档和示例。如下图所示:

图2.4.7 板级支持包
找到GPIO,如下图所示:

图2.4.8 板级支持包中的GPIO
点击Documentation将在浏览器窗口打开GPIO的API文档,里面有关于GPIO的详细信息,如下图所示:

图 2.4.9 GPIO的API文档
想了解GPIO的,可以仔细浏览其中的信息。
5-5 导入示例。如果我们点击Import Examples,会弹出下图所示的导入示例界面,关于GPIO有两个示例,如下图所示:

图 2.4.10 导入示例
这两个示例的介绍可以在刚才打开的API文档中看到。在API文档中点击左侧的Examples,右侧出现这两个示例的介绍信息,如图2.4.11所示:
xgpiops_intr_example.c包含有关如何直接使用XGpiops驱动程序的示例。此示例显示了中断模式下驱动程序的用法,并使用GPIO的中断功能检测按钮事件,根据输入控制LED输出。xgpiops_polled_example.c同样包含有关如何直接使用XGpiops驱动程序的示例。此示例提供了用于读取/写入各个引脚的API的用法。

图 2.4.11 示例的介绍信息
从上面的介绍中,我们因为本实验暂未使用到中断,所以应该选择xgpiops_polled_example示例。选择好示例后,点击“OK”按钮。
5-6 在Explorer中,新增了xgpiops_polled_example_1目录,我们打开其src目录下的xgpiops_polled_example.c文件。

图2.4.12 打开xgpiops_polled_example.c文件
5-7 显示行数。此处我们说一下如何显示代码的行数,在下图所示的1处箭头所指的上或下方点击鼠标右键,在弹出的菜单中选择2处的Show Line Numbers,就会显示代码的行数。

图2.4.13 显示行数
5-8 xgpiops_polled_example.c文件有四个函数,其中GpioInputExample函数由于我们本实验只用MIO输出所以未用到。该文件代码虽然是为特定开发板使用的,不过我们稍作修改也可以拿来使用。从图 2.3.9中我们可以看到有两个LED分别接到PS的MIO38和MIO39,这里我们使用PS_LED1,即连接MIO38。我们修改该文件第193行的Output_Pin为38,保存该文件,然后编译,编译完成后下载到开发板会看到板上的LED1灯闪烁,闪烁时间约为2秒,随后LED灯熄灭。

图 2.4.14 修改输出引脚
可以说我们的本章实验功能到这儿就已经实现了,当然了,实验不能就这么的完了。xgpiops_polled_example.c确实是一份不错的使用说明(如果耐心分析),但再好也是别人写的,我们虽然在上面实现了功能,但对于怎么用还不是太了解,现在我们自己动手写一个驱动MIO的代码。
5-9 新建源文件。首先我们在gpio_mio/src目录上右键,选择New-> File,如下图所示:

图2.4.15 新建源文件
在下图所示的添加源文件界面中,File name一栏我们输入文件名“main.c”,然后点击“Finish”按钮。

图2.4.16 设置文件名
5-10 输入源代码。我们在新建的main.c文件中输入以下代码:
1#include "xparameters.h" //器件参数信息
2#include "xstatus.h"   //包含XST_FAILURE和XST_SUCCESS的宏定义
3#include "xil_printf.h"//包含print()函数
4#include "xgpiops.h"   //包含PS GPIO的函数
5#include "sleep.h"       //包含sleep()函数
6
7//宏定义GPIO_DEVICE_ID
8#define GPIO_DEVICE_ID      XPAR_XGPIOPS_0_DEVICE_ID
9//连接到MIO的LED
10 #define MIOLED0    38   //连接到MIO38
11 #define MIOLED1    39   //连接到MIO39
12
13 XGpioPs Gpio;            // GPIO设备的驱动程序实例
14
15 int main()
16 {
17   int Status;
18   XGpioPs_Config *ConfigPtr;
19
20   print("MIO Test! \n\r");
21   ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
22   Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
23                     ConfigPtr->BaseAddr);
24   if (Status != XST_SUCCESS){
25         return XST_FAILURE;
26   }
27   //设置指定引脚的方向:0输入,1输出
28   XGpioPs_SetDirectionPin(&Gpio, MIOLED0, 1);
29   XGpioPs_SetDirectionPin(&Gpio, MIOLED1, 1);
30   //使能指定引脚输出:0禁止输出使能,1使能输出
31   XGpioPs_SetOutputEnablePin(&Gpio, MIOLED0, 1);
32   XGpioPs_SetOutputEnablePin(&Gpio, MIOLED1, 1);
33
34   while (1) {
35         XGpioPs_WritePin(&Gpio, MIOLED0, 0x0); //向指定引脚写入数据:0或1
36         XGpioPs_WritePin(&Gpio, MIOLED1, 0x0);
37         sleep(1);                              //延时1秒
38         XGpioPs_WritePin(&Gpio, MIOLED0, 0x1);
39         XGpioPs_WritePin(&Gpio, MIOLED1, 0x1);
40         sleep(1);
41   }
42   return XST_SUCCESS;
43 }
该代码实现了LED灯每隔1秒闪一次的功能。
代码第8行我们宏定义了GPIO_DEVICE_ID,使其为XPAR_XGPIOPS_0_DEVICE_ID,如果在Vitis软件中,按住Ctrl键不放,将鼠标移动到XPAR_XGPIOPS_0_DEVICE_ID上,当鼠标变成手指状时,单击鼠标左键,会自动跳转到xparameters.h文件中,该文件定义了各个外设的基地址、器件ID、中断等,我们这里重新宏定义XPAR_XGPIOPS_0_DEVICE_ID是为了以后方便修改。
代码第10行宏定义了MIOLED0,其值为38,因为其连接到PS的MIO38引脚。一般对于这种MIO的使用,驱动某一引脚,在代码中使用该引脚对应的MIO数字标号即可。
代码第21行至26行是获取GPIO的ID和基址信息并初始化其配置,以及判断是否初始化成功。代码第28行的XGpioPs_SetDirectionPin和31行XGpioPs_SetOutputEnablePin函数分别是设置GPIO的方向(输入还是输出)函数和使能输出函数,代码第35行的XGpioPs_WritePin是向指定GPIO引脚写入数据的函数,关于这三个函数的具体使用可以查看其定义。查看其定义的简便方法是在VITIS软件中,按住Ctrl键不放,将鼠标移动到想查看定义的函数名上,当鼠标变成手指状时,单击鼠标左键,即可跳转到定义或声明的地方。
代码第37和第40行的sleep函数为秒延时函数,延时m秒就使用sleep(m)语句。还有一个微秒延时函数usleep(m),延时m微秒。
5-11 编译工程。保存main.c文件,右键点击应用工程gpio_mio,在弹出的菜单中选择Build Project,如下图所示:

图2.4.17 编译工程
编译完成后,生成elf文件,如下图所示:

图2.4.18 生成的elf文件
至此软件设计部分完成。
注:如果此时导入的GPIO示例不再需要,可以将其删除。
2.5下载验证
完成了硬件设计和软件设计后,我们就可以进行板级验证了,也就是设计流程的最后一步。在进行板级验证之前,我们先将开发板上的JTAG与电脑连接,然后使用USB连接线将USB UART(PS_PORT)接口与电脑连接,然后连接开发板的电源,给开发板上电。

图 2.5.1 MPSOC开发板实物图
现在进入最后一步。
step6:板级验证
6-1 打开串口助手或具有串口功能的软件。串口助手是上位机中用于辅助串口调试的小工具,可以选择安装使用开发板随附资料中“6_软件资料/1_软件/ XCOM V2.0”的文件夹中提供的串口助手,也可从网上下载或选择自己常用的串口调试工具。这里我们使用Vitis软件自带的串口终端。
按照《Hello World》实验中的步骤,打开Vitis中的Terminal窗口,如下图所示:

图 2.5.2打开Vitis软件自带的串口终端
我们进入该窗口后,点击上图箭头所指的图标,在弹出的窗口中对串口进行设置,选择串口“Serial Terminal”,COM口依据自己的电脑设置,波特率为“115200”,数据位为8位,停止位为1位,如下图所示:

图 2.5.3 设置UART
点击“OK”,串口终端成功连接,如下图所示:

图 2.5.4 串口连接成功
6-2 下载程序。右键选择gpio_mio工程,在弹出的菜单中选择Run as-> Launch on Hardware,如下图所示:

图2.5.5 选择Launch on Hardware
6-3 显示结果。Launch后程序就会在DDR中运行,在Terminal中,可以看到打印的“MIO Test!”结果。

图 2.5.6 显示打印结果
此时可以看到开发板上两个PS led闪烁,如下图所示:

图 2.5.7 开发板运行结果
至此,本实验完成。
页: [1]
查看完整版本: 《ATK-DFPGL22G之FPGA开发指南_V1.0》第二章GPIO之MIO控制LED实验