基于DM6446的Windows CE显示驱动设计实现

时间:2012-09-18来源:网络

  3.2 显示驱动主要组成部分

  WINCE 的显示驱动程序如图3 所示,由DDI(Display Device Interface) 和HAL(Hardware Abstraction Layer)两部分组成。

  HAL 主要为DirectDraw 服务,只需要在驱动中向GDI 导出HALinit()即可,因此本文研究的重点是DDI 部分,即通常的显示驱动部分。由于在显示中存在大量硬件无关操作,显示驱动通常采用分层结构,采用分层结构有助于降低代码复杂度提高代码效率,其中MDD 层实现缺省的绘图功能,由微软提供的图形原语引擎模块(GPE , GraphicsPrimitive Engine)组成,如果要支持Directdraw,则要使用DDGPE模块;而PDD层与硬件具体相关,则是显示驱动的主要内容,一般由OEM 厂商或独立硬件商实现。

  WINCE 上层程序通过一组(约20 多个)显示驱动接口函数同显示驱动打交道,因此显示设备驱动程序必须实现这些显示驱动接口函数,GDI 通过调用这组函数初始化显示设备驱动程序和将图形输出到显示设备上。由于采用分层结构,显示驱动由MDD 层负责对上层的GWES模块提供函数接口,但是这些函数并不是直接提供出来的,实际上只是通过一个DrvEnabLEDriver( )函数来完成的。作为DDI部分的一个导出函数,DrvEnableDriver会在GDI初始化时被调用。

  DrvEnableDriver 在MDD 层中没有实现,所以需要在PDD层中定义,主要代码如下:

  BOOL APIENTRY DrvEnableDriver

  (ULONG engineVersion,ULONG cj,DRVENABLEDATA *data,PENGCALLBACKS engineCallbacks)

  {

  BOOL fOk = FALSE;

  if(gszBaseInstance[0] != 0)

  {

  fOk =

  GPEEnableDriver(engineVersion, cj, data,engineCallbacks);

  }

  return fOk;

  }

  这里GPEEnableDriver 是微软预先编写的一个MDD层函数。该函数位于源文件ddi_if.cpp里, 因此我们只需简单调用就可以了。

