wince的中断网上有很多文章,我在这里有有个人见解。
首先,准备。我们要对中断源进行定义,我们通常会定义一些宏。定义这些宏的目的就是让我们在ISR中的时候读的寄存器和他们相匹配。然后我们要定义两个数组g_oalsysIntr2iqr[]和g_oaliqr2sysIntr[].这两个数组的的作用是干什么的呢?g_oalsysIntr2iqr[]是用来给我装iqr的,g_oaliqr2sysIntr是用来给我装sysintr的。有了这些准备之后系统在跑起来之后就会怎么做呢?
第二就是初始化。在那里初始化呢在oeminit这个函数中的OALIntrInit中初始化。这个OALIntrInit函数初始化了一些中断寄存器,初始化了g_oalsysIntr2iqr[]和g_oaliqr2sysIntr[]这两个数组,(调用了OALIntrMapInit();调用OALIntrStaticTranslate(UINT32 sysIntr, UINT32 irq);OALIntrStaticTranslate(UINT32 sysIntr, UINT32 irq);这个函数是用来建立iqr和sysintr静态的关系也就是想两个数组对应的写逻辑中断号和物理中断号,)先将两个数组变成未定的,然后在对应要中断如dma,io等将他们联系起来。用的也是这个函数OALIntrStaticTranslate。
第三,我们在相应的驱动当中,动态的申请一个。怎么申请呢,是调用kernelIocontrol,这个函数很重要他不光是申请中断这个一个功能。原型如下BOOL RetVal = KernelIoControl( IOCTL_HAL_REQUEST_SYSINTR,
&Irq,
sizeof( Irq ),
pSysIntr,
sizeof( *pSysIntr ),
NULL );
这里我们我用到中断了,就请他申请中断的使用方法,KernelIoControl主要是根据你的ioctl码来使用相应的函数,他是wince重要输入输出的函数他调用的OEMIOControl会根据 IOCTL_HAL_REQUEST_SYSINTR调用一个对应的函数OALIoCtlHalRequestSysIntr(之所以说是对应就是因为他们两个对应关系是在一个结构体数组里面关联起来的,这个数组在ioctl_tab.h文件中定义的。这个结构体数组还定义了其他ioctl码和其对应处理函数)OALIoCtlHalRequestSysIntr的关键代码是:
//
BOOL OALIoCtlHalRequestSysIntr(
UINT32 code, VOID* pInpBuffer, UINT32 inpSize, VOID* pOutBuffer,
UINT32 outSize, UINT32 *pOutSize
) {
BOOL rc;
UINT32 *pInpData = pInpBuffer, sysIntr;
UINT32 inpElems = inpSize / sizeof (UINT32);
OALMSG(OAL_INTR&&OAL_FUNC, (L"+OALIoCtlHalRequestSysIntr\r\n"));
// We know output size already
if (pOutSize != NULL) *pOutSize = sizeof(UINT32);
// Check input parameters
if ((pInpBuffer == NULL || inpElems < 1 || (inpSize % sizeof(UINT32) != 0)) ||
(!pOutBuffer && pOutSize > 0)
) {
NKSetLastError(ERROR_INVALID_PARAMETER);
rc = FALSE;
OALMSG(OAL_WARN, (
L"WARN: IOCTL_HAL_REQUEST_SYSINTR invalid parameters\r\n"
));
goto cleanUp;
}
if (pOutBuffer == NULL || outSize < sizeof(UINT32)
) {
NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
rc = FALSE;
OALMSG(OAL_WARN, (
L"WARN: IOCTL_HAL_REQUEST_SYSINTR invalid parameters\r\n"
));
goto cleanUp;
}
// Find if it is new or old call type
if (inpElems > 1 && pInpData[0] == -1)
{
if (inpElems > 2 &&
(pInpData[1] == OAL_INTR_TRANSLATE || pInpData[1] == OAL_INTR_DYNAMIC ||
pInpData[1] == OAL_INTR_STATIC || pInpData[1] == OAL_INTR_FORCE_STATIC ||
pInpData[1] == 0 /* default behavior is OAL_INTR_DYNAMIC */)
) {
// Second UINT32 contains flags, third and subsequents IRQs
sysIntr = OALIntrRequestSysIntr(inpElems - 2, &pInpData[2], pInpData[1]);
} else {
NKSetLastError(ERROR_INVALID_PARAMETER);
rc = FALSE;
OALMSG(OAL_WARN, (
L"WARN: IOCTL_HAL_REQUEST_SYSINTR invalid parameters\r\n"
));
goto cleanUp;
}
} else {
if (inpElems == 1) {
// This is legacy call, first UINT32 contains IRQ
sysIntr = OALIntrRequestSysIntr(1, pInpData, 0);
} else {
NKSetLastError(ERROR_INVALID_PARAMETER);
rc = FALSE;
OALMSG(OAL_WARN, (
L"WARN: IOCTL_HAL_REQUEST_SYSINTR invalid parameters\r\n"
));
goto cleanUp;
}
}
// Store obtained SYSINTR
*(UINT32*)pOutBuffer = sysIntr;
rc = (sysIntr != SYSINTR_UNDEFINED);
cleanUp:
OALMSG(OAL_INTR&&OAL_FUNC, (
L"+OALIoCtlHalRequestSysIntr(rc = %d)\r\n", rc
));
return rc;
}
这个函数调用了sysIntr = OALIntrRequestSysIntr(1, pInpData, 0); 在这函数中我们看到逻辑中断号和物理中断号是一一对应的,但是一个物理中断号是可以对应多个逻辑中断号的,我就不知道这是怎么做的了额,返回了一个逻辑的中断号;
我们在写驱动的时候要创建一个中断服务线程IST,我们还要创立一个事件,将事件和逻辑中断号绑定在一起,绑定是用的调用 InterruptInitialize(dwSysIntr,hISTEvent,0,0)将 SYSINTR 与 Event 绑
定,InterruptInitialize 的 代 码 主要功能是
#1 将 Event 的 Ptr 填入一个数组,这个数组是记录每个 SYSINTR 对应激活的Event
#2 调用OEMInterruptEnable,使能SYSINTR所对应的IRQ,并且将标志 IRQ
被引用次数的变量加1(WinCE6 的代码中未见这一变量)
当中断发生的时候ISR就是会返回一个中断逻辑号,由于逻辑中断号和事件绑定了,所以他会激活事件,我们只要中断服务线程里面等待事件的激活,有系统统一调配。但是由于考虑到中断的实时性,所以IST的优先级要高于普通的线程。我们在线程中可以这样写:
while(TRUE) {
WaitForSingleObject(hISTEvent,INFINITE) ==
WAIT_OBJECT_0)
…
}
这样一个中断的流程就结束了,但是我还要补充一点,既然中断的申请可以分为动态和静态,那静态的时候就不需要kernliocontrol进行动态分配了。对于动态申请的中断,我们是可以释放掉的用的KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR, dwSysIntr, sizeof(DWORD),
NULL, 0, NULL);
在arm体系中ISR就是OEMInterruptHandler返回中断逻辑号,那我就是有点疑问了现在还是不太明白。静态分配中断的,我们可以由OEMInterruptHandler返回,动态申请使用kerneliocontrol也会有中断逻辑号通过指针返回呀。那我们中断来了是怎么知道静态和动态的呢?上面只是我的个人见解。也是有那个高手愿意可以给小弟指点迷津。