流接口驱动程序的特点及其实现方法

流接口驱动程序是在应用程序层采用标准的文件操作APIS来调用设备的,最常见的就是串口,我们可以使用CreateFile() 函数来打开一个串口,然后通过ReadFile(),WriteFile() 函数来读写串口,很显然,串口就是一种流接口的驱动程序。这类驱动程序的特点就是有着统一的接口 函数,设备端一般都较简单,以数据提供者为主。
标准的流接口是如下的一组 函数,我们先来看一下这组 函数的基本外观:
1、HANDLE XXX_Init(LPCTSTR pContext,LPCVOID lpvBusContext);
这个 函数是在设备管理程序通过ActiveDeviceEx() 函数激活设备时被系统调用的。它的主要功能是初始化驱动程序中用到的资源,对I/O地址空间和内存进行映射等。
2、BOOL XXX_Deinit(DWORD hDeviceContext);
这个 函数是在设备管理程序通过DeactivateDevice() 函数卸载设备时被系统调用的,它的主要功能是回收驱动程序中用到的资源。
3、DWORD XXX_Open(DWORD hDeviceContext,DWORD AccessCode,DWORD ShareMode);
这个 函数是在应用程序通过CreateFile() 函数打开设备时调用的。
4、BOOL XXX_Close(DWORD hOpenContext);
这个 函数是在应用程序通过CloseHandle() 函数关闭句柄时调用的。
5、DWORD XXX_Read(DWORD hOpenContext,LPVOID pBuffer,DWORD Count);
这个 函数是在应用程序通过ReadFile() 函数读取设备时调用的。
6、DWORD XXX_Write(DWORD hOpenContext,LPCVOID pBuffer,DWORD Count);
这个 函数是在应用程序通过WriteFile() 函数向设备写入数据时调用的。
7、DWORD XXX_IOControl(DWORD hOpenContext,DWORD dwCode,PBYTE pBufIn,DWORD dwLenIn,PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut);
这个 函数是在应用程序通过DeviceIOControl() 函数向设备发送控制字时调用的。
8、void XXX_PowerUp(DWORD hDeviceContext);
这个 函数是在向设备恢复供电的时候调用的。
9、void XXX_PowerDown(DWORD hDeviceContext);
这个 函数是在设备断电的时候调用的。

上述 函数中的XXX部分是在注册表中注册此设备驱动程序的时候Perfix注册表项的值。例如串口,则用Com,并口则用LTP等等。只要一个DLL中针对特定设备实现了上述接口并且正确在注册表中注册,那么就可以在程序中通过那些文件API 函数来访问此设备。下面我们就一起在PB5中按上述接口模拟一个设备。设备的名字就叫做TTT吧。
运行PB5,打开上次做的平台,在文件菜单中执行"New Project or File"命令,新建一个"WCE Dynamic-Link Library"项目,输入项目名字为"Test"。在向导的第一步中输入必要的信息(此处不输也可以,这些信息可供向导自动生成一个README.TXT文件,不过还是写上好,以免以后忘了),在下一步中选择一个空项目,完成。
在新的源程序文件中输入以下程序:
// test.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "memory.h"
#include "windows.h"

HANDLE* hDevice;
#define BUFSIZE 256
WCHAR buffer[BUFSIZE];

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}

HANDLE TTT_Init(LPCTSTR pContext,LPCVOID lpvBusContext)
{
hDevice=(HANDLE*)LocalAlloc(LPTR,sizeof(HANDLE));
memset(buffer,0,sizeof(WCHAR)*BUFSIZE);
return hDevice;
}

BOOL TTT_Deinit(DWORD hDeviceContext)
{
LocalFree(hDevice);
return TRUE;
}

DWORD TTT_Open(DWORD hDeviceContext,DWORD AccessCode,DWORD ShareMode)
{
if (!hDeviceContext)
return FALSE;
return TRUE;
}

BOOL TTT_Close(DWORD hOpenContext)
{
if (!hOpenContext)
return FALSE;
return TRUE;
}

DWORD TTT_Read(DWORD hOpenContext,LPVOID pBuffer,DWORD Count)
{
DWORD dwret=min(BUFSIZE,Count);
wcsncpy((LPWSTR)pBuffer,buffer,dwret);
return dwret;
};

DWORD TTT_Write(DWORD hOpenContext,LPCVOID pBuffer,DWORD Count)
{
DWORD dwret=min(BUFSIZE,Count);
wcsncpy(buffer,(LPWSTR)pBuffer,dwret);
return dwret;
}

