zedboard第十七课(standaloneOS,Interrupt专题,GPIO中断)

我们知道,GIC掌管整个中断系统,先来回顾一下ARM对中断的处理过程。
当一切准备好之后,一旦打开处理器的全局中断就可以处理来自外设的各种中断事件了。
当外设(SOC内部或者外部都可以)检测到了中断事件,就会通过interrupt requestion line上的电平或者边沿(上升沿或者下降沿或者both)通知到该外设连接到的那个中断控制器,而中断控制器就会在多个处理器中选择一个,并把该中断通过IRQ分发给该processor。
ARM处理器感知到了中断事件后,会进行下面一系列的动作:
1、修改CPSR(Current Program Status Register)寄存器中的M[4:0]。M[4:0]表示了ARM处理器当前处于的模式( processor modes),IRQMODE的编码是10010,而USRMODE的编码是10000。
一旦设定了CPSR.M为10010,ARM处理器就会将processor mode切换到IRQ mode。
2、保存发生中断那一点的CPSR值(step 1之前的状态)和PC值。
ARM处理器支持9种processor mode,每种mode看到的ARM core register(R0~R15,共计16个)的集合都是不同的。每种mode都是从一个包括所有的Banked ARM core register中选取自己的模式所需要的寄存器。
对于IRQ,它能看到的是
PC_global,CPSR_global,
r0_usr~r12_usr,
sp_irq,lr_irq,SPSR_irq,

在IRQ mode下,CPU看到的R0~R12寄存器、PC以及CPSR是和usr mode(userspace)或者svc mode(kernel space)是一样的。不同的是IRQ mode下,有自己的R13(SP,stack pointer)、R14(LR,link register)和SPSR(Saved Program Status Register)。
硬件会将发生中断那一刻的CPSR保存在SPSR_irq寄存器中,对于IRQ而言,具体的返回地址保存在lr_irq寄存器中。

3、mask IRQ exception。也就是设定CPSR.I = 1

4、设定PC值为IRQ exception vector。ARM处理器的硬件就只能帮你帮到这里了,一旦设定PC值,ARM处理器就会跳转到IRQ的exception vector地址了,后续的动作都是软件行为了。

当中断处理结束,返回断点继续执行用户程序时,ARM硬件会设置CPSR.I = 0,并修改CPSR.M[4:0]=10000,重新回到USR_MODE。这时,才能够重新响应IRQ。
这种方式下,中断是非重入的。IRQHandler只需要保存现场,调用SSISR,恢复现场,跳转到断点。
如果我们需要可重入的中断,就需要修改IRQHandler中的预处理部分,除了保存现场,还要手工修改CPSR,取消IRQ_MASK。但是通常都不建议使用可重入中断。

当CPSR中的I和F位为1时,IRQ和FIQ全部处于禁止状态。无论你在irq pin和fiq pin上面发什么样的中断信号,ARM不会理你,当I位和F位为0时,irq pin上有中断信号过来时,就会打断arm的当前工作,并且切换到IRQ模式下,处于IRQ的模式的时候,此时如果irq pin上面又来中断信号,此时ARM是不会理你的,但是有一种情况例外,当ARM处在IRQ模式,这个时候fiq pin来了一个中断信号,直接打断,进入到fiq模式,跳到相应的fiq的异常向量表处去执行代码。
所以得出一个结论: IRQ模式只能被FIQ模式打断,FIQ模式下谁也打不断。

再说说Linux的情况。Linux不用FIQ,只用到了IRQ。但是我们有时候一个中断需要处理很长时间,我们需要占用IRQ模式那么长的时间吗?不需要,linux在IRQ模式下只是简单的记录是什么中断,马上切换回SVC模式,换句话说,linux的中断处理都是在SVC模式下处理的。

那么standaloneOS究竟又是怎么执行中断的呢?
一切的起点,起始于vectors,位于standalone_v6_5/src/asm_vectors.S文件中。

.section .vectors
_vector_table:
	B	_boot
	B	Undefined
	B	SVCHandler
	B	PrefetchAbortHandler
	B	DataAbortHandler
	NOP	/* Placeholder for address exception vector*/
	B	IRQHandler
	B	FIQHandler

