MC68EZ328ADS开发板Flash编程实战:从Bootstrap模式到固件烧录
1. 项目概述与核心价值
如果你手头有一块Motorola(后来是Freescale,现在是NXP)的M68EZ328ADS开发板,并且正在为如何把你自己写的程序“烧”进板子上的Flash存储器而发愁,那么这篇笔记就是为你准备的。我最近在整理一些老旧的嵌入式项目资料,重新上手了这块基于MC68EZ328 DragonBall-VZ处理器的经典开发板。它的板载Flash(型号是MBM29LV160T)不能像普通内存一样直接写入,必须遵循一套特定的“解锁-写入”命令序列。官方手册虽然提供了流程,但很多细节对于实际操作来说还是不够直观,尤其是关于Bootstrap模式的理解和B-record文件的使用。经过一番折腾和查阅资料,我梳理出了一套从原理到实操的完整指南,希望能帮你绕过我踩过的那些坑。
简单来说,这个过程的核心就是利用MC68EZ328芯片内置的Bootstrap模式。这是一种特殊的启动方式,芯片上电或复位后,会从特定的地址(通常是内部ROM或通过特定引脚配置)读取一小段引导程序,并通过串口等待接收更大的程序代码。对于ADS板,我们就是利用这个特性,通过PC的串口工具(如BBUG.EXE)将擦除、编程Flash的程序以及我们自己的应用程序镜像(ROM Image)下载到板子的RAM中,然后运行这个Flash编程程序,让它把RAM里的应用程序镜像搬运并写入到Flash的指定位置。下次正常启动时,CPU就能从Flash中直接运行你的程序了。这对于产品开发阶段的固件更新、现场调试以及学习嵌入式系统启动流程至关重要。
2. 核心原理与准备工作拆解
2.1 Flash存储器编程的基本原理
板载的MBM29LV160T是一块2M字节(16Mbit)的NOR Flash。与EEPROM或SRAM不同,Flash在写入前必须先进行擦除(Erase),将目标存储单元置为全1状态(通常为0xFF)。写入(Program)操作则是将特定的位从1变为0。最关键的是,对Flash的擦除和写入操作不是通过简单的内存写指令完成的,而是需要向Flash芯片的特定地址写入一系列特定的命令字(Command Sequence)来“解锁”其内部状态机,然后才能执行真正的操作。
以这块板子使用的Flash为例,其典型的“字节/字编程”命令序列可能如下(具体需查阅MBM29LV160T数据手册):
- 向地址
0xAAA(假设)写入0xAA - 向地址
0x554(假设)写入0x55 - 向地址
0xAAA写入0xA0(编程命令) - 向目标地址写入要编程的数据
这个过程必须在程序控制下完成,这就是为什么我们需要一个专门的“Flash编程程序”(即FLASHNML.B)。这个程序会封装这些底层的、与具体Flash型号相关的命令序列,为我们提供一个简单的“从源地址复制数据到Flash目标地址”的高级接口。
2.2 Bootstrap模式:系统的后门
MC68EZ328的Bootstrap模式是这一切得以实现的基础。当芯片的BST/EMUBRK引脚(在ADS板上通过S2-8开关控制)在复位时被拉高,芯片将进入Bootstrap模式。在此模式下:
- 芯片初始化:芯片使用内部的引导ROM进行最基础的初始化,例如设置时钟、串口等。
- 等待下载:初始化后,芯片会通过UART(通常是PE4/RXD和PE5/TXD引脚)等待主机(PC)发送数据。通信协议通常是摩托罗拉定义的S-record或B-record格式。
- 执行代码:接收到的数据(一段小程序)会被加载到内部RAM或外部RAM的指定地址,然后芯片跳转到该地址开始执行。
这相当于为系统开了一个“后门”,即使Flash里是空的或者程序是错的,我们依然可以通过这个后门把修复程序“送”进去。ADS板上的S2-8拨码开关和RESET按钮就是用来触发这个模式的物理控制。
2.3 工具链与文件解析
要进行编程,你需要准备以下核心文件,它们通常包含在ADS板的开发套件EZTOOLS中:
- BBUG.EXE / STOB.EXE:这是PC端的通信工具。
BBUG.EXE是一个命令行工具,用于通过串口与目标板(在Bootstrap模式下)通信,发送B-record文件、执行程序等。STOB.EXE可能用于格式转换。在实际操作中,我们主要使用BBUG.EXE。 - INIT.B:这是一个B-record格式的文件,其内容是一段初始化代码。它的作用是配置MC68EZ328的关键寄存器,例如设置芯片选择(Chip Select)信号、DRAM控制器、系统时钟(PLL)等,为后续操作准备一个正确的硬件环境。可以把它理解为给板子做一次“快速上电自检”之外的深度硬件配置。
- ERASE.B:同样是B-record文件,它包含了一个小程序,用于执行对目标Flash存储器的全片擦除操作。擦除是写入的前提。
- FLASHNML.B:核心中的核心,即Flash编程程序。它包含了前述的Flash命令序列,以及一个数据搬运和校验循环。它的功能是将存储在系统RAM(SDRAM)中的用户程序镜像(ROM.B),按照指定的源地址和目标地址,逐个字节/字地编程到Flash中,并在写入后进行验证。
- ROM.B:你的应用程序编译链接后生成的机器码,被转换成B-record(或S-record)格式。这就是最终要烧录到Flash里的“固件”。
注意:B-record是Motorola定义的一种二进制记录格式,类似于更常见的S-record(S19格式)。它包含了地址、数据和校验信息,非常适合通过串行链路传输代码和数据。
BBUG.EXE可以直接加载并解释执行B-record文件。
3. 详细操作步骤与实战过程
下面,我将结合手册和实际操作经验,分步详解整个编程流程。请确保你的ADS板已通过串口线(通常是DB9)连接到PC,并准备好相应的串口端口号(如COM1)。
3.1 步骤一:进入Bootstrap模式
- 硬件设置:找到开发板上的拨码开关S2。将第8位(S2-8)拨到“ON”的位置。这个动作将
BST/EMUBRK引脚拉高,告诉芯片下一次复位时进入Bootstrap模式。 - 复位板子:按下开发板上的
RESET按钮。此时,MC68EZ328会执行内部Bootstrap ROM代码,初始化UART,并等待主机连接。 - 观察指示灯:有些板子在进入Bootstrap模式后,特定LED会闪烁或常亮,具体需参考板级说明。但最可靠的判断方式是进行下一步的通信测试。
3.2 步骤二:建立串口通信
- 启动BBUG:在PC上打开命令行(CMD或终端),导航到EZTOOLS工具所在目录,执行命令:
BBUG.EXE -p <COM_PORT> -b 9600。这里-p指定你的串口号(如COM3),-b指定波特率(ADS板Bootstrap模式通常固定为9600 bps,但请以手册为准)。 - 连接测试:如果连接成功,BBUG通常会显示一个简单的提示符(如
>或BBUG>),或者输出一些启动信息。此时,你可以尝试输入简单的命令,如?或help来查看支持的命令列表。常见的命令包括load(加载B-record)、go(执行程序)、dump(查看内存)等。
实操心得:如果BBUG没有任何响应,首先检查串口线、端口号和波特率。其次,确认S2-8是否在复位前已拨到ON。有时需要给板子重新上电,并确保在按下RESET后立即启动BBUG进行连接。老式串口对时序比较敏感。
3.3 步骤三:加载并执行初始化程序
在BBUG命令行中,执行加载命令来运行INIT.B:
load init.b这条命令会让BBUG将INIT.B文件通过串口发送给目标板。目标板接收到B-record后,会自动将其内容(即初始化代码)载入到B-record中指定的地址(这个地址被编码在B-record文件内部),并可能自动跳转执行,或者需要你手动使用go <address>命令来启动。
INIT.B的执行是静默的,它主要是在配置硬件寄存器。执行成功后,系统内存控制器、时钟等都已就绪,为后续在外部SDRAM中运行程序做好了准备。
3.4 步骤四:擦除Flash存储器
在编程新固件前,必须确保目标Flash区域是空的(全0xFF)。执行擦除命令:
load erase.bERASE.B程序会被加载并执行。它会向Flash发送完整的擦除命令序列。对于MBM29LV160T,这通常是一个片擦除(Chip Erase)操作,耗时可能从几百毫秒到几秒不等。程序内部会通过查询RY/BY#引脚或状态寄存器位来等待擦除完成。
注意事项:擦除操作是不可逆的,会清除整个Flash芯片的数据。请确保在擦除前,如果需要保留某些数据(如Bootloader、配置参数),你已经通过其他方式进行了备份,或者你的编程流程是整体更新。
3.5 步骤五:加载编程程序和用户镜像
这是最关键的一步,需要将两个文件依次加载到RAM中。
- 加载Flash编程器:
load flashnml.b这个命令将FLASHNML.B(即我们那个封装了Flash命令序列的程序)加载到它的链接地址(例如手册中提到的0x4000)。这个程序会常驻在RAM中,等待被调用。 - 加载用户程序:
load rom.b这个命令将你的应用程序镜像ROM.B加载到RAM中。这里有一个关键概念:偏移地址(Offset)。ROM.B文件本身有一个“下载地址”(Load Address),但Flash编程程序需要知道它在RAM中的“源地址”(Source Address)和要写入Flash的“目标地址”(Target Address)。这两个地址可能不同。
3.6 步骤六:执行Flash编程
当FLASHNML.B和ROM.B都正确加载到RAM后,就需要启动Flash编程程序。根据手册,如果FLASHNML.B的起始地址是0x4000,那么执行以下B-record命令即可:
0000400000这个B-record的含义是:数据长度为0,起始地址为0x4000,记录类型为0(执行记录)。发送给目标板后,CPU就会跳转到0x4000开始执行FLASHNML.B。
FLASHNML.B程序开始工作后,它会:
- 读取其内部参数(或通过固定地址)获得源地址(
pSOURCE,即ROM.B在RAM中的位置)、目标地址(pTARGET,即Flash中的起始地址,如0x01000000)和大小(pSIZE)。 - 进入主循环,对每一个数据单元(通常是16位字): a. 调用
ENABLE宏,向Flash发送编程解锁命令序列(0xAA到0xAAA,0x55到0x554,0xA0到0xAAA)。 b. 将源地址的一个字写入目标地址(Flash地址)。 c. 进入轮询(POLLING)状态,反复读取刚写入的Flash地址,并与源数据比较,直到写入成功(或超时)。Flash写入需要时间,轮询是通过检查写入操作是否完成的标准方法。 d. 如果验证成功,移动源和目标地址指针,继续下一个字。 - 全部写入完成后,可能会再次进行整体验证(
VERIFY部分),确保所有数据一致。 - 最终通过串口输出
PASS或ERROR信息(从附录C的源代码中可以看到它输出了PASS或ERROR字符串)。
3.7 步骤七:验证与退出
- 验证输出:在BBUG终端中,你应该能看到
FLASHNML.B程序输出的进度字符(如W表示正在写入,V表示正在验证)以及最终的PASS提示。如果看到ERROR,则需要根据错误地址排查问题(常见原因有地址错误、Flash型号不匹配、电源不稳等)。 - 退出Bootstrap模式:编程完成后,将S2-8拨码开关拨回“OFF”位置。
- 复位板子:再次按下
RESET按钮。这次,MC68EZ328将从Flash的默认启动地址(通常是0x00000000或0x01000000,取决于硬件设计)开始执行你刚刚烧录进去的ROM.B程序。
4. 关键问题排查与深度解析
4.1 偏移地址(Offset)问题详解
这是最容易出错的地方。手册中的图C-1和说明指出了“偏移地址”的概念。为什么需要它?
你的编译器/链接器在生成ROM.B时,会假设程序将在某个地址运行(运行地址,Run-Time Address)。例如,你希望程序最终在Flash的0x01000000运行。但是,在编程过程中,ROM.B必须先被下载到RAM中(比如地址0x00010000),然后由FLASHNML.B复制到Flash的0x01000000。
这里有两种情况:
- 情况A(理想情况):你的
ROM.B文件在生成时,其数据记录里的地址就是0x01000000。BBUG.EXE的load命令会智能地把它下载到0x01000000吗?不会。Bootstrap加载器通常会将数据加载到B-record中指定的地址。如果B-record里说数据属于0x01000000,但当前RAM可能没有映射到这个地址,或者这个地址就是Flash本身(不可写),加载就会失败。 - 情况B(常用方法):你生成
ROM.B时,指定其下载地址(Load Address)为RAM中的一个空闲区域,例如0x00010000。这样,load rom.b命令能成功将其加载到RAM的0x00010000。但是,FLASHNML.B程序需要知道两件事:1)源数据在哪(0x00010000);2)要复制到哪(0x01000000)。
FLASHNML.B的源代码(附录C.5)开头有一个参数区(SECTION parameter),里面定义了pSOURCE,pTARGET,pSIZE。在编译FLASHNML.B这个工具本身时,这些参数就被固定写死在二进制文件里了。因此,你必须确保你使用的FLASHNML.B和ROM.B是配套的,即FLASHNML.B中pSOURCE的值等于ROM.B被加载到RAM的实际地址。
如果你需要改变地址,通常有两个方法:
- 修改源程序并重新编译:修改
FLASHNML.ASM(或类似汇编源文件)中的pSOURCE和pTARGET定义,然后使用对应的汇编器(如ASM.EXE)和格式转换工具(如STOB.EXE)重新生成FLASHNML.B。 - 使用工具参数:如手册C.4节提到的,使用像
DOWN.EXE这样的工具生成S-record时,可以用-w offset参数指定一个偏移。这本质上是在生成记录文件时,就调整了其中的地址信息。例如,程序链接地址是0x01000000,但用-w 0x00010000参数,生成的记录文件中的地址会变成0x00010000(链接地址减去偏移),这样它就可以被正确加载到RAM的0x00010000,而FLASHNML.B中的pSOURCE也需对应设置为0x00010000,pTARGET设为0x01000000。
4.2 常见错误与解决方法
| 问题现象 | 可能原因 | 排查步骤与解决方法 |
|---|---|---|
| BBUG无法连接 | 1. 串口线或端口错误 2. 波特率不匹配 3. Bootstrap模式未成功进入 4. 板子供电问题 | 1. 检查设备管理器的串口号,更换串口线。 2. 确认BBUG命令中的波特率与ADS板Bootstrap ROM设定的波特率一致(通常9600)。 3. 确认S2-8在复位前已拨到ON,复位后立即尝试连接。可尝试多次复位。 4. 检查电源指示灯,确保板子供电正常。 |
加载.B文件时出错或板子无响应 | 1. 文件损坏或格式不对 2. 加载地址错误(如试图加载到未初始化的内存区域) 3. 初始化未完成 | 1. 用文本编辑器打开.B文件,检查开头是否为S0或S1等(S-record),或类似二进制格式。确保文件传输完整。2. 确保先执行了 INIT.B来配置内存控制器,使目标RAM区域可访问。3. INIT.B必须第一个加载并执行。 |
| 擦除���编程过程中报错(ERROR) | 1. Flash型号不匹配,命令序列错误 2. 电源电压不稳定,导致编程电压不足 3. 时序问题(等待时间不足) 4. 目标地址超出Flash物理范围 | 1.核对Flash型号:确认板上Flash芯片确是MBM29LV160T。不同厂商(如AMD、Fujitsu)的Flash命令序列可能有细微差别。附录C.5的代码是针对MBM29LV160T的。 2. 使用示波器检查板子供电,尤其在编程瞬间是否有跌落。确保使用稳定电源。 3. 检查 FLASHNML.B源码中的轮询超时值(TIME equ $FFF),如果Flash写入较慢,可以增大这个值。4. 检查 pTARGET地址是否在Flash的地址映射范围内(ADS板Flash通常映射在0x01000000开始)。 |
| 编程成功但板子复位后不运行 | 1. 程序入口点错误 2. 中断向量表未正确设置或搬运 3. 编程后未正确复位(需退出Bootstrap模式) | 1. 确保你的应用程序(ROM.B)的链接脚本中,启动代码的入口地址正确,且与Flash映射的启动地址匹配。2. 对于MC68EZ328,复位后CPU会从地址 0x00000000读取初始SP和PC。你的程序需要将中断向量表(特别是复位向量)正确放置在Flash的对应位置。这可能需要在链接脚本或启动代码中特殊处理。3. 编程完成后,务必将S2-8拨回OFF,再按RESET,否则板子会再次进入Bootstrap模式,而不是从Flash启动。 |
4.3 关于附录D:监控程序初始化代码的解读
附录D提供了Metrowerks和SDS两种监控程序(Monitor)的初始化代码。监控程序是驻留在开发板Flash/ROM中的一段调试代理程序,它通常提供内存查看、修改、断点、单步执行等调试功能。这里的初始化代码RESET.S和MONITOR.H展示了在一个完整系统中,MC68EZ328上电后需要进行的全面硬件初始化,远比INIT.B要复杂。
RESET.S(Metrowerks):这是一段汇编启动代码。它设置了堆栈指针(A7)、屏蔽所有中断(move.w #$2700,sr)、配置PLL系统时钟、关闭看门狗、最关键的是配置了芯片选择(Chip Select)和DRAM控制器。例如:move.w #$0400,GRPBASEA和move.w #$0189,CSA设置了组A基地址和属性,将Flash映射到0x800000。move.w #$8F00,DRAMCFG和move.w #$9667,DRAMCTL配置了EDO DRAM的时序和模式。move.w #$0000,GRPBASED和move.w #$069F,CSD使能了DRAM片选,将其映射到0x000000开始的地址。 这些配置决定了处理器如何访问外部存储器,是系统能正常运行的基石。你的应用程序的启动代码(C运行时环境初始化前)通常也需要包含类似配置。
MONITOR.H(SDS):这是一个C头文件风格的配置集,通过宏定义(RESET_HARD,RESET_SOFT)来组织初始化步骤。其内容与RESET.S本质相同,但以更模块化的方式呈现,方便集成到不同的编译环境中。
理解这两份代码的价值在于:当你需要为自己的应用程序编写启动代码(Bootloader或直接运行的固件)时,你可以参考这些配置来正确初始化你的硬件环境,尤其是内存系统。INIT.B可以看作这些配置的一个最小、最关键的子集,仅保证能运行后续的Flash编程程序。
5. 总结与进阶思考
通过以上步骤,你应该能够成功地对M68EZ328ADS开发板的板载Flash进行编程。这个过程本质上是一个“引导加载程序(Bootloader)”的简易实现:利用芯片固有的Bootstrap模式加载一个一次性的Flash驱动编程器,再由这个编程器将真正的应用程序固化到非易失性存储器中。
对于更复杂的项目,你可以考虑:
- 编写自己的Bootloader:将
FLASHNML.B的功能进行扩展,集成到你的应用程序中。这样,你的产品就可以通过串口、USB甚至网络来接收新的固件并自行更新,无需再手动操作拨码开关和BBUG工具。 - 理解链接脚本(Linker Script):深入研究你的开发工具链(如CodeWarrior、GCC for m68k)的链接脚本。它定义了代码、数据、堆栈在内存中的布局,以及最终的运行地址。正确配置链接脚本是解决“偏移地址”问题的根本。
- 调试技巧:如果编程失败,可以尝试用BBUG的
dump命令查看RAM中ROM.B的数据是否正确,或者用go命令直接跳转到RAM中的程序地址运行,以排除是否是程序本身的问题。
处理这些老旧的硬件和工具虽然有时显得繁琐,但正是通过这些底层操作,你能更深刻地理解嵌入式系统从“一片空白”到“运行程序”的完整链条。每一次成功的烧录,都是对硬件、软件和协议协同工作的一次验证。希望这篇详细的指南能帮你顺利点亮那块尘封的ADS板。