DWORD TTT_IOControl(DWORD hOpenContext,DWORD dwCode,PBYTE pBufIn,
DWORD dwLenIn,PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut)
{
return TRUE;
}

void TTT_PowerUp(DWORD hDeviceContext)
{
return;
}

void TTT_PowerDown(DWORD hDeviceContext)
{
return;
}

编译,然后编译该test文件夹下的test.def文件,将这些实现的接口 函数导出:
LIBRARY TEST.DLL

EXPORTS

TTT_Init
TTT_Deinit
TTT_Open
TTT_Close
TTT_PowerUp
TTT_PowerDown
TTT_IOControl
TTT_Read
TTT_Write

同时编辑此文件夹下的test.reg文件,加入必要的注册表项:
[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/test]
"DeviceArrayIndex"=dword:0
"Prefix"="TTT"
"Dll"="test.Dll"
"Order"=dword:20

好了,一个TTT设备的流接口驱动就写好了,从中可以看到,在写驱动的时候比PB4要更加清晰,在源程序文件夹中的DEF文件,REG文件,BIB文件,DB文件,DAT文件和编译链接时需要的BAT文件都被PB生成好了,开发人员只要作些必要的修改即可以,非常方便。
接下来,为了测试我们的驱动程序是否可以正常工作,还需要写一个测试程序,再次为平台新建一个WCE应用程序项目TestTTT,写入如下代码并编译为EXE文件:
#include "stdafx.h"
#include "windows.h"

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.

HANDLE handle=CreateFile(_T("TTT1:"),GENERIC_READ|GENERIC_WRITE,0,
NULL,OPEN_EXISTING,0,NULL);
ASSERT(handle);
DWORD ret=0;
TCHAR* pstr=_T("This is a TEST of TTT Driver");
WriteFile(handle,pstr,(_tcslen(pstr)+1)*sizeof(TCHAR),&ret,NULL);
TCHAR ReadStr[256];
memset(ReadStr,0,sizeof(TCHAR)*256);
ret=0;
ReadFile(handle,ReadStr,sizeof(ReadStr),&ret,NULL);
MessageBox(NULL,ReadStr,_T("Test"),MB_OK);
CloseHandle(handle);
return 0;
}

好了,一切准备就绪,重新编译平台并下载到设备上运行,在Windows文件夹(修改文件夹选项,把所有的复选框全去掉才能看到隐藏的和系统文件)下可以找到test.dll文件和TestTTT.exe文件,运行后者,结果如图:
 
 
很简单吧,你只要在上述的接口中访问你的设备端口读写数据就可以了,我只是举个例子,希望能对你有所启发,对于驱动程序部分的内容,就先告一段落吧,继续关注后续文章吧
]]> niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bsk.html#comment Thu, 15 Nov 2007 02:34:31 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bsk.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bsj.html BOOL rgfKeyUp[16];
UINT cEvents;

DEBUGCHK(pKeybdIst->hevInterrupt != NULL);
DEBUGCHK(pKeybdIst->pfnGetKeybdEvent != NULL);
DEBUGCHK(pKeybdIst->pfnKeybdEvent != NULL);

SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

wait_for_keybd_interrupt:
if (WaitForSingleObject(pKeybdIst->hevInterrupt, INFINITE) == WAIT_OBJECT_0)
{
cEvents = (*pKeybdIst->pfnGetKeybdEvent)
(pKeybdIst->uiPddId, rguiScanCode, rgfKeyUp);

for (UINT iEvent = 0; iEvent < cEvents; ++iEvent) {
(*pKeybdIst->pfnKeybdEvent)(pKeybdIst->uiPddId,
rguiScanCode[iEvent], rgfKeyUp[iEvent]);
}
// cEvents could be 0 if this was a partial scan code, like 0xE0

InterruptDone(pKeybdIst->dwSysIntr_Keybd);
}

goto wait_for_keybd_interrupt;

