《Windows CE嵌入式开发入门——基于Xscale架构》 第10章 案 例 分 析

 

 

10.1  TouchScreen驱动分析

10.1.1  介绍

触摸屏驱动是从触摸屏硬件上获取用户的输入信息,然后将触摸事件传递给GWES子系统。驱动会矫正坐标值,比如将歪斜的线条修正为直线。

在用户的笔或者手指触摸屏幕的时候,驱动必须随时返回坐标值,当笔或者手指离开屏幕后,必须产生事件来标记“抬笔”事件。

10.1.2  注册表设置

下面是触摸屏驱动的注册表设置。

 

[HKEY_LOCAL_MACHINE/ControlPanel]

    InputConfig"=dword:3   ; 3 => 键盘和触摸屏

[HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/TOUCH]

    "DriverName"="touch.dll"

    "MaxCalError"=dword:10

 

10.1.3  数据结构

1TOUCH_PANEL_SAMPLE_FLAGS

本数据结构绘出返回到系统的坐标值的属性。

2TPDC_CALIBRATION_POINT

本数据结构由TouchDriverCalibrationPointGet.例程代码组成,并运行在系统启动时,用户可以看见屏幕上会有一个X,从中间再到四周指引用户矫正坐标值。TouchDriverCalibrationPointGet例程通常利用屏幕的高和宽来确定中心和四周的坐标值。

 

struct TPDC_CALIBRATION_POINT {

  INT PointNumber;

  INT cDisplayWidth;

  INT cDisplayHeight;

  INT CalibrationX;

  INT CalibrationY;

};

3TPDC_SAMPLE_RATE

本数据结构由DdsiTouchPanelGetDeviceCaps例程代码组成。

 

struct TPDC_SAMPLE_RATE {

  INT SamplesPerSecondLow;

  INT SamplesPerSecondHigh;

  INT CurrentSampleRateSetting;

};

    数据结构中成员说明

SamplesPerSecondLow

    低采样率值。

SamplesPerSecondHigh

    高采样率值。

CurrentSampleRateSetting

 

当前采用的采样率值,1代表高采样率,0代表低采样率。

10.1.4  驱动例程

1TouchPanelEnable

本例程使触摸屏具有采样功能。可以被程序直接调用。

2DdsiTouchPanelAttach

本例程在MDDDLL中被加载,DLL入口获得DLL_PROCESS_ATTACH事件时被执行。在多数系统中,一般只有一个进程加载触摸屏驱动,然而在某些系统中也不排除多个进程都加载触摸屏驱动DLL的可能性。在本例程中可以为它们复制全局变量等信息,这样可使每个加载驱动的进程都从本例程获得一个全局信息的副本。但是在本例程中不需要操作硬件或者中断。硬件和中断的操作应当在DdsiTouchPanelEnableDdsiTouchPanelDisable中进行。

本例程原型:

 

LONG DdsiTouchPanelAttach(void);

 

返回值一般为0

3DdsiTouchPanelDetach

本例程在MDDDLL被卸载,DLL入口获得DLL_PROCESS_DETACH事件时被执行。在多数系统中,一般只有一个进程加载触摸屏驱动,然而在某些系统中也不排除多个进程都加载触摸屏驱动DLL的可能性。在本例程中可以为它们复制全局变量等信息,这样可使每个加载驱动的进程都获得一个全局信息的副本。但是在本例程中不需要操作硬件或者中断。硬件和中断的操作应当在DdsiTouchPanelEnableDdsiTouchPanelDisable中进行。

本例程原型:

 

LONG DdsiTouchPanelDetach(void);

4DdsiTouchPanelDisable

本例程关闭触摸屏设备,删除任何没有被提交给GWES的坐标,关闭电源。

本例程原型:

 

VOID DdsiTouchPanelDisable(void);

 

无参数,无须返回值。

5DdsiTouchPanelEnable

本例程原型:

 

BOOL DdsiTouchPanelEnable(void);

 

返回值:

 

TRUE代表成功,FALSE代表失败。

 

本例程为触摸屏加载电源,并且进行初始化。调用AllocTouchPanelRegs为寄存器分配内存。这些虚拟内存分配时,参数中使用MEM_RESERVED标志,这样被分配的内存不与物理内存绑定,不能被malloc等函数访问,最后使用virtual_copy将虚拟地址和寄存器的物理地址绑定(不是物理内存)。

在为寄存器分配完空间后,建立中断,即“落笔”中断,但是建立后就关闭中断,直到InterruptEnable()被执行后再建立中断。

6DdsiTouchPanelGetDeviceCaps

本例程查询触摸屏设备的精度。

本例程原型:

 

BOOL DdsiTouchPanelGetDeviceCaps(

  ULONG iIndex,

  LPVOID lpOutput

);

其参数为:

iIndex,代表查询的项目。可以有以下类型:

 

TPDC_SAMPLE_RATE_ID           查询采样频率。

TPDC_CALIBRATION_POINT_ID          返回指定精度的坐标值,精度由lpOutput参数的PointNumber域指定。

TPDC_CALIBRATION_POINT_COUNT_ID 查询校准坐标所需要的坐标样本数。

 

lpOutput:指向存放返回信息的内存区域,类型和iIndex对应。

iIndexTPDC_SAMPLE_RATE_ID时,指向TPDC_SAMPLE_RATE结构。

iIndexTPDC_CALIBRATION_POINT_ID时,指向TPDC_CALIBRATION_POINT结构。

lpOutput不能为空指针。

返回值:

TRUE代表成功,FALSE代表失败。

7DdsiTouchPanelGetPoint

