WinCE Display驱动开发介绍

作者:ARM-WinCE

好多天上不了CSDN了,今天终于上来了。写篇Blog吧!!!这篇Blog介绍WinCE下的Display驱动开发。

WinCE 中, Display 驱动由 GWES 模块来管理。 WinCE 提供了两种架构的 Display 驱动模型,可以满足不同的硬件需求。一种是基于 WinCE DDI Display 驱动模型,另一种是基于 DirectDraw Display 驱动模型。下面将对两种架构作简单介绍。

 

1. Display 驱动模型

 

WinCE 下的 Display 驱动直接由 GWES 模块管理,它会直接被 GWES 模块管理和调用。 Display 驱动实际上也是分层的,其中包括 GPE 库,该库处理一些默认的绘图,相当于驱动的 MDD 层。用户只需要开发和硬件相关的 PDD 层驱动就可以了。在 WinCE 中,整个架构如图:

 

如图, Application 为一个应用程序,该程序会调用图形设备接口函数 (GDI) ,而 GDI 函数是由 Coredll.dll 模块导出的。 Coredll.dll 会将函数调用的参数打包,然后触发对另一个进程的本地过程调用 (LPC) ,所有的绘图和开窗口的工作被传给内核中 GWES 模块。 GWES 模块被称为图形,窗口和事件子系统,专门处理图形输出和用户输入等事件及相关的所有交互。 GWES 模块会调用 Display 驱动完成对显示硬件的操作。 Display 驱动由 GPE DDL.dll 组成, GPE 完成基本的默认绘图工作,而 DDI.dll 实际上从 GPE 类上继承而来的,并实现了相关的显示硬件的操作。

 

 

2. DirectDraw Display 驱动模型

 

DirectDraw 提供了独立于硬件的直接访问显示设备的能力。它可以通过直接访问硬件抽象层 (HAL) 中的一些函数来达到直接操作显示设备的目的,在这个过程中,不再需要图形设备接口 (GDI) 的转换。这种直接的方法可以使图像更加连贯,也提高了显示的性能。 为了实现这样的功能,需要在显示驱动上扩展能够直接访问相关硬件的函数。这些函数会被 DirectDraw 模块调用,并形成 DirectDraw 的硬件抽象层 (DDHAL) DirectDraw 显示驱动架构如图:

 

如图, DirectDraw 的真正实现代码都驻留在 gwes.dll 模块中,应用程序只是连接了一个小的客户端,被称为 DDRAW.dll 代理,该代理主要负责用户进程与系统之间的远程 DirectDraw COM 接口连接。这样,用户请求会被传送到内核的 GWES 模块中。 针对 DirectDraw WinCE 提供了一个名为 DirectDraw GPE (DDGPE) ,它是从 GPE 类上面继承而来的。实际上, DirectDraw 显示驱动是由 DDGPE DDHAL 组成,而 DDGPE 中已经包含了 DDHAL 的功能。用户需要从 DDGPE 类继承并实现相关函数即可。 GWES.dll 模块中包含 GDI DDRAW 两个组件,这两个组件会调用驱动中的 DDGPE 的相关接口完成对硬件的操作。

 

在上述两种架构中,用户可以根据自己的硬件情况选择相应的架构。第一种架构是基于 GPE 类继承来实现的,第二种架构是基于 DDGPE 类继承来实现的,而第二种架构的 DDGPE 类又是从第一种架构的 GPE 类继承而来。关于两种类的具体定义,可参见 ” /WINCE600/PUBLIC/COMMON/OAK/INC” 路径下的 gpe.h ddgpe.h 文件。

 

 

本Blog将基于 Display 驱动模型来介绍, DirectDraw Display 驱动模型不在这里介绍。

WinCE 下的 Display 驱动是基于 GPE 类来实现的,其中 GPE 中已经实现了基本的绘制工作,相当于 MDD 层。用户需要继承该类,并实现里面的其他一些函数,所以用户实现的相当于 PDD 层。

GPE 类是一个抽象类,其中包含很多纯虚函数,只能用于继承。用户在继承了 GPE 类以后,要对 GPE 类中的纯虚函数做相应的实现。开发 Display 驱动的大致步骤如下:

(1)    继承 GPE 类并定义一个该类的实例。

(2)    实现 GetGPE() 函数,把该类的实例返回给上层的 DDI 接口。