这是向量表的总入口,我们看到,向量表首先无条件跳转的Handler,例如IRQHandler,FIQHandler。

IRQHandler:					/* IRQ vector handler */
stmdb	sp!,{r0-r3,r12,lr}		/* state save from compiled code*/
bl	IRQInterrupt			/* IRQ vector */
ldmia	sp!,{r0-r3,r12,lr}		/* state restore from compiled code */
	subs	pc, lr, #4			/* adjust return */

上面是IRQHandler的处理过程,它并没有做什么特殊处理,只是首先保存断点现场,然后在调用了IRQInterrupt这个函数后,再返回断点。

其他的Handler与之类似。

那么这个被调用的IRQInterrupt又是什么呢?
位于standalone_v6_5/src/vectors.c中

void IRQInterrupt(void)
{
	XExc_VectorTable[XIL_EXCEPTION_ID_IRQ_INT].Handler(XExc_VectorTable[
					XIL_EXCEPTION_ID_IRQ_INT].Data);
}

它从注册在系统表中的控制块中找到Callback,并将同时注册在系统表中CallbackRef传递给Callback。
它并没有做什么处理,只是通过STI查找到GRP,然后利用GRP里的配置,调用RCF。(Registered Callback Function)
这个封装看似并没有什么作用,但是它最大的好处,是程序架构上的分段。
IRQHandler是First Stage Interrupt Service Routine,(FSISR)
IRQInterrupt是Second Stage Interrupt Service Routine,(SSISR)
我们可以定义一些预处理的操作,放在SSISR中。在预处理完后,再调用TSISR。但是通常并不这样做。
自然的,XExc_VectorTable就是Third Stage Interrupt Service Routine,(TSISR)

其他的Interrupt与之类似。

来看看TSISR究竟是怎么做的。
位于standalone_v6_5/src/xil_exception.c文件中。

typedef struct {
	Xil_ExceptionHandler Handler;
	void *Data;
} XExc_VectorTableEntry;

XExc_VectorTableEntry XExc_VectorTable[XIL_EXCEPTION_ID_LAST + 1] =
{
	{Xil_ExceptionNullHandler, NULL},
	{Xil_UndefinedExceptionHandler, NULL},
	{Xil_ExceptionNullHandler, NULL},
	{Xil_PrefetchAbortHandler, NULL},
	{Xil_DataAbortHandler, NULL},
	{Xil_ExceptionNullHandler, NULL},
	{Xil_ExceptionNullHandler, NULL},
};

可以看到,standaloneOS为XIL_EXCEPTION_ID_IRQ_INT这个STI,配置的是默认的ExceptionNULLHandler。也就是说,如果我们需要使用IRQ的TSISR,那么就需要更新XExc_VectorTable的条目。

这种基于SystemTable的机制,提供了基于全局控制块(GCB,Global Control Block)注册的方式的回调使用方法。
系统提供给用户一个注册机制,将用户所需要的具体操作方法,注册到GCB中,当系统捕获到合适的触发条件时,通过ISR的分段处理,最终会调用注册到GCB中的Callback。这和linux驱动的运行机制是类似的。

我们知道,Callback的运行时机,并不是用户程序能够启动的,而是由系统中的配置的Trigger来启动的。这在客观上,形成了前后台的程序架构,APP位于后台,而ISR位于前台,即,Callback位于前台。

相应的,standaloneOS也提供了配置XExc_VectorTable的机制。
位于standalone_v6_5/src/xil_exception.c文件中。

void Xil_ExceptionRegisterHandler(u32 Exception_id,
				    Xil_ExceptionHandler Handler,
				    void *Data)
{
	XExc_VectorTable[Exception_id].Handler = Handler;
	XExc_VectorTable[Exception_id].Data = Data;
}
void Xil_ExceptionRemoveHandler(u32 Exception_id)
{
	Xil_ExceptionRegisterHandler(Exception_id,
				       Xil_ExceptionNullHandler,
				       NULL);
}
void Xil_ExceptionInit(void)
{
	return;
}

