电池驱动介绍

 

电池驱动介绍

一.整体框架

         电池驱动代码量很小,可是麻雀虽小,五脏俱全。与其他的很多Driver一样,分为PDD+MDD层,双层之间通过PDD的如下导出接口相联系。    

Programming element

Description

BatteryDrvrGetLevels

This function returns the number of levels that the battery driver is capable of returning in the BatteryFlag and BackupBatteryFlag members of the SYSTEM_POWER_STATUS_EX2 structure.

BatteryDrvrSupportsChangeNotification

This function indicates whether the battery driver can report whether the batteries were changed.

BatteryGetLifeTimeInfo

This function retrieves the time the user changed the batteries, the amount of time they used the batteries, and the amount of time they used the batteries before replacing them.

BatteryNotifyOfTimeChange

This function adjusts times to account for the user changing the real time.

BatteryPDDDeinitialize

This function allows the battery PDD to perform hardware-specific cleanup.

BatteryPDDGetLevels

This function indicates how many battery levels are reported in the BatteryFlag and BackupBatteryFlag members of the SYSTEM_POWER_STATUS_EX2 structure filled in by BatteryPDDGetStatus.

BatteryPDDGetStatus

This function obtains the most current battery and power status available on the platform. It fills in the structures pointed to by its parameters.

BatteryPDDInitialize

This function allows the battery PDD to perform hardware-specific initialization.

BatteryPDDPowerHandler

This power callback performs hardware-specific processing for the battery driver.

BatteryPDDResume

This function performs hardware-specific battery processing in a thread context following system resume.

BatteryPDDSupportsChangeNotification

This function indicates whether the battery driver can report whether the batteries were changed.

PFN_BATTERY_PDD_IOCONTROL

This function signature is for the battery driver custom IOCTL handler. It implements the optional PDD IOCTL interface.

         微软提供了电池驱动的Sample Code,从目录/WINCE600/PUBLIC/COMMON/OAK/DRIVERS

/BATTDRVR下可以找到。

         注册表的配置如下:

IF BSP_NOBATTERY !

 

; HIVE BOOT SECTION

 

[HKEY_LOCAL_MACHINE/System/Events]

    "SYSTEM/BatteryAPIsReady"="Battery Interface APIs"

 

; END HIVE BOOT SECTION

 

; These registry entries load the battery driver.  The IClass value must match

; the BATTERY_DRIVER_CLASS definition in battery.h -- this is how the system

; knows which device is the battery driver.  Note that we are using

; DEVFLAGS_NAKEDENTRIES with this driver.  This tells the device manager

; to instantiate the device with the prefix named in the registry but to look

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

; instead of BAT_Init.  This allows the prefix to be changed in the registry (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 !

         PDD层基本上就是实现电池电量的采集和电池一些其它基本信息的获取,而MDD层主要是建立了一个线程BatteryThreadProc,该线程用来和控制面板中电源管理小工具进行通信。

二.值得一说的问题

         电池驱动实在是太简单了,没啥可介绍的。

         就说说常见的问题吧。

1.电池驱动的导出接口没有Prefix

         一般的流接口驱动导出接口都会有一个前缀,即注册表中配置的Prefix的值,为三个字节的大写字母。

         这是注册表中”Flags”的值配置成8DEVFLAGS_NAKEDENTRIES)引起的,在这种情况下Device Manager操作流接口驱动程序的时候,就可以不需要Prefix前导符号。

         详细的”Flags”的配置如下:

Flag

Value

Description

DEVFLAGS_NONE

0x00000000

No flags are defined.

DEVFLAGS_UNLOAD

0x00000001

Driver unloads after a call to the XXX_Init entry point or after the XXX_Init entry point returns. No error code is returned.

Bus Enumerator typically runs with this flag.

DEVFLAGS_LOADLIBRARY

0x00000002

Driver is loaded with LoadLibrary instead of LoadDriver.

DEVFLAGS_NOLOAD

0x00000004

Driver is not loaded.

DEVFLAGS_NAKEDENTRIES

0x00000008

Driver entry points do not have a XXX Prefix prepended.

DEVFLAGS_BOOTPHASE_1

0x00001000

Driver is loaded during boot phase one. By default, device drivers are loaded during boot phase two.

