i.MX25平台WinCE 6.0 BSP中NAND Flash驱动移植实战指南

i.MX25平台WinCE 6.0 BSP中NAND Flash驱动移植实战指南 1. 项目概述在嵌入式产品开发中硬件平台的迭代和元器件选型的变更是家常便饭。我最近就遇到了一个典型的场景一个基于飞思卡尔i.MX25处理器的工控设备项目原先使用的三星K9LBG08U0M型号NAND Flash芯片因为停产或成本原因需要更换为另一款同容量但型号不同的NAND Flash例如K9LAG08U0M。设备运行的是Windows Embedded CE 6.0操作系统这意味着我们需要对板级支持包BSP中的NAND Flash驱动进行适配和移植。这可不是简单地换个芯片就能启动的BSP作为连接硬件与操作系统的桥梁其驱动必须精确匹配硬件的电气特性和访问时序。对于NAND Flash这类有坏块管理、需要特定命令序列的存储介质驱动适配更是重中之重。如果你也正在为i.MX25平台WinCE 6.0的BSP定制尤其是NAND Flash驱动移植而头疼那么这篇基于飞思卡尔官方应用笔记AN4111的深度实践指南将为你梳理出一条清晰的路径。我会结合自己踩过的坑详细拆解从读懂芯片手册到最终生成可启动镜像的全过程让你不仅能“照着做”更能“懂得为什么这么做”。2. 核心原理与架构解析2.1 BSP与驱动分层模型在深入动手修改之前我们必须先理解WinCE 6.0下i.MX25 BSP中NAND驱动的架构。这绝不是一堆散乱代码的堆砌而是一个层次分明、各司其职的软件模型。理解这个模型是后续一切修改工作的基础。整个驱动栈可以清晰地分为两个主要部分操作系统运行时WinCE NK和引导程序Eboot。有趣的是它们共享了最底层的硬件抽象——Flash Media DriverFMD。FMD层是驱动移植的核心战场它向上为文件系统如TFAT、exFAT或Flash分区驱动提供统一的块设备接口向下则直接操作i.MX25内部的NAND Flash控制器NFC和具体的NAND芯片。这种设计的精妙之处在于当我们需要支持一款新的NAND芯片时绝大部分修改都集中在FMD这一层上层的文件系统和下层的硬件控制器驱动通常无需变动。注意这里有一个关键例外那就是XloaderXLDR。XLDR是i.MX25 BSP中一个非常早期的引导阶段它负责初始化最基础的硬件包括NAND控制器并从NAND的特定位置加载Eboot。由于XLDR极度精简它并没有使用完整的FMD层而是直接包含了访问NAND所需的最基本代码。因此移植新NAND型号时XLDR的代码也需要进行相应的适配这是我们后面会重点处理的一个环节。2.2 NAND Flash关键参数详解驱动移植的本质是让软件“认识”并“正确操作”新的硬件。对于NAND Flash这种“认识”就体现在一系列关键参数上。这些参数必须从新芯片的数据手册Datasheet中精准提取任何一个参数的误读都可能导致系统无法启动或数据读写错误。制造商与设备IDNAND MARKER DEVICE ID这是芯片的“身份证”。系统上电后驱动会发送0x90命令读取这两个字节。例如三星的制造商ID通常是0xEC。设备ID则定义了芯片的内部架构、容量和电压等详细信息。驱动代码中会有一个ID匹配表我们必须将新芯片的ID加入其中否则驱动会认为找不到支持的设备。容量与结构参数这组参数定义了芯片的物理布局。NAND_BLOCK_COUNT芯片总共有多少个块Block。块是NAND擦除操作的最小单位。NAND_PAGE_COUNT每个块包含多少个页Page。页是读写操作的基本单位。NAND_PAGE_SIZE每个页的“主数据区”大小常见的有2048字节2KB和4096字节4KB。NAND_SPARE_SIZE每个页的“备用区”Spare Area/OOB大小用于存放ECC校验码、坏块标记等元数据常见的有64字节或128字节。坏块信息BBI参数这是NAND驱动中最为棘手但也最关键的部分。NAND Flash允许出厂时存在坏块并且在生命周期内也可能产生新的坏块。驱动必须知道如何识别它们。BBI_MAIN_ADDR坏块标记Bad Block Marker在页内的主数据区地址偏移。这里最容易出错。它不是简单的备用区起始地址如2048因为i.MX25的NFC内部会将一个大页分割成多个512字节的扇区进行处理。计算这个地址需要考虑NFC的内部缓冲区划分。官方文档给出了计算公式Page_Size - ((512 Spare_Per_Sector) * (Sectors_Per_Page - 1))。例如对于一个2KB页、64字节备用区的芯片NFC将其分为4个扇区51216那么BBI_MAIN_ADDR 2048 - ((51216)*3) 464。BBI_NUM在一个块中有多少个页包含坏块标记。通常是1标记在第一页或最后一页但有些芯片可能用2个页来标记以提高可靠性。BBIMarkPage[]这是一个数组指明坏块标记具体位于一个块中的哪一几页。例如对于128页/块的芯片如果标记在最后一页那么这个值就是{127}如果标记在第一页就是{0}。总线宽度NAND_BUS_WIDTH数据总线是8位还是16位。i.MX25通常支持这两种模式需要根据硬件板子的实际布线来设定。2.3 移植工作的核心思路基于以上理解我们的移植工作可以归结为三个核心步骤其逻辑关系如下图所示概念性描述参数提取与定义从新NAND芯片的数据手册中准确提取上述所有关键参数。代码集成在BSP源代码树中为新的NAND型号创建专属的头文件.h和汇编包含文件.inc并将这些参数填入。然后修改BSP的配置文件让编译系统知道这个新型号的存在并能在编译时通过环境变量选择它。环境配置与构建在Platform Builder集成开发环境中设置对应的环境变量然后完整地重新构建BSP、XLDR、Eboot和NK镜像。整个过程的挑战不在于代码有多复杂而在于对细节的准确把握和对BSP编译系统的理解。下面我们就进入实战环节。3. 实战移植一步步适配新NAND Flash假设我们要将NAND Flash从K9LBG08U0M更换为K9LAG08U0M。请确保你已安装好i.MX25的BSP包和Platform Builder或相应的VS2005/2008开发环境。3.1 第一步创建芯片专属定义文件这是最基础的一步我们需要为新的芯片创建两个文件一个C语言头文件.h供FMD驱动使用一个汇编包含文件.inc供XLDR使用。1. 创建头文件K9LAG08U0M.h这个文件应放置在BSP的公共NAND驱动头文件目录下典型路径是\WINCE600\PLATFORM\COMMON\SRC\SOC\COMMON_FSL_V2_PDK1_x\NAND\INC\文件内容模板如下你需要用从数据手册查到的实际值替换所有XXX标记的部分#ifndef __K9LAG08U0M_H__ #define __K9LAG08U0M_H__ // NAND Flash Chip CMD #define CMD_READID (0x90) // Read ID #define CMD_READ (0x00) // Read data 1st cycle #define CMD_READ2 (0x30) // Read data 2nd cycle #define CMD_RESET (0xFF) // Reset #define CMD_ERASE (0x60) // Erase setup #define CMD_ERASE2 (0xD0) // Erase #define CMD_WRITE (0x80) // Sequential data input #define CMD_WRITE2 (0x10) // Program #define CMD_STATUS (0x70) // Read status // NAND Flash Chip Size #define NAND_BLOCK_CNT (8192) // 例如总块数 #define NAND_PAGE_CNT (128) // 例如每块页数 #define NAND_PAGE_SIZE (2048) // 例如页大小 2KB #define NAND_SPARE_SIZE (64) // 例如备用区大小 64字节 #define NAND_BUS_WIDTH (8) // 例如8位总线 // NAND Flash Chip #define NAND_NUM_OF_CS (1) // 片选数量通常为1 // NAND Flash Chip ID #define NAND_MAKER_CODE (0xEC) // 制造商ID三星为0xEC #define NAND_DEVICE_CODE (0xD3) // 设备ID需查手册 #define NAND_ID_CODE ((NAND_DEVICE_CODE 8) | NAND_MAKER_CODE) // NAND Flash Chip Operation Status #define NAND_STATUS_ERROR_BIT (0) // 状态寄存器错误位 #define NAND_STATUS_BUSY_BIT (6) // 状态寄存器忙位 // SWAP BBI (坏块信息) #define BBI_MAIN_ADDR (464) // 根据公式计算得出 #define BBI_NUM (1) // 通常为1 BYTE BBIMarkPage[1] {127}; // 例如坏块标记在每块的最后一页 #endif2. 创建汇编包含文件K9LAG08U0M.inc这个文件同样放在上述INC目录供XLDR的汇编代码使用。注意这里有些参数需要以2的幂次方形式左移位数LSH表示。;------------------------------------------------------------------------------ ; File: K9LAG08U0M.inc ; Contains definitions for K9LAG08U0M NAND flash memory device. ;------------------------------------------------------------------------------ CMD_READID EQU 0x90 ; Read ID CMD_READ EQU 0x00 ; Read data field CMD_READ2CYCLE EQU 0x30 ; Read CMD second cycle CMD_READ2 EQU 0x50 ; Read spare field CMD_RESET EQU 0xFF ; Reset CMD_ERASE EQU 0x60 ; Erase setup CMD_ERASE2 EQU 0xD0 ; Erase CMD_WRITE EQU 0x80 ; Sequential data input CMD_WRITE2 EQU 0x10 ; Program CMD_STATUS EQU 0x70 ; Read status ; 注意以下参数以2的幂次方形式定义便于汇编进行移位操作 NAND_PAGE_CNT_LSH EQU (7) ; 2^7 128 页/块 NAND_PAGE_SIZE_LSH EQU (11) ; 2^11 2048 字节/页 NAND_BLOCK_SIZE_LSH EQU (NAND_PAGE_CNT_LSHNAND_PAGE_SIZE_LSH) ; 2^18 256KB/块 ; 实际值的常量定义 NAND_PAGE_CNT EQU (1 NAND_PAGE_CNT_LSH) ; 128 NAND_PAGE_SIZE EQU (1 NAND_PAGE_SIZE_LSH) ; 2048 NAND_BLOCK_SIZE EQU (1 NAND_BLOCK_SIZE_LSH) ; 262144 (256KB) NAND_BLOCK_CNT EQU (8192) ; 总块数 NAND_SPARE_SIZE EQU (64) ; 备用区大小 BBI_PAGE_NUM EQU (1) ; BBI_NUM BBI_PAGE_ADDR_1 EQU (NAND_PAGE_CNT - 1) ; 最后一页即127 BBI_PAGE_ADDR_2 EQU (NAND_PAGE_CNT - 1) ; 如果BBI_NUM为2这里可能是另一页地址 NUM_OF_NAND_DEVICES EQU 1 ; NAND器件数量 NUM_OF_NAND_DEVICES_LSH EQU 0 ; NAND_BUS_WIDTH EQU 8 ; 总线宽度实操心得在创建这两个文件时最忌讳的就是“想当然”。NAND_PAGE_CNT_LSH和NAND_PAGE_SIZE_LSH必须严格按照2^N 实际值来计算。例如页大小2048字节是2^11那么LSH就是11。这个值用于XLDR中快速的地址计算通过移位代替乘除法。务必对照数据手册反复核对特别是设备ID和BBI相关参数。3.2 第二步修改BSP配置文件以集成新芯片创建好定义文件后我们需要告诉BSP编译系统“现在多了一个芯片选项可供选择”。这需要通过修改几个关键的配置文件来实现。1. 修改FMD层的头文件包含逻辑找到文件\WINCE600\PLATFORM\iMX25-3DS-PDK1_x\SRC\COMMON\NANDFMD\nandbsp.h这个文件是FMD驱动选择芯片型号的“总开关”。我们需要在条件编译指令中添加对新芯片头文件的引用。#ifndef __NANDBSP_H__ #define __NANDBSP_H__ #ifdef BSP_NAND_K9LBG08U0M #include K9LBG08U0M.h #elif defined(BSP_NAND_K9LAG08U0M) // 修改为#elif并添加新条件 #include K9LAG08U0M.h #else #error No NAND flash type defined! #endif #endif // __NANDBSP_H__2. 修改XLDR的汇编包含逻辑找到文件\WINCE600\PLATFORM\iMX25-3DS-PDK1_x\src\BOOTLOADER\XLDR\NAND\nandchip.inc这是XLDR选择芯片汇编定义的“总开关”。; Include definitions for selected NAND flash device IF :DEF: BSP_NAND_K9LBG08U0M INCLUDE K9LBG08U0M.inc ELIF :DEF: BSP_NAND_K9LAG08U0M ; 添加新的判断分支 INCLUDE K9LAG08U0M.inc ; 包含新芯片的定义 ELSE ; 可以保留一个默认选项或者报错 INCLUDE K9LBG08U0D.inc ; 或者其他默认芯片 ; 或者使用 ERROR “No NAND type defined!” ENDIF3. 修改FMD驱动的Sources文件找到文件\WINCE600\PLATFORM\iMX25-3DS-PDK1_x\SRC\COMMON\NANDFMD\sources这个文件控制着编译FMD库时的预处理器定义。我们需要添加一个新的条件编译块。!IF $(BSP_NAND_K9LAG08U0M) 1 CDEFINES$(CDEFINES) -DBSP_NAND_K9LAG08U0M !ENDIF !IF $(BSP_NAND_K9LBG08U0M) 1 CDEFINES$(CDEFINES) -DBSP_NAND_K9LBG08U0M !ENDIF4. 修改XLDR的Sources文件找到文件\WINCE600\PLATFORM\iMX25-3DS-PDK1_x\src\BOOTLOADER\XLDR\NAND\sources这个文件控制着编译XLDR时的汇编器定义。!IF $(BSP_NAND_K9LBG08U0M) 1 ADEFINES$(ADEFINES) -pd BSP_NAND_K9LBG08U0M SETL {TRUE} !ELIF $(BSP_NAND_K9LAG08U0M) 1 ; 添加新的判断 ADEFINES$(ADEFINES) -pd BSP_NAND_K9LAG08U0M SETL {TRUE} !ELSE ADEFINES$(ADEFINES) -pd BSP_NAND_K9LBG08U0D SETL {TRUE} ; 或其他默认 !ENDIF注意事项修改这些配置文件时务必注意语法。.h和.inc文件是C和汇编语法而sources文件是Windows CE构建系统Build System的语法。!IF、!ELIF、!ENDIF以及环境变量引用$(VAR)的格式必须正确否则会导致构建失败。建议在修改前备份原文件。3.3 第三步在开发环境中配置与构建代码修改完成后最后一步是在Platform Builder中配置环境并触发构建。设置环境变量在VS2005/Platform Builder中打开你的OS设计项目。点击菜单Project-Properties。在属性对话框中导航到Configuration Properties-Environment。点击New...按钮。在Variable name中输入BSP_NAND_K9LAG08U0M必须与sources文件中定义的名称完全一致。在Variable value中输入1。点击确定保存。这个操作相当于在构建系统中定义了一个宏当构建FMD和XLDR时!IF $(BSP_NAND_K9LAG08U0M) 1条件成立从而启用对新芯片的支持。执行清理与构建强烈建议先执行清理在Build菜单下选择Clean Solution然后选择Rebuild Solution。这能确保所有中间文件和旧的目标文件被清除避免因缓存导致的链接错误。构建整个BSP光构建NK镜像是不够的因为XLDR和Eboot也需要重新编译。正确的方法是点击Build菜单 -Advanced Build Commands-Build Current BSP and Subprojects。这个命令会按顺序编译XLDR、Eboot以及内核相关的所有BSP组件确保一致性。生成最终镜像完成BSP构建后再执行一次Build Solution来生成最终的NK.bin和Eboot.bin等镜像文件。4. 深度排查常见问题与解决实录即使严格按照步骤操作在实际移植过程中也难免会遇到各种问题。下面是我总结的几个典型故障场景及其排查思路。4.1 问题一系统无法启动停留在XLDR阶段这是最令人紧张的情况。可能的原因和排查点如下症状串口无输出或输出乱码后停止。排查思路检查XLDR汇编文件首先确认K9LAG08U0M.inc文件中的参数特别是NAND_PAGE_CNT_LSH和NAND_PAGE_SIZE_LSH计算是否正确。这两个值错误会导致XLDR计算NAND物理地址时完全错乱无法读取后续的Eboot代码。检查BBI参数确认BBI_MAIN_ADDR计算是否正确。如果地址算错XLDR在检查坏块时会读到错误的数据可能误将好块标记为坏块而跳过导致找不到有效的Eboot镜像。检查芯片ID确认NAND_MAKER_CODE和NAND_DEVICE_CODE是否与数据手册完全一致。可以用示波器或逻辑分析仪抓取上电后NAND芯片ID引脚的波形与驱动发送的0x90命令序列对比看是否匹配。检查硬件连接确认新的NAND芯片与i.MX25的硬件连接如数据线、控制线、上拉电阻是否与原理图一致特别是RBReady/Busy引脚和WPWrite Protect引脚的电平状态。4.2 问题二Eboot可以启动但加载NK时失败或系统启动后文件系统错误症状Eboot能正常从串口输出信息但在加载NK.bin时卡住报告读取错误或者系统能启动但无法访问文件系统。排查思路对比FMD与XLDR参数这是最常见的原因。务必确保K9LAG08U0M.h和K9LAG08U0M.inc中关于容量、页大小、块大小、备用区大小的定义完全一致。一个在C代码中是2048另一个在汇编中是4096必然导致灾难。检查FMD驱动中的时序或命令虽然大部分NAND命令是标准的但有些芯片可能需要特定的时序延迟或额外的命令序列。查看FMD层驱动代码如nandbsp.cpp中是否有针对特定芯片的初始化、读、写、擦除函数。你可能需要参考原芯片的驱动为新芯片添加类似的特殊处理。验证ECC配置i.MX25的NFC硬件支持ECC纠错码。不同的页大小和备用区大小可能对应不同的ECC强度如4位、8位ECC。检查BSP中关于ECC模式的配置可能在nandbsp.cpp或平台配置头文件中确保其与新NAND芯片的要求匹配。BBI_MAIN_ADDR的计算就与ECC模式强相关见附录A.2.2。使用Eboot的格式化命令如果怀疑是旧的坏块表或分区信息不兼容可以尝试在Eboot命令行中使用f命令对NAND进行低级格式化。警告此操作会清空所有数据4.3 问题三编译过程中出现链接错误或未定义符号症状构建时在命令行输出中报告“undefined symbol”或“link error”。排查思路检查环境变量名确认在Platform Builder中设置的环境变量名与sources文件中!IF判断的条件如BSP_NAND_K9LAG08U0M严格一致包括大小写。Windows CE构建系统对此是敏感的。检查文件路径确认新建的.h和.inc文件是否放入了正确的INC目录并且该目录已被包含在sources文件的INCLUDES路径中。执行完全重建删除整个工程的obj和cesysgen等输出目录然后从“Build Current BSP and Subprojects”开始重新构建。这能彻底清除陈旧的中间文件。4.4 问题速查表为了便于快速定位我将常见问题、可能原因和解决动作整理成下表问题现象可能原因优先排查点XLDR阶段无输出/死机1. XLDR汇编参数错误2. 芯片ID不匹配3. 硬件连接问题1. 核对.inc中LSH值计算2. 核对.h中制造商/设备ID3. 测量关键引脚电平Eboot启动失败1. FMD与XLDR参数不一致2. BBI地址计算错误3. 坏块标记页定义错误1. 对比.h和.inc所有尺寸参数2. 重新计算BBI_MAIN_ADDR3. 检查BBIMarkPage数组NK加载失败/文件系统错误1. ECC配置不匹配2. FMD驱动中芯片特定时序问题3. 旧分区信息干扰1. 检查BSP中ECC设置2. 对比新旧芯片数据手册时序图3. 在Eboot中格式化NAND编译错误未定义符号1. 环境变量未设置或名称错误2. 头文件路径未包含3. 源代码语法错误1. 检查项目属性中的环境变量2. 检查sources文件INCLUDES3. 检查新创建的.h文件语法5. 进阶思考与经验延伸完成一次基本的驱动移植后我们还可以从更深的层次去思考和优化这些经验往往在官方文档中不会提及。5.1 关于BBI地址计算的再理解官方文档附录A给出的BBI_MAIN_ADDR计算公式Page_Size - ((512 Spare_Per_Sector) * (Sectors_Per_Page - 1))其核心逻辑是找到NFC内部最后一个数据扇区所对应的主数据区地址。因为NFC硬件在读写时是按“扇区512字节主数据若干字节备用区”为单位处理的。坏块标记物理上位于一个页的备用区起始处但驱动需要通过操作NFC的特定缓冲区来访问它。这个计算就是为了将物理的备用区地址映射到NFC内部缓冲区的正确偏移。理解这一点就能举一反三即使未来遇到页大小、备用区大小不同的芯片也能自己推导出公式而不是死记硬背几个魔法数字。5.2 多芯片支持与运行时检测我们上述修改是基于编译时通过环境变量选择单一芯片型号。这是一种静态配置方法简单可靠。但在更复杂的场景下比如一个硬件平台可能搭载不同供应商的NAND芯片出于供应链安全考虑我们就需要实现驱动在运行时自动检测芯片型号。思路是在FMD驱动的初始化函数如FMD_Init中发送0x90命令读取芯片的制造商ID和设备ID。然后在一个预定义的芯片参数表中进行查找匹配。这个表可以包含我们为K9LBG08U0M、K9LAG08U0M等芯片定义的所有关键参数块数、页大小、BBI信息等。匹配成功后驱动动态使用对应的参数集。这样一个BSP镜像就能适配多种NAND Flash大大提高了硬件的灵活性和软件的可维护性。实现此功能需要对FMD驱动代码有更深入的掌握。5.3 性能与可靠性调优驱动移植成功只是第一步让系统稳定高效运行才是目标。有两个方面值得关注时序调整i.MX25的NFC控制器有多个时序寄存器如NFCFG、NFACCCFG1/2/3等用于配置命令、地址、数据的建立、保持和等待时间。数据手册中给出的典型值可能不是最优的。如果发现读写NAND时偶尔出错或者速度不理想可以尝试在驱动中微调这些时序参数并在不同温度下进行长时间读写压力测试以找到最稳定可靠的配置。ECC策略硬件ECC能极大提高数据可靠性。除了确保ECC强度与芯片匹配还可以在驱动中增加对ECC错误的统计和日志。当某个块的ECC纠错次数超过阈值时可以主动将其标记为坏块并将数据迁移到好块中。这种主动坏块管理策略能有效预防数据损毁对于工业级产品至关重要。移植i.MX25 WinCE 6.0的NAND Flash驱动是一项对耐心和细致度要求极高的工作。它不像开发一个应用功能那样立竿见影但却是整个系统稳定运行的基石。我的体会是成功的关键在于三点一是对硬件数据手册的精确解读二是对BSP驱动架构的清晰理解三是在修改和调试过程中保持严谨的工程习惯——每次只改一个地方做好备份充分测试。当你看到新的NAND芯片被系统正确识别并稳定运行时那种解决底层技术难题的成就感是其他工作难以替代的。希望这份详细的指南和其中的经验总结能帮助你顺利跨过这道坎。