本例程返回最近的一次数据采样坐标值。在“落笔”之后,就会触发定时器的一系列中断。定时器每秒钟约产生150次中断,每次中断都要返回坐标值。

pxa255中,本例程分为三部分:

n     处理“落笔”中断

关闭“落笔”中断

n     处理定时器中断

递增定时器中断计数。

以上两个中断处理,都会调用SampleTouchScreen,从采集到的3个点中,选出最优化的点,返回给系统。

n     处理“抬笔”中断

最后,打开“落笔”中断,将UCB1400设置为可中断模式。

8DdsiTouchPanelPowerHandler

本例程通知驱动程序,系统正从暂停模式恢复到正常模式,或者进行相反的操作。本例程会被内核态程序调用,因此不要在其中使用系统调用。

10.2  显示驱动分析

基于微软公司的DirectDraw显示驱动,必须在DDI内实现DDHAL用于支持DirectDraw

10.2.1  Windows CE DirectDraw架构

DirectDraw提供了独立于设备的显示驱动功能。它将图形显示数据直接加载到硬件设备上,而不需要图形设备接口来对数据进行转换,从而使图形显示效果更加顺畅,避免了屏幕的闪烁。实现这样的接口,需要扩展能够直接访问硬件的驱动接口,这些能够直接访问硬件的驱动接口组成了DirectDraw硬件抽象层(DirectDraw Hardware Abstraction LayerDDHAL

Windows CE中的DirectDraw架构

10-1  DirectDrawGDI
显示驱动结合

 

10-1显示了DirectDrawGDI显示驱动是如何结合的:

Windows CE中的DirectDrawWindows桌面环境Direct-
Draw
的子集。DirectDraw的核心可执行代码常驻在系统进程gwes.exe中,而应用程序则和较小的客户端代理Ddraw.dll建立连接,这个代理负责维护系统和用户进程之间的远程DirectDraw COM接口连接。Windows CE在默认的情况下不提供进程外部的COM服务器。

Windows CE还提供了一个名为DirectDraw图形原始引擎(DirectDraw Graphics Primitive EngineDDGPE)的抽象层,GDIDirectDraw组件在系统进程中使用这个抽象层将GDI/DDIDDRAW/DDHAL的功能捆绑到一个由C++类和函数组成的框架上。DDGPE提供的C++类实现了GPE类的扩展。

10.2.2  如何创建DirectDraw显示驱动程序

可以复制Platform Builder提供的样例显示驱动程序作为新创建的驱动程序的基础。

1.修改二进制文件导出的接口,二进制文件应当导出HALInit接口。在Platform Builder提供的样例显示驱动程序有一个.def后缀名的文件,在这个文件中添加HALInitHALInitDdgpe.lib库的一部分,应当总是被链接到程序中。

2.在程序中包含路径drive:/wince400/public/directX/oak/inc,这应当是DDHFuncs.h所在路径。这是一个所有显示驱动程序公共的头文件,不应当被修改。

将下列文件从样例显示驱动程序中复制到自己的文件夹下:

n     HalCaps.cpp

n     HalDD.cpp

n     HalSurf.cpp

n     HalPalette.cpp

在选择样例显示驱动程序时,应当选择和自己设备最接近的驱动程序。最后,编译自己创建的驱动程序。

10.2.3  DDGPE基类

1DDGPE

DDGPE类是DirectDraw对象继承的基类。

10-1列出了DDGPE类中的成员函数:

10-1                                                 DDGPE类中的成员函数

   

DDGPE::AllocSurface

为一个surface分配内存

DDGPE::AllocVideoSurface

在内存中创建DDGPESurf对象

DDGPE::BltExpanded

建立并且执行一个GPE blit

DDGPE::BltPrepare

GPE BltPrepare 函数的DDGPE版本

DDGPE::DetectMode

根据提供的参数确定显示模式

DDGPE::DetectPixelFormat

DDPIXELFORMAT转换成EGPEFormat或者EDDGPEPixelFormat.

DDGPE::GetDriverData

返回指向驱动数据的指针

DDGPE::GetDriverGUID

获取驱动的GUID

DDGPE::GetModeInfoEx

返回扩展模式信息

DDGPE::GetPhysicalModeId

获取当前实际的显示模式

DDGPE::InDisplay

判断给定的扫描线是否正在显示

DDGPE::PerformBlt

执行一次blit

DDGPE::SetDriverData

将驱动特定的数据存储到DDGPE对象中

DDGPE::SetDriverGUID

将驱动的GUID赋予特定的对象

DDGPE::SetMode

更改当前的显示模式

DDGPE::SetVisibleSurface

使指定的surface被推到前台,成为可视

DDGPE::WrapSurface

在既存的内存块之上创建一个DDGPESurf对象

2DDGPESurf

DDGPESurf类扩展了GPESurf类,并且代表了由GDI分配的一个surface,如表10-2描述。

10-2                                              DDGPSurf类中的成员函数

   

DDGPESurf::AlignedWidth

获取一个surface内存的宽度

DDGPESurf::Bpp

决定在当前surface中,每个象素占用的位数

DDGPESurf::ColorKey

获取当前surfacecolor key

DDGPESurf::ColorKeyMask

获取当前surfacecolor key掩码

DDGPESurf::DeleteSurface

删除一个surface,然后释放内存

DDGPESurf::Init

初始化当前surface的像素格式

DDGPESurf::GetDDGPESurf

获取和一个DirectDraw surface相关的DDGPESurf对象

DDGPESurf::GetDirectDrawSurface

获取和一个DDGPESurf对象相关的DirectDraw surface

DDGPESurf::GetDriverData

获取指向驱动程序数据的指针,这个驱动程序和某个DDGPESurf对象相关联

续表

   

DDGPESurf::GetDriverGUID

获取当前对象的驱动GUID

DDGPESurf::PixelFormat

获取当前surfaceDDGPE风格的像素格式

DDGPESurf::SetColorKey

为当前surface设置color key

DDGPESurf::SetColorKeyMask

为当前surface设置color key掩码

DDGPESurf::SetDirectDrawSurface

将一个DirectDraw surface和当前的DDGPESurf 对象绑定

DDGPESurf::SetDDGPESurf

将一个DDGPESurf对象和一个DirectDraw surface绑定

DDGPESurf::SetDriverData

将驱动的数据存储到DDGPESurf对象中

DDGPESurf::SetDriverGUID

将驱动的GUID存储到DDGPESurf对象中

10.3  USB驱动分析

Windows CE支持总线的枚举、电源管理、传输类型、类驱动、集成的和外部的集线器及对未知USB设备的有限支持。

Windows CE提供了对总线供电和自供电设备的支持。对于这两种类型的设备,USB驱动模块都会从设备描述符中读出电源需求,如果设备对电源的需求超出了系统的极限,那么这个设备将被停用。

USB总线的拓扑结构

在进入驱动程序开发前,先了解USB设备总线的拓扑结构

主机是USB设备树的根结点并且包含了一个固有的集线器,称为根集线器。集线器是一种功能设备,它能将USB传输通道给多个端口共享,这样,各个端口上就可以连接多个USB外设了。各个叶结点也就是USB外设。如图10-2所显示:

10-2  USB总线的拓扑结构

10.3.1  Windows CE系统中USB设备的传输类型

Windows 2000 DDK一样,Windows CE也支持四种基本的传输类型。

USB规范中定义了四种数据传输方式,如表10-3所示。它们的不同之处有,单个事务能携带的数据量、能否保证特定的周期或延迟、能否自动校正错误。每种传输方式对应特定类型的端点。实际上,给定类型的端点(控制、批量、中断、等时)总是使用对应类型的传输。

10-3                                          USB规范中的四种数据传输方式

传输类型

   

   

包容量(字节)

控制

控制传输是双向传输,被USB系统软件主要用来进行查询、配置和给USB设备发送通用的命令。
控制传输用于主计算机和USB设备端点0之间的传输,但是指定供应商的控制传输可能使用其他端点

少于或等于
8,16,32,64

批量

适合于需要大批量地传输和接收数据的设备,同时在没有带宽和间隔时间要求的情况下,要求保证传输。打印机和扫描仪属于这种类型。这种类型的设备适合于传输非常慢和大量被延误的传输,可以知道所有数据的传输完成之后再接收数据

少于或等于

8,16,32,64

中断驱动传输

中断驱动传输主要用于定时查询是否有中断数据需要传输。设备的端点模式器的结构决定了它的查询频率,频率值可以从1255毫秒。这种传输典型地用于少量分散的、不可预测数据的传输。键盘、操纵杆和鼠标设备属于这一类型

少于或等于64

等时传输

用于发送或接收有周期保证的大块无结构数据

少于或等于1023

10.3.2  Window CE流驱动程序应具有的导出函数

如果把本文的设备前缀定为MDV的话,则导出如下函数。

n     MDV_Init                     由设备管理器调用来初始化设备;

n     MDV_Deinit                 上述例程的逆过程;

n     MDV_Open                  打开设备,由应用程序调用CreateFile

n     MDV_Close                  关闭设备上下文;

n     MDV_IOControl           向设备发送命令;

n     MDV_Write                  把数据写入设备;

n     MDV_Read                   读设备,由应用程序调用readfile

n     MDV_Seek                   数据指针的移动;

n     MDV_PowerUp            电源管理;

n     MDV_PowerDown        解除电源,只有当设备可以在软件控制下可以关机;

n     USBDeviceAttach          激活设备;

n     USBInstallDriver           安装驱动程序;

n     USBUnInstallDriver       卸载驱动程序。

10.3.3  设备描述符及设备配置

上一节只是产生了一个虚拟的驱动程序,没有针对任何硬件设备。但是,如果要对特定设备进行开发,必然需要对硬件状态进行设置。以下是对软件狗模块的设置以及所需要的结构体:

 

1, USB_CONFIGRATION

这个结构用来描述USB设备的配置。

typedef struct {

  UCHAR bLength;//结构体大小,单位是字节

  UCHAR bDescriptorType;// GWFIGuRATION

  USHORT wTotalLength;//该配制返回的数据总长度

  UCHAR bNumInterfaces;//该配制支持的接口数

  UCHAR bConfigurationValue;

  UCHAR iConfiguration;

  UCHAR bmAttributes;

……

} USB_CONFIGURATION;

 

2, USB_DEVICE

这个结构存放了USB设备的信息。

typedef struct {

  UCHAR bLength;

  UCHAR bDescriptorType;// DEVICE常另值

  UCHAR bDeviceClass;

  UCHAR bDeviceSubClass;

  UCHAR bDeviceProtocol;

  UCHAR bMaxPacketSize0;

  USHORT idVendor;//厂商标识符。USB工作组分配厂商标识符

  ……

} USB_DEVICE;

3, USB_DRIVER_SETTINGS

这个结构包含了USB驱动的成员,用来修改注册表。

typedef struct {

  DWORD dwCount;

  DWORD dwVendorId;

  DWORD dwProductId;

  DWORD dwDeviceClass;

  DWORD dwDeviceSubClass;

  DWORD dwDeviceProtocol;

  DWORD dwInterfaceClass;

  ……

} USB_DRIVER_SETTINGS;

4, USB_ENDPOINT

这个结构包含了USB设备端口相关的信息。

typedef struct {

  UCHAR bLength;

  UCHAR bDescriptorType;//常另值

UCHAR bEndpointAddress;

/*--------------------------------------------------------*/

bEndpointAddress,定义了USB设备的端点地址,低4位为端点号,第四位到第六位作为保留位默认为0,第七位是数据流向为0的时候端点为输出端点,为1的时候端点为输入端点。

/*--------------------------------------------------------*/

  UCHAR bmAttributes;

/*--------------------------------------------------------*/

bmAttributes,用于描述端点的类型,0x00位控制,0x01位同步,0x02位块传送,0x03位中断传送

/*--------------------------------------------------------*/

 USHORT wMaxPacketSize;

  UCHAR bInterval;

} USB_ENDPOINT

USB_INTERFACE

typedef struct {

  UCHAR bLength;

  UCHAR bDescriptorType;// Constant value INTERFACE

  UCHAR bInterfaceNumber;

  UCHAR bNumEndpoints;

  UCHAR bInterfaceClass;

  UCHAR bInterfaceSubClass;

  UCHAR bInterfaceProtocol;

  UCHAR iInterface;

} USB_INTERFACE;

 

10-3是驱动程序中获取设备描述符、设置端口的流程图:

10.3.4  实现传输类型的函数接口

如前所述,Windows CE支持5种传输类型的函数接口,因此在读写设备时将调用相应的API如图10-4所示。具体为。

1IssueControlTransfer

在特定端口上初始化控制传输。

2IssueBulkTransfer

在特定端口上初始化块传输。

3IssueInterruptTransfer

在特定端口上初始化中断传输。

4IssueIsochTransfer

在特定端口上初始化同步传输。

10-3  驱动程序中获取设备描述符、设置端口的流程图

在本文的程序中,暂时不用到此传输。

5IssueVendorTransfer

在特定端口上初始化厂商自定义传输。

10.3.5  驱动程序的测试

驱动程序的测试可以通过注册表来查看

注册表:

n     对于注册表的查看可以使用Platform

选择“Builder->tool->remote register reader”进行查看。

n     选择设备Default Device,接着系统就会自动地去连接设备,

并且查询注册表

n     打开[HKEY_LOCAL_MACHINE/Drivers/USB/LoadClients]

查看其下是否有以下子键:

 

[Default/Default/ Default/Philip_USB_Driver]

 

查看该子键是否有以下属性值:

 

"DLL"="USBphlip.dll"

"Prefix"=dword:1

10-4  USB驱动程序和系统、设备交互的结构

n     打开[HKEY_LOCAL_MACHINE/ Drivers/USB/

ClientDrivers/MDV]

查看其下是否有以下属性值:

 

"DLL"="USBphlip.dll"

"Prefix"=dword:1

n     查看当前被激活的设备,可以使用Platform

选择“Builder->tool->Remote System Information

Platform Builder输出的调试信息:

 

Kernel debugger is waiting to connect with target.

  0 PID:0 TID:0 RTC - Status Reg B - 0x02

  0 PID:0 TID:0 X86Init done, OEMAddressTable = 8026a638.

  Welcome to the Windows® CE Shell. Type ? for help. 

  2410 PID:22fc81d2 TID:22fce802 MyDriver - DLL_PROCESS_ATTACH

  2410 PID:22fc81d2 TID:22fce802 MyDriver - MDV_Init -

  2410 PID:22fc81d2 TID:22fce802 Drivers/Active/02

  68640 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Open

  68640 PID:22fc81d2 TID:62e3995a hDeviceContext -

  68640 PID:22fc81d2 TID:62e3995a 4660

  68640 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Open

  75270 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Read

  75270 PID:22fc81d2 TID:62e3995a hOpenContext -

  75270 PID:22fc81d2 TID:62e3995a 22136

  75280 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Read

  77900 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Write

  77900 PID:22fc81d2 TID:62e3995a hOpenContext -

  77900 PID:22fc81d2 TID:62e3995a 22136

  77900 PID:22fc81d2 TID:62e3995a MyDriver –  MDV_Write

同样的,也可以使用Platform Builder自带的远程调试工具来查看驱动程序的加载情况。首先,可以利用Remote Register来查看目标平台被设备跟踪的设备的注册表情况,通过这个工具,可以直接找到驱动程序加载点位置:[HKEY_LOCAL_MACHINE/.Drivers/USB],并查看被跟踪的设备。如图10-5所示。

10-5  查看驱动程序的加载情况

也可以使用Remote File Viewer工具查看USB驱动MyCdriver.d11是否被下载到目标机,一般驱动程序Dll会被下载到目标机的/Windows目录下,如图10-6所示。

10-6  remoteFile Viewer查看dll是否被下载

也可以使用Remote System Information工具查看自定义的MDV1:设备的情况,如图10-7所示。

10-7  remote System Info查看目标平台活动的设备

10.4  Power Button驱动

Koala开发板中,提供了一个典型的Windows CE流式驱动,即处理电源按键事件的Power Button驱动PWRBUTTON

PWRBUTTON是一个完全符合Windows CE要求的标准流式驱动程序,代码简单,结构严谨,为初学者提供了一个易学易用的驱动范例。PWRBUTTONPWR作为驱动程序的设备头,提供了以下接口(其中大部分接口函数为空):

n     PWR_Init

n     PWR_DllEntry

n     PWR_PowerHandler

n     PWR_Open

n     PWR_Close

n     PWR_Read

n     PWR_Write

n     PWR_Seek

n     PWR_PowerUp

n     PWR_PowerDown

n     PWR_Deinit

n     PWR_IOControl

由于PWRBUTTON仅对应按键开关的关机信号,并不提供更为复杂的I/O操作或是数据读写服务,因此只实现了PWR_PowerHandlerPWR_Init以及PWR_DllEntry三个必要的函数,其他接口函数都是空函数,以满足流式驱动的架构要求。

根据Windows CE流式驱动要求,PWR_DLLEntry最先调用,完成驱动程序加载或卸载过程中必要的操作;PWR_Init则初始化按键响应所需要的一些变量,加载一些中断服务历程;PWR_PowerHandler则为可能要用到的状态切换提供一个操作接口。下面将详细介绍这三个函数。

1PWR_DLLEntry函数

作为标准的流驱动入口,

 

BOOL WINAPI PWR_DllEntry(

         HANDLE  hInstDll,

          DWORD   dwReason,

          LPVOID  lpvReserved

          )

提供三个入口参数,hInstDll句柄、dwReason DLL和调用阶段说明和保留的参数lpvReserved。由于PWRBUTTON作为一个built-in驱动一直工作,并不会被系统卸载,因此在PWR_DllEntry函数中,也不需要根据加载和卸载的不同状态,而实现相应的操作。PWR_DllEntry函数仅在Debug版本中打印相应入口信息,代码如下:

 

switch ( dwReason ) {

        case DLL_PROCESS_ATTACH:

        DEBUGREGISTER((HINSTANCE) hInstDll);

         DEBUGMSG(ZONE_INIT, (TEXT("PwrButton : DLL_PROCESS_ATTACH/r/n")));

        break;

 

        case DLL_PROCESS_DETACH:

        // 信号线程

        DEBUGMSG(ZONE_INIT, (TEXT("PwrButton : DLL_PROCESS_DETACH/r/n")));

        break;

}

 

实际上,PWR_DllEntry完全可以作为一个空函数,只返回一个True状态。

2PWR_Init函数

PWR_Init函数是PWRBUTTON驱动的两个重点之一,它完成全局变量的初始化并且安装中断服务程序。

1PWR_Init初始化全局事情gPwrButtonIntrEvent

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

2)与按键事件关联在一起

