基于小梅哥Zynq7020开发板裸机使用GPIO点灯记录

小梅哥Zynq7020开发板GPIO点灯记录

使用小梅哥zynq7020开发板,参考UG585和小梅哥裸机教程及示例代码,实现寄存器方式GPIO点灯记录:

先放一张UG585里面提供的GPIO架构图:


左边就是与GPIO有关的各个寄存器,INT开头的和中断相关,下面的就是读写寄存器、读写方向控制寄存器、输出使能寄存器,MIO Banks被复用到GPIO,GPIO输出三态门(我的理解这个三态门和STM32里面GPIO外设应该是一样道理)

与STM32类似,寄存器控制GPIO写/输出:
1、Vivado部分:block design选择Zynq IP硬核,IO Peripheral中选择GPIO,选几个都行,之后布线布局综合生产xsa

2、Vitis部分:参考UG585 Chapter14.3.2
        1、Set the direction as output: Write 0x0000_0400 to the gpio.DIRM_0 register.
        2、Set the output enable: Write 0x0000_0400 to the gpio.OEN_0 register. The output enable has significance only when the GPIO pin is configured as an output.
        3、GPIO写数:可以使用DATA_0 register或者MASK_DATA_x_MSW/LSW,两个寄存器,我用的是MASK_DATA_x_LSW(小梅哥zynq 7020 板子PS_LED对应管脚是MIO7),处于Bank0,对应Bank0寄存器所在地址XGPIOPS_DATA_LSW_OFFSET。

        注意这个寄存器对应Bank0的0~15的GPIO Pin,MSW的寄存器对应控制Bank0中的16~31的GPIO管脚。MASK_DATA_x_MSW高16位用于使能16个GPIO Pin脚中是否可以输出,低16位对应要输出的值,例如Pin 7应该在第23位(高16位+7位对应Pin7)写入0,低位的第7位写入想要输出的值,从而进行GPIO输出,对应复用MIO 7(LED Pin)的GPIO程序如下,这个程序写的不如小梅哥没有对寄存器其他位的值进行保护,纯自己好玩,仅供参考:
这里用uint32t和u32都可以。Xil_Out/Xil_In是Xilinx官方提供的读写寄存器的函数,在xil_io.h里面。

#include "xil_types.h"
#include "xil_io.h"
#include <stdio.h>

#define GPIO_BASEADDR 0xE000A000
#define XGPIOPS_DIRM_OFFSET 0xE000A204
#define XGPIOPS_OUTEN_OFFSET 0xE000A208
#define XGPIOPS_DATA_LSW_OFFSET 0xE000A000


int main()
{
    uint32_t reg_val = 0;
    uint32_t data_in = 0;
    uint32_t data_out = 0;

    reg_val = Xil_In32(XGPIOPS_DIRM_OFFSET);
    data_in = reg_val|(0x1 << 7);
    Xil_Out32(XGPIOPS_DIRM_OFFSET, data_in);
    Xil_Out32(XGPIOPS_OUTEN_OFFSET, data_in);

    while(1)
    {
        Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0 << 7) <<16 );
        Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0x1 << 7));
        usleep(200000);
        Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0 << 7) <<16);
        Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0x0 << 7));
        usleep(200000);
    }


}

调用BSP库函数的方式读写GPIO:
类似于STM32调用现成的bsp库模式,Vitis也提供一套自己的Bsp库,这个比读写寄存器的方式好弄,不用一个个去查寄存器了,参考Vitis提供的官方例程,xgpiops_polled_example,初始化函数、读写使能函数、读写函数,封装函数都给写好了,直接抄就完了,具体代码参考小梅哥zynq7020开发板代码。
可以使用板子上的按键去控制灯的亮灭,对于小梅哥ZYNQ7020 开发板,按键连接的管脚是MIO47,我的理解对应为BANK1,使用GPIO复用MIO47,使用GPIO bsp库中的函数
XGpioPs_SetDirectionPin设置47管脚为输入,即读取值,如果按键按下,此时管脚被拉到地,读到的值应该是0,以此为判断,给LED管脚Pin 7以一定的延时交替输出0和1即可,注意不同于操作寄存器的方式,调用BSP库的方式读写BANK1的管脚不需要更换寄存器,仍然使用bsp提供的函数(API):XGpioPs_ReadPin即可。