Boot phase zero is before the Device Manager loads.

Boot phase one is to find the registry.

Boot phase two is when initial device drivers load.

Boot phase three is after initial device drivers load.

DEVFLAGS_IRQ_EXCLUSIVE

0x00000100

Driver loads only when it has exclusive access to the IRQ.

DEVFLAGS_TRUSTEDCALLERONLY

0x00010000

Driver can only be opened by a trusted application.

         接下来,我们从Device Manager的代码中找到其原因。通常在应用程序或者一些Driver中尝试去动态加载流驱动的话,可以去调用API ActivateDeviceEx(),其实该函数最终调用的就是Device Manager中的函数I_ActivateDeviceEx()

         在文件/WINCE600/PRIVATE/WINCEOS/COREOS/DEVICE/DEVCORE/ devload.c中可以找到函数I_ActivateDeviceEx()的具体实现,该函数主要完成驱动程序对应的DLL的加载和初始化函数的调用,它首先会去调用函数CreateDevice()创建设备的一些结构体信息。在函数CreateDevice()中可以找到对”Flags”的一些判断处理。

         代码如下:

// This routine allocates a device driver structure in memory and initializes it.

// As part of this process, it loads the driver's DLL and obtains pointers to

// its entry points.  This routine returns a pointer to the new driver description

// structure, or NULL if there's an error.

static fsdev_t *

CreateDevice(

    LPCWSTR lpszPrefix,

    DWORD dwIndex,

    DWORD dwLegacyIndex,

    DWORD dwId,

    LPCWSTR lpszLib,

    DWORD dwFlags,

    LPCWSTR lpszBusPrefix,

    LPCWSTR lpszBusName,

    LPCWSTR lpszDeviceKey,

    HANDLE hParent

    )