GPEEnableDriver 函数通过执行语句memcpy(pded, pDrvFn, cj) 将一个预先定义好的DRVENABLEDATA 结构体变量pDrvFn 的地址传给一个上层结构体指针pded.而在结构体变量pDrvFn 中预先已包含了20 多个底层显示驱动函数指针,这样GWES 就可以通过这些指针操纵底层显示硬件了。例如应用程序想创建一个到图形设备的连接时可以通过GWES.exe 调用CreateDC(),而该函数会调用DrvEnablePDEV()函数,当应用程序需要从显示设备上断开时则会调用DeleteDC() , DeleteDC() 则会调用DrvDisablePDEV() .DrvEnablePDEV() 和DrvDisablePDEV()就属于这20 多个被GWES 调用的底层显示驱动函数。

  以上这些底层显示驱动函数大部分跟硬件密切相关,因此需要进一步调用PDD层函数。由于不同的显示硬件特点都不尽相同,因此势必造成PDD层暴露给MDD层的接口函数各不相同,这样势必会增加代码的复杂性。为此微软设计了一个GPE类,一个GPE类实例代表一个显示设备硬件,其所有数据成员都对应于一个显示设备的属性数据,并设计了多个成员函数用以操纵这些数据成员。考虑到硬件的多样性,GPE 类的有些函数并为全部实现,或为空函数或者虚函数,需要其子类实现或者覆盖。因此不能直接定义GPE类型的变量,只能以先构造GPE类为父类的继承类,然后才能定义实例。

  MDD 层的底层显示驱动函数通过实例化一个GPE 继承类的实例就可以直接调用PDD 层代码了,这一般是通过SafeGetGPE 函数来实现的。

  SafeGetGPE 由微软设计实现,位于MDD 层的ddi_if.cpp,一般无须改动。在SafeGetGPE 函数中调用了GetGPE 函数,这个函数MDD 层没有,需要我们在PDD 层实现。GetGPE 函数可以简单实现如下:

  这里代码利用了C++的多态性和继承性。在C++中父类或更上一级的类的指针可以引用继承类中相同的变量,并且对数据成员和成员函数的引用以继承类的实现或定义优先。这样在MDD 中使用指针gGPE 所指向的数据或函数时得到的都是类DM6446VPBE 的成员变量和成员函数。由此可以看出GetGPE 函数是显示驱动中联系MDD和PDD 的桥梁,通过它MDD 可以直接调用PDD的代码。

  3.3 GPE继承类的实现

  通过上面的分析可以看出,WINCE 的显示驱动主要部分在于PDD 层,而PDD 层除了向MDD导出一些接口函数外如DrvEnableDriver,其余主要是构建一个GPE 或是DDGPE 的子类(如果要实现DirectDraw)。由于DDGPE 的父类是GPE,因此无论是DDGPE 还是GPE 的子类差别并不大。

  构建一个GPE 的子类其实就是实现一个有具体数据和函数并且具体准确的反映了特定显示设备硬件属性的GPE 类的子类,并通过该子类去实例化一个对象。

  一个GPE 子类通常需要重载GPE 类中的同名函数和实现GPE中的虚函数以及子类独有的一些函数如初始化构造函数[3].子类构造函数主要是初始化硬件和子类成员变量,譬如视频处理时钟寄存器设置,OSD Window 的大小和坐标,VENC 的输出模式,以及子类的成员变量如显示宽度m_nScreenWidth 和显示高度m_nScreenHeight 等等。子类要GPE 类中的函数包括GPE 的空函数和虚函数,这些函数实际上就是MDD 调用PDD 层驱动中需要实现的函数,主要函数包括:SetMode(),用于设置一个显示设备能够支持的显示模式;GetPhysicalVideoMemory(),用于获取显示设备内存的系统基地址和内存大小; 以及AllocSurface() SetPointerShape()BltComplete() SetPalette()等。这些函数具体可以参考微软提供的驱动示例代码,它们位于Public CommonOAKDriversDisplay 目录下[ 1].除了这些函数外PDD 还需实现一个MDD 层函数DrvGetMask,但比较简单,只需要定义一个全局数组gBitMasks,该数组内容是代表RGB 的所占的位域,与具体的显示硬件有关。

  3.4 驱动程序与应用程序的通信

  不同于其他流式驱动可以由应用程序直接调用,显示驱动由操作系统调用,应用程序不能直接访问。具体来说,应用程序不是通过CreateFile等这些文件系统API接口来访问,而是通过GDI接口间接访问。对于GDI调用而言,对应的后台服务进程是GWES.exe,然后GWES.exe再进一步调用MDD和PDD函数,即WINCE底层显示驱动。例如如果要画一个矩形,则可以调用SetRect、GetDC和FillRect等函数在图形界面上面进行显示,而要在图形界面上输出一段文字只需调用DrawText函数就可以了,至于显示驱动调用就可以交给GDI就可以了。

  4 结束语

  本文阐述和分析了DM6446 显示硬件原理和Windows CE驱动模型,剖析了显示驱动程序的工作原理和显示工作流程。本文的创新点在于完整的阐述了WINCE显示驱动程序在DM6446上的设计实现,而以往WINCE 的显示驱动都是基于LCD,因此本文对编写同类驱动程序的开发人员将有一定的参考价值。WINCE启动运行后,图形界面运行稳定,并可支持Windows CE下的应用软件运行,表明驱动程序设计良好。

1 2

关键词: 驱动 设计 实现 显示 CE DM6446 Windows 基于

加入微信
获取电子行业最新资讯
搜索微信公众号:EEPW

或用微信扫描左侧二维码

相关文章

查看电脑版