龚松显,张爱民
(总参通信训练基地 教研部科研所,河北 宣化 075100)
基于ARM 开发板的GPIO 端口功能扩展具有高度的灵活性,降低了系统的复杂程度和成本,是目前在嵌入式设备上常用的开发技术之一.WinCE 下GPIO 端口驱动大多采用流接口驱动的方式进行开发,其过程涉及驱动动态链接库的生成、Platform Builder 开发工具中源代码配置和WinCE 内核镜像配置,整个过程较为繁琐复杂.在实际项目的开发中,很多WinCE 程序仅需对GPIO 端口进行简单的控制,如若仍采用流接口的驱动开发方式,将造成整个项目开发效率的降低,项目的工程框架也将变得臃肿冗余.文章通过分析WinCE 操作系统的地址映射机制,借鉴单片机端口驱动的开发思路,实现了在WinCE 程序中对GPIO 端口的直接驱动,简化了此情况下的开发流程,使开发者能专注于WinCE 程序本身的设计,提高了开发效率.
1.1 设计步骤简述 对于常用的基于流驱动的GPIO 端口驱动设计方法及原理,各种文献及资料中已经介绍很多[1],这里不再赘述,其设计步骤要点简述如下:
(1)按照流驱动的设计方法,创建相关的.cpp 文件、.h 文件和.def 文件以供生成驱动程序的动态链接库.
(2)创建相关提供注册信息的.reg 文件以供在WinCE 注册表中注册GPIO 驱动程序.
(3)在Platform Builder 的相关目录下,配置Dirs 文件、Makefile 文件、Sources 文件和Platform.bib 文件中的相关参数,Platform Builder 在编译时按照配置的参数编译驱动程序并将其整合到内核镜像中.
(4)使用Platform Builder 编译环境编译驱动程序和WinCE 内核,编译成功之后即得到按照硬件平台定制的含有驱动程序动态链接库的内核镜像文件,当WinCE 启动时,该驱动会被自动加载.
1.2 存在的问题 基于以上描述,如果在WinCE 程序中仅需对端口进行简单控制,流接口驱动程序设计方法中存在以下两个问题:
(1)在实现流驱动程序时,需要按照流驱动的设计方法来实现相关的流接口函数并生成动态链接库.在该过程中,即使是简单的端口控制功能也需要遵循流驱动固定的实现框架,开发步骤不灵活.
(2)驱动程序和WinCE 内核的整合以及相关配置文件的编写全部依托Platform Builder 编译环境,开发者需要较为熟悉Platform Builder 编译环境的使用,但在实际工程项目的开发中,大多数开发者对其使用基本上处于简单的模仿层次,使用入门门槛较高.
2.1 设计方法原理 在单片机程序设计中,可以通过直接访问端口对应的寄存器来实现对端口的驱动.在ARM 程序设计中,在无操作系统存在的情况下,例如在ADS 编译环境下,依然可以采用类似的方法,通过对端口的相关寄存器的访问来驱动GPIO 端口[2].如果ARM 芯片中已移植了WinCE 操作系统,由于WinCE 操作系统的内存保护机制,WinCE 应用程序是不能直接访问GPIO 端口的物理内存地址的,故在WinCE 程序中对端口的物理地址进行访问就会导致程序异常.在另一方面,WinCE 系统提供了如图1 所示的地址映射机制,可以将ARM 芯片的端口物理地址映射为程序进程空间的某一虚拟内存地址,此时由于端口的物理地址和程序的进程空间地址是一一映射的关系,通过对虚拟地址的操作,实质上就能够完成对GPIO 端口的控制,达到驱动物理端口的目的和效果,程序开发思路实质与单片机保持一致.
图1 WinCE 系统中物理地址和进程地址映射示意图
2.2 方法实现要点步骤 在WinCE 中,主要通过VirtualAlloc 和VirtualCopy 两个函数来实现端口物理地址到程序进程空间虚拟地址的映射[3]:VirtualAlloc 函数负责申请一块虚拟内存空间,VirtualCopy 函数将一段物理内存映射到已申请的虚拟空间中.成功调用以上两个函数后,只需要在WinCE 程序中适当的地方对映射后的虚拟地址进行访问即可实现对GPIO 端口的直接驱动.笔者以飞凌S3C2440 开发板为例,创建了一个WinCE 对话框程序,点击相关按钮可以点亮(熄灭)相应的LED 灯,图2 是本程序的截图.
图2 程序的运行界面
程序中相关的要点代码及解释如下(暂不考虑程序的异常处理).
(1)端口映射
VirtualAlloc 函数调用成功后,将在WinCE 应用程序的进程空间中保留一段虚拟内存地址,并将该地址的值返回给指针变量v_IOPreg.
上面的VirtualCopy 函数将PB 端口的物理地址(S3C2440A_BASE_REG_PA_IOPORT>>8)映射到了虚拟地址v_IOPreg 上,此时端口的物理地址和进程的虚拟地址之间是一一对应的关系.
在WinCE 的SDK 中,MmMapIoSpace 函数亦能实现以上端口映射的功能[4],实质上,MmMapIoSpace函数就是VirtualAlloc 函数和VirtualCopy 函数的一个简单封装.对于MmMapIoSpace 函数的具体使用可参考MSDN,这里不再赘述.
(2)端口访问
按照对PB 端口寄存器的访问方式[5],通过对虚拟地址v_IOPreg 的操作实现了对LED1 灯的控制,其它3 个LED 灯的控制代码与之类似,这里不再给出.
2.3 设计方法的优点 与基于流驱动的设计方法相比,采用WinCE 程序直接驱动GPIO 端口的设计方法,开发者不需要设计、编译驱动程序的动态链接库,也不需要使用Platform Builder 对内核进行配置和编译,完全脱离了流接口固定的实现框架和对Platform Builder 编译环境的使用,直接在WinCE 程序中实现了对端口的驱动,方法快速有效.在程序的开发方式及调试上,该设计方法与常规的WinCE 程序设计方法无任何差别,在对GPIO 端口的控制上,也与单片机的开发思路实质一致.在对端口驱动要求比较简单、需要驱动的端口数量较少的情况下,该方法简单易行.
本文介绍了在WinCE 程序中对GPIO 端口进行简单驱动的一种设计方法,避开了驱动动态链接库的编写和对Platform Builder 编译环境的使用,是对常用的GPIO 端口驱动设计方法的简化,对于类似的GPIO 驱动程序开发,具有一定的借鉴意义.
[1]罗家兵,滕少华,张巍,等.WinCE.NET 下流接口驱动研究与实现[J].微计算机信息,2007,23(9):229-292.
[2]周建设.Windows CE 设备驱动及BSP 开发指南[M].北京:中国电力出版社,2009.
[3]杨泽辉,徐燕玲,刘碧君,等.基于嵌入式WinCE 的GPIO 驱动开发[J].太原科技大学学报,2010,31(6):446-448.
[4]林涛.嵌入式操作系统Windows CE 的研究[J].微计算机信息,2006,2-6:91-93.
[5]张欢,钮文良.Windows CE 系统开发基础与实例[M].北京:中国电力出版社,2010.