{

    fsdev_t *lpdev;

    DWORD dwSize;

    DWORD dwStatus = ERROR_SUCCESS;

    WCHAR szDeviceName[MAXDEVICENAME];

    WCHAR szLegacyName[MAXDEVICENAME];

 

    DEBUGCHK(lpszPrefix != NULL);

    DEBUGCHK(lpszLib != NULL);

    DEBUGCHK(wcslen(lpszPrefix) <= 3);

    DEBUGCHK(dwLegacyIndex == dwIndex || (dwLegacyIndex == 0 && dwIndex == 10));

    DEBUGCHK(lpszBusName != NULL);

 

    // figure out how much memory to allocate

    dwSize = sizeof(*lpdev);

 

    // is the device named?

    if(lpszPrefix[0] == 0) {

        // unnamed device

        szDeviceName[0] = 0;

    } else {

        // named device, allocate room for its names

        StringCchPrintf(szDeviceName,MAXDEVICENAME,TEXT("%s%u"), lpszPrefix, dwIndex);

        if(dwLegacyIndex <= 9) {

            // allocate room for name and null

            StringCchPrintf(szLegacyName, MAXDEVICENAME, L"%s%u:", lpszPrefix, dwLegacyIndex);

            dwSize += (wcslen(szLegacyName) + 1) * sizeof(WCHAR);

        }

 

        // allocate room for name and null       

        dwSize += (wcslen(szDeviceName) + 1) * sizeof(WCHAR);

    }

 

    // If the bus driver didn't allocate a name the device may still support the

    // bus name interface -- use its device name (if present) just in case.

    if(lpszBusName[0] == 0 && szDeviceName[0] != 0) {

        lpszBusName = szDeviceName;

    }

   

    // allocate room for the bus name

    if(lpszBusName[0] != 0) {

        dwSize += (wcslen(lpszBusName) + 1) * sizeof(WCHAR);

    }

 

    // make room to store the device key as well

    if(lpszDeviceKey != NULL) {

        dwSize += (wcslen(lpszDeviceKey) + 1) * sizeof(WCHAR);

    }

   

    // allocate the structure

    if (!(lpdev = LocalAlloc(0, dwSize))) {

        DEBUGMSG(ZONE_WARNING, (_T("DEVICE!CreateDevice: couldn't allocate device structure/r/n")));

        dwStatus = ERROR_OUTOFMEMORY;

    } else {

        LPCWSTR pEffType = NULL;

        LPWSTR psz = (LPWSTR) (((LPBYTE) lpdev) + sizeof(*lpdev));

        memset(lpdev, 0, dwSize);

        lpdev->dwId = dwId;

        lpdev->wFlags = 0;           

        lpdev->dwFlags = dwFlags;

        if(PSLGetCallerTrust() == OEM_CERTIFY_TRUST) {

            lpdev->wFlags |= DF_TRUSTED_LOADER;

            lpdev->hParent = hParent;

        }

        if (lpszPrefix[0] != 0) {

            if(dwLegacyIndex <= 9) {

                lpdev->pszLegacyName = psz;

                wcscpy(lpdev->pszLegacyName, szLegacyName);

                psz += wcslen(lpdev->pszLegacyName) + 1;

            }

            lpdev->pszDeviceName = psz;

            wcscpy(lpdev->pszDeviceName, szDeviceName);

            psz += wcslen(lpdev->pszDeviceName) + 1;

        }

        if(lpszBusName[0] != 0) {

            lpdev->pszBusName = psz;

            wcscpy(lpdev->pszBusName, lpszBusName);

            psz += wcslen(lpszBusName) + 1;

        }

        if(lpszDeviceKey != NULL) {

            lpdev->pszDeviceKey= psz;

            wcscpy(lpdev->pszDeviceKey, lpszDeviceKey);

            psz += wcslen(lpszDeviceKey) + 1;

        }

        if((dwFlags & DEVFLAGS_NAKEDENTRIES) == 0) {

            if(lpszPrefix[0] != 0) {

                DEBUGCHK(lpszBusPrefix[0] == 0 || wcsicmp(lpszBusPrefix, lpszPrefix) == 0);

                pEffType = lpszPrefix;      // use standard prefix decoration

            } else if(lpszBusPrefix[0] != 0 && lpdev->pszBusName != NULL) {

                pEffType = lpszBusPrefix;   // no standard prefix, use bus prefix decoration

            } else {

                if(lpdev->pszDeviceName != NULL) {

                    // device is expected to have a device or bus name, but we don't know

                    // how to look for its entry points

                    DEBUGMSG(ZONE_ACTIVE || ZONE_ERROR,

                        (_T("DEVICE!CreateDevice: no entry point information for '%s' can't load '%s'/r/n"),

                        lpszLib, lpdev->pszDeviceName));

                    dwStatus = ERROR_INVALID_FUNCTION;

                }

            }

        }

        // 这里会去判断Flags的值,决定是否使用Prefix

        if ((dwFlags & DEVFLAGS_LOAD_AS_USERPROC)) {

            lpdev->hLib = NULL;

            lpdev->dwData  = Reflector_Create(lpszDeviceKey, pEffType, lpszLib, dwFlags );

            if (lpdev->dwData != 0 ) {

                lpdev->fnInit = NULL;

                lpdev->fnInitEx = (pInitExFn)Reflector_InitEx;

                lpdev->fnPreDeinit = (pDeinitFn)Reflector_PreDeinit;

                lpdev->fnDeinit = (pDeinitFn)Reflector_Deinit;

                lpdev->fnOpen = (pOpenFn)Reflector_Open;

                lpdev->fnPreClose = (pCloseFn)Reflector_PreClose;

                lpdev->fnClose = (pCloseFn)Reflector_Close;

                lpdev->fnRead = (pReadFn)Reflector_Read;

                lpdev->fnWrite = (pWriteFn)Reflector_Write;

                lpdev->fnSeek = (pSeekFn)Reflector_SeekFn;

                lpdev->fnControl = (pControlFn)Reflector_Control;

                lpdev->fnPowerup = (pPowerupFn)Reflector_Powerup;

                lpdev->fnPowerdn = (pPowerupFn)Reflector_Powerdn;

            }

            else {

                DEBUGMSG(ZONE_WARNING, (_T("DEVICE!CreateDevice: couldn't load(%s) to user mode!!/r/n"),lpszLib));

                dwStatus = ERROR_FILE_NOT_FOUND;

            }

        }

        else {

            DEBUGMSG(ZONE_ACTIVE, (_T("DEVICE!CreateDevice: loading driver DLL '%s'/r/n"), lpszLib));

            // 这里去判断的如何去加载Stream Driver,其实这里最终会影响到驱动DLL需要的内存的Page-inpage-out,这些不是这里要说的重点,暂且不说

            lpdev->hLib =

                (dwFlags & DEVFLAGS_LOADLIBRARY) ? LoadLibrary(lpszLib) : LoadDriver(lpszLib);

            if (!lpdev->hLib) {

                DEBUGMSG(ZONE_WARNING, (_T("DEVICE!CreateDevice: couldn't load '%s' -- error %d/r/n"),

                    lpszLib, GetLastError()));

                dwStatus = ERROR_FILE_NOT_FOUND;

            } else {

                // 最终如果配置为0x08,程序将会走到这里

                lpdev->fnInitEx = NULL;

                lpdev->fnInit = (pInitFn)GetDMProcAddr(pEffType,L"Init",lpdev->hLib);

                lpdev->fnPreDeinit = (pDeinitFn)GetDMProcAddr(pEffType,L"PreDeinit",lpdev->hLib);

                lpdev->fnDeinit = (pDeinitFn)GetDMProcAddr(pEffType,L"Deinit",lpdev->hLib);

                lpdev->fnOpen = (pOpenFn)GetDMProcAddr(pEffType,L"Open",lpdev->hLib);

                lpdev->fnPreClose = (pCloseFn)GetDMProcAddr(pEffType,L"PreClose",lpdev->hLib);

                lpdev->fnClose = (pCloseFn)GetDMProcAddr(pEffType,L"Close",lpdev->hLib);

                lpdev->fnRead = (pReadFn)GetDMProcAddr(pEffType,L"Read",lpdev->hLib);

                lpdev->fnWrite = (pWriteFn)GetDMProcAddr(pEffType,L"Write",lpdev->hLib);

                lpdev->fnSeek = (pSeekFn)GetDMProcAddr(pEffType,L"Seek",lpdev->hLib);

                lpdev->fnControl = (pControlFn)GetDMProcAddr(pEffType,L"IOControl",lpdev->hLib);

                lpdev->fnPowerup = (pPowerupFn)GetDMProcAddr(pEffType,L"PowerUp",lpdev->hLib);

                lpdev->fnPowerdn = (pPowerdnFn)GetDMProcAddr(pEffType,L"PowerDown",lpdev->hLib);

 

                // Make sure that the driver has an init and deinit routine.  If it is named,

                // it must have open and close, plus at least one of the I/O routines (read, write

                // ioctl, and/or seek).  If a named driver has a pre-close routine, it must also

                // have a pre-deinit routine.

                if (!(lpdev->fnInit && lpdev->fnDeinit) ||

                    lpdev->pszDeviceName != NULL && (!lpdev->fnOpen ||

                                 !lpdev->fnClose ||

                                 (!lpdev->fnRead && !lpdev->fnWrite &&

                                  !lpdev->fnSeek && !lpdev->fnControl) ||

                                 (lpdev->fnPreClose && !lpdev->fnPreDeinit))) {

                    DEBUGMSG(ZONE_WARNING, (_T("DEVICE!CreateDevice: illegal entry point combination in driver DLL '%s'/r/n"),

                        lpszLib));

                    dwStatus = ERROR_INVALID_FUNCTION;

                }

 

                if (!lpdev->fnOpen) lpdev->fnOpen = (pOpenFn) DevFileNotSupportedBool;

                if (!lpdev->fnClose) lpdev->fnClose = (pCloseFn) DevFileNotSupportedBool;

                if (!lpdev->fnControl) lpdev->fnControl = (pControlFn) DevFileNotSupportedBool;

                if (!lpdev->fnRead) lpdev->fnRead = (pReadFn) DevFileNotSupportedDword;

                if (!lpdev->fnWrite) lpdev->fnWrite = (pWriteFn) DevFileNotSupportedDword;

                if (!lpdev->fnSeek) lpdev->fnSeek = (pSeekFn) DevFileNotSupportedDword;

            }

        }

    }

 

    // did everything go ok?

    if(dwStatus != ERROR_SUCCESS) {

        if(lpdev != NULL) {

            DeleteDevice(lpdev);

            lpdev = NULL;

        }

        SetLastError(dwStatus);

    }

 

    DEBUGMSG(ZONE_ACTIVE || (dwStatus != ERROR_SUCCESS && ZONE_WARNING),

        (_T("CreateDevice: creation of type '%s', index %d, lib '%s' returning 0x%08x, error code %d/r/n"),

        lpszPrefix[0] != 0 ? lpszPrefix : _T("<unnamed>"), dwIndex, lpszLib, lpdev, dwStatus));

 

    return lpdev;

}

