WINCE驱动开发之中断的使用(经典整合)

        中断的使用

1、wince中断简介

1: ISR的概念

ISR(interrupt service routine)是处理IRQs(interrupt request line)的程序。Windows CE用一个ISR来处理所有的IRQ请求。当一个中断发生时,内核的异常处理程序先调用内核ISR,内核ISR禁用所有具有相同优先级和较低优先级的中断,然后调用已经注册的OAL ISR程序,一般ISR有下列特征:

1) 执行最小的中断处理,最小的中断处理指能够检验、答复产生中断的硬件,而把更多的处理工作留给IST(interrupt service thread)。

2) 当ISR完成时返回中断ID(中断ID大部分是预定义的)。

    

2:中断注册步骤

1) 用SETUP_INTERRUPT_MAP宏关联SYSINTR和IRQ。以“SYSINTR_”为前缀的常量由内核使用,用于唯一标识发生中断的硬件。在Nkintr.h文件中预定义了一些SYSINTR,OEM可以在Oalintr.h文件中自定义SYSINTR。

2) 用HookInterrupt函数关联硬件中断号和ISR。这里的硬件中断号为物理中断号,而非逻辑中断号IRQ。

2、 驱动中IST使用

ISR是中断最小处理函数,因此各个驱动的中断处理函数称为IST。

系统中保留16个虚拟中断号,ak3224_intr.h已经定义好各个ISR的虚拟中断号28个。因此,驱动中的中断处理函数,只要与定义好的28个虚拟中断映射上即可。

例子:

首先,我们创建一个事件

pGPIOInfo->hGPIOEvent1 = CreateEvent(0,FALSE,FALSE,NULL);

其次创建一个处理事件的线程(IST)

pGPIOInfo->hGPIOThread1 = CreateThread(NULL, 0, GPIOFuncThread1, pGPIOInfo, 0, NULL);

然后使用InterruptInitialize让虚拟中断号pGPIOInfo->dwIntID1与创建的事件pGPIOInfo->hGPIOEvent1挂钩。

InterruptInitialize(pGPIOInfo->dwIntID1, pGPIOInfo->hGPIOEvent1, NULL, 0)

那么,当GPIO的中断到来,与GPIO虚拟中断挂钩的事件pGPIOInfo->hGPIOEvent1就会被设为Active。线程pGPIOInfo->hGPIOThread1的语句

WaitForSingleObject(pGPIOInfo->hGPIOEvent1, INFINITE);

GPIOEventHandler1;

就会被唤醒,然后执行下一条指令。这里加入的函数GPIOEventHandler1(中断处理操作)就被执行。

当中断处理结束以后,必须使用 InterruptDone(pGPIOInfo->dwIntID1);

通知系统已经完成中断处理,那么下一次的中断到来,事件pGPIOInfo->hGPIOEvent1就才会再次被设为Active。

驱动卸载时,需要释放申请的事件及线程

CloseHandle(pGPIOInfo->hGPIOEvent1);

CloseHandle(pGPIOInfo->hGPIOThread1);

3、可安装ISR介绍

OEM在OEMInit函数中关联IRQ和SysIntr,当硬件设备发生中断时,ISR会禁止同级和低级中断,然后根据IRQ返回关联的SysIntr,内核根据ISR返回的SysIntr唤醒相应的IST(SysIntr与IST创建的Event关联),IST处理中断之后调用InterruptDone解除中断禁止。在OEMInit中关联的缺点是一旦编译了CE内核后就无法添加这种关联了,而一些硬件设备会随时插拔或者共享中断,要关联这样的硬件设备解决方法就是可安装ISR,可安装ISR专用于处理指定的硬件设备发出的中断,所以如果硬件设备需要可安装ISR必须在注册表中添加IsrDll、IsrHandler。多数硬件设备采用CE默认的可安装ISR giisr.dll,格式如下:

"IsrDll"="giisr.dll"

"IsrHandler"="ISRHandler"

如果一个硬件驱动程序需要可安装ISR而开发者又不想自己写一个,那么可以利用giisr.dll来实现。除了在注册表中添加如上所示外,还要在驱动程序中调用相关函数注册可安装ISR。如下:

