基于小梅哥ZYNQ7020开发板 EMIO GPIO/AXI GPIO点灯记录

在UG585 Chapter14中,针对复用PL端的EMIO为GPIO和复用PS端的MIO为GPIO的区别做了介绍:
the EMIO interface is simply wires between the PS and the PL,The EMIO I/Os are not connected to the MIO I/Os in any way. The EMIO inputs cannot be connected to the MIO outputs and the MIO inputs cannot be connected to the EMIO outputs. Each bank is independent and can only be used as software observable/controllable signals.

所以,EMIO就是一种特殊的MIO,是一种可以导出到FPGA中,由FPGA来驱动和分配的MIO?

EMIO复用GPIO,Vivado配置如下:
 


将EMIO脚引出来,Open Elaborate Design,IO-planning窗口中对管脚配置,配置对应的按键和LED管脚。或者新建一个.xdc约束文件,自己写,模板是:

set_property IOSTANDARD LVCMOS33 [get_ports {EMIO_tri_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {EMIO_tri_io[0]}]
set_property PACKAGE_PIN XXX [get_ports {EMIO_tri_io[1]}]
set_property PACKAGE_PIN XXX [get_ports {EMIO_tri_io[0]}]

之后布线综合,生成bitsream,export xsa硬件平台文件。

对于使用调用BSP库的方式EMIO GPIO点灯:
需要注意:
如果之前在Vivado中配置的是EMIO_tri_io[1]对应按键管脚,EMIO_tri_io[0]对应LED管脚,那么这个时候调用bsp函数中的Pin号按键是55,LED是54
其他的都保持和MIO GPIO点灯一样的配置,流程即:初始化Gpio Instance、设置方向,读写管脚。

使用读写寄存器方式的EMIO GPIO点灯:
与之前MIO GPIO点灯方式基本一样:

#include "unistd.h"

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

#define GPIO_BASEADDR 0xE000A000
#define XGPIOPS_DIRM_OFFSET 0xE000A284
#define XGPIOPS_OUTEN_OFFSET 0xE000A288

#define MASK_DATA_2_LSW 0xE000A010


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

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

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


}

对于使用PS MIO连接的按键控制EMIO GPIO连接的LED,修改管脚号即可。

对于使用EMIO GPIO按键按下产生中断的方式来驱动EMIO GPIO LED:
和使用PS端GPIO LED同理,需要注意除了Pin脚号需要修改,该函数修改为:XGpioPs_IntrEnable(&Gpio, XGPIOPS_BANK2, (1 << Input_Pin - 54)); 其他保持一致。


AXI4允许256个时钟周期突发传输数据,AXI4-Lite无法突发传输,AXI4-Stream允许无限制的数据突发传输。在 ZYNQ PS 与 PL 之间仅支持 AXI4 AXI4-Lite 总线,AXI4-Stream 总线只能在 PL 中实现。
AXI GPIO是
PL中使用 FPGA 逻辑构建的GPIO软核,1个AXI GPIO软核只能提供2个通道32位的GPIO控制,注意AXI_GPIO每个通道32位可以单独设置为输入或者输出,但是中断无法单独每一位触发!!不同于使用PS MIO/EMIO GPIO。

对于使用AXI GPIO点灯:
Vivado设置如下:

使能中断并且连接ZYNQ硬核的中断管脚,ZYNQ硬核的中断使能双击打开之后,在Interrupt选项中勾选PL-PS Interrupt。
Vitis 使用AXI_GPIO按键控制LED亮灭程序如下:

#include "xparameters.h"
#include "xgpio.h"
#include "xil_printf.h"
#include "unistd.h"

/*
 * The following constant is used to determine which channel of the GPIO is
 * used for the LED if there are 2 channels supported.
 */
#define LED_CHANNEL 1
#define KEY_CHANNEL 2

XGpio Gpio;

int main(void)
{
	int Status;

	/* Initialize the GPIO driver */
	Status = XGpio_Initialize(&Gpio, XPAR_GPIO_0_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio Initialization Failed\r\n");
		return XST_FAILURE;
	}

	/* Set the direction for all signals as inputs except the LED output */
	XGpio_SetDataDirection(&Gpio, LED_CHANNEL, 0);			//0 are output
	XGpio_SetDataDirection(&Gpio, KEY_CHANNEL, 1);

	/* Loop forever blinking the LED */

	while (1) {
		if(XGpio_DiscreteRead(&Gpio, KEY_CHANNEL) == 0)
		{
			XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0x1);
			usleep(500000);
	//		XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0x0);
			XGpio_DiscreteClear(&Gpio, LED_CHANNEL, 0x1);
			usleep(500000);
		}
		else
			XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0x0);

	}

}

参考的AXI_GPIO例程写的,注意例程里面函数XGpio_DiscreteClear用法,如果要置为0的话,最后一个参数Mask应该为1,而不是例程里面写的0。
小梅哥教程里面提供的代码给了更多AXI_GPIO读写操作的API,基于官方提供的xgpio库,可以实现通道内32位GPIO的读写,我只设置了2个通道,而且每个通道(Channel)都是1位,所以直接套用的例程。
AXI_GPIO Channel1或者Channel2某一位设置方向函数如下,来源小梅哥例程:

