本文主要是介绍ZYNQ——GPIO之MIO控制LED,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
开发环境 vivado 19.2 vitis
开发板 zynq7010
内容 呼吸灯
ZYNQ 分为 PS 和 PL 两部分,器件的引脚(Pin)资源同样也分成了两部分。ZYNQ PS 中的外设 可以通过 MIO(multiplexed I/O,多路复用 I/O)模块连接到 PS 端的引脚上,也可以通过 EMIO(extended multiplexed I/O interface,扩展多路 I/O 接口)连接到 PL 端的引脚。Zynq-7000 系列芯片一般有 54 个 MIO,
MIO是将来自PS外设和静态存储器接口的访问多路复用到ps端的引脚上。
GPIO 是英文“general purpose I/O”的缩写,即通用的输入/输出。它是 ZYNQ PS 中的一个外设,用于观测(input)和控制(output)器件引脚的状态。如图为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。
MIO 一但选定,引脚位置就已经确定下来了,不需要添加引脚约束。
我们可以看到 PS 通过 APB 总线对控制、状态寄存器的读写实现对 GPIO 的驱动,具体可以参见下图:
硬件平台配置
创建工程
选择对应芯片型号
使用IP Integrator创建Processing System
接下来在Diagram窗口中给设计添加 IP。点击上图中箭头所指示的加号“+”,会打开IP目录(IP Catalog),也可以通过快捷键Ctrl + I,或者右键点击Diagram工作区中的空白位置,然后选择“ADDIP”。
打开IP目录后,在搜索栏中键入“zynq”,找到并双击“ZYNQ7 Processing System”,将ZYNQ7处理系统IP添加到设计中。
添加完成后,ZYNQ7 Processing System模块出现在Diagram中,如下图所示:
双击所添加的ZYNQ7 Processing System模块,进入ZYNQ7处理系统的配置界面。界面左侧为页面导航面板,右侧为配置信息面板,如下图所示:
下面我们简要地介绍一下页面导航面板中各个页面的作用。
在Zynq Block Design页面,显示了Zynq处理系统(PS)的各种可配置块,其中灰色部分是固定的,绿色部分是可配置的,按工程实际需求配置。可以直接单击各种可配置块(以绿色突出显示)进入相应的配置页面进行配置,也可以选择左侧的页导航面板进行系统配置。
PS-PL Configuration页面能够配置PS-PL接口,包括AXI、HP和ACP总线接口。
Peripheral IO Pins页面可以为不同的I/O外设选择对应的MIO/EMIO引脚。
MIO Configuration页面可以为不同的I/O外设具体配置MIO/EMIO引脚,例如电平标准等。
Clock Configuration页面用来配置PS输入时钟、外设时钟,以及DDR和CPU时钟等。
DDR Configuration页面用于设置DDR控制器配置信息。
SMC Timing Calculation页面用于执行SMC时序计算。
Interrupts页面用于配置PS-PL中断端口。
配置PS的DDR3控制器
XC7Z010的核心板选择MT41J128M16 HA-125。需要注意的是,我们在这里选择的型号并不是领航者核心板上的DDR3型号,而是参数接近的型号,或者兼容的型号。“Effective DRAM Bus Width” 一栏选择32bit,2片DDR,每片16bit。
配置PS的时钟
点击左侧的Clock Configuration页面,该界面主要是配置ZYNQ PS中的时钟频率。比如输入时钟默认是33.33333Mhz,这与我们领航者核心板上的PS端输入时钟频率相同。对于CPU的时钟、DDR的时钟以及其它外设的时钟,我们直接保持默认设置即可。如果想设置成其他的时钟频率,可以在Requested Frequency一栏里输入想要的频率,只要保证输入的频率保持在Range一栏中的频率范围之内。配置好的选项卡如下图所示:
配置好DDR3和时钟之后,我们需要取消勾选其中的几个选项,如下所示:
勾选GPIO_MIO
点击左侧的PeripheralI/O Pins,在右侧的界面中勾选GPIO_MIO,另外领航者开发板上的Bank1即原理图中的BANK501为1.8V,所以我们选择Bank1电压为LVCOMS1.8V
最后点击ok,配置完成
本次实验不需要添加其它IP,直接按快捷键Ctrl+S保存当前设计。接下来点击上图箭头所指示的按钮或者是在Block design界面右击弹出的菜单中点击Validate Design,以验证Block design当前的设计和连接是否有错误。验证完成后弹出对话框提示没有错误或者关键警告,点击“OK”,
生成顶层HDL
创建Block Design的HDL封装器文件
生成Bitstream文件并导出硬件
如果设计中使用了PL的资源,则需要添加引脚约束并对该设计进行综合、实现并生成Bitstream文件。由于本次实验未用到PL部分,所以无需生成Bitstream文件,只需将硬件导出即可。
打开vitis
新建工程
添加从vivado中导出的文件
在弹出的会话框选择empty(空工程)即可
接下来编程即可,在这里给出源程序:
#include "stdio.h"
//#include "xparameters.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define MIO0_LED 0
#define MIO7_LED 7
#define MIO8_LED 8
XGpioPs_Config *ConfigPtr;//定义ConfigPtr为结构体指针类型 (指针理解为地址)
XGpioPs Gpio;
int main(){//printf("GPIO MIO TEST\n");u32 led=0;int i,j;//根据器件的ID查找器件的配置信息ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);//初始化GPIO的驱动XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);//&是取地址 ->表示结构体的第一个元素//把GPIO的方向设置为输出(0输入/1输出)XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);//(地址 引脚 方向)XGpioPs_SetDirectionPin(&Gpio, MIO7_LED, 1);//(地址 引脚 方向)XGpioPs_SetDirectionPin(&Gpio, MIO8_LED, 1);//(地址 引脚 方向)//设置输出使能(0关闭 1打开)XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);XGpioPs_SetOutputEnablePin(&Gpio, MIO7_LED, 1);XGpioPs_SetOutputEnablePin(&Gpio, MIO8_LED, 1);//写数据到GPIO的输出引脚(最后一个数是32位,但是只有最低为有效)XGpioPs_WritePin(&Gpio, MIO0_LED, 1);XGpioPs_WritePin(&Gpio, MIO7_LED, 1);XGpioPs_WritePin(&Gpio, MIO8_LED, 1);//点亮while(1){/*//XGpioPs_WritePin(&Gpio, MIO0_LED, 1);XGpioPs_WritePin(&Gpio, MIO7_LED, 1);XGpioPs_WritePin(&Gpio, MIO8_LED, 1);//延时usleep(100000);//微秒为单位,延迟0.1s//熄灭//XGpioPs_WritePin(&Gpio, MIO0_LED, 0);XGpioPs_WritePin(&Gpio, MIO7_LED, 0);XGpioPs_WritePin(&Gpio, MIO8_LED, 0);//延时usleep(100000);*/for (i=0;i<1000;i++){for (j=0;j<1000;j=j+1){usleep(1);if(i<j){XGpioPs_WritePin(&Gpio, MIO0_LED, ~led);}else{XGpioPs_WritePin(&Gpio, MIO0_LED, led);}}}led=~led;}return 0;
}
代码部分也有注释,编程思路就是:1.先根据器件的ID查找器件的配置信息;2.然后初始化GPIO的驱动3.把GPIO的方向设置为输出;4.设置输出使能;5.写数据到GPIO的输出引脚。
其中第1,2步是一些必要的初始化信息;
3,4,5正好对应DATA,DIRM,OEN寄存器的配置,当然这些底层寄存器的配置都是在函数里面实现的,具体实现过程我们可以不用关心,只要会用就可以。
最后补充一点内容,在初始化GPIO驱动部分用到了结构体指针的内容,在这里作简单说明:
XGpioPs_Config *ConfigPtr;//定义ConfigPtr为结构体指针类型 (指针理解为地址)
//初始化GPIO的驱动XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);//&是取地址 ->表示结构体的第一个元素
指针的基本功能就是简介引用,也就是通过指针变量间接的引用另一个变量。给出一个例子,变量间的间接引用
以上代码就是定义a,再定义p为指针变量。把a的地址赋给p,此时p就指向了a的地址,最后把123赋值给了p所指向的变量,那实际上就是a,所以a输出结果就是123 。
结构体指针:c语言结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合。说的通俗一点就是一个集合。c语言是一门面向过程的编程语言,而结构体的使用在某些层次上跟面向对象有点异曲同工之处了。
例:一名学生各项信息如下:学号:1234 姓名:张三 年龄:18 成绩:300
要求:先用初始化方式,把这些数据存入一个结构体变量中,再将该结构体变量的内容复制到另一个结构体变量中,最后,输出第二个结构体变量中的各项内容。
方法一:采用直接引用的方式,直接把结构体变量st1赋值给st2
方法二:采用间接引用的方式
1.首先定义一个包含4个成员的结构体变量
2.通过初始化,将各项数据存入结构体变量中。
3.通过间接引用方式,实现结构体变量的整体赋值
4.最后依次输出第二个结构体变量成员的值。(p和q是两个结构体指针变量)
以上就是利用结构体指针来引用结构体变量,同样结构体指针也可以引用结构体变量的成员。一般形式为(*结构体指针变量).成员名 如图所示:
更简洁的引用方式:结构体指针变量->成员名 如下图所示:
这就与我们初始化GPIO驱动中,利用结构体指针来引用结构体变量的部分对应上了。
这篇关于ZYNQ——GPIO之MIO控制LED的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!