//

// This routine is security check for dwInfo passed in by either RegistryDevice or ActiveDevice

DWORD CheckLauchDeviceParam(DWORD dwInfo)

{

    if (CeGetCallerTrust() != OEM_CERTIFY_TRUST) { // Untrusted caller will do following.

        LPCTSTR lpActivePath = (LPCTSTR) dwInfo; // We assume it is Registry Path.

        if (lpActivePath) {

            HKEY hActiveKey;

            if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, lpActivePath, 0, 0, &hActiveKey) == ERROR_SUCCESS ) { // It is registry.

                // We need check Registry is in secure location.

                CE_REGISTRY_INFO regInfo;

                DWORD dwRet = ERROR_INVALID_PARAMETER;

                memset(&regInfo,0,sizeof(regInfo));

                regInfo.cbSize = sizeof(CE_REGISTRY_INFO);

                if (CeFsIoControl(NULL, FSCTL_GET_REGISTRY_INFO, &hActiveKey, sizeof(HKEY), &regInfo, sizeof(CE_REGISTRY_INFO), NULL, NULL)) { // Succeed

                    if (regInfo.dwFlags & CE_REG_INFO_FLAG_TRUST_PROTECTED) {

                        dwRet = ERROR_SUCCESS;

                    }

                }

                RegCloseKey( hActiveKey );

                return dwRet;

            }

        }

    }

    return ERROR_SUCCESS; 

}

         另外,注册表项”Flags”的值是函数I_ActivateDeviceEx()调用RegReadActivationValues()来获取的。篇幅有限,这里不再对函数RegReadActivationValues()进行解释。

         其实注册表项”Flags”的值很有用处,能够控制加载过程中的很多行为。