ERRORMSG(1, (TEXT("KeybdIstLoop: Keyboard driver thread terminating./r/n")));
return TRUE;
} ]]> niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bsj.html#comment Thu, 15 Nov 2007 02:10:23 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bsj.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bsi.html --所有的.reg文件合并成REGINIT.INI
--所有的.dat文件合并成INITOBJ.DAT
--所有的.db文件合并成INITDB.INI
知道这几个合并以后便于你在RELEASE下查找你做的修改是否确实被置入了目标平台。
注册表压缩会将REGINIT.INT文件压缩成DEFAULT.FDF文件。
资源替换就是将EXE或DLL中的资源替换成本地语言如简体中文。
接下来才是把这些CE.BIB等二进制文件制作NK.BIN文件,这才是我们需要的文件,不过在CE5中好像可以在项目设置中改变这个文件的名称,读者可以自己试试。
以上说的这些编译过程,在Platform Builder中的Build OS菜单下分得比较明确,这一点比CE4要好得多,你可以自己掌握让它单独执行某一步骤。如果你修改了某个组件的源程序,也可以右击这一组件然后单独编译此组件,最后再生成Image,总之,灵活性是比较不错的了。
同时,经过以上的分析也可以看出来,Platform Builder这个IDE在帮助生成CE时基本上是依赖于文件夹结构和那些文本文件以及批处理文件的,因此,对于文件夹结构还是要尽量的熟悉。同时也证明那些总说批处理文件没用的人的无知,在微软的操作系统和开发工具中,批处理文件是至关重要的,说它无用的人只是由于自己的水平不够而已。
好了,这次我们学得多做得少,不过这些知识还是有必要了解的,别忘了对照你的文件夹结构来学习哦!

经过以前的学习,我们已经掌握了生成CE的过程及编译的几个阶段,这次我们再来了解一下Platform Builder中为我们提供的组件包的管理方式以及如何管理自己的组件。
在Platform Builder(以下简称PB)中的右侧有一个"Catalog"窗口,其中列出了所有可供使用的组件,我们可以看到其内容是非常之多的,现在的疑问就产生了,PB是如何管理这些组的呢?如果我有一个新设备的驱动组件要如何才能放到这个组件包窗口中呢?如果我开发了一个设备驱动以供其他人使用那我要如何才能发布我的驱动呢?下面,我们就来解决这些问题。
在PB中,这些组件的管理都是能过一种组件文件(.cec文件)来实现的。在CE4中,系统自带的CEC文件都位于PB的安装文件夹下的CEC文件夹,我们可以在那里很容易的找到它们,但是在CE5中,它的位置变了,你可以在WINCE500/PUBLIC/COMMON/OAK/CATALOG/CEC下找到他们。
如果你开发了OAL,设备驱动或其他组件,你就可以能过CEC文件来把它们加入到PB中。通过在PB环境中导入CEC文件,其他的平台开发人员就可以使用这些组件了。CEC文件是用来描述组件信息的文本文件,它包括了一些块的列表,主要包括以下四种信息块:
--CECInfo块,用来描述此CEC文件的信息,每个CEC文件只能含有一个此信息块,从中你可以看到该CEC的名称、GUID、版本、供应商和简要描述。
--ComponentType块,它描述了最高级别的组件类型,在一个CEC文件中可以有多个此信息块,它通过Group,RequiredCEModules,ExcludeWhenSet,MaxResolvedImplsAllowed等条目来描述此组件所在的组,需要的CE组件,排除的组件及允许在一个CE平台中存在的数量等信息。
--Implementation块,用来描述此组件在编译时需要的各种信息,每个CEC文件中也可以有多个此信息块,它是 ComponentType块的一部分,其中的BSPPlatformDir,Children,OptionalChildren,DefaultDrivers,ExcludeWhenSet,FeatureVariable,ImplSize等条目的具体含义可以在PB的帮助文档中找到,在此不一一细述。
--BuildMethod块,描述了编译的方法,也是ComponentType块的一部分,为了加深对上一次编译阶段的理解,我们详细说一下此块中Step和Action两个条目。
Step用来说明编译此组件将要在哪一步中进行,对照上一次的内容,它的取值为:CESYSGEN,BSP,BUILDREL,MAKEIMG。所以如果你自己开发了OAL或驱动之类的组件,你就需要自己决定让PB在什么阶段来编译它,就要在CEC中通过Step来描述。其实在具体的编译过程中,上述的每一阶段又有PRE和POST两个子阶段,也就是说对于每个阶段其实都有三步,即PRECESYSGEN,CESYSGEN,POSTCESYSGEN,PREBSP,BSP,POSTBSP……一般不用具体到这样详细的程度。
Action则描述了组件编译的行为,有效的行为与编译阶段的对应关系如下表所示:
 
 
这些Action的意义如下:
--#BUILD():使用DIRS文件或SOURCES文件或MAKE文件来调用build.exe,在完全编译平台的时候被使用;
--#CUSTOM():在编译的时候运行批处理文件或可执行文件;
--#COPY():从一个位置复制文件到另一个位置;
--#ENV():设置环境变量;
--#SRCCODE():指定了包含源程序文件的文件夹,只在编译所选组件时应用。
对于以上每个条目的具体用法可以参阅PB的帮助文档。为了获得感性认识,我们最好用记事本打开几个CEC文件来看一下。比如打开serial.cec看看串口驱动组件的实现方法等。
以上我们了解了CEC文件,我们再来解决后面的问题,即假设我从别的供应商处拿到了一个设备的驱动程序,它带了CEC文件,那么我要如何把这个组件加入到PB的组件包中呢?这个问题其实很简单,只要在PB的File菜单下使用"Manage Catalog Items"命令即可。在这个弹出的对话框中的内容和使用方法就不用我说了吧。
再来解决下一个问题,我要如何为我开发的组件编写CEC文件呢?这个也好办,在Tools菜单下使用"CEC Editor"命令即可,如果不会写,可以先打开一个现有的CEC文件看看是如何组织的,然后再仿照它来写自己的就可以了,别忘了写完后要保存哦,同时也要注意GUID的问题。
此次内容的最后,我们再来看一下BSP。什么是BSP呢,其实就是为某一种开发板实现了设备驱动的软件包,它包含了源程序文件,二进制文件等,还有OAL适配层,Bootloader和其他有关的配置文件。比如CE5的评估版就带了x86的BSP,AMD的BSP和Emulator的BSP。
通常对BSP的操作都在BSP向导中进行,它位于Platform菜单中,利用它可以新建自己的BSP,复制现存的BSP,修改现存的BSP或创建全局的驱动程序。如果你准备好了BSP所需要的那些东西就可以用它来生成你的BSP了,它会选择必须的CE内核组件。
]]--> niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bsi.html#comment Thu, 15 Nov 2007 01:53:30 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bsi.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bgj.html

