目录
一、ZYNQ中MIO和EMIO简介
二、Vivado中搭建block design
1.配置PS端MIO:
2.配置PS端EMIO:
三、Vitis中新建工程进行GPIO控制
1. GPIO操作头文件gpio_hdl.h:
2.GPIO操作源文件gpio_hdl.c:
3.main函数进行调用
例程开发环境:
SOC芯片:ZYNQ7020
开发环境:Vivado2020.2,Vitis2020.2
一、ZYNQ中MIO和EMIO简介
以ZYNQ7020为例,GPIO总共118个,分为了4个bank(ZU+的GPIO和bank数量有所增加),其中MIO有两个bank,需要注意的是bank1的GPIO数量只有22个,其余三个都有32个。
MIO和EMIO均为PS端的GPIO,由PS控制,其中MIO可直接配置复用成PS外设,而EMIO则可以连接到PL端,复用为PL端搭建的外设资源
-
二、Vivado中搭建block design
Vivado工程详细搭建方法,可见以下文章:
ZYNQ-Linux开发之(二)Vivado工程搭建、Block Design设计搭建、PS、PL的IP核的使用配置
该工程是在ZYNQ-Vitis(SDK)裸机开发之(一)基础上进行的修改,具体文件见如下连接:
ZYNQ-Vitis(SDK)裸机开发之(一)串口收发使用:PS串口+PL串口、多个串口使用方法
1.配置PS端MIO:
双击IP核进行配置,选择MIO Configuration-----I/O Peripherals-----GPIO MIO,勾选后,默认全部MIO都启用
PS端的MIO是不需要在XDC文件中进行约束的,可直接使用
我的板卡使用的是MIO7和MIO8,分别控制两个LED灯,需要根据自己的原理图进行选择
2.配置PS端EMIO:
双击IP核进行配置,选择MIO Configuration-----I/O Peripherals-----GPIO EMIO,勾选后,选取使用的EMIO个数,我这选择使用4个EMIO,其中两个用来控制PL端的LED,剩余两个用作其他使用,这里不用管。
勾选EMIO后,要将连接LED的PL端引脚与EMIO进行约束,这样才能通过EMIO来控制PL端LED的亮灭,值得注意的是,选取EMIO后,系统默认是从GPIO0开始使用,约束的话也要从GPIO[0]开始约束,在XDC文件中增加管脚约束,具体对应引脚需要根据自己项目的硬件原理图确定,我的PL端两个LED分别连接到了L15和H15,对应XDC文件中约束到GPIO[0]和GPIO[1]上如下:
-
三、Vitis中新建工程进行GPIO控制
1. GPIO操作头文件gpio_hdl.h:
(1)定义GPIO初始化以及配置使用的实例(多个GPIO其实可以公用同一个实例,这里为了方便分区才每个GPIO都实例化了一个结构体)
(2)使用宏定义重新定义PS GPIO的外设ID号
(3)定义工程中使用到的GPIO号,这里有个需要注意的地方,在第一章节就说明了,GPIO分为4个bank,其中0、1bank是MIO,2、3bank是EMIO,工程中使用了两个MIO和两个EMIO,那么这四个GPIO对应的IO号并不是连续的,参见如下关系可知,本工程中MIO对应的GPIO号是7、8,EMIO对应的GPIO号是54、55(因为在vivado中约束时,将PL端的LED约束到了EMIO的[0]和[1]上,从bank的开头数起,对应bank2上的GPIO号即54、55)
/*
* Max pins in the GPIO device ZYNQ
* 0 - 31, Bank 0
* 32 - 53, Bank 1
* 54 - 85, Bank 2
* 86 - 117, Bank 3
*/
(4)定义一些枚举变量,用来表示GPIO的输入输出方向、电平的高低、以及是否使能状态等
(5)声明一些GPIO操作相关的函数,例如GPIO初始化、GPIO点评输出、GPIO输入等操作函数
/*!
\file gpio_hdl.h
\brief firmware functions to manage gpio
\version 2024-04-10, V1.0.0
\author tbj
*/
#ifndef GPIO_HDL_H
#define GPIO_HDL_H
#include "xgpiops.h"
#ifdef __cplusplus
extern "C" {
#endif
//GPIO初始化实例
XGpioPs MIOLed0, MIOLed1, EMIOLed0, EMIOLed1;
//GPIO外设地址ID
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
//LED灯对应的PS和PL的IO
#define LED0_GPIO_PS_MIO 7
#define LED1_GPIO_PS_MIO 8
#define LED0_GPIO_PL_EMIO 55
#define LED1_GPIO_PL_EMIO 54
typedef enum{
GPIO_DIR_INPUT = 0,
GPIO_DIR_OUTPUT,
}GPIO_DIR;
typedef enum{
GPIO_VALUE_OFF = 0,
GPIO_VALUE_ON,
}GPIO_VALUE;
typedef enum{
GPIO_DISABLE = 0,
GPIO_ENABLE,
}GPIO_EN_STU;
//初始化GPIO
int gpio_polled_init(XGpioPs * Gpio_Ptr, u32 Gpio_Pin, GPIO_DIR Gpio_dir, GPIO_VALUE Gpio_init_value,
GPIO_EN_STU Gpio_en);
//设置GPIO输出
int set_gpio_value(XGpioPs * Gpio_Ptr, u32 Gpio_Pin, GPIO_VALUE Gpio_value);
//读取GPIO输入
int read_gpio_value(XGpioPs * Gpio_Ptr, u32 Gpio_Pin, GPIO_VALUE *Gpio_value);
#ifdef __cplusplus
}
#endif
#endif /* GPIO_HDL_H */
2.GPIO操作源文件gpio_hdl.c:
(1)分别对头文件中声明的三个GPIO相关函数进行了实现
/*!
\file gpio_hdl.c
\brief firmware functions to manage gpio
\version 2024-04-10, V1.0.0
\author tbj
*/
#include "gpio_hdl.h"
//GPIO初始化实例,因为EMIO也属于PS,所以都使用XGpioPs结构体(多个GPIO也可以只初始化一个实例共用)
/* 功能:gpio初始化函数
* 参数1:GpioPtr-GPIO对象指针
* 参数2:Gpio_Pin-GPIO对应pin
* 参数3:Gpio_dir-GPIO方向,输入还是输出
* 参数4:Gpio_init_value-GPIO初始化值
* 参数5:Gpio_en-GPIO是否使能
* 说明:Gpio_init_value和Gpio_en,只有配置输出时有效,配置为输入模式时,可以随意填写
*/
int gpio_polled_init(XGpioPs * Gpio_Ptr, u32 Gpio_Pin, GPIO_DIR Gpio_dir, GPIO_VALUE Gpio_init_value,
GPIO_EN_STU Gpio_en){
int Status;
XGpioPs_Config *ConfigPtr;
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
Status = XGpioPs_CfgInitialize(Gpio_Ptr, ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//配置GPIO的输入输出模式
XGpioPs_SetDirectionPin(Gpio_Ptr, Gpio_Pin, Gpio_dir);
//如果GPIO配置为输出模式,还要配置使能和默认输出值
if(Gpio_dir == GPIO_DIR_OUTPUT){
//使能输出的GPIO
XGpioPs_SetOutputEnablePin(Gpio_Ptr, Gpio_Pin, Gpio_en);
//初始化GPIO的值
XGpioPs_WritePin(Gpio_Ptr, Gpio_Pin, Gpio_init_value);
}
/*
* Max pins in the ZynqMP GPIO device ZU+
* 0 - 25, Bank 0
* 26 - 51, Bank 1
* 52 - 77, Bank 2
* 78 - 109, Bank 3
* 110 - 141, Bank 4
* 142 - 173, Bank 5
*/
/*
* Max pins in the GPIO device ZYNQ
* 0 - 31, Bank 0
* 32 - 53, Bank 1
* 54 - 85, Bank 2
* 86 - 117, Bank 3
*/
return XST_SUCCESS;
}
/* 功能:设置GPIO的值
* 参数1:GpioPtr-GPIO对象指针
* 参数2:Gpio_Pin-GPIO对应pin
* 参数3:Gpio_value-GPIO输出值
*/
int set_gpio_value(XGpioPs * Gpio_Ptr, u32 Gpio_Pin, GPIO_VALUE Gpio_value){
u32 Data;
XGpioPs_WritePin(Gpio_Ptr, Gpio_Pin, Gpio_value);
Data = XGpioPs_ReadPin(Gpio_Ptr, Gpio_Pin);
if (Data != Gpio_value) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
/* 功能:读取GPIO的值
* 参数1:GpioPtr-GPIO对象指针
* 参数2:Gpio_Pin-GPIO对应pin
* 参数3:Gpio_value-GPIO读取值
*/
int read_gpio_value(XGpioPs * Gpio_Ptr, u32 Gpio_Pin, GPIO_VALUE *Gpio_value){
*Gpio_value = (GPIO_VALUE)XGpioPs_ReadPin(Gpio_Ptr, Gpio_Pin);
return XST_SUCCESS;
}
3.main函数进行调用
(1)初始化GPIO相关状态,进行输入输出、使能等配置
(2)每隔1秒进行LED等的亮灭操作,查看GPIO输出操作是否好用
int main()
{
//MIO EMIO测试
#ifdef GPIO_Test
//初始化GPIO,包括输入输出模式、初始值、是否使能等
gpio_polled_init(&MIOLed0, LED0_GPIO_PS_MIO, GPIO_DIR_OUTPUT, GPIO_VALUE_OFF, GPIO_ENABLE);
gpio_polled_init(&MIOLed1, LED1_GPIO_PS_MIO, GPIO_DIR_OUTPUT, GPIO_VALUE_OFF, GPIO_ENABLE);
gpio_polled_init(&EMIOLed0, LED0_GPIO_PL_EMIO, GPIO_DIR_OUTPUT, GPIO_VALUE_OFF, GPIO_ENABLE);
gpio_polled_init(&EMIOLed1, LED1_GPIO_PL_EMIO, GPIO_DIR_OUTPUT, GPIO_VALUE_OFF, GPIO_ENABLE);
#endif
while(1){
//************************************GPIO-Test*********************************//
#ifdef GPIO_Test
set_gpio_value(&EMIOLed0, LED0_GPIO_PL_EMIO, GPIO_VALUE_ON);
sleep(1);
set_gpio_value(&EMIOLed0, LED0_GPIO_PL_EMIO, GPIO_VALUE_OFF);
set_gpio_value(&EMIOLed1, LED1_GPIO_PL_EMIO, GPIO_VALUE_ON);
sleep(1);
set_gpio_value(&EMIOLed1, LED1_GPIO_PL_EMIO, GPIO_VALUE_OFF);
set_gpio_value(&MIOLed0, LED0_GPIO_PS_MIO, GPIO_VALUE_ON);
sleep(1);
set_gpio_value(&MIOLed0, LED0_GPIO_PS_MIO, GPIO_VALUE_OFF);
set_gpio_value(&MIOLed1, LED1_GPIO_PS_MIO, GPIO_VALUE_ON);
sleep(1);
set_gpio_value(&MIOLed1, LED1_GPIO_PS_MIO, GPIO_VALUE_OFF);
#endif
}
return 0;
}
创作不易,希望大家点赞、收藏、关注哦!!!ヾ(o◕∀◕)ノ