WINCE 键盘驱动 分析

本文详细探讨了Windows CE键盘驱动的工作原理,包括驱动的层次结构、MDD和PDD的区别、布局管理器处理扫描码的过程,以及KeybdDriverInitializeEx函数的详细分析。同时介绍了初始化硬件、中断处理、虚拟键转换为字符等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

键盘驱动有点繁杂,可以配合以下资料查阅:

1.     Platform Build自带的帮助文件.

2.     阅读源代码:

2.1  C:/WINCE420/Public/common/oak/drivers/keybd

2.2  C:/WINCE420/Platform/smdk2410/drivers/keybd

3.     网上的一些相关资料

初步查阅后可以知道,键盘驱动是分层(Layered Driver)的,上面的第2.1可以理解为MDD层,2.2是我们要实现的PDD层。

看下面这段(注:网上摘的):

一个简单的过程描述:输入系统(GWES)在启动时装在键盘驱动。首先,从HKEY_LOCAL_MACHINE/Hardware/DeviceMap/KEYBD/Drivername注册表项获得dll名,如果没有,则用默认的名字:Keybddr.dll。然后就是装载dll, 并且确定函数进入点是否存在。然后输入系统调用函数KeybdDriverInitialize来一次性初始化驱动。在这个函数里,驱动在本地保存了一份输入系统回调函数的副本以及初始化硬件和IST来处理中断。当一个中断信号来的时候,键盘驱动负责把硬件扫描码转换为虚拟键值。然后虚拟键值会再发送给输入系统。输入系统从队列中取出按键事件,然后返回到驱动程序的函数KeybdDriverVKeyToUnicode中。驱动程序根据分析特定的键事件和虚拟键的状态产生相应的字符。输入系统把虚拟键值和字符发送给合适的程序。
Layout Manager
WinCE下的驱动从层次这角度大概可以分2种:monolithic driver 和layered driver。其实2者的区别正如他们字面意义一样:monolithic driver单一驱动,不分层,没有MDD和PDD之分;layered driver具有层次架构,一般都有分为MDD和PDD。这里,鼠标键盘驱动就是layered driver。
这里有一个Layout Manager的概念


布局管理器处理扫描码的步骤:

PDD接受到一个扫描码; 扫描码被送到布局管理器; 布局管理器依据当前设备的布局和事件将其转换成虚拟键值; 布局管理器依据当前设备的布局和事件将重新映射; 布局管理器设置自动重复功能,所有的键盘都将共享相同的自动重复设置;

布局管理器调用函数keybd_event发送一个或多个事件。
PDD ----Platform Dependent Driver
PDD是下层的,负责从硬件拿到扫描码(上层的是MDD,负责将扫描码转换成字符).键盘PDD是键盘驱动中与设备相关的一部分代码。键盘PDD包括初始化和电源函数。可以使用公共的ist,也可以包括自己的。当GWES初始化键盘驱动时,它初始化每一个PDD. 每个键盘PDD有一个函数返回关于该PDD的描述和函数指针。当布局管理器初始化这个PDD,键盘驱动传递PDD一个唯一的标示符。有时,多种设备能使用同样的PDD,比如2个独立的PS/2 控制器。每个PDD和布局管理器在同样的DLL里。不可以在运行时加一个PDD(但可以不同的PDD之间切换!)

按照顺序看一下这个KeybdDriverInitializeEx()函数,它在我的电脑中位于C:/Wince420/public/common/oak/drivers/keybd/Laymgr/laymgr.cpp文件,这个函数功能主要是:

1.创建几个事件,注意下面两个:   
g_hevBeginEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
g_hevEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
然后创建两个线程:
    g_hEventThread = CreateThread(NULL, 0, KeybdEventThreadProc, NULL, 0, NULL);
注意这个线程函数KeybdEventThreadProc,功能是等待上面创建的g_hevBeginEvent事件,然后调用ScanCodeToVKey得到虚拟键值(这个函数是调用我们的PDD实现的DEVICE_LAYOUT,这是什么呢,可以简单理解为扫描码到虚拟键值的对应数组:ScanCodeToVKeyTable它位于C:/Wince420/platform/smdk2410/drivers/keybd/matrix_0409/s3c2410.cpp),ScanCodeToVKey调用后,再调用SendRemappedEvent(这个函数是再次map虚拟键值->CallDLRemapFn->MatrixUsRemapVKey(这个函数位置同ScanCodeToVKeyTable数组的文件),使得我们有机会再次映射一些功能键Fn,为什么这么做?往往Matrix键盘按键不会太多,大多功能复用!)然后执行keybd_event()API函数,KeybdEventThreadProc的功能分析完成。执行时机是g_hevBeginEvent事件被触发,记住了,下面会分析到。

2.KeybdDriverInitializeEx接下来初始化每个键盘驱动PDD,PDD数组位于c:/wince420/platform/smdk2410/drivers/keybd/pddlist/pddlist.cpp:
PFN_KEYBD_PDD_ENTRY g_rgpfnPddEntries[] = {
    PS2_NOP_Entry,
    Matrix_Entry,
    NULL
};

我们关心的是Matrix_Entry,这是个函数,位于C:/wince420/platform/smdk2410/drivers/keybd/kbdcommon/kbd.cpp
BOOL WINAPI Matrix_Entry(
    UINT uiPddId,
    PFN_KEYBD_EVENT pfnKeybdEvent,
    PKEYBD_PDD *ppKeybdPdd
    )
这个函数功能就是初始目标平台硬件了,具体可以查看源代码,请注意这里的形参pfnKeybdEvent,它很重要,以致于要保存到全局变量,供之后的PDD其它函数调用。下面还会提到,记住这个全局变量是: v_pfnKeybdEvent.由于KeybdDriverInitializeEx是以KeybdEventCallback函数为参数调用这个Matrix_Entry的,所以v_pfnKeybdEvent = KeybdEventCallback.