InterruptInitialize(SYSINTR_POWER, gPwrButtonIntrEvent, NULL, 0)

3PWR_Init加载键按中断服务程序PwrButtonIntrThread

 

CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PwrButtonIntrThread, NULL, 0, NULL)

 

这样,PWR_Init函数便完成了所有的初始化工作,而将按键处理过程交给了PwrButtonIn-
trThread
,而PwrButtonIntrThread则是PWRBUTTON驱动的另一个重点。

PwrButtonIntrThread是一个死循环函数,无限期等待按键事件gPwrButtonIntrEvent发生。

 

WaitForSingleObject(gPwrButtonIntrEvent, INFINITE);

 

一旦电源按键按下,硬件产生中断,Windows CE的中断服务线程IST立即响应,并产生SYSINTR_POWER事件,从而触发了中断服务线程PwrButtonIntrThread,来完成关机操作。

3PWR_PowerHandler函数

PWR_PowerHandlerWindows CE下的电源管理接口。

 

void WINAPI PWR_PowerHandler(BOOL bOff)

参数bOffTrue时,系统正在关机,bOffFalse的时候,系统正在开机,一般流式驱动根据硬件特点,决定是否需要单独设置设备的工作状态。在Power Button的实现中,并不需要单独设置状态,因此该函数基本上也是一个空函数

 

