分类: LINUX
2008-05-06 20:25:46
随着嵌入式Linux 应用的不断发展,嵌入式处理器运算能力的不断增强, 越来越多的嵌入式设备开始采用较为复杂的GUI 系统,手持设备中的GUI 系统发展得非常迅速。传统的GUI 系统,如Microwindows 等,由于项目规模较小、功能较为薄弱, 缺乏第三方软件开发的支持等诸多原因,在比较高级的手持或移动终端设备( 如PDA、Smart-Phone、车载导航系统)中应用较少。
Qt/Embedded 是著名的Qt 库开发商 Trolltech 公司开发的面向嵌入式系统的Qt 版本, 开发人员多为KDE 项目的核心开发人员。许多基于Qt 的X Window 程序可以非常方便地移植到Qt/Embedded 上,与X11 版本的Qt 在最大程度上接口兼容, 延续了在X 上的强大功能, 在底层彻底摒弃了X lib,仅采用framebuffer 作为底层图形接口。Qt/Embedded 类库完全采用C++ 封装。丰富的控件资源和较好的可移植性是Qt/Embedded 最为优秀的一方面,使用X 下的开发工具Qt Designer 可以直接开发基于Qt/Embedded 的UI(用户操作接口)界面。越来越多的第三方软件公司也开始采用Qt/Embedded 开发嵌入式Linux 下的应用软件。其中非常著名的Qt Palmtop Environment(Qtopia) 早期是一个第三方的开源项目,并已经成功应用于多款高档PDA。Trolltech 公司针对Smart-Phone 中的应用需求,于2004 年5 月底发布了Qtopia 的Phone 版本。
1 Qt/Embedded的实现技术基础分析
横向来看,由于发布的版权问题,Qt/Embedded 采用两种方式进行发布:在GPL 协议下发布的free 版与专门针对商业应用的commercial 版本。二者除了发布方式外, 在源码上没有任何区别。纵向看来, 当前主流的版本为Qtopia 的2.x 系列与最新的3.x 系列。其中2.x 版本系列较多地应用于采用Qtopia 作为高档PDA 主界面的应用中;3.x 版本系列则应用于功能相对单一,但需要高级GUI 图形支持的场合,如Volvo 公司的远程公交信息系统。图1 为Qt/Embedded 的实现结构。
3.x 版本系列的Qt/Embedded 相对于2.x 版本系列增加了许多新的模块, 如SQL 数据库查询模块等。几乎所有2.x 版本中原有的类库,在3.x 版本中都得到极大程度的增强。这就极大地缩短了应用软件的开发时间,扩大了Qt/Embedded 的应用范围。
在代码设计上,Qt/Embedded 巧妙地利用了C++ 独有的机制,如继承、多态、模板等,具体实现非常灵活。但其底层代码由于追求与多种系统、多种硬件的兼容,代码补丁较多, 风格稍显混乱。
1.1 Qt/Embedded 的图形引擎实现基础
Qt/Embedded 的底层图形引擎基于framebuffer。framebuffer 是在Linux 内核架构版本2.2 以后推出的标准的显示设备驱动接口。采用mmap 系统调用,可以将framebuffer 的显示缓存映射为可连续访问的一段内存指针。由于目前比较高级的ARM 体系的嵌入式CPU 中大多集成了LCD 控制模块,LCD 控制模块一般采用双DMA控制器组成的专用DMA 通道。其中一个DMA 可以自动从一个数据结构队列中取出并装入新的参数, 直到整个队列中的DMA 操作都已完成为止。另外一个DMA 与画面缓冲区相关, 这部分由两个DMA 控制器交替执行, 并每次都自动按照预定的规则改变参数。虽然使用了双DMA , 但这两个DMA 控制器的交替使用对于CPU 来说是不可见的。CPU 所获得的只是由两个DMA 组成的一个“通道” 而已。
framebuffer 驱动程序的实现分为两个方面:一方面是对LCD 及其相关部件的初始化, 包括画面缓冲区的创建和对DMA 通道的设置;另外一方面是对画面缓冲区的读写,具体到代码为read、write、lseek 等系统调用接口。至于将画面缓冲区的内容输出到LCD 显示屏上,则由硬件自动完成。对于软件来说是透明的。当对于DMA通道和画面缓冲区设置完成后,DMA 开始正常工作, 并将缓冲区中的内容不断发送到LCD 上。这个过程是基于DMA 对于LCD 的不断刷新的。基于该特性,framebuffer驱动程序必须将画面缓冲区的存储空间( 物理空间) 重新映射到一个不加高速缓存和写缓存的虚拟地址区间中,这样才能够保证应用程序通过mmap 将该缓存映射到用户空间后,对于该画面缓存的写操作能够实时的体现在LCD 上。
在Qt/Embedded 中,QScreen 类为抽象出的底层显示设备基类,其中声明了对于显示设备的基本描述和操作方式,如打开、关闭、获得显示能力、创建GFX 操作对象等。另外一个重要的基类是QGfx 类。该类抽象出对于显示设备的具体操作接口( 图形设备环境) , 如选择画刷、画线、画矩形、alpha 操作等。以上两个基类是Qt/Embedded 图形引擎的底层抽象。其中所有具体函数基本都是虚函数,Qt/Embedded 对于具体的显示设备,如Linux的framebuffer、Qt Virtual Framebuffer做的抽象接口类全都由此继承并重载基类中的虚函数实现。图2 为Qt/Embedded 中底层图形引擎实现结构。
在图2 中,对于基本的framebuffer 设备,Qt/Embedded用QLinuxFbScreen 来处理。针对具体显示硬件(如Mach 卡、Voodoo 卡)的加速特性,Qt/Embedded 从QLinuxFbScreen 和图形设备环境模板类QGfxRaster
Qt/Embedded 在体系上为C/S 结构,任何一个Qt/Embedded 程序都可以作为系统中唯一的一个GUI Server存在。当应用程序首次以系统GUI Server 的方式加载时,将建立QWSServer 实体。此时调用QWSServer::openDisplay()函数创建窗体,在QWSServer::openDisplay()中对QWSDisplay::Data 中的init()加以调用;根据QGfxDriverFactory 实体中的定义(QLinuxFbScreen)设置关键的QScreen 指针qt_screen 并调用connect()打开显示设备(dev/fb0)。在QWSServer 中所有对于显示设备的调用都由qt_screen 发起。至此完成了Qt/Embedded 中QWSServer 的图形发生引擎的创建。当系统中建立好GUI Server 后,其它需要运行的Qt/Embedded 程序在加载后采用共享内存及有名管道的进程通信方式, 以同步访问模式获得对共享资源framebuffer 设备的访问权。
1.2 Qt/Embedded 的事件驱动基础
Qt/Embedded 中与用户输入事件相关的信号,是建立在对底层输入设备的接口调用之上的。Qt/Embedded中的输入设备, 分为鼠标类与键盘类。以3.x 版本系列为例, 其中鼠标类设备的抽象基类为QWSMouseHandler,从该类又重新派生出一些具体的鼠标类设备的实现类。该版本系列的Qt/Embedded 中,鼠标类设备的派生结构如图3 所示。
与图形发生引擎加载方式类似的, 在系统加载构造QWSServer 时,调用QWSServer::openMouse 与QWSServer::openKeyboard 函数。这两个函数分别调用QMouseDriverFactory::create()与QKbdDriverFactory::create() 函数。这时会根据Linux 系统的环境变量QWS_MOUSE_PROTO 与QWS_KEYBOARD 获得鼠标类设备和键盘类设备的设备类型和设备节点。打开相应设备并返回相应设备的基类句柄指针给系统, 系统通过将该基类指针强制转换为对应的具体子类设备指针,获得对具体鼠标类设备和键盘类设备的调用操作。
值得注意的是,虽然几乎鼠标类设备在功能上基本一致,但由于触摸屏和鼠标底层接口并不一样,会造成对上层接口的不一致。举例来讲,从鼠标驱动接口中几乎不会得到绝对位置信息,一般只会读到相对移动量。另外,鼠标的移动速度也需要考虑在内,而触摸屏接口则几乎是清一色的绝对位置信息和压力信息。针对此类差别,Qt/Embedded 将同一类设备的接口部分也给予区别和抽象,具体实现在QMouseDriverInterface 类中。键盘类设备也存在类似问题,同样引入了QKbdDriver Inteface 来解决。具体实现此处暂不赘述。
2 Qt/Embedded的移植与应用
针对Qt/Embedded 的实现特点,移植该嵌入式GUI系统一般分为以下几个步骤:
framebuffer 设备驱动程序提供出的接口是标准的, 除了注意endian 问题外,配置Qt/Embedded 时选择相应的色彩深度支持即可, 因此该部分的移植难点就在于framebuffer 驱动程序的实现。Qt/Embedded 部分的QWSServer 打开/dev/ 中的framebuffer 设备后读出相应的显示能力( 屏幕尺寸、显示色彩深度),模板QGfxRaster
2.1 在PXA255 平台上移植和应用
在笔者参与设计的某Smart-Phone 开发平台中,GUI 系统实现方案采用了Qt/Embedded 2.3.7 和Qtopia 1.7.0(基于Qt/Embedded 2.x系列的手持套件),硬件平台采用了基于Intel XScale PXA255 处理器的嵌入式开发系统。该开发系统采用640×480 分辨率的TFT LCD和PXA255 内部LCD 控制模块作为显示设备,ADS7846N作为外部电阻式触摸屏控制器;另外, 采用了五方向按键作为板载键盘。由于该系统采用了ISP1161 作为USB Host 控制器,较好地支持了USB 接口的键盘和鼠标,操作系统为ARM Linux 2.4.19。参考Linux 2.4.19 内核目录drivers/input部分,可以按照标准内核中input device接口设计实现触摸屏和键盘, 在实现了基于ISP1161 的EHCI 驱动程序后, 移植标准的USB 接口的人机界面设备驱动HID 和USB 键盘、鼠标的驱动程序后,可以获得对于该类设备的调用接口。此过程不属本文讨论范畴,此处暂不赘述。
Qt/Embedded 2.x 系列对于输入设备的底层接口与3.x 系列不同,触摸屏设备和键盘设备需要根据具体的驱动程序接口在Qt/Embedded 中设计实现对应的设备操作类。其中对应于鼠标类设备的实现位于src/kernel/qmouse_qws.cpp 中。由于触摸屏在实现原理上存在着A/D 量化误差的问题,因此所有的触摸屏接口实现类需要从特殊的QCalibratedMouseHandler 继承,并获得校正功能。其具体的实现接口如表1 所列。
Qt/Embedded 2.x中对于键盘响应的实现函数位于src/kernel/qkeyboard_qws.cpp 中。在qkeyboard_qws.h中,定义了键盘类设备接口的基类QWSKeyboardHandler,移植时需要根据键盘驱动程序从该类派生出实现类,实现键盘事件处理函数processKeyEvent(),并在QWSServer::newKeyboardHandler 函数中注册自己的键盘类设备即可。其中对于点击键的键码定义在Qt/Embedded 的命名空间—— src/kernel/qnamespace.h中。表2是具体的实现接口。(以USB 接口的101 键键盘为例)
2.2 在MC9328 平台上移植和应用
在某车载导航辅助系统的开发平台设计中,采用了Qt/Embedded 3.3.2 版本作为其GUI 系统的实现方案。硬件平台采用自行设计的以Motorola MC9328 MX1 为核心的开发系统。该系统采用CPU 内部LCD 控制器和240×320 分辨率的16bpp TFT LCD 作为显示设备,采用I2C总线扩展出16 按键以及MX1 集成的ASP模块和电阻触摸屏。操作系统为ARM Linux 2.4.18。
Qt/Embedded 3.x 版本系列中与底层硬件接口相关部分的源码位于src/embedded/ 目录中。该部分包含三类设备的接口:framebuffer、鼠标与键盘。参照该目录中相关设备的具体接口代码, 根据自身硬件平台增添接口即可。
由于系统LCD 的分辨率为240×320,物理尺寸较小,在实现基于该系统的framebuffer 驱动程序时并没有将其本身与Linux 字符控制台设备挂靠,因此framebuffer 并不具备TEXT 模式的工作方式。在移植Qt/Embedded 时,无需作framebuffer 设备的工作方式转换。正确配置色彩显示支持后,Qt/Embedded 能够在LCD 上显示出正确的图形。由于该平台的显示系统为纵向320 行,在设计时考虑到人对于非手持设备的视觉习惯为宽度大于高度的观察方式,为了符合这种习惯性的观察方式,在移植Qt/Embedded 时采用了Transformed 的旋转图形显示方式,在软件上实现了显示方向的旋转变换。
鼠标设备接口这一基类QWSMouseHandler 的实现位于src/embedded/qmouse_qws.cpp中。与2.x版本系列不同的是,3.x 中所有的Linux 触摸屏示例接口代码均实现在src/embedded/qmouselinuxtp_qws.cpp 中的QWSLinuxTPMouseHandler 类中。其中对于不同型号的触摸屏的接口实现代码,采用不同的宏定义和预编译的方式将它们分隔开。笔者还通过从QWSLinuxTPMouseHandler 中继承自身触摸屏接口类,替代原有的QWSLinuxTPMouseHandlerPrivate 类,而在QWSLinuxTPMouseHandler 生成自身触摸屏接口对象的方式, 较好地将移植部分的代码与原有比较混乱的代码分隔开来。3.x 触摸屏类接口如表3 所列。
3.x 中键盘接口基类位于src/embedded/qkbd_qws.cpp中,为QWSKeyboardHandler。实现I2C 总线扩展出的16键键盘接口类方式与触摸屏类似,此处不赘述。需要注意的是,Qt/Embedded提供了事件过滤器(key event filter)的接口,在键盘点击事件从QWSServer 截获并发送到相应的client之前会经过函数QWSServer::KeyboardFilter。在此函数中可以按照自身需求生成新的键盘点击事件,而后利用QWSServer::sendKeyEvent()发送新的点击事件到client 中。利用该方式可以将各种键盘点击无法输入的unicode 字符转换出来,从而可以在较少的按键键盘上实现多unicode 字符输入法。Qt/Embedded 3.x 键盘接口的移植与鼠标设备接口类似,此处不赘述。
3 总结
随着嵌入式处理器运算能力的不断提高, 对外设支持的不断丰富,嵌入式Linux 系统的应用也逐渐增多。Qt/Embedded 延续了Qt 在桌面系统的所有功能,丰富的API接口和基于组件的编程模型使得嵌入式Linux 系统中的应用程序开发更加便捷。由于Qt/Embedded 本身面向高端的手持设备和移动设备, 将成为未来嵌入式系统的主流GUI。
原文来自: