当前位置: 首页 > news >正文

Xilinx zynq 7010/7020 GPIO

在这里插入图片描述

GPIO外围设备提供软件可控的54个IO的MIO模块。也可以提供PL端64个IO的输入和128个输出的EMIO。
GPIO作为通用输入输出口,在这里定义为一种外设功能,使用软件自由控制和读取的IO。
GPIO外设的实际IO口引脚可以对应到物理引脚是分为两大类,MIO和EMIO。MIO是属于PS端的专用IO。EMIO是PL端的外设,PS端可以使用EMIO,理论上是像一条导线 一样连接到PL 的EMIO。MIO本质是BANK0,BANK1的多路复用器。MIO有54个,也就说可以吧连接
到MIO的外设进行多路复用到BANK0,BANK1的物理IO上。EMIO就是PL的BANK多路复用器。

在这里插入图片描述
IO 按照BANK进行分组,有点像STM32 中的GPIOA,GPIOB的逻辑。
每个BANK有 VMODE引脚,用来确定引脚电平标准。上图每种颜色表示一个BANK。
在这里插入图片描述

在这里插入图片描述
软件可读写 MIO:
Bank0: 32-bit bank controlling MIO pins[31:0]
Bank1: 22-bit bank controlling MIO pins[53:32]
上图可以看到MIO是Inout类型的IO

EMOI Extend MIO
Bank2: 32-bit bank controlling EMIO signals[31:0]
Bank3: 32-bit bank controlling EMIO signals[63:32]

GPIO由软件通过一系列内存映射寄存器进行控制。每个BANK的控制是相同的,尽管由于MIO和EMIOBANK的功能不同,它们之间有微小的差异。
GPIO是一个外设所以外设本身控制寄存器,如下图的左边所示,表示的是GPIO外设的寄存器。那么GPIO的物理引脚是需要进行指定的。
这个指定的过程在Vivado 的BLOCK DESIGN ZYNQ中进行设计,是硬件层面上的。也就是本文第一张图的配置。
在这里插入图片描述
MIO:
DATA_RO寄存器总是返回GPIO引脚的状态,而不管GPIO是否设置为输入。也就是只读取IO的电平值。
如果MIO没有配置为使该引脚作为GPIO引脚,那么DATA_RO是不可预测的,因为软件不能通过GPIO寄存器观察非GPIO引脚上的值。

DATA:这个寄存器控制当GPIO信号被配置为输出时要输出的值。这个寄存器的所有32位同时写入。从该寄存器读取将返回写入DATA或MASK_DATA_{LSW,MSW}的前一个值;它不返回设备引脚上的当前值。也就是写入寄存器的值。

DIRM:方向模式。这控制I/O引脚是作为输入还是作为输出。由于输入逻辑总是启用的,这有效地启用/禁用输出驱动程序。当DIRM[x]==0,输出驱动被禁用。

OEN:输出使能

EMIO:
EMIO接口只是PS和PL之间的连线。也就是说实际上EMIO不是一个设备,是一条导线。通过导线传达输入输出的值。
所以和MIO有所区别。

输出线不是3态的,所以它们不受OEN的影响。要输出的值是使用DATA

所以DATA是用来输出数据的,DATA_RO用来读取数据。

BANK 0 的8,7引脚叫做VMODE,是整个BANK的电平输入引脚,如实是3.3V那么BANK 0 所有IO的电压就是3.3V 为高电平。

在这里插入图片描述
GPIO 寄存器概览图如上。

UG585 附录有相应寄存器的地址 偏移地址。
在这里插入图片描述
具体的GPIO C语言编程操作是在SDK中完成的。这个操作是建立在硬件设计之上的,也就是BSP开发。
xilinx 提供了bsp 库,相当于我们在Vivado中构建好了一个硬件。
在这里插入图片描述
然后把硬件导入到SDK中,那么SDK会根据使用的模块自动生成bsp库函数,也就是类似STM32的标准库函数。
在SDK中就可以完全按照MCU的开发方式对纯软件进行开发。

如果有FPGA基础,可以非常深刻的理解外设的构成,甚至可以设计自己的外设。

开始SDK下的C语言编程

xgpiops.h 是BSP为GPIO准备的GPIO标准库文件。
一眼就能看到GPIO操作的API。我们可以用寄存器的方式开发,但是效率比较低,所以还是使用库函数。
在这里插入图片描述
GPIO的读写就是上图的红框部分,所以需要提供两个参数。一个是 XGpioPS,还有一个是Pin

typedef struct {
	XGpioPs_Config GpioConfig;	/**< Device configuration */
	u32 IsReady;			/**< Device is initialized and ready */
	XGpioPs_Handler Handler;	/**< Status handlers for all banks */
	void *CallBackRef; 		/**< Callback ref for bank handlers */
	u32 Platform;			/**< Platform data */
	u32 MaxPinNum;			/**< Max pins in the GPIO device */
	u8 MaxBanks;			/**< Max banks in a GPIO device */
} XGpioPs;

这里用了一个结构体描述PS 端的GPIO

