zedboard第十八课(standaloneOS,Interrupt专题,TIMER中断)
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。
上一篇: 第十八课:函数:灵活即强大