GPIO中断:

ug585 Chapter7给出了中断的架构:


中断分为SPI、SGI、PPI,对应共享外设中断、软件生成中断和私有外设中断,PPI对应定时器、看门狗等,GPIO产生中断对应SPI。
 

之后参考xilinx提供的GPIO中断例程和小梅哥教程,中断部分的函数如下所示:

#define GPIO_DEVICE_ID		XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID	XPAR_XGPIOPS_0_INTR		
#define GPIO_BANK	XGPIOPS_BANK1
static XGpioPs Gpio;
static XScuGic Intc;

//中断相关函数
XScuGic_Config *IntcConfig;

Xil_ExceptionInit();

        IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
        if (NULL == IntcConfig) {
            return XST_FAILURE;
        }

        Status = XScuGic_CfgInitialize(&Intc, IntcConfig,
                        IntcConfig->CpuBaseAddress);
        if (Status != XST_SUCCESS) {
            return XST_FAILURE;
        }

        Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                    (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                    &Intc);

        Status = XScuGic_Connect(&Intc, GPIO_INTERRUPT_ID,
                    (Xil_ExceptionHandler)XGpioPs_IntrHandler,
                    &Gpio);
        if (Status != XST_SUCCESS) {
            return Status;
        }

        /* Enable falling edge interrupts for all the pins in bank 0. */
        XGpioPs_SetIntrType(&Gpio, GPIO_BANK, 0x00, 0xFFFFFFFF, 0x00);

        /* Set the handler for gpio interrupts. */
        XGpioPs_SetCallbackHandler(&Gpio, &Gpio, IntrHandler);


        /* Enable the GPIO interrupts of Bank 0. */
        XGpioPs_IntrEnable(&Gpio, GPIO_BANK, (1 << Input_Pin - 32));


        /* Enable the interrupt for the GPIO device. */
        XScuGic_Enable(&Intc, GPIO_INTERRUPT_ID);


        /* Enable interrupts in the Processor. */
        Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

XScuGic_InterruptHandler是总的中断处理函数,GIC相关中断处理函数。

XScuGic_Connect处理的是:GPIO中断服务函数和GPIO的中断源进行关联,如果产生GPIO中断,那么此时应该去执行这个中断处理函数XGpioPs_IntrHandler,如果ID的是GPIO的中断源,那么运行这个中断函数。其中中断处理函数为:

void XGpioPs_IntrHandler(const XGpioPs *InstancePtr)
{
	u8 Bank;
	u32 IntrStatus;
	u32 IntrEnabled;

	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	for (Bank = 0U; Bank < InstancePtr->MaxBanks; Bank++) {
		IntrStatus = XGpioPs_IntrGetStatus(InstancePtr, Bank);
		IntrEnabled = XGpioPs_IntrGetEnabled(InstancePtr,Bank);
		if ((IntrStatus & IntrEnabled) != (u32)0) {
			XGpioPs_IntrClear(InstancePtr, Bank,
					(IntrStatus & IntrEnabled));
			InstancePtr->Handler(InstancePtr->
					CallBackRef, Bank,
					(IntrStatus & IntrEnabled));
		}
	}
}

这个函数我理解是处理INT_STAT、INT_EN寄存器,使能中断。后面的XGpioPs_SetIntrType处理的是INT_TYPE、INT_POLARITY、INT_ANY寄存器。
SetCallbackHandler函数设置IntrHandler回调函数,回调函数代码:

static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
	XGpioPs *Gpio = (XGpioPs *)CallBackRef;
	u32 DataRead;

	/* Push the switch button */
	DataRead = XGpioPs_ReadPin(Gpio, Input_Pin);
	if (DataRead == 0) {
		flag++;
	}
}