/*定义了一个读按键事件数组,当按下按键时,中断服务线程IST通过事件gReadKeyEvent[0]通知本驱动的读函数KEY_Read():按键按下;当本驱动退出或卸载时,通过事件gReadKeyEvent[1]通知读函数KEY_Read():驱动已经关闭或卸载。*/

 

/* 按键按下中断事件*/
PRIVATE HANDLE gWaitEvent;

/* 是否退出中断服务线程*/
PRIVATE UINT32 g_bKillIST = FALSE;

/*此处定义了一个全局变量g_bKillIST,它用于当驱动卸载时通知中断服务线程退出,这样才能完全卸载驱动*/

/* 中断处理线程*/
PRIVATE HANDLE gEINTIntrThread;

/* 驱动打开计数器*/
PRIVATE UINT32 gOpenCount = 0;


/* EINT的物理中断号及逻辑中断号*/
PRIVATE UINT32 g_EINTIrq = XLLP_INTC_GPIOXX_2;
PRIVATE UINT32 g_EINTSysIntr = SYSINTR_UNDEFINED;

 

/* GPIO寄存器对应的虚拟地址,定义了一个处理器GPIO相关寄存器的结构体变量,该结构体的定义在xllp_gpio.h头文件中。其他全局变量在使用时说明*/
PRIVATE volatile XLLP_GPIO_T  * v_pGPIOReg;

 

/* 中断寄存器对应的虚拟地址,定义了一个处理器的中断寄存器的结构体变量,该结构体的定义在xllp_intc.h头文件中。其他全局变量在使用时说明*/
PRIVATE volatile XLLP_INTC_T  * v_pICReg;

 

/* 驱动动态库入口函数*/
BOOL WINAPI DllEntry (HANDLE hInstDll,DWORD dwReason,LPVOID lpvReserved)

{
 switch(dwReason)
 {
   case DLL_PROCESS_ATTACH:
  RETAILMSG(1,(TEXT("Key:DLL_PROCESS_ATTACH./r/n")));
  DisableThreadLibraryCalls((HMODULE)hInstDll);
  break;//挂载成功

   case DLL_PROCESS_DETACH:
  RETAILMSG(1,(TEXT("Key:DLL_PROCESS_DETACH./r/n")));
  break;//卸载成功

 }

 return  (TRUE);

}

 

 

 


/* 申请GPIO寄存器地址对应的虚拟空间,在WinCE中,程序访问的地址都是虚地址,因此,要访问硬件物理地址,必须将物理地址空间映射到虚拟地址空间*/

 