(3)    实现 DrvEnableDriver(..) DisplayInit(..) 函数并导出这两个接口。

(4)    实现 GPE 类中的函数。

      

下面将具体介绍实现的步骤:

1 继承 GPE

首先,基于 GPE 类进行继承,如果想在 Display 驱动支持 Rotation 可以从 GPERotate 类上面继承。实际上,在 ”gpe.h” 中有如下定义:

            typedef GPE     GPERotate;

可以看出 GPERotate 类就是 GPE 类。在这里,用户从 GPE 类上面继承就可以了,举个例子如下:

     class NewGPE: public GPE

  {

    private :

         GPEMode           m_ModeInfo;

         DWORD             m_colorDepth;

         DWORD             m_VirtualFrameBuffer;

         DWORD             m_FrameBufferSize;

         BOOL              m_CursorDisabled;

         BOOL              m_CursorVisible;

        

    public :

         NewGPE(void );

         virtual INT NumModes(void );

         virtual SCODE  SetMode(INT modeId,    HPALETTE *palette);

         virtual INT InVBlank(void );

         virtual SCODE  SetPalette(const PALETTEENTRY *source, USHORT firstEntry, USHORT numEntries);

         virtual SCODE GetModeInfo(GPEMode *pMode, INT modeNumber);

         virtual SCODE  SetPointerShape(GPESurf *mask, GPESurf *colorSurface, INT xHot, INT yHot, INT cX, INT cY);

         virtual SCODE MovePointer(INT xPosition, INT yPosition);

         virtual void   WaitForNotBusy(void );

         virtual INT   IsBusy(void );

         virtual void       GetPhysicalVideoMemory(unsigned long *physicalMemoryBase, unsigned long *videoMemorySize);

        virtual SCODE AllocSurface(GPESurf **surface, INT width, INT height, EGPEFormat format, INT surfaceFlags);

         virtual SCODE     Line(GPELineParms *lineParameters, EGPEPhase phase);

         virtual SCODE     BltPrepare(GPEBltParms *blitParameters);

         virtual SCODE BltComplete(GPEBltParms *blitParameters);

         virtual ULONG GetGraphicsCaps();

         virtual ULONG DrvEscape(

                         SURFOBJ *pso,

                         ULONG    iEsc,

                         ULONG    cjIn,

                         PVOID    pvIn,

                         ULONG    cjOut,

                         PVOID    pvOut);

         SCODE WrappedEmulatedLine (GPELineParms *lineParameters);

         void   CursorOn(void );

         void   CursorOff(void );

#ifdef ROTATE

         void SetRotateParms();

         LONG DynRotate(int angle);

#endif

    };

     

NewGPE GPE 类上面继承,其中包括一些属性,如下:

            m_ModeInfo 显示模式,结构如下

               struct GPEMode {

                               int modeId;                             // 开发者定义的显示模式的索引号

                               int width;                                // 显示宽度

                               int height;                                // 显示高度

                               int Bpp;                                  // 显示深度

                               int frequency;                          // 显示频率

                               EGPEFormat format;              // RGB 格式,各占多少 bit

               };

 

            m_colorDepth 显示深度

            m_VirtualFrameBuffer FrameBuffer的地址

      m_FrameBufferSize FrameBuffer的大小

      m_CursorDisabled 光标使能标记

      m_CursorVisible 光标可视标记

 

用户可以根据需要定义相应的属性,在 NewGPE 类中,需要定义并实现基类中的纯虚函数,上面的 NewGPE 类中已经包含了这些函数的定义,还包括了其他一些函数,将在下面介绍。

 

 

2 实现 GetGPE 函数

在定义了 NewGPE 类之后,我们需要实现一个实例,首先定义一个该类的指针:

            static  GPE    *gGPE = (GPE*)NULL;

 

然后实现 GetGPE 函数,如下:

    GPE *GetGPE(void )

    {

         if (!gGPE)

         {

             gGPE = new NewGPE();

         }

 

         return gGPE;

    }

在该函数中,创建了一个 NewGPE 的实例。在这个时候 NewGPE 构造函数会被调用,一般我们会在这里面作一些与显示相关的初始化的工作。该函数返回 gGPE 指针给上层接口。

 

 

3 实现 DrvEnableDriver DisplayInit 函数