g_IsrHandle = LoadIntChainHandler(IsrDll, IsrHandler, (BYTE)Irq);

GIISR_INFO Info;

Info.SysIntr = dwSysIntr;

Info.CheckPort = TRUE;

Info.PortIsIO = (dwIOSpace) ? TRUE : FALSE;

Info.UseMaskReg = TRUE;

Info.PortAddr = PhysAddr + 0x0C;

Info.PortSize = sizeof(DWORD);

Info.MaskAddr = PhysAddr + 0x10;

KernelLibIoControl(g_IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL);

LoadIntChainHandler函数负责注册可安装ISR,参数1为DLL名称,参数2为ISR函数名称,参数3为IRQ。

如果要利用giisr.dll作为可安装ISR,必须先填充GIISR_INFO结构体,CheckPort=TRUE表示giisr要检测指定的寄存器来确定当前发出中断的是否是这个设备。

PortIsIO表示寄存器地址属于哪个地址空间,FALSE表示是内定空间,TRUE表示IO空间。

UseMaskReg=TRUE表示设备有一个掩码寄存器,专用于指定当前设备是否是中断源,也就是发出中断

而MaskAddr表示掩码寄存器的地址。

如果对Info.Mask赋值,那么PortAddr表示一个特殊的寄存器地址,这个寄存器的值与Mask的值&运算的结果如果为真,则证明当前设备是中断源,否则返回SYSINTR_CHAIN(表示当前ISR没有处理中断,内核将调用ISR链中下一个ISR),如果UseMaskReg=TRUE,那么MaskReg寄存器的值与PortAddr指定的寄存器的值&运算的结果如果为真,则证明当前设备是中断源。

可见,可安装ISR对与再次分解的中断处理是非常方便的。不过需要占用系统的一个虚拟中断号。而整个系统的虚拟中断号只有64个。

 

上面 算是 理论 知识   下面是在网上找到的一个实例,可以好好理解学习一下

WinCE如何响应硬件中断以及读写物理地址

1. 到http://www.pudn.com/downloads178/sourcecode/embed/detail828740.html下载源程序。

2. 该驱动程序响应硬件中断IRQ5,及读写0xD0000处的3000字节物理内存。详细说,是当硬件中断到来时,通知读函数 XXX_Read()读取。因此,在应用程序中,读函数是一个阻塞型的处理,不使用查询。可以创建一个线程,像套接字recvfrom那样使用,非常方便。

3. 该文件是用 "Windows CE Developer Samples" -> "Windows CE 5.0 Embedded Development Labs" -> "DrvWiz.exe" 框架产生的,需要的朋友自己到微软网站上找找,下一个。

4. 使用"DrvWiz.exe"产生驱动的框架TST后,首先使其响应硬件中断 IRQ5,来看函数:

DWORD SetupInterrupt( void )

{

       HANDLE g_htIST;        //线程返回句柄

       BOOL fRetVal;

       DWORD dwThreadID;

 

       // Create an event 中断来了,通知做一个事的信号

       g_hevInterrupt = CreateEvent(NULL, FALSE, FALSE, NULL);

       if(!g_hevInterrupt) return -10;

 

       // Have the OAL Translate the IRQ to a system irq 关联硬件中断

       //

       fRetVal = KernelIoControl( IOCTL_HAL_TRANSLATE_IRQ,

                                                        &dwIrq,                             // dwIrq = 5,硬件中断 IRQ5

                                                        sizeof( dwIrq ),

                                                        &g_dwSysInt,

                                                        sizeof( g_dwSysInt ),

                                                        NULL );

 

       kkk = (char)g_dwSysInt;

      

       if( !fRetVal )

       {

              kkk++;

              return -kkk;

       }

             

       // Create a thread that waits for signaling 硬件中断来了,关联一个线程

       //

       g_htIST = CreateThread(NULL,// CE Has No Security

                                                 0, // No Stack Size

                                                 ThreadIST,// Interrupt Thread

                                                 NULL,// No Parameters

                                                 CREATE_SUSPENDED,// Create Suspended until we are done

                                                 &dwThreadID // Thread Id

                                                 );

       if( !g_htIST )

       {

              kkk++;

              return -kkk;

       }

       // Set the thread priority to real time

       //

       int m_nISTPriority = 7;

       if(!CeSetThreadPriority( g_htIST, m_nISTPriority))

       {

              kkk++;

              return -kkk;

       }

       // Initialize the interrupt

       //

       if ( !InterruptInitialize(g_dwSysInt, g_hevInterrupt, NULL, 0) )

       {

              kkk++;

              return -kkk;

       }

       ResumeThread( g_htIST );

 

       return 1;

}