PRIVATE BOOL EINT_InitializeAddresses(VOID)
{

 BOOL RetValue = TRUE;


 RETAILMSG(1,(TEXT(">>>EINT_initalization address..set../r/n")));
 
 /* IO Register Allocation,VirtualAlloc()函数的功能是申请一块虚拟内存空间,该空间的大小为sizeof(PXA270寄存器结构体的大小),*/


 v_pGPIOReg = (volatile XLLP_GPIO_T *)VirtualAlloc(0,sizeof(XLLP_GPIO_T),MEM_RESERVE,PAGE_NOACCESS);

 if (v_pGPIOReg ==NULL)
 {
  ERRORMSG(1,(TEXT("For IOPregs : VirtualAlloc failed! /r/n")));
  RetValue = FALSE;
 }
 else
 {

/*VirtualCopy()函数的功能是将VirtualAlloc()函数申请的虚拟空间(起始地址为v_pGPIOReg)映射到GPIO寄存器的物理地址,经过映射后,通过全局变量指针v_pGPIOReg就可以访问GPIO寄存器了*/

  if(! VirtualCopy((PVOID)v_pGPIOReg,(PVOID)(0x40E00000>>8),
     sizeof(XLLP_GPIO_T),PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE))

  {
   ERRORMSG(1,(TEXT("For IOPregs : VirtualCopy failed! /r/n")));
   RetValue = FALSE;
  }
 
 }

 
 if(! RetValue)

 {
  RETAILMSG(1,(TEXT(":::EINT_InitializeAddresses - Fail!!/r/n")));
 
  if (v_pGPIOReg)
  {
   VirtualFree((PVOID)v_pGPIOReg,0,MEM_RELEASE);
  }
  v_pGPIOReg = NULL;


 }

 else
  RETAILMSG(1,(TEXT(":::EINT_InitializeAddresses - Success/r/n")));


return (RetValue);
}

 

 


/* 编写配置EINT(GPIO100)引脚为外部中断引脚,这里示例了如何操作硬件寄存器:*/

PRIVATE VOID EINT_ConfigInterruptPin(VOID)
{

 v_pGPIOReg->GPDR2 &=~( 0x1<<30 );//XLLP_GPIO_BIT_KP_MKIN1,设置方向寄存器
 v_pGPIOReg->GRER2 |=( 0x1<<30 );//使能上升沿中断
 v_pGPIOReg->GFER2 &=~( 0x1<<30 );//禁止下降沿中断
 v_pGPIOReg->GAFR2_U &=~( 0x3<<28);//设置为通用的IO口

 v_pICReg->icmr |=( 0x1<<10 );//开屏蔽中断位
 v_pICReg->iclr &=~( 0x1<<10 );//中断被传递到IRQ中断输入
 v_pICReg->iccr |=0x01;        //没有被屏蔽的中断才能将处理器从空闲状态中唤醒
 v_pICReg->ipr[10] =0x8000000a;

}

 

 


PRIVATE BOOL Key_IsPushed(VOID)
{
 return ((v_pGPIOReg->GEDR2&(1<<30)?TRUE:FALSE));//??????
}

PRIVATE VOID EINT_ConfigPinDefault(VOID)
{
 v_pGPIOReg->GEDR2|=(1<<30);
}

 

DWORD EINTKey_IntrThread(PVOID pArg)

{
 DWORD ret;

 /*创建外部中断事件,用于ISR通知ISR外部中断触发,然后调用内核函数InterruptInitialize()
 将逻辑中断号g_EINTSysIntr与事件gWaitEvent关联起来,并使能该中断,当该中断触发时,ISR就
 触发事件gWaitEvent生效,完成以上工作,该线程就进行无限循环,等待事件生效;*/

 

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

 //初始化外部按键中断:注册外部中断事件,允许外部中断


 if ( !( InterruptInitialize(g_EINTSysIntr,gWaitEvent,0,0)))
 {
  RETAILMSG(1,(TEXT("ERROR:EINTKey:InterruptInitialize failed./r/n")));
  CloseHandle(gWaitEvent);
  return 0;
 
 }

 while (1)
 {
   ret = WaitForSingleObject(gWaitEvent,INFINITE);
   if ( (ret == WAIT_OBJECT_0)&&(g_bKillIST == FALSE))
  {
   if(Key_IsPushed())
   {
    Sleep(20);        //延时20ms用于滤去噪声
    if (Key_IsPushed()) //外部中断按键确实已经按下
    {
     SetEvent(gReadKeyEvent[0]); //通知读函数,外部中断按键按下
     RETAILMSG(1,(TEXT(":::The Key1 Pushed./r/n")));
                 //EINT_ConfigPinDefault();
    }
   
   }
  }
   else

   {
   CloseHandle(gWaitEvent);
      RETAILMSG(1,(TEXT(":::EINTKey_IntrThread Exit./r/n")));
   return 0;
   }//if (ret != WAIT_OBJECT_0) or Error occurs
 
   InterruptDone(g_EINTSysIntr);//通知内核,中断处理结束
 }

  return 1;
}

 