Display 驱动对上层的 GWES 模块提供了 20 多个函数接口,但是这些函数并不是直接提供出来的,实际上只是通过一个 DrvEnableDriver(..) 函数来完成的。该函数在 Display 驱动的 MDD 层中没有实现,所以需要在 PDD 层中定义,如下:

BOOL APIENTRY DrvEnableDriver(ULONG engineVersion, ULONG cj, DRVENABLEDATA *data, PENGCALLBACKS  engineCallbacks)

{

      BOOL fOk = FALSE;

 

      // make sure we know where our registry configuration is

      if (gszBaseInstance[0] != 0) {

            fOk = GPEEnableDriver(engineVersion, cj, data, engineCallbacks);

      }

 

      return fOk;

}

 

            engineVersion DDI版本号,目前为 DDI_DRIVER_VERSION

            cj DRVENABLEDATA 结构的大小。

            data 指向 DRVENABLEDATA 结构体。

engineCallbacks 指向一个回调函数结构体,传入一些 GDI 函数到 Display 驱动中。

 

其中, DRVENABLEDATA 结构中包含了 Display 驱动中的设备接口函数的指针,在 DrvEnableDriver 函数中调用了 GPEEnableDriver 函数,该函数会导出 GWES 模块所需的所有 Display 驱动的接口函数。同时 GWES 模块通过第四个参数 engineCallbacks 提供回调函数供 Display 驱动调用。该函数在 ”ddi_if” 中定义。

 

另一个重要的函数是 DisplayInit 函数,它是第一个被执行的 Display 驱动中的函数,该函数主要用于读取注册表中的一些信息并作判断。该函数是可选的,也可以不在驱动中实现它。

BOOL APIENTRY DisplayInit(LPCTSTR pszInstance, DWORD dwNumMonitors)

{

      DWORD dwStatus;

      HKEY hkDisplay;

      BOOL fOk = FALSE;

 

    if(pszInstance != NULL) {

        _tcsncpy(gszBaseInstance, pszInstance, dim(gszBaseInstance));

    }

 

      // sanity check the path by making sure it exists

      dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, gszBaseInstance, 0, 0, &hkDisplay);

      if(dwStatus == ERROR_SUCCESS) {

            RegCloseKey(hkDisplay);

            fOk = TRUE;

      }

else

{

RETAILMSG(0, (_T("SALCD2: DisplayInit: can't open '%s'/r/n"), gszBaseInstance));

      }

 

    return fOk;

}

 

            pszInstance 注册表中显示驱动的相关注册表值

            dwNumMonitors 支持的 Monitor的个数

 

在该函数中主要通过读取注册表信息判断显示驱动的存在,如果返回错误,则 GWES 会停止 Display 驱动的初始化。当然,用户可以根据自己的要求灵活掌握,也可以在这里初始化显示设备或做其他的初始化工作。

 

 

4 实现 GPE 类中的函数

由于 NewGPE 继承于 GPE 类,所以必须实现 GPE 类中的所有纯虚函数,这些函数实际上就是 PDD 层驱动中需要实现的函数,如下:

 

4.1 virtual SCODE GetModeInfo(GPEMode *pMode, INT modeNumber)

            获得显示模式。

                pMode 输出显示模式结构

                modeNumber 显示模式索引号

 

4.2 virtual int NumModes(void)

            获得当前驱动支持的显示模式的个数

 

4.3 virtual SCODE SetMode(INT modeId, HPALETTE *palette)

            设置显示模式。

                modeId 显示模式索引号

                palette 调色板指针,指向一个由 EngCreatePalette函数创建的调色板

 

4.4 virtual SCODE AllocSurface(GPESurf **surface, INT width, INT height, EGPEFormat format, INT surfaceFlags)

            在系统内存中创建一个绘图平面。

                surface 指向被分配的内存的指针

                width 宽度

                height :高度

                format :绘图平面格式

                surfaceFlags :标记位,标明在哪分配内存

 

4.5 virtual SCODE SetPointerShape(GPESurf *pMask, GPESurf *pColorSurface, INT xHot, INT yHot, INT cX, INT cY);

            设置光标形状。

                pM ask 指向一个包含光标形状的掩码

                pColorSurface 指向被光标使用的颜色绘图平面

                xHot :光标热点的 X 坐标

                yHot :光标热点的 Y 坐标

                cX :光标宽度

                cY :光标高度

 