void WINAPI PWR_PowerHandler(BOOL bOff)

{

    if (bOff) {

 

    }

    else {

 

     }

 

}

10.5  电池驱动

10.5.1  功能介绍

电池驱动为操作系统其他模块提供了主电池和备用电池的电力信息。由设备管理器加载的电池驱动提供流式接口。

Battery驱动例程被转换为驱动IOCTL控制字传递给驱动程序,然后驱动(MDD)调用相应的Battery PDD接口。

10.5.2  数据结构

数据结构描述

 

_SYSTEM_POWER_STATUS_EX2

 

本结构体描述了系统电源状态如表10-4

10-4                             SYSTEM_POWER_STATUS_EX2结构体描述

   

   

   

ACLineStatus

BYTE

AC电源的状态,可以有以下值:
0
代表没有AC电源
1
代表使用AC供电
255
代表位置状态

BatteryFlag

BYTE

电源电力状态,可有以下值:
1
代表高电力
2
代表低电力
4
代表极端低电力
8
代表正在充电
128
代表没有电池
255
代表未知状态

续表

   

   

   

BatteryLifePercent

BYTE

剩余电池电量百分比,值可以从0100255代表未知状态

Reserved1

BYTE

保留,应当设置为0

BatteryLifeTime

DWORD

电池剩余的供电时间(单位:秒),如果未知则值为0xFFFFFFFF

