转载自:http://blog.csdn.net/dijkstar/archive/2009/07/02/4315156.aspx
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
}
}
//写入
BOOL ret = WriteFile(hFile, buf, len, &actlen, NULL);