可以看出,注册和注销,都是用来填充GCB用的。用户提供Callback的指针和CallbackRef的指针,填充XExc_VectorTable的对应条目。

再来看位于standalone_v6_5/src/xil_exception.h文件中。

#define XIL_EXCEPTION_ID_IRQ_INT		5U
#define XIL_EXCEPTION_ID_INT	XIL_EXCEPTION_ID_IRQ_INT
/**
 * This typedef is the exception handler function.
 */
typedef void (*Xil_ExceptionHandler)(void *data);
typedef void (*Xil_InterruptHandler)(void *data);

#define Xil_ExceptionEnable() \
		Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ)

#define Xil_ExceptionDisable() \
		Xil_ExceptionDisableMask(XIL_EXCEPTION_IRQ)

这里定义了INT_ID等同于IRQ_ID,并定义了两个宏拟函数,用来使能或者禁止Exception。

分析至此,我们已经清楚了,如果要使用GIC进行控制,就需要配置好XExc_VectorTable的对应条目。

void Exception_Init_Register_Enable()
{
		Xil_ExceptionInit();
		Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
		Xil_ExceptionEnable();
 }

从中可以看出,我们把XScuGic_InterruptHandler作为Callback,把pScuGic作为CallbackRef注册到了XExc_VectorTable的对应IRQ的条目中。

那么,XScuGic_InterruptHandler又是什么?
位于scugic_v3_8/src/xscugic_intr.c文件中。

void XScuGic_InterruptHandler(XScuGic *InstancePtr)
{

	u32 InterruptID;
	    u32 IntIDFull;
	    XScuGic_VectorTableEntry *TablePtr;

	    IntIDFull = XScuGic_CPUReadReg(InstancePtr, XSCUGIC_INT_ACK_OFFSET);
	    InterruptID = IntIDFull & XSCUGIC_ACK_INTID_MASK;

	  	...
	    TablePtr = &(InstancePtr->Config->HandlerTable[InterruptID]);
		if(TablePtr != NULL) {
	        TablePtr->Handler(TablePtr->CallBackRef);
		}
		...
}

在省去了与分析无关的代码后,核心代码如上。
当系统Trigger触发时,TSISR被执行。
它利用传入的资源控制块RCB,找到需要相应的INT_ID。然后通过ID找到对应HandlerTableEntry,从而找到下一级的Callback以及对应的CallbackRef,并执行。

在位于scugic_v3_8/src/xscugic.h文件中。有如下定义。

typedef struct
{
	Xil_InterruptHandler Handler;
	void *CallBackRef;
} XScuGic_VectorTableEntry;
typedef struct
{
	u16 DeviceId;		/**< Unique ID  of device */
	u32 CpuBaseAddress;	/**< CPU Interface Register base address */
	u32 DistBaseAddress;	/**< Distributor Register base address */
	XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
				 Vector table of interrupt handlers */
} XScuGic_Config;
typedef struct
{
	XScuGic_Config *Config;  /**< Configuration table entry */
	u32 IsReady;		 /**< Device is initialized and ready */
	u32 UnhandledInterrupts; /**< Intc Statistics */
} XScuGic;

GIC的资源控制块RCB就是 XScuGic。而它对应的资源描述块RDB是XScuGic_Config。
在RDB中,定义了HandlerTable,它的条目被定义成XScuGic_VectorTableEntry类型。
每一个需要被GIC处理的IRQ,都需要注册到GIC的RDB中。在GIC的RDB中注册的Callback,是FhSISR(Forth Stage Interrupt Sevice Routine) 。

同样的,standaloneOS提供了对GIC的数据对象进行操作的函数。
位于scugic_v3_8/src/xscugic_sint.c文件中。

XScuGic_Config *XScuGic_LookupConfig(u16 DeviceId)
{
	XScuGic_Config *CfgPtr = NULL;
	u32 Index;

	for (Index=0U; Index < (u32)XPAR_SCUGIC_NUM_INSTANCES; Index++) {
		if (XScuGic_ConfigTable[Index].DeviceId == DeviceId) {
			CfgPtr = &XScuGic_ConfigTable[Index];
			break;
		}
	}

	return (XScuGic_Config *)CfgPtr;
}