Reserved2

BYTE

保留,应当设置为0

BackupBatteryFlag

BYTE

备用电池电量状态。可以为以下值:
BATTERY_FLAG_HIGH
BATTERY_FLAG_CRITICAL
BATTERY_FLAG_CHARGING
BATTERY_FLAG_NO_BATTERY
BATTERY_FLAG_UNKNOWN
BATTERY_FLAG_LOW

BackupBatteryLifePercent

BYTE

备用电池剩余供电电量百分比,可以为0100,或者为BATTERY_PERCENTAGE_UNKNOWN

Reserved3

BYTE

保留,应当设置为0

BackupBatteryLifeTime

DWORD

备用电池剩余供电时间

BatteryVoltage

DWORD

电池电压(单位:mV),值可以从065535

BatteryCurrent

DWORD

瞬间的负载(单位mA),充电时可以取值032767;放电时可以取值032768

BatteryAverageCurrent

DWORD

平均的设备负载(单位:mA),充电时可以取值032767;放电时可以取值032768

BatteryAverageInterval

DWORD

用于计算BatteryAverageCurrent的计时常数(单位:ms

BatteryTemperature

DWORD

电池温度

BackupBatteryVoltage

DWORD

后备电池电压

BatteryChemistry

BYTE

电池化学材料,可以有以下选择:
BATTERY_CHEMISTRY_ALKALINE
BATTERY_CHEMISTRY_NICD
BATTERY_CHEMISTRY_HIMH
BATTERY_CHEMISTRY_LION
BATTERY_CHEMISTRY_LIPOLY
BATTERY_CHEMISTRY_UNKNOWN

10.5.3  注册表设置

下列代码是pxa255的电池驱动所需要更改的注册表设置:

 

IF BSP_NOBATTERY !

 

; HIVE BOOT SECTION

 

[HKEY_LOCAL_MACHINE/System/Events]

     "SYSTEM/BatteryAPIsReady"="Battery Interface APIs"

; 这段注册表负责加载电池驱动,Iclass值必须跟Battery.h内定义的

; BATTERY_DRIVER_CLASS一样,这样系统才知道那个设备是电池驱动。这里我们使用

; DEVFLAGS_NAKEDENTRIES,这样就告诉设备管理器,加载驱动的时候忽略prefix键也就

; 是说,当驱动进行加载的时候设备管理器找到的是Init函数而不是BAT_Init这样就允许

; 在注册表内更改Prefix值而不需要再重新更改驱动了。

; for DLL entry points without the prefix.  For example, it will look for Init

if

; desired) without editing the driver code.

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Battery]

  "Prefix"="BAT"

  "Dll"="battdrvr.dll"

  "Flags"=dword:8                      ; DEVFLAGS_NAKEDENTRIES

  "Order"=dword:0

  "IClass"="{DD176277-CD34-4980-91EE-67DBEF3D8913}"

 