typedef struct {
	u16 DeviceId;		/**< Unique ID of device */
	u32 BaseAddr;		/**< Register base address */
} XGpioPs_Config;

这里是设备ID和基础地址。ID目前不了解,基础地址应该就是GPIO的寄存器地址了。

到这里好像有点迷茫,不知道如何去配置一个GPIO了,在嵌入式ARM开发中这是最基本的操作。
在这里插入图片描述
在BSP的最下方有个一个*.mss文件
在这里插入图片描述
外设驱动中,点开
这里有文档和例子,这就好办了,有了文档和例子,差不多就知道怎么搞了。

先导入例子,然后结合文档看例子。生成了下面的例子。
在这里插入图片描述

int main(void)
{
	int Status;
	u32 InputData;

	printf("GPIO Polled Mode Example Test \r\n");
	Status = GpioPolledExample(GPIO_DEVICE_ID, &InputData);
	if (Status != XST_SUCCESS) {
		printf("GPIO Polled Mode Example Test Failed\r\n");
		return XST_FAILURE;
	}

	printf("Data read from GPIO Input is  0x%x \n\r", (int)InputData);
	printf("Successfully ran GPIO Polled Mode Example Test\r\n");
	return XST_SUCCESS;
}
int GpioPolledExample(u16 DeviceId, u32 *DataRead)
{
	int Status;
	XGpioPs_Config *ConfigPtr;
	int Type_of_board;

	Type_of_board = XGetPlatform_Info();
	switch (Type_of_board) {
		case XPLAT_ZYNQ_ULTRA_MP:
			Input_Pin = 22;
			Output_Pin = 23;
			break;

		case XPLAT_ZYNQ:
			Input_Pin = 14;
			Output_Pin = 10;
			break;
		}

	/* Initialize the GPIO driver. */
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
					ConfigPtr->BaseAddr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/* Run the Output Example. */
	Status = GpioOutputExample();
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/* Run the Input Example. */
	Status = GpioInputExample(DataRead);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

通过对以上代码的分析,可以了解GPIO的初始化过程。
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);

通过以上两个函数完成输出化
ID目前推测是外设的ID号比如有两个串口那么就有ID 0,1。目前推测。

XGpioPs_Config *XGpioPs_LookupConfig(u16 DeviceId)
{
	XGpioPs_Config *CfgPtr = NULL;
	u32 Index;

	for (Index = 0U; Index < (u32)XPAR_XGPIOPS_NUM_INSTANCES; Index++) {
		if (XGpioPs_ConfigTable[Index].DeviceId == DeviceId) {
			CfgPtr = &XGpioPs_ConfigTable[Index];
			break;
		}
	}

	return (XGpioPs_Config *)CfgPtr;
}

从这里看应该是实体序号,和推测的一致。这里必须理解GPIO是一个外设,不是具体的物理引脚。
GPIO称为一般MCU的外设,是在Vivado中完成了硬件设计,从而GPIO通过MIO映射到了物理引脚。
XGpioPs_Config XGpioPs_ConfigTable[XPAR_XGPIOPS_NUM_INSTANCES] =
{
{
XPAR_PS7_GPIO_0_DEVICE_ID,
XPAR_PS7_GPIO_0_BASEADDR
}
};
结合这个来看定义的是外设的实体和寄存器首地址。这样可以通过这个找到外设的实体。
对于CPU来说一切都是地址而已。

ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
这个函数的意义是获取外设的实体。

s32 XGpioPs_CfgInitialize(XGpioPs *InstancePtr, XGpioPs_Config *ConfigPtr,
				u32 EffectiveAddr)
{
	s32 Status = XST_SUCCESS;
	u8 i;
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(ConfigPtr != NULL);
	Xil_AssertNonvoid(EffectiveAddr != (u32)0);
	/*
	 * Set some default values for instance data, don't indicate the device
	 * is ready to use until everything has been initialized successfully.
	 */
	InstancePtr->IsReady = 0U;
	InstancePtr->GpioConfig.BaseAddr = EffectiveAddr;
	InstancePtr->GpioConfig.DeviceId = ConfigPtr->DeviceId;
	InstancePtr->Handler = (XGpioPs_Handler)StubHandler;
	InstancePtr->Platform = XGetPlatform_Info();

	/* Initialize the Bank data based on platform */
	if (InstancePtr->Platform == XPLAT_ZYNQ_ULTRA_MP) {
		/*
		 *	Max pins in the ZynqMP GPIO device
		 *	0 - 25,  Bank 0
		 *	26 - 51, Bank 1
		 *	52 - 77, Bank 2
		 *	78 - 109, Bank 3
		 *	110 - 141, Bank 4
		 *	142 - 173, Bank 5
		 */
		InstancePtr->MaxPinNum = (u32)174;
		InstancePtr->MaxBanks = (u8)6;
	} else {
		/*
		 *	Max pins in the GPIO device
		 *	0 - 31,  Bank 0
		 *	32 - 53, Bank 1
		 *	54 - 85, Bank 2
		 *	86 - 117, Bank 3
		 */
		InstancePtr->MaxPinNum = (u32)118;
		InstancePtr->MaxBanks = (u8)4;
	}

	/*
	 * By default, interrupts are not masked in GPIO. Disable
	 * interrupts for all pins in all the 4 banks.
	 */
	for (i=0;i<InstancePtr->MaxBanks;i++) {
		XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
					  ((u32)(i) * XGPIOPS_REG_MASK_OFFSET) +
					  XGPIOPS_INTDIS_OFFSET, 0xFFFFFFFFU);
	}

	/* Indicate the component is now ready to use. */
	InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

	return Status;
}

Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
这个函数是对指定GPIO外设实体进行默认参数初始化。