void AXI_GPIO_SetPin_Dir(XGpio *InstPtr, uint8_t Channel, uint8_t GPIO_Num, uint8_t Dir)
{
	uint32_t Mode;
	Mode = XGpio_GetDataDirection(InstPtr, Channel);
	if(Dir == INPUT)
		Mode = Mode | (1 << GPIO_Num);//把对应Pin位设为1
	else if(Dir == OUTPUT)
		Mode = Mode & (~(1 << GPIO_Num));//把对应Pin位设为0
	XGpio_SetDataDirection(InstPtr, Channel, Mode);
}

对于某个Channel某一位的读写,同理,借用XGpio_DiscreteSet和XGpio_DiscreteClear就行。

AXI GPIO中断读写:
如之前所述,AXI GPIO只能整个通道一起触发中断,所以很多应用做起来不如使用PS端 MIO/EMIO GPIO灵活。
参考Xilinx官方中断教程和小梅哥ACZ702教程写的,按键对应AXI GPIO Channel2,LED对应AXI GPIO Channel1,相关代码如下,实现按键按下,LED开始闪烁。

#include "xparameters.h"
#include "xgpio.h"
#include "xscugic.h"
#include "xil_printf.h"
#include "unistd.h"

/*
 * The following constant is used to determine which channel of the GPIO is
 * used for the LED if there are 2 channels supported.
 */
#define LED_CHANNEL 1
#define KEY_CHANNEL 2

#define GPIO_DEVICE_ID		XPAR_GPIO_0_DEVICE_ID
#define XGPIO_IR_CH2_MASK	0x2

#define INTERRUPT_CONTROL_VALUE 0x7

#define INTC_DEVICE_ID	XPAR_SCUGIC_SINGLE_DEVICE_ID
#define INTC_GPIO_INTERRUPT_ID	XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR

#define INTC_HANDLER	XScuGic_InterruptHandler

XGpio Gpio;
XScuGic Intc;

static volatile u32 IntrFlag;

void GpioHandler(void *CallbackRef)
{
	XGpio *GpioPtr = (XGpio *)CallbackRef;

	IntrFlag = 1;

	/* Clear the Interrupt */
	XGpio_InterruptClear(GpioPtr, KEY_CHANNEL);

}


int main()
{
	int Status;
	u32 DataRead;

	Status = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

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

	XGpio_SetDataDirection(&Gpio, LED_CHANNEL, 0);			//0 are output
	XGpio_SetDataDirection(&Gpio, KEY_CHANNEL, 1);

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


	XScuGic_SetPriorityTriggerType(&Intc, INTC_GPIO_INTERRUPT_ID,
					0xA0, 0x3);

	/*
	 * Connect the interrupt handler that will be called when an
	 * interrupt occurs for the device.
	 */
	Status = XScuGic_Connect(&Intc, INTC_GPIO_INTERRUPT_ID,
				 (Xil_ExceptionHandler)GpioHandler, &Gpio);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	/* Enable the interrupt for the GPIO device.*/
	XScuGic_Enable(&Intc, INTC_GPIO_INTERRUPT_ID);
	XGpio_InterruptEnable(&Gpio, KEY_CHANNEL);
	XGpio_InterruptGlobalEnable(&Gpio);

	/*
	 * Initialize the exception table and register the interrupt
	 * controller handler with the exception table
	 */
	Xil_ExceptionInit();

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

	/* Enable non-critical exceptions */
	Xil_ExceptionEnable();

	XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0x0);

	while(1)
	{
		if(IntrFlag > 0)
		{
            
			XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0x1);
			usleep(500000);
			XGpio_DiscreteClear(&Gpio, LED_CHANNEL, 0x1);
			usleep(500000);
		}
	}

}

AXI GPIO中断流程基本与PS GPIO中断流程一致,流程基本是:
初始化GIC控制器、注册总异常函数XScuGic_InterruptHandler,绑定外设中断,PS GPIO中绑定的是XGpioPs_IntrHandler,后面绑定了回调函数:XGpioPs_SetCallbackHandler(&Gpio, &Gpio, IntrHandler); ,但是在XGpioPs_IntrHandler调用了该回调函数:

		if ((IntrStatus & IntrEnabled) != (u32)0) {
			XGpioPs_IntrClear(InstancePtr, Bank,
					(IntrStatus & IntrEnabled));
			InstancePtr->Handler(InstancePtr->
					CallBackRef, Bank,
					(IntrStatus & IntrEnabled));
		}

而AXI GPIO直接调用中断函数GpioHandler。其他流程包括设置中断寄存器、使能中断、设置触发模式等,代码可以写在前面也可以写到后面。
AXI GPIO和PS MIO/EMIO GPIO中断号是不一样的:

PS GPIO是52,AXI GPIO是61
两者触发方式也有区别,AXI GPIO使用上升沿触发,PS GPIO是High Level,需要和上面两图(UG585 Chapter7)对应上。设置触发类型调用的API两者不一样,但是BSP库函数中都有解释,自己写的话需要注意

来源说明:参考UG585、小梅哥ACZ702开发板裸机教程,复习顺便记录一下

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值