ENDIF BSP_NOBATTERY !

10.5.4  Battery Driver例程

以下例程可以在应用程序中调用。并非所有例程都需要被支持。

1BatteryDrvrGetLevels

通过本例程可以查询驱动程序对系统电力状态的监控功能,具体指GetSystemPowerStatusEx2结构中BatteryFlag域里支持的电源状态数量。如果BatteryFlag里只能支持BATTERY_FLAG_HIGHBATTERY_FLAG_LOW,那么就支持2level。返回值为双字,高字代表备用电池,低字代表主电池。

其代码如下:

 

LONG BatteryDrvrGetLevels( void)

{

  return MAKELONG (1 /*main battery levels*/1/*backup battery levels*/);

}

2BatteryDrvrSupportsChangeNotification

通过本例程可以查询驱动程序是否能检测出电池是否该被更换。

返回值为TRUE,则代表驱动支持通知机制,否则代表不支持。返回值为FALSE情况下,当调用BatteryPDDGetStatus时,参数pfBatteriesChanged应当设置为FALSE

 

BOOL BatteryDrvrSupportsChangeNotification(void )

{

  return FALSE;

}

3BatteryGetLifeTimeInfo

通过本例程可以查询用户更换电池以后的时间:

 

void BatteryGetLifeTimeInfo(

  LPSYSTEMTIME pstLastChange,

  DWORD* pcmsCpuUsage,

  DWORD* pcmsPreviousCpuUsage

);

 

其参数为:

 

pstLastChange

 

填写指向用户更换电池以来的时间数据。

 

pcmsCpuUsage

 

填写指向当前电池被激活后的时间数据。

 

pcmsPreviousCpuUsage

 

填写前一个电池被使用的时间数据。

10.5.5  PDD调用

1BatteryPDDInitialize

本调用完成硬件初始化工作。函数原型如下:

 

BOOL WINAPI BatteryPDDInitialize(

  LPCTSTR pszRegistryContext

);

 

参数pszRegistryContext指向驱动的注册表键。如果操作成功则返回TRUE。如果返回错误,则驱动将被设备管理器卸载。

PXA255的驱动中,只是设置了模块名称,初始化上下文,然后打印调试信息,并且返回TRUE

 

BOOL WINAPI BatteryPDDInitialize(LPCTSTR pszRegistryContext)

{

    BOOL fOk = TRUE;

    SETFNAME(_T("BatteryPDDInitialize"));

    UNREFERENCED_PARAMETER(pszRegistryContext);

    DEBUGMSG(ZONE_PDD, (_T("%s: returning %d/r/n"), pszFname, fOk));

    return fOk;

}

2BatteryPDDDeinitialize

完成硬件特定的删除卸载任务。在PXA255的驱动中,仅仅打印调试信息。

void WINAPI BatteryPDDDeinitialize(void)

{

    SETFNAME(_T("BatteryPDDDeinitialize"));

    DEBUGMSG(ZONE_PDD, (_T("%s: invoked/r/n"), pszFname));

}

3BatteryPDDResume

在系统从睡眠中恢复后,执行硬件电池处理操作。在PXA255的驱动中,仅仅打印调试信息。

 

void WINAPI BatteryPDDResume(void)

{

    SETFNAME(_T("BatteryPDDResume"));

    DEBUGMSG(ZONE_PDD, (_T("%s: invoked/r/n"), pszFname));

}

4BatteryPDDGetStatus

获取当前的电源状态。下面pxa255驱动中的代码,重新填写了PSYSTEM_POWER_STA-
TUS_EX2
结构的各个域。

 