/*流驱动接口函数的初始化函数,该函数的主要工作是进行外部中断引脚的初始化,申请的逻辑中断号并保存到全局变量*/

 DWORD KEY_Init(DWORD dwContext)
{
 DWORD IDThread;
 
 //取得GPIO相关寄存器的虚拟地址空间
 if ( EINT_InitializeAddresses() == FALSE )
 return 0;

 //使能EINT引脚为中断引脚,并为上升沿触发
 EINT_ConfigInterruptPin();

 //从OAL请求一个SYSINTR值

 

 if( ! KernelIoControl( IOCTL_HAL_REQUEST_SYSINTR,&g_EINTIrq,sizeof(UINT32),
                    &g_EINTSysIntr,sizeof(UINT32),NULL))

 {
  
  RETAILMSG(1,(TEXT("ERROR:EINTKey:Failed to request sysintr value for EINT interrupt./r/n")));
  return(0);

 }


 RETAILMSG(1,(TEXT("INFO:EINTKey:Mapped Irq 0x%x to SysIntr 0x%x./r/n"),g_EINTIrq,g_EINTSysIntr));
 
 //创建一个外部中断处理线程IST
 gEINTIntrThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)EINTKey_IntrThread,0,0,&IDThread);


 if ( gEINTIntrThread == NULL )
 {
  RETAILMSG(1,(TEXT(":::KEY_Init:CreateThread()Fail./r/n")));
  KernelIoControl( IOCTL_HAL_RELEASE_SYSINTR,&g_EINTIrq,sizeof(UINT32),
                    NULL,0,NULL);
  return 0;
 }

 gReadKeyEvent[0] = CreateEvent(NULL,FALSE,FALSE,NULL);
 gReadKeyEvent[1] = CreateEvent(NULL,FALSE,FALSE,NULL);

 RETAILMSG(1,(TEXT(":::KEY_Init Sucessfully!/r/n")));

 //返回不为0的数
 return (DWORD)gEINTIntrThread;
}

 

 


DWORD KEY_Open(DWORD hDeviceContext,DWORD AccessCode,DWORD ShareMode)
{
 if ( gOpenCount > 0 )//本驱动只允许单一访问
       return 0;
 
 gOpenCount++;
 return gOpenCount;   //返回一个不为零的数

}


DWORD KEY_Read(DWORD Handle,LPVOID pBuffer,DWORD dwNumBytes)
{
 DWORD ret;
 uchar * pReadBuffer;

 if (( pBuffer == NULL ) || (dwNumBytes <= 0 ))
  return 0;

 pReadBuffer = MapPtrToProcess(pBuffer,GetCallerProcess());
 * pReadBuffer = 0;
 /*挂起当前线程,直到KEY1按键按下或驱动关闭*/
 ret = WaitForMultipleObjects(2,gReadKeyEvent,FALSE,INFINITE);

 if ( ret == WAIT_OBJECT_0)
 {
  ResetEvent(gReadKeyEvent[0]);
  * pReadBuffer = 1;
  return 1;
 }

 else if( ret == ( WAIT_OBJECT_0 + 1 ) )
 {
  ResetEvent(gReadKeyEvent[1]);
  * pReadBuffer = 0;
  return 1;
 
 }
 return 0;

}


BOOL KEY_Close(DWORD Handle)
{
 if ( gOpenCount > 0 )
  SetEvent(gReadKeyEvent[1]);//通知读函数线程驱动已经关闭
 
 gOpenCount = 0;
 return TRUE;

}//KEY_Close

 

 

