欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

zedboard第十八课(standaloneOS,Interrupt专题,TIMER中断)

程序员文章站 2022-07-14 19:00:10
...

TIMER中断和GPIO中断,大体流程是一样的。
如前所述,当触发条件满足时,将触发中断,从而使系统进入ISR运行。ISR中注册的Callback,逐级向前调用,最终会调用到IRSA,这是针对中断的具体操作行为。
从前后台的角度来讲,IRSA是后台程序,它的运行时机,是中断触发并生效时。
也就是说,为了能够让系统实现后台响应,就必须有中断触发。
GPIO输入源,是一种交互中断源,但是GPIO输入源,其触发中断的时机是不确定的。类似的,外设中断源,也是交互中断源,触发时机不确定。
TIMER中断源,其触发中断的时机是确定的。这也是linux系统做系统调度的基础,基于时间片进行调度,每当TIMEOUT的时候,就会进入中断处理。

在SOD设计思想下,SCUTIMER的模块设计,有两个重要的结构体。

typedef struct {
	XScuTimer_Config Config; /**< Hardware Configuration */
	u32 IsReady;		/**< Device is initialized and ready */
	u32 IsStarted;		/**< Device timer is running */
} XScuTimer;
typedef struct {
	u16 DeviceId;	/**< Unique ID of device */
	u32 BaseAddr;	/**< Base address of the device */
} XScuTimer_Config;

XScuTimer是资源控制块RCB,而 XScuTimer_Config是资源描述块RDB。
位于scutimer_v2_1/src/xscutimer_sint.c文件中,提供了获取全局资源的API。

XScuTimer_Config *XScuTimer_LookupConfig(u16 DeviceId)
{
	XScuTimer_Config *CfgPtr = NULL;
	u32 Index;

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

	return (XScuTimer_Config *)CfgPtr;
}

和其他的Lookup一样,该函数利用STI,查找到GRP,并将GRP返回给调用者。

位于scutimer_v2_1/src/xscutimer.c文件中,是主要的API。

s32 XScuTimer_CfgInitialize(XScuTimer *InstancePtr,
			 XScuTimer_Config *ConfigPtr, u32 EffectiveAddress)
{
	s32 Status;
	
	if (InstancePtr->IsStarted != XIL_COMPONENT_IS_STARTED) {
		InstancePtr->Config.BaseAddr = EffectiveAddress;
		InstancePtr->IsStarted = (u32)0;
		InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
		Status =(s32)XST_SUCCESS;
	}
	else {
		Status = (s32)XST_DEVICE_IS_STARTED;
	}
	return Status;
}

初始化函数,利用RDB来填充RCB。

void XScuTimer_SetPrescaler(XScuTimer *InstancePtr, u8 PrescalerValue)
{
	u32 ControlReg;

	ControlReg = XScuTimer_ReadReg(InstancePtr->Config.BaseAddr,
					XSCUTIMER_CONTROL_OFFSET);
	ControlReg &= (u32)(~XSCUTIMER_CONTROL_PRESCALER_MASK);
	ControlReg |= (((u32)PrescalerValue) << XSCUTIMER_CONTROL_PRESCALER_SHIFT);
	XScuTimer_WriteReg(InstancePtr->Config.BaseAddr,
			  XSCUTIMER_CONTROL_OFFSET, ControlReg);
}

该函数设置TIMER的参数,设置预分频系数。

void XScuTimer_Start(XScuTimer *InstancePtr)
{
	u32 Register;
	Register = XScuTimer_ReadReg(InstancePtr->Config.BaseAddr,
				  XSCUTIMER_CONTROL_OFFSET);
	Register |= XSCUTIMER_CONTROL_ENABLE_MASK;
	XScuTimer_WriteReg(InstancePtr->Config.BaseAddr,
			XSCUTIMER_CONTROL_OFFSET, Register);
	InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;
}
void XScuTimer_Stop(XScuTimer *InstancePtr)
{
	u32 Register;
	Register = XScuTimer_ReadReg(InstancePtr->Config.BaseAddr,
				  XSCUTIMER_CONTROL_OFFSET);
	Register &= (u32)(~XSCUTIMER_CONTROL_ENABLE_MASK);
	XScuTimer_WriteReg(InstancePtr->Config.BaseAddr,
			XSCUTIMER_CONTROL_OFFSET, Register);
	InstancePtr->IsStarted = (u32)0;
}

该函数通过配置TIMER的寄存器,启动或者停止TIMER。

位于scutimer_v2_1/src/xscutimer.h文件中,是主要的宏拟函数API。

#define XScuTimer_LoadTimer(InstancePtr, Value)				\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
			XSCUTIMER_LOAD_OFFSET, (Value))