BOOL WINAPI BatteryPDDGetStatus(

                        PSYSTEM_POWER_STATUS_EX2 pstatus,

                        PBOOL pfBatteriesChangedSinceLastCall

                        )

{

    pstatus->ACLineStatus               = AC_LINE_ONLINE;

    pstatus->BatteryFlag                = BATTERY_FLAG_NO_BATTERY;

    pstatus->BatteryLifePercent         = 0;

    pstatus->BatteryLifeTime            = BATTERY_LIFE_UNKNOWN;

    pstatus->BatteryFullLifeTime        = BATTERY_LIFE_UNKNOWN;

    pstatus->BackupBatteryFlag          = BATTERY_FLAG_HIGH;

    pstatus->BackupBatteryLifePercent   = 0;

    pstatus->BackupBatteryLifeTime      = BATTERY_LIFE_UNKNOWN;

    pstatus->BackupBatteryFullLifeTime  = BATTERY_LIFE_UNKNOWN;

    pstatus->BatteryChemistry           = BATTERY_CHEMISTRY_UNKNOWN;

    pstatus->BatteryVoltage             = 0;

    pstatus->BatteryCurrent             = 0;

    pstatus->BatteryAverageCurrent      = 0;

    pstatus->BatteryAverageInterval     = 0;

    pstatus->BatterymAHourConsumed      = 0;

    pstatus->BatteryTemperature         = 0;

    pstatus->BackupBatteryVoltage       = 0;

    *pfBatteriesChangedSinceLastCall = FALSE;

    return (TRUE);

}

 

在一般的产品中,应当探测AC电源和电池电源的剩余电力然后再进行域填充。对电力的探测可以使用IsACOnlineBattery_sampleADC等操作,具体可以参考PLATFORM BUILDER所自带的驱动demo

5BatteryPDDGetLevels

查询驱动程序对系统电力状态的监控功能,具体指GetSystemPowerStatusEx2结构中BatteryFlag域里支持的电源状态数量。如果BatteryFlag里只能支持BATTERY_FLAG_HIGHBATTERY_FLAG_LOW,那么就支持2level。返回值为双字,高字代表备用电池,低字代表主电池。

功能,返回值基本和BatteryDrvrGetLevels相同。

 

LONG BatteryPDDGetLevels( void)

{

    LONG lLevels = MAKELONG (3 /* main battery levels   */, 

                               3 /* backup battery levels */);

    SETFNAME(_T("BatteryPDDPowerHandler"));

    DEBUGMSG(ZONE_PDD, (_T("%s: returning %u (%d main levels, %d backup levels)/r/n"),pszFname, LOWORD(lLevels), HIWORD(lLevels)));

    return lLevels;

}

6BatteryPDDSupportsChangeNotification

功能和返回值与BatteryDrvrSupportsChangeNotification相同。在pxa255中将返回值设置为FALSE

 

BOOL BatteryPDDSupportsChangeNotification(void)

{

    BOOL fSupportsChange = FALSE;

    SETFNAME(_T("BatteryPDDPowerHandler"));

    DEBUGMSG(ZONE_PDD, (_T("%s: returning %d/r/n"), pszFname, fSupportsChange));

    return fSupportsChange;

}

10.6  音频驱动

Windows CE中的音频驱动接口分为三个种类。

1UAM驱动:Unified Audio Model统一音频模型。这是一种新的音频驱动模型,它将Wave audioMicrosoft® DirectSound® audio整合到了一起;

2Waveform驱动:作为UAM驱动的一种替代方案。沿用传统的MDDPDD架构;

3ACM音频压缩管理驱动:Audio Compression Manager Drivers,是一种流接口驱动程序,其中包含了不同的音频压缩管理驱动类型并且提供开发ACM驱动的必要信息。

在本书中将针对kaola开发板中使用的普通音频MDDPDD驱动进行分析。

作为UAM驱动或者流驱动的替代方案,开发者可以使用由微软公司提供的MDDWavemdd.lib。在这个库中,通过音频设备驱动服务提供接口(DDSI)实现了所有的流接口。如果要使用Wavemdd.lib库,则必须自己实现平台相关的PDD库,DDSI函数将使用到PDD库中的接口PDD库通常被命名为Wavepdd.lib。然后,这两个库可以被链接到音频驱动程序中,这种类型的音频驱动通常被命名为Wavedev.dll

音频硬件通常支持比文件操作更多的接口。例如文件不需要音量控制而声卡需要。文件操作中的DeviceIOControl操作,在音频操作中同样可以由WAV_IOControl函数来实现,程序可以通过WAV_IOControl向声卡发送不同的命令。

10-8显示了应用程序如何通过驱动和音频硬件进行数据交互。

10-8  驱动和音频
硬件进行数据交互

 

如图10-8所示,在应用程序进行音频操作之前,首先向内核产生一个调用。内核将这个调用传递给驱动程序的WAV_IOControl进行处理。内核传递这个调用的模块被称为Wave API管理器。然后驱动程序对硬件进行操作。和普通的流接口驱动程序一样,音频驱动也使用注册表来存储信息并且向系统注册自身。如果音频驱动在Platform.reg中没有被列出,则在注册表中添加如下键: HKEY_LOCAL_MACH-
INE/Drivers/Builtin/Audio
用于存储信息。在系统启动时,设备管理器将加载音频驱动,并且创建
HKEY_LOCAL_MAC-
HINE/Drivers/Active
键。

流接口驱动程序需要实现下列接口:

n     WAV_Close

n     WAV_Deinit

n     WAV_Init

n     WAV_IOControl

n     WAV_Open

n     WAV_PowerDown

n     WAV_PowerUp

n     WAV_Read

n     WAV_Seek