表示按键按下,读到MIO47对应GPIO为0,全局静态变量flag = 1,在主函数中设置flag为1的时候,LED闪烁的函数即可实现按键按下,读到GPIO输入为0产生中断,控制LED灯的亮灭。
里面的XGpioPs_IntrEnable(&Gpio, GPIO_BANK, (1 << Input_Pin - 32)); 这个函数操作的是XGPIOPS_INTEN_OFFSET,作用是:使能GPIO的输入作为中断源。需要注意的是这里Key按键是47,前面制定了Bank1,那么对于MIO47复用的GPIO,对应的应该是GPIO Bank1的第47-32,即第15位输入为中断源,而不是整个GPIO的47。对于GPIO的读写操作函数比如XGpioPs_WritePin,可以直接写对应的管脚号47,那是因为函数内部有相关判断和操作代码:

	if (PinNumber > 15U) {
		/* There are only 16 data bits in bit maskable register. */
		PinNumber -= (u8)16;
		RegOffset = XGPIOPS_DATA_MSW_OFFSET;
	} else {
		RegOffset = XGPIOPS_DATA_LSW_OFFSET;
	}

	DataVar &= (u32)0x01;
	Value = ~((u32)1 << (PinNumber + 16U)) & ((DataVar << PinNumber) | 0xFFFF0000U);
	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(Bank) * XGPIOPS_DATA_MASK_OFFSET) +
			  RegOffset, Value);

整体代码如下:

#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include <xil_printf.h>



#define GPIO_DEVICE_ID		XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID	XPAR_XGPIOPS_0_INTR					//#define XPS_GPIO_INT_ID			52U
/* The following constants define the GPIO banks that are used. */
#define GPIO_BANK	XGPIOPS_BANK1

#define Output_Pin 7
#define Input_Pin 47

static XGpioPs Gpio;
static XScuGic Intc;
static int flag;

static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
	XGpioPs *Gpio = (XGpioPs *)CallBackRef;
	u32 DataRead;

	/* Push the switch button */
	DataRead = XGpioPs_ReadPin(Gpio, Input_Pin);
	if (DataRead == 0) {
		flag++;
	}
}

int main()
{
	XGpioPs_Config *ConfigPtr;
	int Status;

	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	if (ConfigPtr == NULL) {
		return XST_FAILURE;
	}

	XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
	XGpioPs_SetDirectionPin(&Gpio, Input_Pin, 0x0);

	XGpioPs_SetDirectionPin(&Gpio, Output_Pin, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, Output_Pin, 1);
	XGpioPs_WritePin(&Gpio, Output_Pin, 0x0);



	XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */

	Xil_ExceptionInit();

		/*
		 * Initialize the interrupt controller driver so that it is ready to
		 * use.
		 */
		IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
		if (NULL == IntcConfig) {
			return XST_FAILURE;
		}

		Status = XScuGic_CfgInitialize(&Intc, IntcConfig,
						IntcConfig->CpuBaseAddress);
		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}

		Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
					(Xil_ExceptionHandler)XScuGic_InterruptHandler,
					&Intc);

		Status = XScuGic_Connect(&Intc, GPIO_INTERRUPT_ID,
					(Xil_ExceptionHandler)XGpioPs_IntrHandler,
					&Gpio);
		if (Status != XST_SUCCESS) {
			return Status;
		}

		/* Enable falling edge interrupts for all the pins in bank 0. */
		XGpioPs_SetIntrType(&Gpio, GPIO_BANK, 0x00, 0xFFFFFFFF, 0x00);

		/* Set the handler for gpio interrupts. */
		XGpioPs_SetCallbackHandler(&Gpio, &Gpio, IntrHandler);


		/* Enable the GPIO interrupts of Bank 0. */
		XGpioPs_IntrEnable(&Gpio, GPIO_BANK, (1 << Input_Pin - 32));


		/* Enable the interrupt for the GPIO device. */
		XScuGic_Enable(&Intc, GPIO_INTERRUPT_ID);


		/* Enable interrupts in the Processor. */
		Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);


	    while(1)
	    {
	        if(flag > 0)
	        {
	            flag--;
	            XGpioPs_WritePin(&Gpio, Output_Pin, 0x1);
	            usleep(500000);
	            XGpioPs_WritePin(&Gpio, Output_Pin, 0x0);
	            usleep(500000);
	        }
	    }

}

实现按键按下,GPIO控制的灯亮。

整体代码仅供参考,参考来源为小梅哥ACZ702裸机教程和Xilinx GPIO中断例程,写这篇文章纯粹自己为了记录一下,捋一下思路。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值