BOOL KEY_Deinit(DWORD dwContext)
{
 SetEvent(gWaitEvent);//通知中断服务线程退出
 g_bKillIST = TRUE;
 Sleep(200);          //等待中断服务线程退出

 SetEvent(gReadKeyEvent[1]);//通知读函数线程驱动已经关闭

    //释放中断资源

 InterruptDone(g_EINTSysIntr);
 InterruptDisable(g_EINTSysIntr);

 KernelIoControl( IOCTL_HAL_RELEASE_SYSINTR,&g_EINTSysIntr,sizeof(UINT32),
                    NULL,0,NULL);


 //恢复外部中断引脚为输入GPIO
 //EINT_ConfigPinDefault();

 //释放申请的虚拟空间

 if (v_pGPIOReg)
  VirtualFree((PVOID)v_pGPIOReg,0,MEM_RELEASE);

 gOpenCount = 0;

 CloseHandle(gReadKeyEvent[0]);
 CloseHandle(gReadKeyEvent[1]);

 return TRUE;
}


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

 return 0;
}

DWORD KEY_Seek(DWORD hOpenContext,long Amount ,DWORD Type)

 return 0;
}


BOOL KEY_IOControl(DWORD hOpenContext,
     DWORD dwCode ,
     PBYTE pBufIn,
     DWORD dwLenIn,
     PBYTE pBufOut,
     DWORD dwLenOut,
     PDWORD pdwActualOut)

 return FALSE;
}


void KEY_PowerUp(void)
{
   ;
}

void KEY_PowerDown(void)
{
   ;
}

]]> niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bgj.html#comment Wed, 24 Oct 2007 06:54:15 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bgj.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bg6.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bg6.html#comment Wed, 24 Oct 2007 06:23:14 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bg6.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bfj.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bfj.html#comment Wed, 24 Oct 2007 01:22:02 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bfj.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bf6.html
"Prefix"="SPL"
"Dll"="***.dll"
"FriendlyName"="*** Driver"
"Index"=dword:0
"Order"=dword:1
4)在PB中打开菜单Build OS ,先确保Clean Before Building选项处于选中状态,然后再在该菜单中选择Sysgen,重新编译,生成新的操作系统映像。
 
"
]]> niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bf6.html#comment Tue, 23 Oct 2007 10:13:09 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bf6.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bf4.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bf4.html#comment Tue, 23 Oct 2007 09:20:40 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bf4.html http://blog.sina.com.cn/s/blog_4ca7dcd801000bce.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000bce.html#comment Tue, 16 Oct 2007 02:48:04 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000bce.html http://blog.sina.com.cn/s/blog_4ca7dcd801000b4d.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000b4d.html#comment Thu, 27 Sep 2007 02:07:25 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000b4d.html http://blog.sina.com.cn/s/blog_4ca7dcd801000apt.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000apt.html#comment Mon, 27 Aug 2007 01:10:13 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000apt.html http://blog.sina.com.cn/s/blog_4ca7dcd801000aan.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000aan.html#comment Wed, 25 Jul 2007 05:47:06 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000aan.html http://blog.sina.com.cn/s/blog_4ca7dcd801000aam.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd801000aam.html#comment Wed, 25 Jul 2007 05:39:40 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd801000aam.html http://blog.sina.com.cn/s/blog_4ca7dcd8010009u4.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd8010009u4.html#comment Mon, 18 Jun 2007 02:42:49 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd8010009u4.html http://blog.sina.com.cn/s/blog_4ca7dcd8010009np.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd8010009np.html#comment Wed, 06 Jun 2007 04:58:01 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd8010009np.html http://blog.sina.com.cn/s/blog_4ca7dcd8010009mu.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd8010009mu.html#comment Mon, 04 Jun 2007 08:57:46 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd8010009mu.html http://blog.sina.com.cn/s/blog_4ca7dcd8010009ei.html "Prefix"="XXX"
"Dll"="MyDev.Dll"
"Order"=dword:1

 在BIB文件中添加项目,将所用到的文件加入BIN文件(一般放在Platform.bib)。
MyDev.dll $(_FLATRELEASEDIR)/MyDev.dll NK SH
 注:
SampleDev为任意与其它项目不重名的字符串.
每个函数名的前缀XXX可以是任意大写的字符串,只要保证与注册表中Prefix后面的值相同就行。
3.3编译程序
现在,已经知道了需要实现哪些东西,一定想知道如何去实现它。一个最直接的方法就是在platform/BSP/drivers 下新建一个目录,然后在drivers目录中的dirs文件中加入以你刚新建的目录名。
在刚新建的目录下,新建你的C源代码文件,在其中实现上面所述的函数,及其功能。新建名称分别为sources, makefile, mydev.def的文件。其内容如下:
makefile: 只需要这样一行
!INCLUDE $(_MAKEENVROOT)/makefile.def