在SystemTable中遍历查找,如果有匹配的DeviceID,那么返回全局资源指针GRP。

位于scugic_v3_8/src/xscugic.c文件中。

s32  XScuGic_CfgInitialize(XScuGic *InstancePtr,
				XScuGic_Config *ConfigPtr,
				u32 EffectiveAddr)
{
	u32 Int_Id;
	u32 Cpu_Id = CpuId + (u32)1;
	(void) EffectiveAddr;

	if(InstancePtr->IsReady != XIL_COMPONENT_IS_READY) {
		InstancePtr->IsReady = 0U;
		InstancePtr->Config = ConfigPtr;


		for (Int_Id = 0U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id++) {		
			if 	((InstancePtr->Config->HandlerTable[Int_Id].Handler == NULL)) {
				InstancePtr->Config->HandlerTable[Int_Id].Handler =
									StubHandler;
			}
			InstancePtr->Config->HandlerTable[Int_Id].CallBackRef =
								InstancePtr;
		}
		XScuGic_Stop(InstancePtr);
		DistributorInit(InstancePtr, Cpu_Id);
		CPUInitialize(InstancePtr);

		InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
	}

	return XST_SUCCESS;
}

从中可以看出,初始化主要工作就是将RCB和RDB关联起来。
填充Callback和CallbackRef,把RCB的指针填充进去。从而形成回溯引用(Backtrace Reference)。也可以称为源端对象引用(Source Object Reference)。
从XScuGic->XScuGic_Config-> XScuGic_VectorTableEntry->XScuGic。

s32  XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
                      Xil_InterruptHandler Handler, void *CallBackRef)
{
	InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
	InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;

	return XST_SUCCESS;
}
void XScuGic_Disconnect(XScuGic *InstancePtr, u32 Int_Id)
{
	u32 Mask;
	Mask = 0x00000001U << (Int_Id % 32U);
	XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_DISABLE_OFFSET +
						((Int_Id / 32U) * 4U), Mask);
	InstancePtr->Config->HandlerTable[Int_Id].Handler = StubHandler;
	InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = InstancePtr;
}

从中可以看出,XScuGic_Connect的作用就是更新FhSISR,用实际需要的Callback和CallbackRef来取代初始化时填充的默认的FhSISR。

void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{
	u32 Mask;
	Mask = 0x00000001U << (Int_Id % 32U);
	XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +
						((Int_Id / 32U) * 4U), Mask);
}
void XScuGic_Disable(XScuGic *InstancePtr, u32 Int_Id)
{
	u32 Mask;
	Mask = 0x00000001U << (Int_Id % 32U);
	XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_DISABLE_OFFSET +
						((Int_Id / 32U) * 4U), Mask);
}

从中可以看出,使能或者禁止某个INT_ID时,就是将MASK写入GIC的配置寄存器。

void XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
					u8 Priority, u8 Trigger)
{
	u32 RegValue;
	u8 LocalPriority;
	LocalPriority = Priority;

	RegValue = XScuGic_DistReadReg(InstancePtr,
			XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id));

	LocalPriority = LocalPriority & (u8)XSCUGIC_INTR_PRIO_MASK;
	RegValue &= ~(XSCUGIC_PRIORITY_MASK << ((Int_Id%4U)*8U));
	RegValue |= (u32)LocalPriority << ((Int_Id%4U)*8U);
	XScuGic_DistWriteReg(InstancePtr, XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id),
				RegValue);
	RegValue = XScuGic_DistReadReg(InstancePtr,
			XSCUGIC_INT_CFG_OFFSET_CALC (Int_Id));
	RegValue &= ~(XSCUGIC_INT_CFG_MASK << ((Int_Id%16U)*2U));
	RegValue |= (u32)Trigger << ((Int_Id%16U)*2U);
	XScuGic_DistWriteReg(InstancePtr, XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id),
				RegValue);
}