n     WAV_Write

DDSI的接口罗列如下:

n     PDD_AudioDeinitialize

n     PDD_AudioGetInterruptType

n     PDD_AudioInitialize

n     PDD_AudioMessage

n     PDD_AudioPowerHandler

n     PDD_WaveProc

10.6.1  主要数据结构

主要数据结构如表10-5所示。

10-5                                                     主要用到的数据结构

   

   

MMDRV_MESSAGE_PARAMS

这个结构体代表了要传输给WAV_IOControl的数据

WAVEOPENDESC

包含了waveform输入输出必要的信息

WAVEOUTEXTCAPS

包含额外的设备功能信息

10.6.2  主要接口函数

10-6wave驱动程序的流接口。

10-6                                                 Wave驱动程序的流接口

   

   

WAV_IOControl

I/O控制例程

WAV_Init

初始化WAV I/O设备

WAV_Deinit

初始化WAV I/O设备的反操作

WAV_Open

打开WAV I/O设备

WAV_Close

关闭WAV I/O设备

WAV_Read

WAV I/O设备的读操作

WAV_Write

WAV I/O设备的写操作

WAV_Seek

WAV I/O设备的寻址操作

WAV_PowerUp

通知WAV I/O设备,系统已经离开suspend状态

WAV_PowerDown

关闭(停止供电)WAV I/O

10.6.3  输入消息

wave驱动程序的输入消息如表10-7

10-7                                               Wave驱动程序的输入消息

   

   

WIDM_ADDBUFFER

请求waveform输入驱动在输入队列中添加一个buffer

WIDM_CLOSE

请求waveform输入驱动关闭一个指定的设备实例,这个设备实例由先前的WIDM_OPEN创建并且打开

WIDM_GETDEVCAPS

请求waveform输入驱动返回指定设备的功能

WIDM_GETNUMDEVS

请求waveform输入驱动返回其支持的设备数量

WIDM_GETPOS

请求输入流接口输入,驱动返回当前输入位置,位置值是相对于第一个采样的waveform样本

续表

   

   

WIDM_OPEN

请求waveform输入驱动打开一个设备对应的流接口

WIDM_PREPARE

请求waveform输入驱动为输入准备一个系统中唯一的数据缓冲

WIDM_RESET

请求waveform输入驱动停止录音并且将所有的缓冲数据返回给调用者

WIDM_START

请求waveform输入驱动开始录音

WIDM_STOP

请求waveform输入驱动停止录音

WIDM_UNPREPARE

WIDM_PREPARE的逆操作

10.6.4  输出消息

wave驱动程序的输出消息如表10-8

10-8                                               Wave驱动程序的输出消息

   

   

WODM_BREAKLOOP

请求waveform输出驱动停止由WODM_WRIT建立的输出循环

WODM_CLOSE

请求waveform输出驱动关闭由WODM_OPEN建立的流接口

WODM_GETDEVCAPS

请求waveform输出驱动返回特定设备的功能

WODM_GETNUMDEVS

请求waveform输出驱动返回其支持的设备实例数量

WODM_GETPLAYBACKRATE

请求waveform输出驱动返回特定设备当前的播放率放大值

WODM_GETPOS

请求返回当前数据流中相对于waveform起始的相对位置

WODM_GETVOLUME

请求waveform输出驱动返回指定设备的音量水平

WODM_OPEN

请求waveform输出驱动打开指定设备上的流接口

WODM_PAUSE

请求waveform输出驱动暂停waveform的播放

WODM_RESET

请求waveform输出驱动停止发送输出数据并且返回所有的输出缓冲到列表

WODM_RESTART

请求waveform输出驱动继续播放被暂停的waveform

WODM_SETPLAYBACKRATE

请求waveform输出驱动设置特定设备当前的播放率放大值

WODM_SETVOLUME

请求waveform输出驱动设置指定设备的音量水平

10.6.5  PDD函数

PDD函数如表10-9所示。

10-9                                                          PDD函数描述

   

   

PDD_AudioDeinitialize

关闭并且断开和声卡的连接

PDD_AudioGetInterruptType

判断音频中断的原因,并且返回当前设备的状态

PDD_AudioInitialize

初始化音频设备

续表

   

   

PDD_AudioMessage

将应用程序的消息发送到PDD

PDD_AudioPowerHandler

负责在POWER_UPPOWER_DOWN时管理硬件

PDD_WaveProc

将消息发送到驱动的PDD

10.6.6  WPDM消息

WPDM消息如表10-10所示。

10-10                                                     WPDM消息描述

   

   

WPDM_CLOSE

这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,驱动程序的waveXXXClose函数已经被调用

WPDM_CONTINUE

这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,从wave header指向的数据开始继续处理

WPDM_ENDOFDATA

这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,已经没有输出数据需要处理

WPDM_GETDEVCAPS

这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD查询硬件的功能

WPDM_GETVOLUME

这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD查询当前音频设备的音量水平

WPDM_OPEN

这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,将有特定格式的数据被发送到waveform设备

WPDM_PAUSE

这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,暂停播放

WPDM_RESTART

这个消息被传递到PDD_WaveProc函数来通知音频驱动继续播放被暂停的数据

WPDM_SETVOLUME

这个消息被传递到PDD_WaveProc函数来通知音频驱动调整音频设备的音量

WPDM_STANDBY

这个消息被传递到PDD_WaveProc函数来通知音频驱动将设备设置为standby状态

WPDM_START

这个消息被传递到PDD_WaveProc函数来通知音频驱动,从wave header指向的数据开始处理

WPDM_STOP

这个消息被传递到PDD_WaveProc函数来通知音频驱动,停止录音

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值