天天看点

Xilinx ZYNQ 7000 AXI GPIO 读写/中断

打开SDK 后,创建官方例程

Xilinx ZYNQ 7000 AXI GPIO 读写/中断
Xilinx ZYNQ 7000 AXI GPIO 读写/中断

打开官方例程后,会发现这个AXI GPIO设置和 PS MIO/EMIO一模一样

int main(void)
{
	int Status;
	volatile int Delay;

	/* Initialize the GPIO driver */
	Status = XGpio_Initialize(&Gpio, GPIO_EXAMPLE_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, ~LED);

	/* Loop forever blinking the LED */

	while (1) {
		/* Set the LED to High */
		XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, LED);

		/* Wait a small amount of time so the LED is visible */
		for (Delay = 0; Delay < LED_DELAY; Delay++);

		/* Clear the LED bit */
		XGpio_DiscreteClear(&Gpio, LED_CHANNEL, LED);

		/* Wait a small amount of time so the LED is visible */
		for (Delay = 0; Delay < LED_DELAY; Delay++);
	}

	xil_printf("Successfully ran Gpio Example\r\n");
	return XST_SUCCESS;
}
           

这是什么情况,那怎么区分AXI GPIO和PS MIO EMIO呢

Xilinx ZYNQ 7000 AXI GPIO 读写/中断

在BSP中有gpio和gpiops两个文件夹,分别使用两套函数。

Xilinx ZYNQ 7000 AXI GPIO 读写/中断

请仔细看下面两个获取gpio实体的函数

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;
}
XGpio_Config *XGpio_LookupConfig(u16 DeviceId)
{
	XGpio_Config *CfgPtr = NULL;

	int Index;

	for (Index = 0; Index < XPAR_XGPIO_NUM_INSTANCES; Index++) {
		if (XGpio_ConfigTable[Index].DeviceId == DeviceId) {
			CfgPtr = &XGpio_ConfigTable[Index];
			break;
		}
	}

	return CfgPtr;
}
           

没错,AXI GPIO和PS GPIO使用了两套ConfigTable。

两套table 分别定义了不同的实体位置

Xilinx ZYNQ 7000 AXI GPIO 读写/中断

也就是说AXI GPIO和PS GPIO使用了两套地址,分别指向了不同的地址。

对于APU来说一切都是地址。

需要注意的是Channel 是AXI GPIO的Channel,在IP生成的时候可以选择 2个通道。

data写入的数据是整个AXI GPIO的位宽。

/****************************************************************************/
/**
* Write to discretes register for the specified GPIO channel.
*
* @param	InstancePtr is a pointer to an XGpio instance to be worked on.
* @param	Channel contains the channel of the GPIO (1 or 2) to operate on.
* @param	Data is the value to be written to the discretes register.
*
* @return	None.
*
* @note		The hardware must be built for dual channels if this function
*		is  used with any channel other than 1.  If it is not, this
*		function will assert. See also XGpio_DiscreteSet() and
*		XGpio_DiscreteClear().
*
*****************************************************************************/
void XGpio_DiscreteWrite(XGpio * InstancePtr, unsigned Channel, u32 Data)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
	Xil_AssertVoid((Channel == 1) ||
		     ((Channel == 2) && (InstancePtr->IsDual == TRUE)));

	XGpio_WriteReg(InstancePtr->BaseAddress,
			((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_DATA_OFFSET,
			Data);
}
           

AXI GPIO中断之前先来看一下AXI GPIO Read

以ZC702官方开发板为例

Xilinx ZYNQ 7000 AXI GPIO 读写/中断

配置AXI GPIO 1,2位按键输入

AXI GPIO 3 位LED输出

#define LED 0x04   /* Assumes bit 0 of GPIO is connected to an LED  */
int main(void)
{
	int Status;
	u32 SwState;
	volatile int Delay;

	/* Initialize the GPIO driver */
	Status = XGpio_Initialize(&Gpio, GPIO_EXAMPLE_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, ~LED);

	/* Loop forever blinking the LED */

	while (1) {
		/* Set the LED to High */
		XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, LED);

		/* Wait a small amount of time so the LED is visible */
		for (Delay = 0; Delay < LED_DELAY; Delay++);

		/* Clear the LED bit */
		XGpio_DiscreteClear(&Gpio, LED_CHANNEL, LED);

		/* Wait a small amount of time so the LED is visible */
		for (Delay = 0; Delay < LED_DELAY; Delay++);
		SwState = XGpio_DiscreteRead(&Gpio, LED_CHANNEL);
		xil_printf("AXI GPIO Read:%x \r\n",SwState);
	}

	xil_printf("Successfully ran Gpio Example\r\n");
	return XST_SUCCESS;
}
           

按下按键查看,输出值得变化

AXI GPIO Read:0

AXI GPIO Read:1

AXI GPIO Read:1

AXI GPIO Read:0

AXI GPIO Read:0

AXI GPIO Read:2

AXI GPIO Read:2

AXI GPIO Read:1

AXI GPIO Read:2

读取数据的API也是采用MASK的方式。

那么中断,本质上来说和GPIO中断的方式大致相同,区别在于有一条专门的中断信号线连接到GIC全局中断控制器上。

第二部分

回到中断的例程中

XScuGic_Config *IntcConfig;

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

	Result = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	if (Result != XST_SUCCESS) {
		return XST_FAILURE;
	}
           

GIC初始化的套路。

设置中断优先级

XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId,
					0xA0, 0x3);
           

这部分和GPIO中断是一样把,中断服务函数GpioHandler,装填到GIC的中断向量表中,注意这里不是CPU中断向量。

/*
	 * Connect the interrupt handler that will be called when an
	 * interrupt occurs for the device.
	 */
	Result = XScuGic_Connect(IntcInstancePtr, IntrId,
				 (Xil_ExceptionHandler)GpioHandler, InstancePtr);
	if (Result != XST_SUCCESS) {
		return Result;
	}

           

这里是基本的操作,没什么好解释的。

/* Enable the interrupt for the GPIO device.*/
	XScuGic_Enable(IntcInstancePtr, IntrId);


	/*
	 * Enable the GPIO channel interrupts so that push button can be
	 * detected and enable interrupts for the GPIO device
	 */
	XGpio_InterruptEnable(InstancePtr, IntrMask);
	XGpio_InterruptGlobalEnable(InstancePtr);

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

这里是指定CPU Vector Table IRQ项的跳转函数。

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			 (Xil_ExceptionHandler)INTC_HANDLER, IntcInstancePtr);

           

最后使能中断

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

以上过程并没有什么特别注意的地方,几乎和MIO,EMIO一致。

所以处理PL的中断在ARM端几乎和处理PS端中断一致。主要还是GIC依照中断号进行IRQ向量表装填。

CPU收到GIC发来的中断后硬件跳转到Vector Table执行IRQ函数,用来判断中断号调用GIC中断表。

继续阅读