mio
1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
5)关注正点原子公众号,获取最新资料
第二章GPIO之MIO控制LED实验
ZYNQ PS中包含一组丰富的外设,如USB控制器、UART控制器、I2C控制器以及GPIO等等。他们提供了各种工业标准的接口,用于和外部设备进行通信。其中GPIO外设一般用于控制一些简单的外设,如LED和蜂鸣器,此时GPIO用作输出;也可以用于观测一些简单外设的状态,如按键,此时GPIO用作输入。 GPIO可以通过MIO连接到PS端的引脚,也可以通过EMIO连接到PL。本章将介绍如何使用GPIO外设通过MIO控制PS端的LED。本章包括以下几个部分: 11.1简介 1.2实验任务 1.3硬件设计 1.4软件设计 1.5下载验证 简介 ZYNQ分为PS和PL两部分,那么器件的引脚(Pin)资源同样也分成了两部分。ZYNQ PS中的外设可以通过MIO(Multiuse I/O,多用输入/输出)模块连接到PS端的引脚上,也可以通过EMIO连接到PL端的引脚。Zynq-7000系列芯片一般有54个MIO,个别芯片如7z007s只有32个。 GPIO是英文“general purpose I/O”的缩写,即通用的输入/输出。它是ZYNQ PS中的一个外设,用于观测和控制器件引脚的状态。图 2.1.1是GPIO的框图,从中我们可以看到GPIO分为4个Bank,其中Bank0和Bank1连接到MIO;而Bank2和Bank3连接到EMIO。 除Bank1之外的Bank都具有32bit,Bank1只具有22bit是因为总共只有54个MIO,其中32bit的Bank0控制了MIO[0~31],剩下的MIO[31~53]就由22bit的Bank1控制。Bank2和Bank3用于控制扩展的MIO即EMIO,也就是说总共可以有32+32=64个EMIO。
图 2.1.1 GPIO框图
PS所有的外设都可以通过MIO访问,这些外设也是与MIO进行连接,每个MIO虽然可以独立控制,以及独立驱动单个引脚的外设,但对于QSPI、USB、以太网等这些外设,其于MIO的连接有着特殊的要求,如图 2.1.2所示,(图中灰色框表示在CLG225封装的芯片中不可用)对于以太网而言,其只能与MIO16~27和MIO28~39引脚连接,而且以太网与MIO28连接的引脚只能作为以太网的tx_clk使用,可见当其作为以太网的接口引脚时,相应的MIO的功能就已经确定下来了。MIO还有一特点,如MIO28~39引脚即可以与以太网进行连接,也可以作为USB以及其它外设的接口引脚,所以当我们设计PS的外设时要合理分配MIO。从图 2.1.2 MIO一览表中我们可以看到MIO一但选定,引脚位置就已经确定下来了,不需要添加引脚约束。
图 2.1.2 MIO一览表
通过图 2.1.3 MIO一览表我们了解到了MIO与外设的连接情况,例如当我们想要两个MIO作为UART的接口时,可以使用MIO8、9或者MIO10、11等引脚,如果选用MIO8、9作为UART的接口,MIO8就是UART的tx引脚,MIO9为rx引脚。那么MIO与PS是怎么连接的呢? 图 2.1.4 I/O外设系统图展示了MIO与PS的连接。MIO的PS外设的大多数I/O信号(USB除外)可以通过MIO路由到PS引脚,或通过EMIO路由到PL引脚。除千兆以太网外,大多数外设还在MIO和EMIO之间保持相同的协议。千兆以太网为减少引脚数,使用4位的RGMII接口以250 MHz数据速率(125 MHz时钟,双倍数据速率)通过MIO。如果接到EMIO,就使用一个以125 MHz数据速率运行的8位GMII接口。需要注意的是Quad-SPI、USB和SMC接口不适用于PL的EMIO接口。
图 2.1.5 I/O外设系统图
这里我们重点介绍图 2.1.5 I/O外设系统图中箭头所指的部分。从图 1.1.5中我们可以看到PS通过APB总线对控制、状态寄存器的读写实现对GPIO的驱动,具体可以参见下图。
图 2.1.6 GPIO通道
左边的一列是寄存器,图 2.1.7上半部分是关于中断的,这部分我们在涉及到中断的时候会讲解,这里我们重点介绍下红色框圈出的下半部分。 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 [x] == 0时,输出驱动器被禁用,该引脚作为输入引脚使用。 OEN是使能输出寄存器。将I/O配置为输出时,该寄存器控制是否启用输出。禁用输出时,引脚为3态。当OEN [x] == 0时,输出被禁用。 从这些寄存器中我们可以看到,如果配置引脚为输出,不仅需要设置方向,还要使能输出。关于这些寄存器的具体介绍,可参考ug585手册的Appx.B:Register Details中的General Purpose I/O (gpio)一节。需要说明的是我们在程序中操作MIO时直接调用Xilinx官方提供的函数即可,无需直接操作这些寄存器。 另外需要说明的是MIO信号对PL部分是透明的,所以对MIO的操作是纯PS的操作,且每个GPIO都可独立动态编程为输入、输出或中断检测,此处需要注意的是MIO7和8只能做为输出IO使用。 实验任务 本章的实验任务是使用GPIO通过MIO控制PS端LED的亮灭,实现LED闪烁的效果。 硬件设计 发光二极管的原理图如图 2.3.1所示,PL LED0发光二极管的阴极通过330欧姆的电阻连到地(GND)上,阳极与ZYNQ的IO相连。LED与地之间的电阻起到限流作用。
图 2.3.1LED灯原理图
从实验任务我们可以画出如下的系统框图,DDR3中存放和运行程序、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”,工程位置保持默认即可,如下图所示,然后点击“OK”按钮。
图 2.3.4 另存为工程为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窗口,双击打开ZYNQ7 Processing System重定义窗口。
图 2.3.7 重定义ZYNQ7 Processing System
2-3 在下图所示的重定义窗口,点击左侧的Peripheral I/O Pins,在右侧的界面中勾选GPIO_MIO,另外领航者开发板上的Bank1即原理图中的BANK501为1.8V,所以我们选择3处的Bank1电压为LVCOMS 1.8V。勾选GPIO_MIO后,发现在GPIO_MIO这一栏GPIO_MIO14和15是灰色的,这是因为这两个GPIO_MIO用做UART0的接口引脚了。注意下图与图 2.1.2 MIO一览表相对应,可以互相参考。
图 2.3.8 勾选GPIO
完成配置后,点击4处的“OK”按钮。 需要说明的是虽然这里我们勾选GPIO_MIO后有很多GPIO,但我们实际用到的GPIO_MIO与原理图相关。为了方便大家的查找和使用,领航者ZYNQ PS端IO引脚分配我们都列在了教程《领航者ZYNQ之FPGA开发指南》的3.1.2节表3.1.2领航者ZYNQ PS端IO引脚分配总表中,我们摘录部分如下图,可以看到领航者开发板有5个GPIO_MIO连接到外设LED和KEY上,这些GPIO_MIO当作GPIO使用来驱动外设LED和KEY。由于这些引脚都是PS的引脚,不需要在PL中进行引脚位置约束。
图 2.3.9连接到外设LED和KEY的GPIO_MIO
2-4 由于不需要添加其它IP,按Ctrl+S快捷键保存Diagram。此时我们的第二步完成,进入第三步 step3:生成顶层HDL 3-1 在sources面板中,右键选择Design Sources下的sysetm_wrapper中的system_i:system,在弹出的菜单中选择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文件并导出到SDK 由于本实验未用到PL部分,所以无需生成Bitstream文件,只需导出到SDK即可。如果使用到PL,则需要添加引脚约束以及对该系统进行综合、实现并生成Bitstream文件。 4-1 导出硬件。 选择 File > Export > Export hardware
图 2.3.14 导出硬件
在弹出的下图所示界面中,因为没有生成bitstream文件,所以无需勾选“Include bitstream”,直接点击“OK”按钮。
图 2.3.15 无需勾选“Include bitstream”
因为是在前一工程的基础上建立的,还保留着前一工程的结果,所以会弹出下图所示的信息,我们点击“Yes”按钮。
图 2.3.16 覆盖先前导出结果
4-2 硬件导出完成后,选择菜单File->Launch SDK,如下图所示,启动SDK开发环境。
图 2.3.17 启动SDK开发环境
在弹出的下图所示界面中,直接点击“OK”
图 2.3.18 登录SDK开发环境
至此第四步完成,下面进入第五步,进入到SDK软件中开发,也就是软件设计部分。 软件设计 在硬件设计的最后,我们打开了SDK开发环境,如下图所示。
图 2.4.1 SDK开发环境界面
可以看到还保留着前一实验的结果,因为此处我们不需要,所以将其删除。选择hello_world、hello_world_bsp和system_wrapper_hw_platform_0,按键盘上的Delete键将其删除,在弹出的删除界面中,如下图,如果勾选2处的方框,则从磁盘上删除,如果不勾选,只是在SDK中删除,可以重新导入,因为此处我们不需要上一实验的结果,所以勾选,然后点击“OK”按钮。
图 2.4.2 删除不需要的工程
下面我们开始第五步——创建应用工程。 step5:在SDK中创建应用工程 5-1 选择菜单File->New->Application Project, 新建一个空的SDK软件工程。
图 2.4.3 新建一个SDK软件工程
5-2 在弹出的图 2.4.4所示界面中,输入工程名“gpio_mio”,注意2处的硬件平台,如果我们没有删除system_wrapper_hw_platform_0,该处可能是system_wrapper_hw_platform_0,所以要注意选择正确。其它选项保持默认即可,点击“Next >”按钮 5-3 选择工程模版Empty Application,然后点击“Finish”按钮,如图 2.4.5所示。
图 2.4.4 配置工程
图 2.4.5 选择工程模版Empty Application
5-4 可以看到SDK创建了一个gpio_mio目录和gpio_mio_bsp目录。我们打开gpio_mio_bsp目录下的system.mss文件,找到ps7_gpio_0,如图 2.4.6所示:
图 2.4.6 system.mss文件
点击Documentation将在浏览器窗口打开GPIO的API文档,里面有关于GPIO的详细信息,如下图所示:
图 2.4.7 GPIO的API文档
想了解GPIO的,可以仔细浏览其中的信息。 5-5 导入示例。如果我们点击Import Examples,会弹出下图所示的导入示例界面,关于GPIO有两个示例,如下图所示:
图 2.4.8 导入示例
这两个示例的介绍可以在刚才打开的API文档中看到。在API文档中点击左侧的Examples,右侧出现这两个示例的介绍信息,如图 2.4.8所示: xgpiops_intr_example.c包含有关如何直接使用XGpiops驱动程序的示例。此示例显示了中断模式下驱动程序的用法,并使用GPIO的中断功能检测按钮事件,根据输入控制LED输出。xgpiops_polled_example.c同样包含有关如何直接使用XGpiops驱动程序的示例。此示例提供了用于读取/写入各个引脚的API的用法
图 2.4.9 示例的介绍信息
从上面的介绍中,我们因为本实验暂未使用到中断,所以应该选择xgpiops_polled_example示例。选择好示例后,点击“OK”按钮。 5-6 在Project Explorer中,新增了gpio_mio_bsp_xgpiops_polled_example_1目录,我们打开其src目录下的xgpiops_polled_example.c文件。
图 2.4.10 打开xgpiops_polled_example.c文件
5-7 显示行数。由于默认没有显示行数,此处我们说一下如何显示代码的行数,在下图所示的1处箭头所指的上或下方点击鼠标右键,在弹出的菜单中选择2处的Show Line Numbers,就会显示代码的行数。
图 2.4.11 显示行数
5-8 xgpiops_polled_example.c文件有四个函数,其中GpioInputExample函数由于我们本实验只用MIO输出所以未用到。该文件代码虽然是为特定开发板使用的,不过我们稍作修改也可以拿来使用。从图 2.3.9中我们可以看到有一LED接到PS的MIO0,所以当我们修改该文件第172行的Output_Pin为0时,保存该文件,然后编译,编译完成后下载到领航者开发板会看到核心板上的LED2灯闪烁。
图 2.4.12 修改输出引脚
可以说我们的本章实验功能到这儿就已经实现了,当然了,实验不能就这么的完了。xgpiops_polled_example.c确实是一份不错的使用说明(如果耐心分析),但再好也是别人写的,我们虽然在上面实现了功能,但对于怎么用还不是太了解,现在我们自己动手写一个驱动MIO的代码。 5-9 新建源文件。首先我们在gpio_mio/src目录上右键,选择New->Source File,如下图所示:
图 2.4.13 新建源文件
在下图所示的弹出的添加源文件界面中,Source file一栏我们输入文件名“main.c”,然后点击“Finish”按钮。
图 2.4.14 设置文件信息
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 7 //连接到MIO7
- 11 #define MIOLED1 8 //连接到MIO8
- 12 #define MIOLED2 0 //连接到MIO0
- 13
- 14 XGpioPs Gpio; // GPIO设备的驱动程序实例
- 15
- 16 int main()
- 17 {
- 18 int Status;
- 19 XGpioPs_Config *ConfigPtr;
- 20
- 21 print("MIO Test! \n\r");
- 22 ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
- 23 Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
- 24 ConfigPtr->BaseAddr);
- 25 if (Status != XST_SUCCESS){
- 26 return XST_FAILURE;
- 27 }
- 28 //设置指定引脚的方向:0输入,1输出
- 29 XGpioPs_SetDirectionPin(&Gpio, MIOLED0, 1);
- 30 XGpioPs_SetDirectionPin(&Gpio, MIOLED1, 1);
- 31 XGpioPs_SetDirectionPin(&Gpio, MIOLED2, 1);
- 32 //使能指定引脚输出:0禁止输出使能,1使能输出
- 33 XGpioPs_SetOutputEnablePin(&Gpio, MIOLED0, 1);
- 34 XGpioPs_SetOutputEnablePin(&Gpio, MIOLED1, 1);
- 35 XGpioPs_SetOutputEnablePin(&Gpio, MIOLED2, 1);
- 36
- 37 while (1) {
- 38 XGpioPs_WritePin(&Gpio, MIOLED0, 0x0); //向指定引脚写入数据:0或1
- 39 XGpioPs_WritePin(&Gpio, MIOLED1, 0x0);
- 40 XGpioPs_WritePin(&Gpio, MIOLED2, 0x0);
- 41 sleep(1); //延时1秒
- 42 XGpioPs_WritePin(&Gpio, MIOLED0, 0x1);
- 43 XGpioPs_WritePin(&Gpio, MIOLED1, 0x1);
- 44 XGpioPs_WritePin(&Gpio, MIOLED2, 0x1);
- 45 sleep(1);
- 46 }
- 47 return XST_SUCCESS;
- 48 }
该代码实现了LED灯每隔1秒闪一次的功能。 代码第8行我们宏定义了GPIO_DEVICE_ID,使其为XPAR_XGPIOPS_0_DEVICE_ID,如果在SDK软件中,按住Ctrl键不放,将鼠标移动到XPAR_XGPIOPS_0_DEVICE_ID上,当鼠标变成手指状时,单击鼠标左键,会自动跳转到xparameters.h文件中,该文件定义了各个外设的基地址、器件ID、中断等,我们这里重新宏定义XPAR_XGPIOPS_0_DEVICE_ID是为了以后方便修改。 代码第10行宏定义了MIOLED0,其值为7,因为其连接到PS的MIO7引脚。一般对于这种MIO的使用,驱动某一引脚,在代码中使用该引脚对应的MIO数字标号即可。 代码第22行至27行是获取GPIO的ID和基址信息并初始化其配置,以及判断是否初始化成功。代码第29行的XGpioPs_SetDirectionPin和33行XGpioPs_SetOutputEnablePin函数分别是设置GPIO的方向(输入还是输出)函数和使能输出函数,代码第38行的XGpioPs_WritePin是向指定GPIO引脚写入数据的函数,关于这三个函数的具体使用可以查看其定义。查看其定义的简便方法是在SDK软件中,按住Ctrl键不放,将鼠标移动到想查看定义的函数名上,当鼠标变成手指状时,单击鼠标左键,即可跳转到定义或声明的地方。 代码第41和第45行的sleep函数为秒延时函数,延时m秒就使用sleep(m)语句。还有一个毫秒延时函数usleep(m),延时m毫秒。 5-6 编译工程。保存main.c文件,我们打开Binaries目录,看到已经有elf文件,说明工程已经编译过了。
图 2.4.15 elf文件
如果工程没有编译,可以在选中应用工程gpio_mio后鼠标右键单击,在弹出的菜单中选择“Build Project”,即可编译工程,如下图所示:
图 2.4.16 编译工程
至此软件设计部分完成 注:如果此时导入的GPIO示例不再需要,可以将其删除 下载验证 完成了硬件设计和软件设计后,我们就可以进行板级验证了,也就是设计流程的最后一步。在进行板级验证之前,我们先将领航者开发板上的JTAG与电脑连接,然后使用Mini USB连接线将USB UART接口与电脑连接,然后连接领航者开发板的电源,给开发板上电。
图 2.5.1领航者ZYNQ开发板实物图
现在进入最后一步。 step6:板级验证 6-1 打开串口助手或具有串口功能的软件。串口助手是上位机中用于辅助串口调试的小工具,可以选择安装使用开发板随附资料中“6_软件资料/1_软件/ XCOM V2.0”的文件夹中提供的串口助手,也可从网上下载或选择自己常用的串口调试工具。这里我们使用SDK软件自带的串口助手,其它串口助手的设置也是一样。 在SDK软件的下方,有一个SDK Terminal的小窗口,如下图所示:
图 2.5.2 SDK软件自带的串口助手。
我们进入该窗口后,点击右边箭头所指的加号,在Basic Settings的Port中下拉选择连接相应的端口,每个人端口号不一样,具体的端口号可以在电脑的设备管理器中看到,本机为COM3。
图 2.5.3 设置UART
由于我们在硬件中设置UART的波特率为默认值115200,所以此处无需再设置,其它的保持默认即可,点击“OK”按钮。 6-2 下载程序。此处我们换一种方式下载。右键选择gpio_mio工程,在弹出的菜单中选择Run as->Run Configuration…,如下图所示:
图 2.5.4 Run Configuration
在弹出的下图所示界面中Xilinx C/C++ applicaton (system debugger)下有三个elf文件,第一个是前一工程留下的,第二个是我们导入示例时生成的,第三个才是我们工程的真正的elf文件,前两个不需要的可以分别右键点击将其删除,当然了也可以保留。一般新建工程后,如果其下没有elf文件,我们可以双击Xilinx C/C++ applicaton (system debugger)新建。注意硬件平台选择,当有多个硬件平台时。点击按钮“Run”,下载运行程序。
图 2.5.5 下载程序
6-3 显示结果。Launch后程序就会在DDR3中运行,在SDK Terminal中,可以看到打印的“MIO Test!”结果。
图 2.5.6 显示打印结果
此时可以看到开发板上PS的led闪烁,如下图所示:
图 2.5.7 领航者开发板运行结果
至此,本实验完成。