ZYNQ——GPIO之MIO控制LED

开发环境        vivado 19.2     vitis

开发板            zynq7010

内容                呼吸灯

        ZYNQ 分为 PS PL 两部分,器件的引脚(Pin)资源同样也分成了两部分。ZYNQ PS 中的外设 可以通过 MIOmultiplexed I/O,多路复用 I/O)模块连接到 PS 端的引脚上,也可以通过 EMIOextended  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 都具有 32bitBank1 只具有 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 的驱动,具体可以参见下图:

        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 时,输出被禁用。
        从这些寄存器中我们可以看到,如果配置引脚为输出,不仅需要设置方向,还要使能输出。

硬件平台配置

创建工程

选择对应芯片型号

使用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中断端口。

配置PSDDR3控制器

        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 DesignHDL封装器文件

​​​​​​​

生成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.最后依次输出第二个结构体变量成员的值。(pq是两个结构体指针变量

        以上就是利用结构体指针来引用结构体变量,同样结构体指针也可以引用结构体变量的成员。一般形式为(*结构体指针变量).成员名    如图所示:

        更简洁的引用方式:结构体指针变量->成员名    如下图所示:

        这就与我们初始化GPIO驱动中,利用结构体指针来引用结构体变量的部分对应上了。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱你等于1+1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值