SetupInterrupt函数是放在 XXX_init中的。

再来看IST函数:ThreadIST

DWORD WINAPI ThreadIST( LPVOID lpvParam )

{

       DWORD dwStatus = 0;

       // Always chec the running flag

       //

       while(1)

       {

              dwStatus = WaitForSingleObject(g_hevInterrupt, INFINITE);

              // Make sure we have the object

              //

              if( (dwStatus == WAIT_OBJECT_0) && (g_bKillIST == FALSE))

              {

                     //处理中断

                     kkk++;

                    

 

                     //向读线程发信号,可以读取了

                     SetEvent(gReadEvent);

 

                     // Finish the interrupt

                     //

                     InterruptDone( g_dwSysInt );

              }

              else

              {     

                     CloseHandle(g_hevInterrupt);

                     RETAILMSG(1, (TEXT("::: ThreadIST Exit. \r\n")));

                     return 0; 

              } //if (ret != WAIT_OBJECT_0) or Error occurs

       }

       return 1L;

}

 

再来看 TST_Read()函数:

DWORD TST_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count )

{

       DWORD ret = 0;

 

       if (pBuffer == NULL)

              return 0;

 

       uchar *pReadBuffer = NULL;

       pReadBuffer = (uchar *)MapPtrToProcess((LPVOID)pBuffer, GetCallerProcess());

 

       /* 挂起当前线程,直到 中断到来时才读 */

       ret = WaitForSingleObject(gReadEvent, INFINITE); 

       if (ret == WAIT_OBJECT_0)

       {

              //

              // 将映射到的虚地址空间拷贝到用户程序中去

              //

              memcpy(pReadBuffer, g_BufSpace, Count);

              return Count;

       }

      

       return 0;

}

上述函数非常关键,能够想象到它一定在应用时被放在while(1)中,有中断来时,正好可以读取数据,这正是所想要的。

 

5. 中断部分已经说完,下面说明如何映射物理地址,看TST_init ()函数:

DWORD TST_Init( LPCTSTR pContext, LPCVOID lpvBusContext)

{

       //

       //映射内存空间, 宏 MY_PHYSICAL_ADDRESS = 0xD0000

       //

       PHYSICAL_ADDRESS ioPhysicalBase = {MY_PHYSICAL_ADDRESS, 0}; //0xD0000

       g_BufSpace = (PUCHAR)MmMapIoSpace(ioPhysicalBase, 3000, FALSE);

       if(g_BufSpace == NULL)

              return TRUE;

       //

       // 创建读事件

       //

       gReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

       if(gReadEvent == NULL)

              return TRUE;

 

       //     安装中断

       //

       int ret = SetupInterrupt();

       if(ret < 0)

              return TRUE;

}

使用时,在TST_Read()和TST_Write()中,只需看TST_Write()函数:

DWORD TST_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count )

{

       uchar *pWriterBuffer = NULL;

       pWriterBuffer = (uchar *)MapPtrToProcess((LPVOID)pBuffer, GetCallerProcess());

 

       memcpy(g_BufSpace, pWriterBuffer, Count);

       return Count;

}

如何使用PB5编译该驱动,参见源文件中说明。编译该驱动项目完毕后,应该在$(_FLATRELEASEDIR)目录中出现Tst.dll。

 

6. 应用程序使用驱动程序说明:

// 打开 TST 驱动,注意TEXT("TST1:")

       hFile = CreateFile(TEXT("TST1:"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);

 

 

//读取。应该放在一个线程中,因为 ReadFile和套接字中的readfrom一样 属于 "阻塞事件通知型"

       while(1)

       {

              //     该接收是个阻塞函数,它会一直阻塞在此

              if(ReadFile(hFile, buf, len, &retval, NULL) == TRUE)

              {

                     //使用 buf

              }

       }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值