WinCE 6.0嵌入式开发全程实战教程
Windows Embedded CE 6.0作为微软推出的经典嵌入式操作系统,其开发依赖于完整的工具链与特定的硬件支持。搭建开发环境首先需安装Microsoft Platform Builder 6.0,并确保开发主机运行Windows XP或Server 2003(x86)操作系统,同时配置Visual Studio集成支持。
简介:《WinCE 6.0开发教程》由天嵌科技出品,面向嵌入式系统开发者,系统讲解从开发环境搭建到项目编译、测试的完整流程。内容涵盖Platform Builder配置、Visual Studio集成、BSP定制、操作系统映像构建及硬件调试等关键环节。以TQ2440平台为例,深入解析板级支持包结构与驱动实现,并指导用户完成项目创建、编译优化与系统性能测试。本教程适用于初学者快速入门WinCE开发,也帮助资深工程师提升嵌入式系统定制能力。 
1. WinCE 6.0开发环境搭建与系统架构概述
开发环境搭建基础与系统架构概览
Windows Embedded CE 6.0作为微软推出的经典嵌入式操作系统,其开发依赖于完整的工具链与特定的硬件支持。搭建开发环境首先需安装Microsoft Platform Builder 6.0,并确保开发主机运行Windows XP或Server 2003(x86)操作系统,同时配置Visual Studio集成支持。WinCE采用微内核架构,核心组件包括OAL(OEM Abstraction Layer)、Kernel、Bootloader及设备驱动层,通过模块化设计实现高度可裁剪性,适用于工业控制、手持终端等资源受限场景。
2. Microsoft Platform Builder使用详解与BSP基础分析
Microsoft Platform Builder(简称PB)是Windows Embedded CE 6.0开发的核心集成环境,专用于构建定制化嵌入式操作系统映像。它不仅提供了图形化界面来管理平台配置、内核裁剪和驱动集成,还深度集成了编译系统、调试工具链以及目标设备通信机制。在实际项目中,开发者通过Platform Builder完成从BSP导入、功能组件选择到最终NK.bin映像生成的全流程操作。本章将深入剖析Platform Builder的使用方法,并结合TQ2440这一典型ARM9架构开发板,解析BSP包的组织结构与适配逻辑,帮助读者建立对WinCE底层构建体系的系统性认知。
2.1 Platform Builder集成开发环境配置
Platform Builder作为WinCE开发的“中枢大脑”,其配置质量直接影响后续开发效率与系统稳定性。一个规范的开发环境不仅能确保编译过程顺利进行,还能为跨团队协作提供一致的基础框架。本节重点讲解Platform Builder的安装初始化、主机依赖项管理及工程工作区结构,旨在打造可复用、易维护的开发基础。
2.1.1 安装与初始化设置
Platform Builder 6.0通常随Windows Embedded CE 6.0 R3 SDK一同发布,需在兼容的操作系统上安装,推荐使用Windows XP SP3或Windows 7(32位/64位)。安装前必须确认已关闭UAC(用户账户控制),并以管理员权限运行安装程序。
安装过程中需注意以下关键选项:
- 安装路径 :建议避免包含空格或中文字符的路径,如 C:\WINCE6\ 。
- 组件选择 :务必勾选“Platform Builder”、“Target Analyzer”、“Remote Tools”等核心模块。
- 许可证激活 :部分版本需要输入合法授权码,否则仅支持有限时间试用。
安装完成后首次启动Platform Builder时,系统会提示进行初始化设置。此时应执行以下步骤:
- 打开Visual Studio 2005 Shell(PB基于此外壳);
- 进入菜单
Tools → Options → Projects and Solutions → VC++ Directories; - 设置库文件、包含目录和可执行文件路径,确保指向CE6安装目录下的相应子目录,例如:
Include directories:
$(CE6_BASE)\PUBLIC\COMMON\OAK\INC
$(CE6_BASE)\PRIVATE\WINCEOS\COREOS\NK\INC
Library directories:
$(CE6_BASE)\LIB\$(PLATFORM)\RETAIL
Executable directories:
$(CE6_BASE)\TOOLS\$(PLATFORM)
参数说明:
-$(CE6_BASE):环境变量,指向WinCE安装根目录,如C:\WINCE6;
-$(PLATFORM):平台名称变量,在x86环境下为“x86”,ARM下为“ARMV4I”;
- RETAIL表示发布版库,DEBUG则用于调试构建。
初始化成功后,Platform Builder主界面将显示“Catalog Items for Windows Embedded CE”,标志着开发环境已准备就绪。
此外,建议启用自动备份功能:进入 Tools → Options → Environment → Documents ,勾选“Save backup copies of files”以防止意外丢失 .pbxml 配置文件。
2.1.2 开发主机环境依赖项管理
为了保障Platform Builder稳定运行,开发主机必须满足一系列软硬件依赖条件。这些依赖项涵盖操作系统补丁、第三方工具链、网络服务等多个层面。
必要依赖清单如下表所示:
| 依赖项 | 版本要求 | 安装位置 | 作用 |
|---|---|---|---|
| .NET Framework | 2.0 或更高 | 系统全局 | 支持PB插件与UI控件渲染 |
| Visual Studio 2005 Shell | 随PB安装 | C:\Program Files\Microsoft Visual Studio 8 | 提供IDE容器 |
| ActiveSync 4.5 | 必须安装 | Microsoft官网下载 | 实现目标机连接与同步 |
| JTAG调试器驱动 | 如H-JTAG、OpenOCD | 厂商提供 | 下载Bootloader与Kernel |
| Python 2.7(可选) | 仅脚本自动化需要 | 自定义路径 | 编写批处理辅助脚本 |
其中,ActiveSync的正确配置尤为关键。若未正确安装或服务异常,会导致无法建立Kermit连接或远程调试失败。可通过以下命令验证服务状态:
net start "RasMan"
net start "SSTPFWD"
若提示服务不存在,说明ActiveSync未完整安装,需重新运行安装包并选择“修复”。
此外,防火墙策略也需调整。应允许 ceshmmap.exe 、 tcpconn.dll 等远程调试组件通过防火墙,端口范围一般为1025–5000。
对于多平台共存开发场景(如同时开发X86与ARM平台),推荐使用虚拟机隔离不同环境,避免注册表冲突与路径污染。
2.1.3 工程工作区结构解析
Platform Builder创建的每个平台项目都对应一个独立的工作区(Workspace),其内部结构遵循严格的层级组织模式。理解该结构有助于快速定位源码、配置文件与输出产物。
典型工作区目录布局如下所示:
MyTQ2440Platform/
├── Build.bat # 构建入口脚本
├── Sources # 当前平台的构建描述文件
├── Makefile # 平台级Make规则
├── Config.bib # 内存映像布局定义
├── Config.db # 数据库配置(注册表项)
├── Project.reg # 初始注册表设置
├── Files/
│ └── Filesys.reg # 文件系统相关注册表
├── Src/
│ └── Bootloader/ # 可选自定义Bootloader源码
├── Drivers/
│ └── MyDriver/ # 外部驱动源码目录
└── RelDir/ # 编译输出目录(NK.bin在此)
其中最关键的几个文件解析如下:
- Config.bib :Binary Image Builder文件,定义哪些DLL、EXE、SYS文件被烧录进ROM。示例如下:
; MODULES Section – 加载到RAM中运行
MODULES
nk.exe $(_FLATRELEASEDIR)\nk.exe RAM INCXIP
kernel.dll $(_FLATRELEASEDIR)\kernel.dll RAM INCXIP
; FILES Section – 包含在映像中的静态文件
FILES
gwenut.exe $(_FLATRELEASEDIR)\gwenut.exe RAM INCXIP
逻辑分析:
- 每行格式为:文件名 原始路径 加载区域 属性;
-INCXIP表示支持XIP(eXecute In Place),即直接从Flash执行;
-RAM表示必须复制到内存后执行。
- Project.reg :定义操作系统启动时加载的初始注册表项,常用于配置串口波特率、网卡MAC地址等硬件参数。
[HKEY_LOCAL_MACHINE\Drivers\Serial\COM1]
"Dll"="s3c2440_serial.dll"
"Prefix"="COM"
"Index"=dword:0
"BaudRate"=dword:384000 ; 38400 bps
参数说明:
-Dll:指定驱动动态库名称;
-Prefix:设备名前缀;
-Index:设备索引号;
-BaudRate:十进制值表示波特率。
整个工作区由Platform Builder通过 .pbxml 文件进行元数据管理,该XML文件记录了平台名称、所选组件、BSP引用关系等信息。任何手动修改均可能破坏工程一致性,因此建议通过GUI操作变更配置。
graph TD
A[Platform Builder IDE] --> B[加载.pbxml配置]
B --> C{是否已有BSP?}
C -->|是| D[导入现有BSP]
C -->|否| E[新建空白平台]
D --> F[生成Sources/Makefile]
E --> F
F --> G[调用Build.bat]
G --> H[执行Sysgen生成Catalog]
H --> I[编译内核与组件]
I --> J[输出NK.bin至RelDir]
上述流程图展示了从工程创建到映像输出的完整生命周期。每一阶段均可通过日志文件(如 %_FLATRELEASEDIR%\build.log )追踪执行细节,便于排查编译错误。
2.2 BSP包的导入与TQ2440平台适配
BSP(Board Support Package)是连接操作系统内核与具体硬件的关键桥梁。针对TQ2440开发板(基于Samsung S3C2440A ARM9处理器),需导入专用BSP包并完成软硬件资源映射配置。本节详细阐述BSP的组成结构、各模块职责划分及平台适配方法。
2.2.1 TQ2440开发板硬件特性概览
TQ2440是一款广泛应用的教学与工业级嵌入式开发板,搭载Samsung S3C2440A处理器(主频400MHz),采用ARM920T核心,具备MMU支持,适用于WinCE等复杂操作系统运行。
主要硬件资源配置如下表所示:
| 模块 | 规格 | 地址空间(物理) |
|---|---|---|
| SDRAM | 64MB | 0x30000000 – 0x33FFFFFF |
| NAND Flash | 64MB | 0x4E000000 – 0x4EFFFFFF |
| NOR Flash(可选) | 2MB | 0x00000000 – 0x001FFFFF |
| LCD控制器 | TFT 3.5寸 | 0x4D000000 |
| UART | 3通道(UART0/1/2) | 0x50000000, 0x50004000, 0x50008000 |
| USB Host/Device | OHCI + SL811HS | 0x49000000 |
| GPIO | 多组通用IO | 分布于多个寄存器区块 |
处理器内部集成多种外设控制器,通过AHB/APB总线互联。WinCE通过OAL层访问这些寄存器,实现中断响应、时钟管理、DMA传输等功能。
由于S3C2440A无内置SRAM,所有代码必须从NAND/NOR启动后搬运至SDRAM执行。因此Bootloader设计需支持ECC校验、坏块管理与YAFFS文件系统解析。
2.2.2 BSP目录结构深度剖析
标准BSP包以特定目录树形式组织,位于 $(_TARGETPLATROOT) 路径下,典型结构如下:
TQ2440_BSP/
├── BSP_cfg.h ; 全局编译宏定义
├── OALLIB/
│ ├── oal_lib.vcxproj ; OAL静态库工程
│ └── src/
│ ├── clock.c ; 系统时钟驱动
│ ├── intr.c ; 中断控制器封装
│ └── timer.c ; 定时器实现
├── Drivers/
│ ├── lcd/
│ │ └── lcd_driver.cpp ; LCD帧缓冲驱动
│ └── uart/
│ └── s3c2440_uart.c ; 串口驱动源码
├── Src/
│ ├── Kernel/
│ │ └── arm/ ; 内核汇编启动代码
│ └── Bootloader/
│ └── eboot/ ; Ethernet Bootloader源码
├── Config/
│ ├── flashcfg.inc ; Flash分区配置
│ └── memmap.inc ; 内存映射定义
└── Data/
└── tq2440.bat ; 平台构建脚本
2.2.2.1 Drivers、Src、Config子目录功能说明
| 子目录 | 功能描述 |
|---|---|
| Drivers | 包含流接口驱动(Stream Interface Driver),如LCD、UART、USB、Touch Panel等,通过 XXX_Init / XXX_IOControl 等标准接口与Device Manager交互 |
| Src | 存放内核级代码,包括Bootloader、OEM Adaptation Layer(OAL)、Kernel移植层等,通常用C/C++与汇编混合编写 |
| Config | 定义平台相关的配置片段,如BIB、DB、REG文件模板,供Platform Builder在sysgen阶段合并生成最终映像配置 |
例如,在 Drivers\uart\s3c2440_uart.c 中实现的串口初始化函数:
DWORD S3C2440_UART_Init(LPCTSTR pContext, LPCVOID pBusAddr, DWORD dwIntr) {
volatile S3C2440A_UART_REG *pReg = (S3C2440A_UART_REG*)pBusAddr;
// 设置波特率:PCLK=50MHz, BRDIV=(50000000/(115200*16))-1 ≈ 26
OUTREG8(&pReg->ULCON, 0x03); // 8位数据,1停止位,无奇偶校验
OUTREG8(&pReg->UCON, 0x05); // 中断模式接收/发送
OUTREG16(&pReg->UBRDIV, 26);
// 映射中断号
g_hUartIntrHandle[dwIntr] = CreateEvent(NULL, FALSE, FALSE, NULL);
InterruptInitialize(SYSINTR_UART0 + dwIntr, g_hUartIntrHandle[dwIntr], NULL, 0);
return (DWORD)pReg;
}
逐行解读:
-OUTREG8():宏函数,向8位寄存器写值;
-ULCON:Line Control Register,配置数据格式;
-UCON:Control Register,选择中断或轮询方式;
-UBRDIV:Baud Rate Divider,决定通信速率;
-InterruptInitialize():将物理中断IRQ映射到SYSGINTR逻辑中断号。
该驱动最终编译为 s3c2440_serial.dll ,并在 Project.reg 中注册加载。
2.2.2.2 OAL、Kernel、Bootloader模块职责划分
| 模块 | 职责 | 运行阶段 |
|---|---|---|
| OAL (OEM Adaptation Layer) | 提供内核与硬件之间的抽象接口,如 OEMInit() , OEMIoControl() |
内核启动早期 |
| Kernel | 处理器架构相关代码,如上下文切换、异常向量表设置 | 最早执行(汇编) |
| Bootloader | 初始化DDR、Flash、串口,加载NK.bin至RAM | 上电第一阶段 |
三者协同构成系统启动链条:
sequenceDiagram
participant Hardware
participant Bootloader
participant OAL
participant Kernel
Hardware->>Bootloader: 上电复位
Bootloader->>Hardware: 初始化SDRAM、串口
Bootloader->>Bootloader: 读取NK.bin from NAND
Bootloader->>Kernel: 跳转至Kernel入口(0x30010000)
Kernel->>OAL: 调用OEMInit()
OAL->>Hardware: 启动定时器、中断控制器
Kernel->>Kernel: 启动调度器,进入Idle线程
这种分层设计使得同一BSP可在不同主板间迁移,只需替换OAL与驱动部分即可。
2.2.3 平台注册表与资源映射配置
注册表是WinCE资源配置的核心机制。通过对 Project.reg 和 Config.db 的编辑,可精确控制设备行为。
例如,定义SPI触摸屏驱动的注册表项:
[HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\TOUCH]
"DriverName"="ads7846.dll"
"Prefix"="TCH"
"Dll"="mdd.dll"
"IoPort"=hex:58,00,00,4d
"Interrupt"=dword:1e
"SamplingRate"=dword:10
参数说明:
-IoPort:SPI控制器寄存器基址;
-Interrupt:对应GPIO中断号;
-SamplingRate:采样频率(单位ms);
同时,在 Config.bib 中声明内存保留区:
RESERVED
0x30000000 0x00100000 RAM ; 保留1MB给Bootloader
0x33F00000 0x00100000 RAM ; 保留1MB给显存
此类配置确保关键区域不被操作系统覆盖,保障图形与实时任务正常运行。
2.3 操作系统特征集定义与内核裁剪策略
WinCE的一大优势在于高度可定制性。通过Platform Builder的组件模型(Catalog),开发者可根据应用场景灵活裁剪内核功能,实现最小化系统部署。
2.3.1 组件模型(Catalog)的组织逻辑
Catalog是一个XML格式的组件数据库,存储在 $(_WINCEROOT)\PUBLIC\COMMON\CATALOG 目录下。每个条目定义了一个可选功能单元,如文件系统、网络协议栈、GUI子系统等。
Catalog项的基本结构如下:
<catalog>
<component name="FILESYSTEM" type="Feature">
<registry>
<hkey_local_machine>
<system>
<winver value="CE6.0"/>
</system>
</hkey_local_machine>
</registry>
<dependency>
<component ref="COREDLL"/>
</dependency>
<file source="$(_TARGETPLATROOT)\FILES\filesys.dll" dest="filesys.dll"/>
</component>
</catalog>
解析要点:
-type="Feature"表示这是一个功能组件;
-<dependency>定义前置依赖,防止缺失关键DLL;
-<file>指定需包含的二进制文件及其目标路径。
在Platform Builder GUI中,通过“Catalog Items”窗口可可视化选择所需组件。系统自动解析依赖关系,避免遗漏。
2.3.2 内核模块选择与系统功能定制
典型裁剪方案对比:
| 功能需求 | 推荐组件 | 占用空间估算 |
|---|---|---|
| 最小内核(无GUI) | COREDLL, DEVICE.EXE, FILESVC.EXE | ~800KB |
| 带文件系统 | + FATFS, REGSVR | +300KB |
| 支持TCP/IP | + IPHELPER, TCPIP | +1.2MB |
| 图形界面(GWE) | + GWES, EXPLORER | +2.5MB |
裁剪原则:
- 移除未使用的服务(如蓝牙、Wi-Fi);
- 关闭调试符号输出( _RETAILRUN=1 );
- 使用静态链接减少DLL数量。
2.3.3 最小化映像构建实践
创建最小映像的操作步骤如下:
- 打开Platform Builder;
- 新建平台 → 选择“Empty Platform”;
- 导入TQ2440 BSP;
- 在Catalog中仅勾选:
- Core OS → System Services → Device Manager
- Core OS → File Systems → FAT FileSystem
- Core OS → Device Drivers → Serial Port Driver - 保存并构建(Build → Sysgen);
- 查看
%_FLATRELEASEDIR%\nk.bin大小,预期小于2MB。
通过合理裁剪,可在资源受限设备上实现高效稳定的WinCE运行环境,为后续驱动开发与应用部署奠定坚实基础。
3. WinCE操作系统项目创建与映像生成流程
在嵌入式系统开发中,从硬件平台适配到最终可运行的操作系统映像输出,是一个高度集成且逻辑严密的过程。Windows CE 6.0作为微软推出的经典实时嵌入式操作系统,其构建过程依赖于Platform Builder这一专用开发环境,通过工程化方式完成操作系统的定制、编译和部署。本章聚焦于如何基于Platform Builder创建完整的WinCE操作系统项目,并深入剖析从配置到生成可执行映像(NK.bin)的全流程机制。整个过程不仅涉及图形化向导操作,还包括底层构建脚本的调度逻辑、注册表资源映射以及编译产物结构分析。对于具备五年以上嵌入式开发经验的技术人员而言,理解该流程中的关键节点——如 .pbxml 配置文件的作用、 Sysgen 阶段的模块展开机制、NK镜像内存布局等——是实现高效调试与性能优化的前提。
3.1 基于Platform Builder的操作系统工程建立
在完成BSP导入并确认目标硬件平台支持后,下一步便是创建一个完整的操作系统工程。Platform Builder提供了向导驱动式的项目创建流程,但其背后隐藏着复杂的元数据管理与构建上下文初始化机制。掌握这一过程不仅能提升工程组织效率,还能为后续的自动化构建与版本控制打下坚实基础。
3.1.1 新建平台向导操作步骤详解
Platform Builder中的“新建平台向导”是启动WinCE项目开发的第一步。该向导引导开发者逐步选择BSP、定义功能组件集合,并生成初始工程结构。尽管界面操作看似简单,每一步的选择都会直接影响最终系统的功能完整性与资源占用。
首先,在Platform Builder主界面中选择 File → New Platform ,进入向导界面。第一步为“Choose a BSP”,此时需从已安装的BSP列表中选择适用于TQ2440开发板的 SMDK2440 或类似命名的BSP包。此选择决定了OAL(OEM Adaptation Layer)、Bootloader、设备驱动等底层代码的来源路径。
第二步为“Choose Design Template”,即设计模板选择。常见的选项包括:
- Consumer Electronics (Standalone) :适用于独立运行的消费类设备;
- Headless Device :无显示接口的设备,常用于工业控制;
- Internet Appliance :强调网络连接能力的小型终端。
以TQ2440为例,若用于教学演示或原型验证,推荐选择“Standalone”模板,因其默认包含GUI子系统与基本外设支持。
第三步为“Select Components”,在此阶段可通过树形Catalog浏览器添加或移除系统级组件。例如勾选 Core OS → File Systems and Data Store → FAT FileSystem 以启用FAT支持;或选择 Device Drivers → Display → Frame Buffer Display Driver 来激活LCD驱动。这些选择将被记录在 .pbp 工程文件中,并影响后续 SYSGEN 变量的设置。
最后,输入平台名称(如 TQ2440_Platform_V1 ),完成向导。Platform Builder会自动生成以下核心文件:
- .pbp :平台项目文件,存储用户配置;
- .bib :Binary Image Builder文件,定义内存映像布局;
- .reg :注册表配置文件;
- .dat :设备布局描述文件;
- .db :数据库文件,用于存储运行时对象信息。
整个向导流程虽仅需数分钟,但其生成的工程框架将成为后续所有构建活动的基础。
3.1.2 目标设备配置文件(.pbxml)的作用机制
.pbxml 文件是Platform Builder引入的一种XML格式的平台描述文件,用于抽象化地表达目标设备的软硬件配置。它取代了早期版本中分散的 .cec 和 .bif 文件,实现了更高层次的配置解耦。
该文件主要包含以下四类信息:
| 元素 | 描述 |
|---|---|
<Hardware> |
定义CPU架构(ARMV4I)、时钟频率、内存地址范围等 |
<Software> |
列出所选Catalog组件及其依赖关系 |
<Configuration> |
指定启动参数、调试串口、内核堆大小等 |
<BuildSettings> |
控制编译器标志、优化等级、是否启用调试符号 |
示例片段如下:
<Platform>
<Hardware>
<Processor>ARMV4I</Processor>
<MemoryLayout>
<RAM Start="0x30000000" Size="0x08000000"/>
<ROM Start="0x00000000" Size="0x00800000"/>
</MemoryLayout>
</Hardware>
<Software>
<Component Name="FATFS" Selected="true"/>
<Component Name="ETHERNET" Selected="true"/>
</Software>
<Configuration>
<KernelHeapSize>0x00100000</KernelHeapSize>
<DebugSerialPort>COM1</DebugSerialPort>
</Configuration>
</Platform>
当执行构建命令时,Platform Builder解析 .pbxml 文件,并将其转换为一组环境变量注入构建环境。例如, FATFS 组件的存在会触发 SYSGEN_FATFS=1 的设定,从而激活相关源码目录的编译。此外, .pbxml 还支持条件配置块,允许根据不同构建变体(如Debug/Release)动态调整参数。
mermaid流程图展示了 .pbxml 在整个构建链路中的作用位置:
graph TD
A[用户启动 Build] --> B{读取 .pbxml}
B --> C[解析 Hardware 配置]
B --> D[提取 Software 组件]
B --> E[加载 Configuration 参数]
C --> F[设置 TARGETCPU, MEMORYMAP]
D --> G[生成 SYSGEN_* 变量]
E --> H[配置 Kernel Heap, Debug Port]
F --> I[调用 build.bat]
G --> I
H --> I
I --> J[开始 Sysgen 过程]
由此可见, .pbxml 不仅是静态配置容器,更是构建系统的“元控制器”。通过对其进行脚本化修改,可实现跨平台批量配置生成,极大提升团队协作效率。
3.1.3 自定义平台命名与版本控制
良好的命名规范与版本管理体系是大型嵌入式项目可持续维护的关键。在Platform Builder中,平台名称不仅出现在IDE界面,还会嵌入到生成的映像文件名、注册表项及调试信息中。
建议采用统一的命名格式: <Project>_<Board>_<FeatureSet>_<Version>.pbp
例如: IndustrialController_TQ2440_ETH+USB_V2.1.pbp
其中:
- Project 表示应用领域;
- Board 标识硬件平台;
- FeatureSet 简要说明启用的功能组合;
- Version 遵循语义化版本号规则(主版本.次版本.修订号)。
进一步地,应将整个Platform Builder工程目录纳入Git或SVN等版本控制系统。但由于 .pbp 、 .sln 等文件含有本地路径信息,直接提交易引发冲突,因此需进行适当裁剪。
推荐保留的核心文件包括:
- .pbxml
- .bib , .reg , .dat , .db
- 修改过的OAL/CDD驱动源码
- 构建脚本(如custom_build.bat)
而应忽略的文件有:
- .pdb , .idb , .res 等中间产物
- _project.dat , *.user 用户状态文件
- RelData 、 Debug 输出目录
可通过 .gitignore 配置实现自动过滤:
# Platform Builder Intermediate Files
*.pdb
*.idb
*.res
_project.dat
*.user
RelData/
Debug/
Release/
同时,编写 readme.md 说明各分支用途,如:
- main :稳定发布版
- dev/oal-porting :OAL移植实验分支
- feature/ui-enhancement :GUI功能扩展
这种结构化的版本管理模式,使得多工程师协同开发成为可能,并为后期固件回滚与审计提供依据。
3.2 内核编译过程与映像输出分析
一旦操作系统工程建立完毕,接下来的核心任务便是执行编译并生成可烧录的二进制映像。WinCE 6.0的构建体系沿用了传统的批处理驱动模式,其核心由 build.bat 和 sysgen.bat 构成,结合Makefile风格的 .def 与 .mak 文件完成多层次的源码组织与依赖解析。
3.2.1 编译命令执行流程(Build.bat与Sysgen)
WinCE的编译入口通常为Platform Builder IDE中的 Build → Clean Sysgen 和 Build → Sysgen Platform ,但其底层调用的是标准CMD命令行脚本。
完整构建流程可分为三个阶段:
- Clean Phase :清除旧构建产物
- Sysgen Phase :展开Catalog组件,生成构建上下文
- Build Phase :调用
nmake编译源码并链接映像
第一阶段:Clean
cd %_PROJECTROOT%
del /q /s RelData > nul 2>&1
此步骤删除 RelData 目录下的所有输出文件,确保无残留对象干扰新构建。
第二阶段:Sysgen
set CESYSGEN=1
call %_WINCEROOT%\platform\common\src\generic\sysgen\sysgen.bat
sysgen.bat 是整个构建流程的中枢脚本。它根据当前 .pbp 或 .pbxml 中选定的组件,逐个判断是否需要激活对应的 SYSGEN_xxx 环境变量。例如,若选择了“Telnet Server”组件,则设置:
set SYSGEN_INETTELNETD=1
随后,这些变量将在各个模块的 .def 文件中被检测,决定是否参与编译。
关键逻辑伪代码如下:
FOR EACH Component IN SelectedComponents DO
IF Component EXISTS IN Catalog.db THEN
SET SYSGEN_%ComponentName%=1
CALL ProcessDependencies(Component.Dependencies)
END IF
END FOR
第三阶段:Build
cd %_PROJECTROOT%
%_WINCEROOT%\bin\nmake.exe /f %_PROJECTROOT%\makefile.inc clean
%_WINCEROOT%\bin\nmake.exe /f %_PROJECTROOT%\makefile.inc
makefile.inc 是Platform Builder自动生成的顶层Makefile,内部引用了多个子Makefile,形成树状构建结构。典型结构如下:
!IF "$(SYSGEN_FATFS)"=="1"
SUBDIRS=$(SUBDIRS) fs/fatfs
!ENDIF
!IF "$(SYSGEN_USBMASS)"=="1"
SUBDIRS=$(SUBDIRS) drivers/usbfm
!ENDIF
每条 SUBDIRS 指令触发对应目录下的 sources 文件解析,进而调用 $(CESDK_ROOT)\bin\nmake.exe 递归编译。
逻辑分析:
- build.bat 本身不直接编译代码,而是协调环境变量与目录切换;
- sysgen 阶段决定了哪些模块会被纳入构建范围;
- 实际编译由 nmake 驱动,遵循WinCE特有的 sources + makefile 构建范式;
- 所有输出集中于 %_PROJECTROOT%\RelData\<CPU>\ 目录下。
3.2.2 映像格式(NK.bin)生成原理
WinCE生成的最终映像通常命名为 nk.bin ,代表“Kernel Binary”,但实际上包含了内核、OAL、设备驱动、文件系统及初始注册表等内容,是一种扁平化的ROM映像。
其生成过程由 romimage.exe 工具主导,输入为 .bib (Binary Image Builder)文件,输出为 .bin 或 .nb0/.nb1 格式。
.bib 文件定义了四个核心段:
| 段名 | 含义 | 示例 |
|---|---|---|
$(_FLATRELEASEDIR)\kernel.exe |
内核实例 | nk.exe |
$(_FLATRELEASEDIR)\*.dll |
动态库 | device.dll , coredll.dll |
$(_FLATRELEASEDIR)\*.exe |
应用程序 | explorer.exe |
FILES |
注册表与配置文件 | \windows\platform.reg |
示例 .bib 内容节选:
MODULES
nk.exe $(_FLATRELEASEDIR)\kernel.exe NK SH
coredll.dll $(_FLATRELEASEDIR)\coredll.dll NK SH
device.exe $(_FLATRELEASEDIR)\device.exe NK SH
FILES
platform.reg $(_FLATRELEASEDIR)\platform.reg REGISTRY 1
romimage.exe 按如下流程处理:
1. 解析 .bib ,收集所有需打包的文件;
2. 根据内存布局分配虚拟地址(VA)与物理地址(PA);
3. 将文件内容复制到连续缓冲区;
4. 添加头部信息(如校验和、入口点地址);
5. 输出固定格式的二进制流。
生成的 nk.bin 在内存中的典型布局如下图所示:
memoryDiagram
title WinCE NK.bin Memory Layout
section ROM (0x00000000)
Header : 0x0000, 0x1000
Code : 0x1000, 0x7FFFFF
section RAM (0x30000000)
Kernel Heap : 0x30000000, 0x00100000
User Processes: 0x31000000, dynamic
其中,前1MB空间通常映射至NOR Flash或SD卡起始位置,由Bootloader加载至RAM执行。值得注意的是, nk.bin 并非ELF或PE格式,而是专有的扁平二进制格式,不具备重定位能力,因此必须精确匹配链接地址。
3.2.3 编译日志解读与常见错误排查
构建过程中产生的日志文件位于 %_PROJECTROOT%\RelData\build.log ,是诊断问题的主要依据。典型的成功构建日志包含如下阶段标记:
BUILD: [01] Building for ARMV4I CPU
BUILD: [02] Macro defines set
BUILD: [03] Cleaning output directories...
BUILD: [04] Starting Sysgen...
BUILD: [05] Processing component: FATFS
BUILD: [06] Invoking NMAKE on makefile.inc
BUILD: [99] romimage -o nk.bin project.bib
常见错误类型及其解决方案如下表所示:
| 错误代码 | 描述 | 解决方案 |
|---|---|---|
| LNK2019 | 未解析的外部符号 | 检查 .def 文件中LIBS是否包含对应库 |
| C1083 | 无法打开头文件 | 确认INCLUDE路径是否包含BSP头目录 |
| SYSGEN not defined | 组件未激活 | 在Catalog中重新勾选并执行Clean Sysgen |
| romimage out of space | 映像超出ROM容量 | 裁剪非必要组件或调整BIB内存分配 |
例如,出现 fatal error C1083: Cannot open include file: 's3c2440.h' 时,应检查:
1. BSP的 INC 路径是否正确注册;
2. sources 文件中是否有 INCLUDES=$(BSPROOT)\INC ;
3. 头文件实际是否存在。
此外,启用详细日志模式可通过修改 build.bat 加入:
set VERBOSE_BUILD=1
从而输出更详细的编译命令行,便于追踪问题根源。
3.3 映像烧录与启动验证
生成 nk.bin 只是第一步,真正验证系统可用性还需将其部署到目标硬件并观察启动行为。TQ2440平台支持多种烧录方式,各有优劣,需根据开发阶段灵活选用。
3.3.1 JTAG与SD卡烧写方式对比
目前主流的两种烧录方式为JTAG下载与SD卡启动,其技术特点对比如下:
| 特性 | JTAG烧录 | SD卡烧录 |
|---|---|---|
| 硬件要求 | JTAG仿真器(如ULINK2) | SD卡读卡器 |
| 速度 | 中等(~512KB/s) | 快(~4MB/s) |
| 调试支持 | 支持单步调试、断点 | 仅运行态 |
| 使用场景 | OAL调试初期 | 功能测试与量产 |
| 可靠性 | 高(直接访问内存) | 受卡质量影响 |
JTAG方式依赖OpenOCD或H-JTAG等工具链,通过ARM JTAG接口直接向SDRAM写入 nk.bin ,然后跳转执行。优点在于无需修改Bootloader即可反复试验,适合调试内存初始化代码。
SD卡方式则要求将 nk.bin 放置于特定扇区(通常是第1024扇区),由Bootloader读取并搬运至RAM。优势是接近真实部署环境,可用于压力测试。
操作步骤示例(SD卡烧录):
# 使用dd命令写入bin文件(Linux)
sudo dd if=nk.bin of=/dev/mmcblk0 seek=1024 conv=notrunc
sync
3.3.2 Bootloader阶段行为监控
Bootloader是系统启动的第一道程序,负责初始化DDR、串口,并加载 nk.bin 。TQ2440通常使用EBoot(Ethernet Bootloader),其启动流程如下:
sequenceDiagram
participant CPU
participant SDRAM
participant UART
participant NAND/SD
participant RAM
CPU->>SDRAM: 初始化存储控制器
CPU->>UART: 配置调试串口
UART-->>PC: 输出 "EBOOT: Starting..."
CPU->>NAND/SD: 读取 nk.bin 到 RAM
RAM->>CPU: 跳转至入口点
CPU->>Kernel: 启动 WinCE 内核
通过串口终端(如SecureCRT)可捕获如下典型输出:
EBOOT: Samsung S3C2440 Boot Loader
Initializing SDRAM... OK
Configuring UART0 @ 115200bps
Waiting for download (Press ESC to skip)...
Downloaded 0x380000 bytes to 0x30008000
Jumping to RAM execution at 0x30008000...
若在此阶段卡住,常见原因包括:
- 晶振频率配置错误导致PLL失败;
- SDRAM时序参数不匹配;
- 串口波特率设置不符。
此时应检查 oal_start.S 和 clock.c 中的低级初始化代码。
3.3.3 串口调试信息捕获与启动故障诊断
WinCE内核启动后,默认通过串口输出大量调试信息,格式为:
Kernel Verifier: Start
OAL: Timer Initialize... Success
PCI: No bus found
DSK1: Drive ready
这些信息由 OAL_LOG_MSG() 宏输出,级别由 DEBUGMSG() 控制。若系统在某处死机,可根据最后一条输出定位问题区域。
例如,若停留在“Timer Initialize”,则可能是 OALTimerInitialize() 函数中循环等待定时器中断未响应,需检查:
- 定时器寄存器基址映射是否正确;
- IRQ线是否使能;
- IST线程是否正常创建。
建议配合逻辑分析仪抓取GPIO信号,辅助判断外设响应状态。
综上所述,从项目创建到映像烧录的全过程,构成了WinCE嵌入式开发的核心闭环。深入理解每个环节的技术细节,尤其是构建系统与启动流程之间的耦合关系,是解决复杂问题、提升开发效率的根本保障。
4. 硬件驱动开发与系统级调试技术实战
在嵌入式操作系统开发中,硬件驱动的实现是连接底层物理设备与上层应用逻辑的关键桥梁。Windows CE 6.0作为一款高度可裁剪、实时性强的嵌入式操作系统,其驱动架构设计充分体现了模块化与分层解耦的思想。本章节将深入探讨基于WinCE平台的硬件抽象层(HAL)构建、流接口驱动开发以及系统级调试机制的实际应用,重点围绕TQ2440开发板平台展开具体编码实践和调试策略分析。
通过本章内容的学习,读者不仅能够掌握从零开始编写符合WinCE规范的设备驱动程序的方法,还能熟练运用多种内核级和用户态调试工具进行故障定位与性能优化。尤其对于具备5年以上嵌入式开发经验的工程师而言,本章所涉及的中断处理机制、ISR/IST协同调度模型、远程调试通道搭建等内容具有极强的工程指导意义。
4.1 硬件抽象层(HAL)设计与OAL代码实现
硬件抽象层(Hardware Abstraction Layer, HAL),在Windows CE系统中主要由OEM Adaptation Layer(OAL)承担其实现职责。OAL作为操作系统内核与目标硬件之间的中间层,屏蔽了不同BSP(Board Support Package)平台间的差异性,使得WinCE核心可以跨平台复用。理解并正确实现OAL代码,是确保系统稳定启动和外设正常工作的前提条件。
4.1.1 OAL层在系统启动中的角色定位
当TQ2440开发板上电后,Bootloader完成基本初始化并将控制权移交至NK.exe(即WinCE内核镜像)时,OAL模块便成为第一个被调用的核心组件之一。它负责执行一系列关键任务,包括设置CPU模式、初始化内存映射、配置中断控制器、建立实时时钟以及提供基本的串行输出功能用于调试信息打印。
OAL在整个启动流程中的作用可以通过以下mermaid流程图清晰展示:
graph TD
A[Power On] --> B[Bootloader Execution]
B --> C{Load NK.bin?}
C -->|Yes| D[Jump to Kernel Entry Point]
D --> E[OAL Startup: OEMInitGlobals]
E --> F[OEMInitArch]
F --> G[OEMInit]
G --> H[Kernel Initialization]
H --> I[Device Driver Loading]
I --> J[System Ready]
该流程表明,OAL并非单一函数调用,而是一组按照特定顺序执行的初始化例程集合。其中最核心的是 OEMInit() 系列函数,它们由Platform Builder自动链接进内核映像,并在内核初始化阶段依次调用。
这些函数通常定义于BSP的 Src\Oal\Oallib 目录下,常见结构如下所示:
// oeminit.c
BOOL OEMInit(void)
{
// 初始化全局变量
if (!OEMInitDebugSerial()) {
return FALSE;
}
// 初始化中断控制器
if (!xllIntInitialize()) {
return FALSE;
}
// 启动系统滴答定时器
if (!xllTimerInitialize()) {
return FALSE;
}
// 开启中断使能
xllIntEnable(TRUE);
return TRUE;
}
代码逐行解读与参数说明:
OEMInitDebugSerial():初始化调试串口(如UART0),用于后续KdPrint或RETAILMSG输出。xllIntInitialize():底层中断子系统初始化,注册中断向量表基地址。xllTimerInitialize():设置系统节拍定时器(通常为PCLK驱动的PWM Timer),为调度器提供时间基准。xllIntEnable(TRUE):启用ARM异常模式下的IRQ/FIQ中断响应。
上述函数的成功返回是系统继续运行的前提。若任一环节失败,系统将卡死在启动过程中,因此建议在此类函数中加入最低限度的日志输出(例如通过GPIO点亮LED指示错误码)以辅助诊断。
此外,OAL还需实现 OEMIoControl() 接口,以便内核或其他驱动通过IOCTL方式访问平台特定功能,例如获取电池电压、控制背光亮度等。
4.1.2 中断向量表与时钟驱动初始化流程
中断管理是OAL中最复杂的部分之一。WinCE采用静态中断向量表(Interrupt Vector Table, IVT)机制,在启动初期由 OEMInit() 调用 HalInitSystem() 间接完成中断服务例程(ISR)的绑定。
中断向量映射机制
ARM920T处理器支持7种异常模式,WinCE仅使用其中6种,忽略未定义指令异常(可通过编译选项关闭)。每个异常入口点指向一段汇编跳转代码,最终跳转至C语言编写的处理函数。
| 异常类型 | 地址偏移 | 对应OAL函数 |
|---|---|---|
| Reset | 0x00 | OEMStartUp |
| Undefined Instruction | 0x04 | Not used |
| Software Interrupt (SWI) | 0x08 | Kernel SWI handler |
| Prefetch Abort | 0x0C | KData.o 相关处理 |
| Data Abort | 0x10 | 内存保护异常 |
| IRQ | 0x18 | OEMInterruptHandler |
| FIQ | 0x1C | 可选高速中断处理 |
重点关注 IRQ 入口,它指向 OEMInterruptHandler ,这是所有外部中断的统一入口点。
// oemhal.c
DWORD OEMInterruptHandler(DWORD ra)
{
UINT32 irqNum = INREG32(&g_pINTREG->I_ISPR); // 读取正在服务的中断源
DWORD sysIntr = SYSINTR_NOP; // 默认无中断
switch (irqNum) {
case INT_UART0:
sysIntr = SYSINTR_SERIAL;
break;
case INT_TIMER0:
sysIntr = SYSINTR_TIMEROEM;
break;
default:
break;
}
return sysIntr; // 返回对应的SYSINTR值,触发IST
}
逻辑分析:
- 此函数运行在 中断上下文 (IRQL >= DISPATCH_LEVEL),必须快速执行。
- 它不直接处理硬件,而是识别中断源并将其转换为WinCE定义的
SYSINTR常量。 - 返回非
SYSINTR_NOP值时,内核会唤醒对应注册的 中断服务线程 (IST)来执行实际处理逻辑。
系统时钟初始化
系统节拍(tick)依赖于高精度定时器。在TQ2440平台上,通常选用Timer0作为主时钟源,频率设为1ms(即1000Hz)。
// xlltimer.c
BOOL xllTimerInitialize(void)
{
volatile S3C2440A_TIMERS_REG *pTimers = (S3C2440A_TIMERS_REG *)OALPAtoVA(0x51000000, FALSE);
// 关闭Timer0
OUTREG32(&pTimers->TCON, 0);
// 设置预分频器为15,使PCLK=50MHz -> 3.125MHz
OUTREG32(&pTimers->TCFG0, (15 << 0));
// 自动重载 + 手动更新
OUTREG32(&pTimers->TCON, (1 << 3) | (1 << 1));
OUTREG32(&pTimers->TCON, (1 << 3));
// 计数值 = 3125,对应1ms周期
OUTREG32(&pTimers->TCNTB0, 3125);
OUTREG32(&pTimers->TCMPB0, 0);
// 启动Timer0,开启中断
OUTREG32(&pTimers->TCON, (1 << 3) | (1 << 0));
// 注册到OAL时钟框架
OALTimerSetClock(1000); // 告知内核节拍率为1000Hz
return TRUE;
}
参数说明:
OALPAtoVA():将物理地址转换为虚拟地址,需配合MMU页表使用。TCFG0:定时器预分频寄存器,影响输入时钟频率。TCNTB0:计数缓冲寄存器,决定溢出周期。TCON:控制寄存器,包含启动、更新、自动重载等位域。
此段代码成功执行后,每毫秒触发一次Timer0中断,经由 OEMInterruptHandler 映射为 SYSINTR_TIMEROEM ,进而激活系统调度器进行时间片轮转。
4.1.3 GPIO与定时器底层接口封装方法
为了提升代码复用性和可维护性,应在OAL中对常用外设进行统一接口封装。以GPIO为例,TQ2440拥有多达117个GPIO引脚,分布在GPA~GPH等多个端口上。
GPIO操作抽象层设计
设计一个轻量级API集,便于其他驱动模块调用:
// gpio_hal.h
typedef enum {
GPIO_DIR_INPUT,
GPIO_DIR_OUTPUT
} GPIO_DIRECTION;
BOOL GPIO_SetDirection(UINT port, UINT pin, GPIO_DIRECTION dir);
BOOL GPIO_WritePin(UINT port, UINT pin, BOOL level);
BOOL GPIO_ReadPin(UINT port, UINT pin);
void GPIO_EnablePullUp(UINT port, UINT pin, BOOL enable);
// gpio_hal.c
BOOL GPIO_SetDirection(UINT port, UINT pin, GPIO_DIRECTION dir)
{
volatile UINT32 *pConReg = NULL;
switch(port) {
case 'A': pConReg = (UINT32*)OALPAtoVA(0x56000000, FALSE); break;
case 'B': pConReg = (UINT32*)OALPAtoVA(0x56000010, FALSE); break;
...
default: return FALSE;
}
UINT32 val = INREG32(pConReg);
if (dir == GPIO_DIR_OUTPUT) {
val |= (1 << (pin * 2));
} else {
val &= ~(1 << (pin * 2));
}
OUTREG32(pConReg, val);
return TRUE;
}
扩展性说明:
- 支持动态端口选择与位操作,避免硬编码。
- 结合宏定义可进一步简化调用,例如
#define LED_GPIO_PORT 'F'。 - 在
OEMInit()中可预先配置关键引脚方向,防止冲突。
定时器延时函数封装
除系统节拍外,某些驱动需要微秒级精确延时(如LCD初始化)。此时应基于Timer寄存器实现:
void uDelay(UINT32 usec)
{
volatile S3C2440A_TIMERS_REG *pTimers =
(S3C2440A_TIMERS_REG *)OALPAtoVA(S3C2440A_BASE_REG_PA_TIMER, FALSE);
UINT32 count = usec * 50; // 假设PCLK=50MHz,每cycle=20ns
OUTREG32(&pTimers->TCNTB3, count);
OUTREG32(&pTimers->TCON, (1 << 21)); // 手动更新Timer3
OUTREG32(&pTimers->TCON, (1 << 20)); // 启动Timer3,单次模式
while(INREG32(&pTimers->TCNTO3) > 0); // 忙等待
}
该函数虽占用CPU资源,但在早期初始化阶段不可或缺。后期可通过高分辨率定时器+事件通知机制替代。
4.2 设备驱动开发实例:串口与中断处理
WinCE设备驱动可分为原生驱动(Built-in Drivers)和流接口驱动(Stream Interface Drivers)。后者更灵活,适用于大多数外设,如UART、SPI、I2C等。本节将以TQ2440的UART0为例,完整演示一个流接口驱动的开发过程。
4.2.1 流接口驱动框架(XXX_Init/Deinit/Open等接口)
流接口驱动遵循标准命名约定,必须导出以下七个函数:
| 函数名 | 调用时机 | 功能描述 |
|---|---|---|
XXX_Init |
系统启动或设备加载 | 分配资源、初始化硬件 |
XXX_Deinit |
驱动卸载 | 释放资源 |
XXX_Open |
CreateFile() | 获取设备句柄 |
XXX_Close |
CloseHandle() | 释放句柄 |
XXX_Read |
ReadFile() | 读取数据 |
XXX_Write |
WriteFile() | 写入数据 |
XXX_IOControl |
DeviceIoControl() | 控制命令交互 |
驱动入口点由 .def 文件声明:
LIBRARY "SERIAL"
EXPORTS
COM_Init
COM_Deinit
COM_Open
COM_Close
COM_Read
COM_Write
COM_IOControl
初始化函数实现
HANDLE COM_Init(LPCTSTR pContext, LPCVOID pBusContext)
{
PHYSICAL_ADDRESS phyAddr = {0x50000000, 0};
PUART_DEVICE pDev = (PUART_DEVICE)LocalAlloc(LPTR, sizeof(UART_DEVICE));
if (!pDev) return NULL;
pDev->pReg = (S3C2440A_UART_REG*)MmMapIoSpace(phyAddr, sizeof(S3C2440A_UART_REG), TRUE);
if (!pDev->pReg) goto fail;
// 配置UART0: 115200bps, 8N1
OUTREG32(&pDev->pReg->ULCON0, 0x3); // 8位数据,无校验,1停止位
OUTREG32(&pDev->pReg->UCON0, 0x5); // 中断模式,发送/接收使能
OUTREG32(&pDev->pReg->UFCON0, 0x50); // FIFO使能,触发级别=8
OUTREG32(&pDev->pReg->UBRDIV0, 26); // Baud=115200 @ PCLK=50MHz
// 注册中断
pDev->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!KernelLibIoControl(NK_FLAG_ISRINFO, (LPVOID)INT_UART0, sizeof(DWORD),
&pDev->dwSysIntr, sizeof(DWORD), NULL)) {
goto fail;
}
InterruptInitialize(pDev->dwSysIntr, pDev->hEvent, NULL, 0);
pDev->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)IST_Uart, pDev, 0, NULL);
CeSetThreadPriority(pDev->hThread, 100);
return (HANDLE)pDev;
fail:
if (pDev) LocalFree(pDev);
return NULL;
}
逻辑分析:
MmMapIoSpace():将物理寄存器地址映射为进程可访问的虚拟地址。KernelLibIoControl():获取指定IRQ对应的SYSINTR值。InterruptInitialize():绑定IST事件对象。- 创建专用线程
IST_Uart监听事件信号。
4.2.2 ISR与IST协同工作机制详解
WinCE采用双层中断处理模型:
- ISR (Interrupt Service Routine):运行在内核空间,响应速度快,仅做最小判断并返回
SYSINTR。 - IST (Interrupt Service Thread):用户态线程,执行耗时操作,如数据收发、协议解析。
二者通过事件对象同步:
DWORD IST_Uart(PUART_DEVICE pDev)
{
while (1) {
WaitForSingleObject(pDev->hEvent, INFINITE);
while (IsRecvReady(pDev)) {
BYTE data = INREG8(&pDev->pReg->URXH0);
Enqueue(&pDev->rxQueue, data);
}
NotifyDriverOfData(); // 触发ReadFile阻塞唤醒
}
return 0;
}
工作流程表格:
| 阶段 | 执行位置 | 时间限制 | 典型操作 |
|---|---|---|---|
| ISR | 内核态 | < 10μs | 读状态寄存器,返回SYSINTR |
| IST | 用户态 | 无严格限制 | 数据拷贝、队列管理、回调通知 |
⚠️ 注意:若IST长时间不响应,可能导致中断丢失。应合理设置线程优先级并避免阻塞。
4.2.3 实现UART驱动并注册到设备管理器
最后需在 platform.reg 中添加注册表项,使驱动自动加载:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial]
"Dll"="serial.dll"
"Prefix"="COM"
"Index"=dword:0
"Order"=dword:0
"FriendlyName"="UART0 Controller"
"IoPort"=dword:50000000
"Irq"=dword:28
重启后可通过 handle COM0: 测试通信:
echo "Hello CE" > COM0:
4.3 调试手段与远程连接配置
高效的调试体系是嵌入式开发成败的关键。WinCE提供了多层次的调试支持。
4.3.1 使用CEDebugLog进行内核级日志输出
CEDebugLog 是一个轻量级宏,可用于内核或驱动中输出格式化信息:
#include <nkintr.h>
#define DEBUG_SERIAL_ZONE 0x0001
extern DBGPARAM dpCurSettings;
// 在驱动中使用
CEDebugLog((DEBUG_SERIAL_ZONE, "UART TX complete, len=%d\n", len));
需在 oemdebug.cfg 中启用相应区域:
[Zones]
"SERIAL"=1:0x0001
输出将通过调试串口(默认COM1)传送到主机终端。
4.3.2 ActiveSync与Kermit协议建立通信通道
ActiveSync已过时,推荐使用开源替代方案如 cecopy 配合Kermit协议传输文件:
kermit -l /dev/ttyUSB0 -b 115200 -C "connect"
可在目标机运行 kermserver 接受上传:
kermserver -s incoming/
4.3.3 远程桌面与目标机资源监控工具联动
借助Remote Tools包中的 Remote Registry Editor 、 Remote Performance Monitor ,可实时查看内存、CPU、句柄使用情况,极大提升排查效率。
示例:使用
RapiConfig设置网络IP后,即可通过TCP/IP连接进行远程调试。
RapiConfig -set ip 192.168.1.10 netmask 255.255.255.0 gateway 192.168.1.1
综上所述,完整的驱动开发与调试闭环包含:代码编写 → 编译集成 → 下载运行 → 日志捕获 → 故障定位 → 优化迭代。掌握这一流程,方能在复杂嵌入式项目中游刃有余。
5. 嵌入式系统功能测试与性能优化全流程实践
5.1 文件系统与外设兼容性验证
在完成WinCE 6.0操作系统映像构建并成功部署至TQ2440开发板后,必须对系统的文件系统结构和外部设备的兼容性进行全面验证。此阶段的目标是确保存储介质可持久化保存数据、外设能被正确识别,并为上层应用提供稳定运行环境。
5.1.1 注册表持久化与存储分区布局检验
WinCE默认将注册表分为两种模式: RAM-based 和 Hive-based 。为实现重启后配置不丢失,需启用基于Hive的注册表(HKEY_LOCAL_MACHINE\Comm\Registry)。通过修改 platform.reg 文件中的以下键值:
[HKEY_LOCAL_MACHINE\init]
"Launch56"="hivesetup.exe"
并确保 hiveboot.bin 正确生成,系统启动时会自动挂载注册表镜像到NAND Flash指定分区。
可通过Platform Builder的 Remote File Viewer 工具连接目标机,查看 \Windows\ 目录下是否存在 user.hv 、 system.hv 等Hive文件。同时使用 fsdiag 命令行工具检查当前存储分区分配情况:
| 分区编号 | 设备路径 | 类型 | 容量(MB) | 挂载点 |
|---|---|---|---|---|
| 0 | NAND | RAW | 64 | \Boot |
| 1 | NAND | FAT32 | 256 | \Disk |
| 2 | NAND | Hive Registry | 16 | \Windows |
| 3 | SD Card Slot | Removable | 4096 | \SDCARD |
执行如下命令验证注册表写入是否持久:
reg add "HKEY_LOCAL_MACHINE\TestKey" /v Data /d "PersistentValue"
重启设备后再次查询该键值是否存在,确认Hive机制生效。
5.1.2 SD卡、USB设备接入响应测试
针对常见的可移动存储设备,需验证其即插即用(PnP)支持能力。插入SD卡或U盘后,系统应自动加载 usdhc.dll 或 usbmscd.dll 驱动模块,并触发设备管理器枚举流程。
通过串口输出日志捕获关键事件:
+ USB: Mass Storage Class Driver loaded
+ DiskIoctl: Partition 1 mounted as \SDCARD
+ Files: Created mount point \Storage Card
编写自动化测试脚本 test_storage.bat 批量执行读写操作:
@echo off
echo 测试开始 > \SDCARD\test.log
for /L %%i in (1,1,100) do (
echo 迭代%%i: %time% >> \SDCARD\stress_test.log
)
echo 测试完成 >> \SDCARD\test.log
记录平均写入速度(单位:KB/s),评估底层FAT32文件系统性能表现。
5.1.3 应用程序运行环境兼容性评估
部署典型Win32 API调用的应用程序(如基于MFC CE子集编写的GUI程序),检测动态链接库依赖关系是否完整。利用Dependency Walker(CE版本)分析EXE引用的DLL列表:
| DLL 名称 | 是否内置 | 加载状态 | 备注 |
|---|---|---|---|
| coredll.dll | 是 | 成功 | 核心C运行时 |
| coredll.lib | 否 | 忽略 | 静态库 |
| aygshell.dll | 是 | 成功 | Shell扩展支持 |
| commctrl.dll | 是 | 成功 | 控件库 |
| winsock.dll | 是 | 成功 | 网络通信 |
| msxml3.dll | 否 | 失败 | 需手动添加组件至Catalog |
若发现缺失依赖项,应回到Platform Builder中启用对应组件(例如“Internet Explorer XML Parser”),重新生成NK.bin映像。
此外,还需验证多线程应用程序在线程创建、同步原语(如CreateMutex、WaitForSingleObject)方面的行为一致性,防止因OAL调度异常导致死锁。
DWORD WINAPI ThreadEntry(LPVOID lpParam) {
CEDebugLog(L"Thread %d running\n", GetCurrentThreadId());
Sleep(1000);
return 0;
}
// 主函数中创建多个线程进行压力测试
for(int i = 0; i < 10; ++i) {
CreateThread(NULL, 0, ThreadEntry, NULL, 0, NULL);
}
观察系统是否出现句柄泄漏或堆栈溢出错误。
flowchart TD
A[启动系统] --> B{注册表持久化?}
B -->|是| C[挂载Hive分区]
B -->|否| D[警告: 配置丢失风险]
C --> E[初始化文件系统]
E --> F[检测SD卡/USB]
F --> G[加载相应驱动]
G --> H[创建挂载点]
H --> I[运行兼容性测试套件]
I --> J[生成测试报告]
简介:《WinCE 6.0开发教程》由天嵌科技出品,面向嵌入式系统开发者,系统讲解从开发环境搭建到项目编译、测试的完整流程。内容涵盖Platform Builder配置、Visual Studio集成、BSP定制、操作系统映像构建及硬件调试等关键环节。以TQ2440平台为例,深入解析板级支持包结构与驱动实现,并指导用户完成项目创建、编译优化与系统性能测试。本教程适用于初学者快速入门WinCE开发,也帮助资深工程师提升嵌入式系统定制能力。
更多推荐

所有评论(0)