mydriver.def文件定义需要输出的函数,这些函数能够被其它代码用动态加载的方法调用。格式:

LIBRARY MyDev(这个字符串要和将要生成的动态库的文件名一样)

EXPORTS
XXX_Init
XXX_Deinit
XXX_Open
XXX_Close
XXX_PowerOff
XXX_Power_Down
XXX_IOControl
Sources:这个文件很重要,内容也多,最基本的一个文件该有如下内容。

TARGETNAME= MyDev(指定要生成的动态库的名称)
TARGETTYPE=DYNLINK(指定要生成的是一个动态库)
(下面两项指定需要与哪些动态库链接,一般要第一项就足够了)
TARGETLIBS=$(_COMMONSDKROOT)/lib/$(_CPUINDPATH)/coredll.lib
SOURCELIBS= $(_COMMONOAKROOT)/lib/$(_CPUINDPATH)/ceddk.lib
DEFFILE=MyDev.def (指定def文件)

DLLENTRY=DllEntry(指定动态库的入口函数

SOURCES=(请在这写上你所有源文件的名字,它们将会被编译)


好了,现在万事俱备,只剩编译啦。

3.4用一个Project文件来编译出驱动程序库文件
如果您在用CE5.0,那用一个Project来构造一个驱动程序将是一个不错的选择。即在新建一个Project时设置其类型为DLL,其它设置根据提示即可。并且可以将注册表设置放在Project所在文件夹。
3.5基本调试方法
一般驱动程序可以用DEBUG版来调试,也可以用输出调试信息的方法。我们一般用这两个函数输出调试信息:RETAILMSG和DEBUGMS,后者只能在DEBUG版中输出,而前者在RELEASE和DEBUG版中都可以输出,而且,可以在系统运行时刻根据Debug Zone选择让DEBUGMSG输出哪些调试信息。
驱动程序的调试一般可以分为以下几步:
1.看驱动程序的DllEntry是否被调用。如果这个函数被调用,说明驱动程序的文件已经在CE的image中,而且与注册表中设置的文件名相同。
2.看Init 函数是否被调用。如果它被调用,刚说明注册表设置正确。如果它没有被调用,一般是因为注册表中的Prefix设置与Init函数前面那三个字符不相同。或者def文件中没有定义Init函数。如果这个函数能够被调用,但驱动程序还是不能正确加载,请详细检查代码.

 

]]> niuhg http://blog.sina.com.cn/s/blog_4ca7dcd8010009ei.html#comment Thu, 17 May 2007 16:20:23 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd8010009ei.html http://blog.sina.com.cn/s/blog_4ca7dcd80100092s.html niuhg http://blog.sina.com.cn/s/blog_4ca7dcd80100092s.html#comment Thu, 19 Apr 2007 06:22:14 GMT+8 http://blog.sina.com.cn/s/blog_4ca7dcd80100092s.html  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RDA5807FP是一款低功耗、单芯片、立体声FM收音机芯片。它能够用于各种电子设备中,如智能手机、平板电脑、音乐播放器等等。为了让RDA5807FP能够正常工作,需要编写相关的驱动程序。 RDA5807FP驱动程序的目的是将硬件和软件进行有效地连接。它通过控制相关的寄存器来配置和控制RDA5807FP芯片的各个功能模块。驱动程序需要对RDA5807FP的寄存器及其对应的值进行设置,以达到用户所需的功能。 首先,驱动程序需要初始化RDA5807FP芯片。它通过一系列的配置操作来设置芯片的初始状态。接下来,驱动程序需要控制RDA5807FP的频率调谐功能,使其能够收听不同的FM频率。这一过程涉及到对频率寄存器的操作,通过设置寄存器的值来实现频率的调节。 除了频率调谐,RDA5807FP还具有其他一些功能,如立体声/单声道切换、静音控制、信号强度检测等。驱动程序需要对这些功能进行设置和控制,以实现用户期望的效果。 最后,驱动程序还需要处理来自RDA5807FP芯片的中断,以及与其他设备的接口。它需要根据芯片的状态变化来作出相应的处理,以便及时更新相关的状态信息。 总的来说,RDA5807FP驱动程序负责控制RDA5807FP芯片的各个功能,实现对FM收音功能的控制和操作。它是硬件和软件之间的桥梁,确保RDA5807FP能够正常工作,并且满足用户的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值