整理一下思路,对外设初始化,需要外设的寄存器首地址,也就是对应实体的地址。
然后使用默认参数对实体进行初始化。

然后对着例子写一段代码

#include "xparameters.h" 
#include "xstatus.h"     
#include "xil_printf.h"  
#include "xgpiops.h"     
#include "sleep.h"  

#define GPIO_DEVICE_ID      XPAR_XGPIOPS_0_DEVICE_ID
#define PIN_LED0 7

XGpioPs Gpio; 
int main(void)
{
    int Status;
	XGpioPs_Config *ConfigPtr;

    ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);

    if (Status != XST_SUCCESS) {
    return XST_FAILURE;
	}

    XGpioPs_SetDirectionPin(&Gpio, PIN_LED0, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, PIN_LED0, 1);
    

    while (1)
    {
        /* code */
         XGpioPs_WritePin(&Gpio, PIN_LED0, 0x1);
         sleep(1); 
         XGpioPs_WritePin(&Gpio, PIN_LED0, 0x0);
         sleep(1);
    }
    
}

在这里插入图片描述
OK,到此第一个点灯程序完成。

修改一下完成,GPIO的读

#include "xparameters.h" 
#include "xstatus.h"     
#include "xil_printf.h"  
#include "xgpiops.h"     
#include "sleep.h"  

#define GPIO_DEVICE_ID      XPAR_XGPIOPS_0_DEVICE_ID
#define PIN_LED0 7
#define PS_KEY_1 12
XGpioPs Gpio; 
int main(void)
{
    int Status;
	XGpioPs_Config *ConfigPtr;

    ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);

    if (Status != XST_SUCCESS) {
    return XST_FAILURE;
	}

    XGpioPs_SetDirectionPin(&Gpio, PIN_LED0, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, PIN_LED0, 1);
    

    while (1)
    {
        if(XGpioPs_ReadPin(&Gpio,PS_KEY_1))
        {
            XGpioPs_WritePin(&Gpio,PIN_LED0,0x01);
        }
        else
        {
            XGpioPs_WritePin(&Gpio,PIN_LED0,0x00);
        }
    }
    
}

相关文章:

  • 【ESP32】13.DS18B20温度传感器实验(OneWire和DallasTemperature库)
  • 『 云原生·Docker』Dockerfile是什么?如何使用 Dockerfile文件构建镜像?
  • 【嵌入式数据库】一文带你吃透通过apiBaseUrl获取FlexManager监控点数据
  • XTTS基于rman全量迁移Oracle
  • [LeetCode]-使用特殊算法的题目-2
  • 比较CPU和GPU中的矩阵计算
  • 【数据结构】树形结构——线索二叉树
  • 突如其来的第一个1024要笑着过
  • 2022年都快结束了,Java的这些新技术、热门技术,你不会还不知道吧?
  • 【Linux】Linux文件权限的理解
  • 力扣(LeetCode)2008. 出租车的最大盈利(C语言)
  • 【正点原子I.MX6U-MINI应用篇】5、嵌入式Linux在LCD上显示BMP、JPG、PNG图片
  • 四非到保研厦大,我们还有多少路要走----技术人的保研之路
  • 美团Leaf分布式ID源码启动部署
  • 归一化小程序
  • Android Volley源码解析
  • angular组件开发
  • HTTP请求重发
  • JavaScript函数式编程(一)
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • Objective-C 中关联引用的概念
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • 简析gRPC client 连接管理
  • 经典排序算法及其 Java 实现
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 数组大概知多少
  • 线性表及其算法(java实现)
  • 正则表达式
  • Java数据解析之JSON
  • ​HTTP与HTTPS:网络通信的安全卫士
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • #14vue3生成表单并跳转到外部地址的方式
  • #预处理和函数的对比以及条件编译
  • $(selector).each()和$.each()的区别
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (Python) SOAP Web Service (HTTP POST)
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (定时器/计数器)中断系统(详解与使用)
  • (二)springcloud实战之config配置中心
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (转)EOS中账户、钱包和密钥的关系
  • .NET : 在VS2008中计算代码度量值
  • .net 8 发布了,试下微软最近强推的MAUI
  • .NET MVC第三章、三种传值方式
  • .net wcf memory gates checking failed
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • .netcore如何运行环境安装到Linux服务器
  • .net反编译的九款神器
  • .NET面试题(二)
  • .考试倒计时43天!来提分啦!
  • /dev/VolGroup00/LogVol00:unexpected inconsistency;run fsck manually