4.     之后KeybdDriverInitializeEx函数设置输入语言与Device Layout,这里可以理解为设置键盘布局,键盘布局不同,同一个Virtual Key代表的字符不同。比如我们常用的0409语言布键盘数字8的上档键是”*”,而0411日本数字8的上档键是”(”,下面会讲到如何更改键盘布局。

好了,下面我们换一个角度来看键盘驱动流程,从键盘中断开始:
在初始化的时候调用Matrix_Entry函数,在这个函数内部初始化硬件信息:
v_pp2k = new Ps2Keybd;
    if (v_pp2k->Initialize()) {
        v_pp2k ->IsrThreadStart();
    }
    else {
        ERRORMSG(1,(TEXT("Could not initialize ps2 keyboard./r/n")));
        delete v_pp2k;
        v_pp2k = NULL;
    }
      if (!KeybdDriverInitializeAddresses()) {
           goto leave;
      }
      v_pp2k->KeybdPowerOn();
….
这些函数在kbd.cpp与s3c2410kbd.cpp中,初始化硬件管脚、中断等。进入IsrThreadStart->KeybdIstLoop等待键盘中断。这里的KeybdIstLoop函数参数,是个结构体,注意最后两个域:
keybdIst.pfnGetKeybdEvent = KeybdPdd_GetEventEx2;
keybdIst.pfnKeybdEvent = v_pfnKeybdEvent;
KeybdIstLoop位于C:/wince420/public/common/oak/drivers/keybd/ist/keybdist.cpp中,就是等待键盘中断,然后依次调用上面的KeybdPdd_GetEventEx2与v_pfnKeybdEvent,由上面分析可知v_pfnKeybdEvent是指向KeybdEventCallback的函数指针.

KeybdPdd_GetEventEx2函数是从硬件获取键盘扫描码后转换成ScanCodeToVKeyTable数组的下标.所以,这里要做的工作即是将扫描码与虚拟键对应起来,注意这里ScanCodeToVKeyTable数组里的值是虚拟键,但不是字符,比如你可以将一个扫描码与’A’对应,但不能与’a’对应。

keybdEventCallbac()函数设置g_hevBeginEvent事件,结合上面的分析KeybdEventThreadProc()函数等待这个事件后,进行扫描码到虚拟键值的转换,最后执行keybd_event()API函数。这里就回到了上面分析的内容了。

我的分析到这里就暂时结束了,事实上还有流程没有跟完,键盘驱动尚未结束,后续的大致是根据键盘状态与键盘布局将虚拟键转成字符。如果你的键盘很简单,只有一些数字或常用字母,需要更改的不多,如ScanCodeToVKeyTable与获取扫描码的对应,如果你的键盘功能复用与0409也就是我们常用的键盘不同,那你可以增加一个自已的键盘布局或是更改0409,这些文件位于c:/wince420/public/common/oak/drivers/keybd/InputLangs/0409目录下。其实也就是些数组,不难。至于如何更改/增加键盘中断,在kernel目录下的cfw.c与armint.c中,如果连这个都不会,那你就不要写驱动了。


我写的一个是Matrix驱动,通过spi协议与键盘板上的cpld通讯,这些在KeybdPdd_GetEventEx2中获取扫描码的时候实现,看一下这个函数:
static UINT KeybdPdd_GetEventEx2(UINT uiPddId, UINT32 rguiScanCode[16], BOOL rgfKeyUp[16])
参数rguiScanCode数组内容即是扫描码对应ScanCodeToVKeyTable的数组下标,rgfKeyUp是键的状态(按下或松开),所以最好是在按键按下或松开时都能触发中断,否则若只有一个按下状态,系统会认为此键一下按下。参数是数组,所以可以是多个按键同时按下,函数返回值即是按下或松开按键的个数。这里也可以模拟按错,比如只要大写字母,可以在有按键按下时,在rguiScanCode数组中先填入VK_SHIFT对应的ScanCodeToVKeyTable数组下标值,再填入虚拟键值。当然,松开按键时,也要两个同时松开,最好按相返的顺序填值。

内容概要:本文详细介绍了文生视频大模型及AI人应用方案的设计与实现。文章首先阐述了文生视频大模型的技术基础,包括深度生成模型、自然语言处理(NLP)和计算机视觉(CV)的深度融合,以及相关技术的发展趋势。接着,文章深入分析了需求,包括用户需求、市场现状和技术需求,明确了高效性、个性化和成本控制等关键点。系统架构设计部分涵盖了数据层、模型层、服务层和应用层的分层架构,确保系统的可扩展性和高效性。在关键技术实现方面,文章详细描述了文本解析与理解、视频生成技术、AI人交互技术和实时处理与反馈机制。此外,还探讨了数据管理与安全、系统测试与验证、部署与维护等重要环节。最后,文章展示了文生视频大模型在教育、娱乐和商业领域的应用场景,并对其未来的技术改进方向和市场前景进行了展望。 适用人群:具备一定技术背景的研发人员、产品经理、数据科学家以及对AI视频生成技术感兴趣的从业者。 使用场景及目标:①帮助研发人员理解文生视频大模型的技术实现和应用场景;②指导产品经理在实际项目中应用文生视频大模型;③为数据科学家提供技术优化和模型改进的思路;④让从业者了解AI视频生成技术的市场潜力和发展趋势。 阅读建议:本文内容详尽,涉及多个技术细节和应用场景,建议读者结合自身的专业背景和技术需求,重点阅读与自己工作相关的章节,并结合实际项目进行实践和验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值