2.电池电量更新的问题

         CE5.0中,默认情况下系统会每0.5s去获取一次主电池和辅助电池的电量,其默认值得定义在文件battdrvr.c中,如下:

#define DEF_BATTERYPOLLTIMEOUT          500         // in milliseconds

 

         6.0中,可能微软也觉得0.5s获取一次电池电量有点变态,而且也没有必要,所以改为5s去获取一次电量,同样通过宏定义进行定义。

// 5 Seconds for average delay of 2.5 seconds

#define DEF_BATTERYPOLLTIMEOUT          (5*1000)         // in milliseconds

         当然,你可以通过注册表项"PollInterval"去配置这个值,如下:

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Battery]

"Prefix"="BAT"

"Dll"="battdrvr.dll"

"Flags"=dword:8                      ; DEVFLAGS_NAKEDENTRIES

"Order"=dword:45

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

"PollInterval"=dword:1388

         其实,就算是用户去通过控制面板去查看电池电量,也没有必要把这个时间设置的很短。再者,一般的手持设备上都会去调用API  GetSystemPowerStatusEx()去获取电池电量百分比。

         毫无疑问,将更新电池电量的时间配置的越大越好,可是有些时候,我们可能希望在用户插入AC进行充电的时候,控制面板中的电池电量马上有反应,而不是在等待5s钟之后,这样显得产品更加人性化一点。这种情况下,可以通过使AC的插入产生一个中断,然后把这个中断和电池驱动MDD层中的线程BatteryThreadProc Event联系起来,这样就可以满足插入AC,控制面板中的电池电量马上就有反应的需求。