#define XScuTimer_EnableAutoReload(InstancePtr)				\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
			XSCUTIMER_CONTROL_OFFSET,			\
			(XScuTimer_ReadReg((InstancePtr)->Config.BaseAddr, \
				XSCUTIMER_CONTROL_OFFSET) |		 \
				XSCUTIMER_CONTROL_AUTO_RELOAD_MASK))

#define XScuTimer_EnableInterrupt(InstancePtr)				\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
			XSCUTIMER_CONTROL_OFFSET,			\
			(XScuTimer_ReadReg((InstancePtr)->Config.BaseAddr, \
					XSCUTIMER_CONTROL_OFFSET) |	\
					XSCUTIMER_CONTROL_IRQ_ENABLE_MASK))

#define XScuTimer_GetInterruptStatus(InstancePtr)			\
	XScuTimer_ReadReg((InstancePtr)->Config.BaseAddr,		\
			XSCUTIMER_ISR_OFFSET)

#define XScuTimer_ClearInterruptStatus(InstancePtr)			\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
		XSCUTIMER_ISR_OFFSET, XSCUTIMER_ISR_EVENT_FLAG_MASK)

再来看如何配置系统,使系统具备TIMER的中断响应功能。
GPIO的中断,是由118个GPIO共享IRQ#52的,所以,为了能够进一步分辨具体的GPIOPIN,BSP在FhSISR中,又调用了FfSISR。FfSISR的HandlerVectorEntry,是存放在GPIO的RCB中的,从而使得FhSISR能够查找到FfSISR的HandlerVectorEntry。

TIMER并不像GPIO,TIMER的中断是私有的。所以,它并不需要FfSISR来进一步细分处理。对于TIMER,FhSISR就是FnSISR,即IRSA。
所以,我们看到,TIMER的RCB中,并没有HandlerVectorEntry来设置Callback.
TIMER的IRSA,只需要直接注册到GIC上,就可以了。由GIC的Handler来调用IRSA。

来看一个具体的例子。

int Timer_init_setup(XScuTimer *TimerPtr, u8 PrescalerValue , u32 Load_Value,u32 DeviceId)
{
     XScuTimer_Config *TMRConfigPtr;    
     TMRConfigPtr = XScuTimer_LookupConfig(DeviceId);
     XScuTimer_CfgInitialize(TimerPtr, TMRConfigPtr,TMRConfigPtr->BaseAddr);
     
     XScuTimer_SetPrescaler(TimerPtr,  PrescalerValue) ;
     XScuTimer_LoadTimer(TimerPtr, Load_Value);
     XScuTimer_EnableAutoReload(TimerPtr);
     return 1;
}

该函数初始化了TIMER的RCB,并利用RCB配置了TIMER的参数。

int Init_GIC(XScuGic * IntcInstancePtr)  //一般不需要修改
{
	int Status;

	XScuGic_Config *IntcConfig;
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	return Status ;
}
static void TimerIntrHandler(void *CallBackRef)
{
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);

    printf("seconds is %d \n",sec++);
	return ;
}

void Timer_Setup_Intr(XScuGic *GicInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{
        XScuGic_Connect(GicInstancePtr, TimerIntrId,
                        (Xil_ExceptionHandler)TimerIntrHandler,//set up the timer interrupt
                        (void *)TimerInstancePtr);

        XScuGic_Enable(GicInstancePtr, TimerIntrId);//enable the interrupt for the Timer at GIC
        XScuGic_SetPriorityTriggerType(GicInstancePtr,  TimerIntrId, 0XA0 , 3 ) ;
        XScuTimer_EnableInterrupt(TimerInstancePtr);//enable interrupt on the timer
 }

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

以上函数,实现中断系统的配置,包括IRQ的Callback注册到GIC,GIC的Handler注册到Exception。

来看具体的部署。

XScuGic GIC; //GIC
XScuTimer Timer;//timer
int main()
{
    init_platform();
    print("Hello World\n\r");
    unsigned int TIMER_LOAD_VALUE ;
    	TIMER_LOAD_VALUE =  667*1000*1000 / 2 ;
    	TIMER_LOAD_VALUE -= 1 ; 	
    	Timer_init_setup(&Timer,0 ,TIMER_LOAD_VALUE,0);
    	
    	Init_GIC(&GIC);
    	Timer_Setup_Intr(&GIC,&Timer,TIMER_IRPT_INTR);
    	Exception_Init_Register_Enable();
    	
    	XScuTimer_Start(&Timer);

    	while(1) ;

    cleanup_platform();
    return 0;
}

主函数中,配置了TIMER之后,TIMER就具备了工作参数了。
然后开始配置中断系统,包括各个分景的注册。
最后启动TIMER,用户APP工作在前台,TIMER工作在后台,每次TIMEOUT的时候,触发一次TIMER的Handler。

相关标签: vivado