打开官方例程后,会发现这个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呢
在BSP中有gpio和gpiops两个文件夹,分别使用两套函数。
请仔细看下面两个获取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 分别定义了不同的实体位置
也就是说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官方开发板为例
配置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中断表。