3.电池电量显示不准确

         最简单的电池电量的获取是通过当前AD值和满电量AD值相比得到的。举个例子,假设满电量的时候AD值为100,当前的值为50,则当前的电量就是50/100*100% = 50%,并通过PDD层函数BatteryPDDGetStatus()返回给上层调用者。

         实际使用中发现,电池电量为100%的时候很耐用,可是从电池电量80%左右的时候开始,电池电量很快就被消耗完毕。

         为什么?

         根本原因是,电池电量和电池电压并不成正比,而AD转换的结果恰巧反映的就是电池电压。

         了解了电池的充放电原理后发现,电池电量和电池电压的关系是一条抛物线。接下来就有两种改进的方法获取电池电量:

         第一种方法:得到电池电量和电池电压的准确关系。

         事先去测量电池电量和电池电压的关系,得到这条抛物线的方程,从而得到准确的电池电量和电池电压的关系。通常不是计算这条抛物线,而是将这条抛物线简单的分隔成几个段,近似的认为每一段是一条斜率恒定的直线,然后通过测量结果确定这几条直线的斜率以及合时的分隔点。

         做过衡器或者其它一些仪器仪表的朋友,对这种方法肯定不陌生,呵呵。

         这种做法的最大缺点是,每更换一种电池都需要重新的去测时充放电曲线,比较麻烦。优点是不用增加硬件成本。

         第二种方法:利用电源管理芯片。

         这种方法没用过,只是听有人在论坛上讨论过。利用一颗电源管理芯片去获取电池电量,其原理是通过AD转换值经过一套复杂的算法得到电量,搞不清楚,据说很好用。但是Cost呵呵

        

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/243293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将1bpp的bmp图像存储为1bpp或者2bpp的tiff格式

// 将1bpp的位图转换为 1bit/2bit tiff /** 参数&#xff1a;BYTE *src 二值图像的像素数据&#xff0c;不包含头部信息&#xff0c; 1bpp, int src_width 原图的宽度, in pixles,int src_height 原图的高度, in pixlesint bpp 转换tiff指定的bpp */ static BYTE *BW2Tif(…

关于联合体的内容

关于联合体的内容 联合体只储存一个值 联合体只存储一个元素&#xff0c;但这个元素可以有多种表示方法&#xff0c;每种表示可以是不同的数据类型。 联合体的声明语法类似于结构体&#xff0c;联合体的成员的引用也跟结构体一样。 union{int i;int unsigned u; }data; ... d…

Point-BERT:一种基于Transformer架构的点云深度网络

目录 1. 前言 2. Point Tokenization 3. Transformer Backbone 4. Masked Point Modeling 5. Experiments Reference 1. 前言 从PointNet [1] 开始&#xff0c;点云深度网络逐渐成为解决点云特征提取与语义分析的主要研究方向。尤其在OpenAI的GPT模型获得了突破性成果后&#…

GNS3 VoIP Lab (Cisco 3725 and CME 4.3)