从中可以看出,XScuGic_SetPriorityTriggerType就是将某个INT_ID的对应配置参数,写入GIC的相应控制器中。

来看一个具体的例子。

XScuGic ScuGic;
XScuGic_Config* pScuGicCfg;
XGpioPs Gpio;

void XScuGIc_Init_Connect_SetTrigger_Enable()
{
	 pScuGicCfg=XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
	 XScuGic_CfgInitialize(&ScuGic,pScuGicCfg,pScuGicCfg->CpuBaseAddress);

	 XScuGic_Connect(&ScuGic, 52,
					(Xil_ExceptionHandler)XGpioPs_IntrHandler,
					(void *)&Gpio);
	 XScuGic_SetPriorityTriggerType(&ScuGic,52,0xA0,0x01);//CPU0     bit0=1
	 XScuGic_Enable(&ScuGic,52); 
}

这个函数中,用XGpioPs_IntrHandler作为INTR_ID为52的IRQ实际Callback,并把RCB的指针作为CallbackRef,注册到GIC的RDB中。

那么,接下来,XGpioPs_IntrHandler又是干什么用的呢?
位于gpiops_v3_3/src/xgpiops_intr.c文件中。

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

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

			if((IntrStatus & IntrEnabled) == 0) continue;

			XGpioPs_IntrClear((XGpioPs *)InstancePtr, Bank,
							(IntrStatus & IntrEnabled));
			InstancePtr->Handler(InstancePtr->
					     CallBackRef, Bank,
					     (IntrStatus & IntrEnabled));
		}
	}
}

从中可以看出,通过遍历XGpioPs的所有BANK,查找具体是哪个BANK的哪个GPIO触发了IRQ#52。然后执行对应的Callback。这个Callback是FfSISR(Fifth Stage Interrupt Service Routine)

来看这个FfSISR究竟是如何工作的?

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;

对于GPIO的RCB而言,在其中注册了Callback和CallbackRef,从ISR相应的分景上看,这个Callback属于FfSISR。
在StandaloneOS的体系里,FfSISR是最终的Callback了。所以它也是FnSISR(Final Stage Interrupt Service Routine)。
FnSISR,通常也被成为IRSA(Interrupt Response Specific Action )。

也就是说,如果需要GPIO能够响应中断,那么必须要配置好它的RCB。即,XGpioPs,更具体的说,是其中的XGpioPs_Handler 和CallbackRef。

位于xgpiops_intr.c文件中,有几个与GPIO_INTR相关的操作函数。

void XGpioPs_SetIntrTypePin(XGpioPs *InstancePtr, u32 Pin, u8 IrqType)
{
	u32 IntrTypeReg;
	u32 IntrPolReg;
	u32 IntrOnAnyReg;
	u8 Bank;
	u8 PinNumber;

	XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
	IntrTypeReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
				       ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
				       XGPIOPS_INTTYPE_OFFSET);

	IntrPolReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
				      ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
				      XGPIOPS_INTPOL_OFFSET);
	IntrOnAnyReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
					((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
					XGPIOPS_INTANY_OFFSET);

	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTTYPE_OFFSET, IntrTypeReg);

	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTPOL_OFFSET, IntrPolReg);

	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTANY_OFFSET, IntrOnAnyReg);
}

这个函数用来设置某个PINNUM的GPIO,它所能触发的中断的类型。

void XGpioPs_SetCallbackHandler(XGpioPs *InstancePtr, void *CallBackRef,
				 XGpioPs_Handler FuncPointer)
{
	InstancePtr->Handler = FuncPointer;
	InstancePtr->CallBackRef = CallBackRef;
}

这个函数,只是简单的填充了RCB的Callback和CallbackRef。

