Xilinx zynq 7010/7020 GPIO - MIO

在这里插入图片描述
有条件的可以买一块xilinx zc702官方开发板,能够从中受益匪浅。
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);
        }
    }
    
}

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值