Here is a simple VoIP Lab in GNS3 environment. It is only used for my lab test and recorded here for future reference. 1. Topology: GNS3 Topology:Logic Topology:xp(192.168.2.60)——–C3725 Router(192.168.2.10) 2. Enviroment: ESXi 5.5 (or Vmware Workstation…

谁知道这个代码片段干嘛的

int value 0xAAAA;for (int i0; i<8; i){int tmp value & 0x3; // 取出第两个比特位置if (tmp 0x0){//}else if (tmp 0x1){//}else if (tmp 0x2){//}else if(tmp 0x3){TRACE0("???");}TRACE1("tmp0x%x\n\n", tmp);value >> 2;}

关于数组的内容

关于数组的内容 Verilog数组声明的基本语法 <data_type><vector_size><array_name><array_dimension> 例如&#xff1a; reg[15:0] RAM [0:4095];//储存器数组SystemVerilog允许任何数据类型的非压缩数组 SystemVerilog将非压缩数组的声明进行了扩展…

Touch Driver介绍

Touch Driver介绍 一&#xff0e;相关知识介绍 1&#xff0e;Touch Driver的加载过程 GWES到[HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/TOUCH]的“Driver name”获取Driver DLL的名字&#xff0c;如果没有找到该键值&#xff0c;则使用默认名字Touch.dll。 Touch Driver的加…

BreadCrumb控件

BreadCrumb控件&#xff0c;如上图所示&#xff0c;即面包屑导航控件&#xff0c;类似于TreeCtrl&#xff0c;但不是一次显示所有的Item&#xff0c;VC 2010可以编译通过&#xff0c;稍微修改一下其他的也可以编译&#xff0c;源代码下载&#xff1a; http://download.csdn.net…

foreach数组循环结构体

foreach数组循环结构体 foreach循环遍历任何维数的数组 Systemverilog增加了foreach循环&#xff0c;它可用来对一维或多维数组中的元素进行迭代&#xff0c;而不必指定数组每个维度的宽度。foreach循环的自变量是数组名&#xff0c;它后面是方括号内用逗号隔开的循环变量列表…

Android简介

最近Android很火&#xff0c;小弟也想了解一下它的结构。跟CE或者Mobile比起来&#xff0c;它的结构是有点凌乱&#xff0c;也难怪&#xff0c;毕竟是基于别人的内核在上层开发了一些应用 ---------------------------------------------------------------------------------…

说不尽的刘恒

认识刘恒快三十年了&#xff0c;作为曾经的同事和他小说的责任编辑&#xff0c;我只写过他一篇文章&#xff0c;还是在二十多年前。有时候特别熟悉的人反而不知道从何写起&#xff0c;因为一想起往事&#xff0c;各种记忆像开闸的水一样涌满眼前&#xff0c;让人很难落笔。1985…

印前处理的“发动机”——RIP

对于许多印刷厂来说&#xff0c;数字印前技术仍是个谜&#xff0c;尤其是光栅处理器RIP。除了RIP之处&#xff0c;我们也总听说打印机的内置控制单元。其实&#xff0c;RIP与内置控制单元在本质上是一样的&#xff0c;但也有所不同。听说起来好像有些玄乎&#xff0c;下面我们来…

组合逻辑过程块

组合逻辑过程块 always_comb代表组合逻辑 always_comb过程块表示建立组合逻辑模型 always_comb if(!mode)y a b; elsey a - b;always_comb能推断出其敏感表 与通用always过程块不同&#xff0c;always_comb块的后面不需要指明敏感表。软件工具已经知道设计的意图是建立一个…

外行看Flash的存储原理

突然在网上看到别人两年前写的一篇关于nor和nand的好文章&#xff0c;做为csdn的合法公民&#xff0c;有必要转 一、存储数据的原理 两种闪存都是用三端器件作为存储单元&#xff0c;分别为源极、漏极和栅极&#xff0c;与场效应管的工作原理相同&#xff0c;主要是利用电场的…

数码印刷

数码印刷 目前&#xff0c;RIP已经变成了印前生产的核心问题。它影响到从色彩和文件管理到印刷的整个生产过程的方方面面。而且&#xff0c;像陷印和拼大版这些以前需要单独的应用程序处理的功能&#xff0c;现在也被加到了RIP中。    新的RIP产品和销售商有很多。象Agfa,Ha…

锁存逻辑过程块

锁存逻辑过程块 always_latch描述锁存逻辑 always_latch过程块表示过程块描述的是基于锁存器的逻辑。和always_comb一样&#xff0c;always_latch的敏感表示推断出来的。 always_latchif(enable) q < d;always_latch与always_comb语义相同 always_latch过程的语义规则与al…

一种User Mode下访问物理内存及Kernel Space的简单实现

一种User Mode下访问物理内存及Kernel Space的实现 一&#xff0e;背景 WinCE发展到6.0之后&#xff0c;内存结构和管理方法进行了完善。对应用程序影响比较大的有Virtual Memory Layout的变化&#xff0c;如每个进程的虚拟内存空间扩展为2GB。对驱动程序影响比较大的有Pointe…

什么是ICC曲线

什么是ICC曲线 很多人都问及ICC曲线&#xff0c;但又都很不了解&#xff0c;甚至有些人认为有了ICC曲线就能打印出很漂亮的图片&#xff0c;其实不然&#xff0c;下面就我所掌握的知识给大家做个通俗的理解.国际色彩协会The international Color Consortium简称ICC&#xff0c;…

时序逻辑过程块

时序逻辑过程块 always_ff描述时序逻辑 always_ff专用过程块表示设计的意图是描述可综合的时序逻辑。 always_ff&#xff08;posedge clock,negedge resetN&#xff09;if(!resetN) q <0;else q < d;always_ff过程块的敏感表必须明确列出。这样就可以根据敏感表的内容&…

快速计算整数的二进制表示法中1的个数

快速计算整数的二进制表示法中1的个数 题目&#xff1a;给定一个无符号32位整数x&#xff0c;求x的二进制表示法中含1的个数&#xff1f; 第一种算法&#xff1a; int OneCount(unsigned int x){ for(int count0; x>0; count) x&x-1;//把最后面的1变0 return …