4.6 virtual SCODE MovePointer(int x, int y)

            移动光标到指定位置或者隐藏光标

                x 光标移动位置的 x 坐标,若为 -1 表示隐藏光标。

                y :光标移动位置的 y 坐标

 

4.7 virtual SCODE BltPrepare(GPEBltParms *blitParameters)

            在做位块传输前会先执行该函数,用于确定执行 BLT 的函数

                blitParameters 指向一个 GPE 的位块传输参数的结构体

 

4.8 virtual SCODE BltComplete(GPEBltParms *blitParameters)

            该函数用于释放在 BltPrepare 中申请的资源

                blitParameters 指向一个 GPE 的位块传输参数的结构体

 

4.9 virtual SCODE Line(GPELineParms *lineParameters, EGPEPhase phase)

            画线函数

                lineParameters 指向一个 GPE Line 结构体,描述所画的线

                phase :画线所处的阶段,具体描述如下

                                    gpeSingle :画单根线

                                    gpePrepare :准备画线

                                    gpeContinue :画线过程中

                                    gpeComplete :画线完成

 

      在这里要提一点,有时我们会看到在该函数中调用另一个函数WrappedEmulatedLine(..), 这个函数 WinCE PUBLIC 目录下的参考 Display 驱动中也可以找到,该函数是一个快速的画线函数,里面采用了 Bresenham 画线算法,通过采用运行速度快的加减和移位运算来完成画线。

 

 

4.10 virtual SCODE SetPalette(const PALETTEENTRY *pSource, USHORT firstEntry, USHORT numEntries)

            设置调色板

                pSource 指向一个调色板入口信息的结构体

                firstEntry :第一个入口

                numEntries :入口的个数

 

4.11 virtual int  InVBlank(void)

            显示设备是否处于垂直消隐期间

 

 

上述函数在 GPE 类中均被定义为纯虚函数,需要在继承类中实现,也就是在我们的驱动程序中实现。这些函数是必须实现的。根据显示的需求,还可以在显示驱动中添加其他的函数,比如对光标的支持,对旋转的支持等,如下:

 

 

4.12 void  CursorOn(void)

            使能光标显示。

 

4.13 void  CursorOff(void)

            禁止光标显示。

 

4.14 void  SetRotateParms(void)

            设置屏幕翻转参数。

 

4.15 void  DynRotate(int angel)

            支持动态翻转。

                angel :翻转角度

 

4.16 ULONG *APIENTRY DrvGetMasks(DHPDEV dhpdev)

            获得显示模式的 RGB 掩码

                dhpdev :指向掩码信息,比如 RGB565 模式为 (0xf800 0x07e0 0x001f)

 

            NOTE :该函数必须在驱动中被实现。

 

4.17 PowerHandler(BOOL bOff)

            电源控制。

                bOff TRUE 表示关闭电源, FALSE 表示打开电源

 

4.18 ULONG DrvEscape(DHPDEV dhpdev, SURFOBJ* pso, ULONG iEsc, ULONG cjIn, PVOID pvIn, ULONG cjOut, PVOID pvOut)

        该函数提供给应用程序的一个直接访问显示驱动的接口,和流设备驱动中的 IoCtls 函数类似。应用程序通过调用 ExtEscape 函数传送操作码和数据给显示设备驱动, DrvEscape 函数会接收到数据并进行处理,然后返回相应结果给 EstEscape 函数。用户也可以根据需要自己定义相应的操作码。

                dhpdev :设备句柄

                pso :指向一个绘图平面的结构

                iEsc :操作码

                cjIn :输入数据 buffer 的大小

                pvIn :指向输入数据 buffer

                cjOut :输出数据 buffer 的大小

                pvOut :指向输出数据 buffer

 

 

 

 

大 致就是这些内容,GPE类中的纯虚函数是肯定要实现的,其他的一些函数根据需要来实现。我在写这篇Blog的时候,有些地方有些犹豫,开始觉得自己语文水 平不够,不太会表达,但是也许是因为自己对Display驱动中的一些知识还是理解的不够彻底吧。如果有什么问题,请大家谅解,并请指点。

发布了23 篇原创文章 · 获赞 31 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/memory01/article/details/6219773