void XGpioPs_IntrEnablePin(XGpioPs *InstancePtr, u32 Pin)
{
	u8 Bank;
	u8 PinNumber;
	u32 IntrReg = 0U;
	XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);

	IntrReg = ((u32)1 << (u32)PinNumber);
	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTEN_OFFSET, IntrReg);
}
void XGpioPs_IntrDisablePin(XGpioPs *InstancePtr, u32 Pin)
{
	u8 Bank;
	u8 PinNumber;
	u32 IntrReg = 0U;
	XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);

	IntrReg =  ((u32)1 << (u32)PinNumber);
	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTDIS_OFFSET, IntrReg);
}

这个函数,利用PINNUM生成对应的MASK,并将MASK写入GPIO的配置寄存器。

来看一个具体的例子。

XGpioPs Gpio;
void XGpioPs_SetIntr_SetCallback_Enable()
{
	XGpioPs_Config *ConfigPtr;
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DeviceID);
	 XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
	 
	XGpioPs_SetDirectionPin(&Gpio,INTR_GPIO_NUM,GPIO_INPUT);
	XGpioPs_SetIntrTypePin(&Gpio,INTR_GPIO_NUM,XGPIOPS_IRQ_TYPE_EDGE_RISING);
	 XGpioPs_SetCallbackHandler(&Gpio, (void *)&Gpio, GpioHandler);
	 XGpioPs_IntrEnablePin(&Gpio,INTR_GPIO_NUM);
}

在这个函数内,配置了GPIO的IRSA是函数GpioHandler,其对应的CallbackRef是XGpioPs的指针pGpio。通过将XGpioPs的RCB作为CallbackRef注册到RCB中,实现了回溯引用(Backtrace Reference)。也可以称为源端对象引用(Source Object Reference)。
XGpioPs->CallbackRef->XGpioPs。

IRSA是需要用户来实现的函数。来看一个例子。

static void GpioHandler(void *CallBackRef, u32 Bank, u32 Status)
{
	static int  i = 0;
    XGpioPs* pGpioPs=(XGpioPs*)CallBackRef;
   XGpioPs_IntrDisablePin(pGpioPs,INTR_GPIO_NUM);
	printf("Gpio Handler %d...   BANK = %d  STATUS = %X \n\r",i++,(int)Bank,(unsigned int)Status );
    XGpioPs_IntrClearPin(pGpioPs,INTR_GPIO_NUM);
    XGpioPs_IntrEnablePin(pGpioPs,INTR_GPIO_NUM);
}
/*
*void XGpioPs_IntrHandler(XGpioPs *InstancePtr)
*{
*	u8 Bank;
*	u32 IntrStatus;
*	u32 IntrEnabled;

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

结合XGpioPs_IntrHandler的执行过程,能够更清晰的看出IRSA是如何被调用的。
对于GPIO的IRSA而言,在被调用时,不仅传递给它CallbackRef,同时还传递给它前面的阶段,经过预处理后的有用的信息,例如当前被响应的中断所在的BANK以及BITS_VECTOR的STATUS。

至此,我们已经完整的分析了GPIO中断的整个响应过程。
总结如下:
FSISR: _vector_table==>
SSISR:IRQHandler==>
TSISR:XExc_VectorTable ->XScuGic_InterruptHandler ==>
FhSISR:XScuGic_VectorTableEntry->XGpioPs_IntrHandler==>
FfSISR(IRSA): XGpioPs_Handler-> GpioHandler

其中,需要注册HandlerVectorEntry的位置有三个:
Exception,将GIC的Handler注册到系统的ExceptionVectorTable中,对应的ID的ENTRY中。
IRQ,将IRQ的Handler注册到GIC的IRQHandlerTable中,对应的ID的ENTRY中。
PostIRQ,将GPIO的Handler注册到GPIO的PostIRQHandlerVectorEntry中。

来看一个具体的例子。

void init_intr()
{
	void XGpioPs_SetIntr_SetCallback_Enable();
	void XScuGIc_Init_Connect_SetTrigger_Enable();
	void Exception_Init_Register_Enable();
}

在初始化配置时,我们要按照End_To_Start的顺序进行配置,即,首先配置INTR_of_GPIO的Callback,然后再配置IRQ52_of_GIC的Callback,
然后在配置Exception_of_GIC_of_Vector的Callback。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值