1. 项目概述与核心价值
在嵌入式DSP系统开发中,如何让一段精心编写的代码在芯片上电后“活”起来,是每个工程师必须跨过的第一道门槛。这背后依赖的,就是Bootloader(引导加载程序)。它不是用户应用的一部分,却是应用能够正确执行的“幕后推手”。对于德州仪器(TI)经典的TMS320C20x系列DSP,尤其是C203和C206/LC206,其Bootloader的设计体现了早期嵌入式系统在资源受限环境下追求灵活性与可靠性的智慧。今天,我们就来深入拆解这套Bootloader的运作机理,特别是其支持的多模式启动方案。理解它,不仅能让你搞定这些老芯片的启动问题,更能深刻领悟嵌入式系统初始化的通用设计思想。无论你是正在维护一个基于C20x的遗留系统,还是单纯对底层启动流程感兴趣,这篇文章都将提供从原理到实操的完整视角。
Bootloader的核心任务很明确:在处理器复位后,自动将存储在非易失性存储器(如EPROM)中的用户程序,搬运到可执行的内存区域(通常是更快的RAM中),并跳转到程序入口开始执行。对于C20x系列,这个过程并非一成不变,它通过硬件引脚和配置字,支持从并行EPROM、同步/异步串口甚至并行I/O端口加载代码,为不同的应用场景(如批量生产的固化启动、开发阶段的串口下载、多处理器系统中的主机加载)提供了多样化的选择。接下来,我们将从最经典的C203 EPROM启动模式入手,逐步剖析其流程,并扩展到C206增强型Bootloader的丰富模式。
2. TMS320C203 Bootloader 原理与执行流程详解
C203的Bootloader是理解整个C20x系列启动逻辑的基础。它是一种相对固定的模式,主要服务于从外部8位EPROM启动的场景。
2.1 硬件准备与使能条件
要让C203的Bootloader工作,需要满足几个硬件和配置条件:
- MP/MC引脚置低:这个引脚决定DSP是工作在微处理器模式(Microprocessor)还是微计算机模式(Microcomputer)。Bootloader功能位于片内ROM中,只有当MP/MC引脚在复位时被采样为低电平(微计算机模式),CPU才会从片内ROM的起始地址(0xFF00)开始执行,这里存放的正是Bootloader代码。
- 外部EPROM已编程并安装:用户程序需要预先通过编程器烧录到一块8位宽的EPROM中。这块EPROM需要被映射到DSP的全局数据存储空间(Global Data Memory)的起始地址0x8000处。
- 硬件复位:上电,或者将RS(复位)引脚拉低再置高,会触发一个硬件复位,启动Bootloader流程。
注意:这里的“全局数据存储空间”是一个关键概念。C20x DSP的地址空间可以划分为程序空间(Program Space)和数据空间(Data Space)。通过全局存储器分配寄存器(GREG),可以将一段64K字(Word)的地址范围定义为全局空间,该空间内的存储单元既能被程序空间访问,也能被数据空间访问。Bootloader正是利用了这一特性,从数据空间读取EPROM(源),向程序空间的RAM写入代码(目标)。
2.2 Bootloader 执行五步法
一旦条件满足,Bootloader便开始自动执行。其核心是一个五步循环搬运过程,我们可以将其理解为一段硬编码在ROM中的固定流程:
第一步:读取目标地址Bootloader首先从EPROM的起始地址(0x8000)读取两个字节(一个16位字)。这个字被解释为用户代码在程序空间中的起始地址(Destination Address)。在典型的C203应用中,这个地址通常是0x0000,因为中断向量表必须位于程序空间的开头。
第二步:读取代码长度接着,Bootloader读取下一个16位字。这个字定义了紧随其后的、需要被搬运的用户代码的长度(Length),单位是字(Word)。
第三步:搬运一个字的数据这是循环的核心步骤。Bootloader再读取两个字节,先读高字节(MSB),后读低字节(LSB),组合成一个16位的程序字。然后,它将这个字写入到第一步确定的目标地址指向的程序内存位置。完成后,源地址(EPROM地址)和目标地址(程序RAM地址)都自动加1,指向下一个待处理的数据单元。
第四步:循环判断Bootloader检查是否已经搬运了第二步中指定长度的所有代码字。
- 如果未完成:则跳回第三步,继续读取、组合、写入下一个字。
- 如果已完成:则进入第五步。
第五步:收尾与跳转当所有指定长度的代码搬运完毕后,Bootloader执行收尾操作:
- 禁用全局内存:将GREG寄存器清零,关闭全局数据内存映射。这步操作将之前临时映射的EPROM区域从内存空间中移除,恢复正常的存储器配置。
- 强制跳转:执行一条
INTR 0软件中断指令。在C20x中,中断0是复位中断,其向量位于程序内存的0x0000地址。这条指令会强制CPU跳转到0x0000地址开始执行。这里有一个至关重要的细节:INTR指令会将返回地址压入堆栈,但Bootloader程序本身并不执行RET(返回)指令。因此,你的用户程序在开始处,必须执行一条POP指令,将这个无用的返回地址从堆栈中清除,否则会导致堆栈指针错乱,引发不可预知的错误。
整个流程中,数据仅通过数据总线的低8位(D7-D0)进行读取,高8位被忽略。这也是为什么要求使用8位EPROM的原因。下图清晰地展示了数据从8位EPROM到16位RAM的搬运格式:
EPROM地址 数据 (8位) RAM地址 数据 (16位) 0x8000 目标地址高字节(00h) 0x0000 代码字1 (高+低) 0x8001 目标地址低字节(00h) 0x0001 代码字2 (高+低) 0x8002 长度高字节(Nh) ... ... 0x8003 长度低字节(Nl) ... ... 0x8004 代码字1高字节 0xnnnE 代码字n (高+低) 0x8005 代码字1低字节 0xnnnF 代码字n (高+低) 0x8006 代码字2高字节 0x8007 代码字2低字节 ... ... ... ... 0xnnnE 代码字n高字节 0xnnnF 代码字n低字节2.3 中断向量表的处理要点
C203的中断向量表固定位于程序内存的0x0000-0x003F区域。因此,在组织EPROM中的代码映像时,必须将中断向量表放在最前面,确保它们被搬运到RAM的0x0000-0x003F地址。每个中断向量是一条分支指令,占用4个字节(2个字)。总共32个中断向量槽位,占用了128个字节(64个字)。所以,EPROM中开头的128个字节,就应该存放这组中断向量。
实操心得:在编写链接器命令文件(.cmd)时,务必确保
.vectors段或包含中断服务程序跳转指令的代码段,其加载地址(Load Address)被明确指定在EPROM映像的开头(例如0x8004,因为前4个字节是目标地址和长度),而其运行地址(Run Address)则必须在0x0000。这样,经过汇编和链接生成的.out文件,再通过Hex转换工具生成二进制或Hex格式的EPROM编程文件时,中断向量才能被正确放置。
2.4 C203 Bootloader 程序代码浅析
虽然Bootloader代码固化在ROM中,但研究其汇编实现有助于深刻理解其行为。代码的核心逻辑与我们描述的五步法完全对应:
- 初始化:设置数据页指针(DP)、状态寄存器(ST0, ST1),最关键的是配置GREG寄存器为0x80,将地址0x8000-0xFFFF映射为全局数据空间,以便访问EPROM。
- 确定目标地址(ADDR段):使用辅助寄存器AR1指向源地址0x8000,分两次读取高低字节,组合成16位目标地址并存储。
- 确定代码长度(LEN段):同样方法,读取下一个字作为长度N,并将N存入循环计数器AR0。
- 循环搬运(LOOP段):这是一个
BANZ(非零则跳转)循环。每次循环读取两个字节组成一个字,使用TBLW(表写)指令写入程序内存的目标地址,然后递增目标地址。循环次数由AR0控制。 - 结束处理:循环结束后,清零GREG寄存器禁用全局内存,然后执行
INTR 0跳转到用户代码。
这段代码的精妙之处在于大量使用了C2x系列高效的寻址和循环指令,在有限的ROM空间内实现了稳定的数据搬运功能。
3. TMS320C206/LC206 增强型Bootloader与多模式启动
C206/LC206在C203的基础上,大幅增强了Bootloader的功能,引入了可配置的多模式启动,极大地提升了灵活性。其核心思想是:通过采样EXT8引脚和在I/O空间读取一个“引导例程选择字”(BRS Word),来决定使用哪一种启动方式。
3.1 启动模式决策树
C206的上电启动逻辑是一个清晰的决策过程:
- 第一级判断:MP/MC引脚。若为高(微处理器模式),则从外部存储器0x0000开始执行,不启动片内Bootloader。若为低(微计算机模式),则进入片内ROM的Bootloader。
- 第二级判断:EXT8引脚。Bootloader会读取PMST寄存器的
LEVEXT8位(该位锁存了复位时EXT8引脚的状态)。- EXT8 = 低:进入“C203风格”的Bootloader(模式1)。这是一种兼容模式,行为与前述C203 Bootloader类似,但有一些细微差别(例如最终跳转方式)。
- EXT8 = 高:进入“C206增强型”Bootloader。此时,Bootloader会去读取I/O空间地址0x0000处的一个16位字。
- 第三级判断:BRS Word。读取的I/O字,其低8位决定了具体的启动模式。高8位被忽略。这个字可以通过硬件DIP开关、上拉/下拉电阻或外部逻辑电路来设置。
可用的增强型启动模式(模式2-9)包括:
- 模式2:8位同步串行口(SSP)启动,外部提供帧同步和时钟。
- 模式3:16位同步串行口(SSP)启动,外部提供帧同步和时钟。
- 模式4:8位并行I/O启动(使用BIO/XF握手)。
- 模式5:16位并行I/O启动(使用BIO/XF握手)。
- 模式6:8位异步串行口(UART/ASP)启动。
- 模式7:8位并行EPROM启动。
- 模式8:16位并行EPROM启动。
- 模式9:热启动(Warm-Boot)模式。
3.2 关键寄存器:PMST
程序存储器状态寄存器(PMST, I/O地址0xFFE4)在启动配置中扮演了重要角色。除了锁存EXT8状态的LEVEXT8位,它还控制着片内SARAM的映射方式(通过PON和DON位),以及MP/MC模式。Bootloader执行前,会进行如下初始化:
- 将片内DARAM的B0块映射到程序空间(
CNF=1)。 - 将片内SARAM块同时映射到程序和数据空间(
PON=1,DON=1)。 - 全局关闭中断(
INTM=1)。 - 为所有外部存储空间设置7个等待状态。
这些初始化确保了Bootloader运行时,有足够的片内RAM(B0和SARAM)可用于暂存数据和执行代码,并且访问外部低速EPROM时插入足够的等待周期以保证可靠性。
3.3 内存映射与目标地址空间
理解C206 Bootloader的内存映射至关重要,它决定了你的用户代码可以被加载到哪里。与C203不同,C206的Bootloader ROM占据了程序空间的0x0000-0x7FFF(当MP/MC=0时)。因此,在增强型Bootloader模式(模式2-9)下,用户代码不能加载到0x0000-0x7FFF这个区域,否则会覆盖Bootloader本身。可用的目标地址空间主要是:
- 片内SARAM(0x8000-0x8FFF)。
- 外部程序RAM(0x9000-0xFDFF)。
警告:SARAM的0x8000-0x807F区域被保留用于第二中断向量表(下文详述)。在将代码加载到SARAM区域时,必须避开这段地址,或者确保你的代码不会破坏其中的向量。
对于C203风格模式(模式1)和热启动模式(模式9),其目标地址空间有所不同,具体可参考芯片手册中的内存映射图。设计链接器命令文件时,必须根据所选启动模式,正确设置代码段的运行地址。
3.4 中断向量重映射机制
这是C206 Bootloader设计中一个非常巧妙且重要的特性。由于Bootloader ROM占用了低地址空间,用户的中断向量不能直接放在0x0000。C206采用了两级中断向量表机制:
- 第一级向量表(ROM中):位于片内ROM的0x0000-0x0040。当中断发生时,CPU首先跳转到这里的固定地址。
- 第二级向量表(SARAM中):位于片内SARAM的0x8000-0x807F。ROM中的第一级向量表里,每条指令都是一个分支(Branch)指令,跳转到SARAM中对应的第二级向量地址。
- 用户中断服务程序:第二级向量表中,存放的是跳转到用户实际中断服务程序(ISR)的指令。
因此,用户的启动代码必须负责初始化这片SARAM中的第二级向量表。例如,你需要将定时器中断服务程序的入口地址,通过一条分支指令,写入到SARAM中对应的位置(如tintv= 0x8004)。下面是一个初始化示例的片段:
; 定义第二级向量表在SARAM中的地址 int1_holdv .set 8000h ; 用户可屏蔽中断1 tintv .set 8004h ; 定时器中断向量 ; ... 其他向量定义 .sect ".vectors_remap" ; 定义一个段,用于存放第二级向量 B _c_int0 ; 复位向量,跳转到用户主程序入口_c_int0 NOP NOP B _int1_isr ; 跳转到用户定义的中断1服务程序 NOP NOP B _timer_isr ; 跳转到用户定义的定时器中断服务程序 NOP NOP ; ... 初始化其他向量在你的链接器命令文件中,需要确保这个.vectors_remap段被加载到EPROM中合适的位置,并且在运行时定位(Run-Time Location)到SARAM的0x8000起始处。
4. 各启动模式数据传输格式与协议解析
不同的启动模式,其数据流格式和硬件接口协议各不相同。理解这些格式是正确准备启动映像和设计硬件连接的前提。
4.1 同步串行口(SSP)启动模式(模式2 & 3)
这种模式适用于通过同步串行接口(如SPI)从主机或其他设备加载代码。时钟(CLKX/CLKR)和帧同步信号(FSX/FSR)需要由外部主机提供。
数据格式(以16位模式3为例):
- 第一个16位字:目标地址(Destination16)。
- 第二个16位字:本段代码长度(Length16)。注意:这里的长度N = (实际代码字数 - 1)。例如,要传输100个字的代码,则此处应写入99(0x0063)。
- 后续N个16位字:实际的程序代码。
- 段结束判断:传输完一段代码后,DSP会检查下一个收到的字。
- 如果为0x0000,表示所有代码传输结束,DSP跳转到第一段代码的目标地址执行。
- 如果不为0x0000,则将其解释为下一段代码的目标地址,并开始新一轮的“长度+代码”传输。这允许将代码加载到多个不连续的内存区域。
8位模式(模式2)与16位模式类似,只是每个16位字由两个连续的8位字节组成,先高字节后低字节。因此,目标地址和长度各需2个字节(4个8位传输),代码也是以字节流形式组织。
硬件连接提示:主机需要与DSP的SSP接口正确连接。通常,主机作为主设备(Master),提供时钟和帧同步信号。需要仔细配置SSP的时钟极性和相位,确保数据采样边沿对齐。在Bootloader阶段,DSP的SSP是被动接收方,其相关寄存器已由ROM代码初始化。
4.2 异步串行口(UART/ASP)启动模式(模式6)
这是非常实用的模式,允许通过PC的串口(RS-232)直接给DSP下载程序,极大方便了开发和调试。
数据格式:与SSP的8位模式(模式2)完全相同。特殊协议:在开始传输实际的代码数据流之前,主机必须先发送一个ASCII字符 ‘a’ (或 ‘A’)。DSP的Bootloader代码会检测这个字符来自动适应波特率(自动波特率检测),并回显一个 ‘a’ 作为应答。此后,DSP才准备接收正式的代码数据流。工具链:TI提供的DSPHEX工具可以将链接生成的COFF格式(.out)文件转换为适合UART Bootloader的Hex文件。但需要注意,这个工具生成的Hex文件不包含起始的 ‘a’ 字符和结束的0x0000字。你需要手动(或用脚本)在Hex文件开头添加 ‘a’,在文件末尾添加0x0000。
一个典型的DSPHEX命令文件(.cmd)示例如下:
/* DSPHEX command file for UART bootloader */ my_app.out /* 输入文件 */ -a /* 输出ASCII-Hex格式 */ -o my_app.hex /* 输出文件名 */ -byte /* 按字节组织 */ -order MS /* 高位字节在前 */ -memwidth 8 /* 存储器宽度8位 */ -romwidth 8 /* ROM宽度8位 */ SECTIONS { .text: boot = 0x9000 /* 指定.text段的加载地址,例如到外部RAM */ }生成my_app.hex后,你需要用文本编辑器在文件开头插入字符a,并确保最后一行数据之后有0000(或手动添加)。
4.3 并行EPROM启动模式(模式7 & 8)
这是最传统和常见的启动方式,与C203模式类似,但更灵活。
核心配置:模式选择(7或8)由BRS字的低两位决定。源地址由BRS字中的6位SRCE_AD字段决定。这6位作为高6位,与低10位全零组成一个16位地址。这意味着EPROM必须被映射到以0x0000,0x0400,0x0800...为边界的全局数据存储空间页上。例如,若SRCE_AD=100000b(0x20),则源地址为0x8000(因为0x20 << 10 = 0x8000)。
数据格式:
- 16位模式(模式8):与SSP的16位模式格式完全一致。从源地址开始,依次读取:目标地址、长度、代码块...,以0x0000结束。
- 8位模式(模式7):与SSP的8位模式格式一致。从源地址开始,以字节流形式读取:目标地址高、低字节,长度高、低字节,代码高、低字节...。
重要延迟:Bootloader在从EPROM读取一个字和向目标地址写入一个字之间,会插入至少4个指令周期的延迟。这个延迟是必要的,特别是当目标地址在外部快速SRAM时,它确保了在写操作发生前,有足够的时间关闭EPROM(源)的数据总线驱动,防止总线冲突。
4.4 并行I/O启动模式(模式4 & 5)
此模式允许通过一个简单的8位或16位并行端口(映射到DSP的I/O空间地址0x0001)加载代码,适用于与慢速主机处理器(如MCU)通信。
数据格式:与并行EPROM模式相同。握手协议:这是该模式的关键。它使用DSP的BIO(分支控制输入)和XF(外部标志输出)引脚进行硬件握手。
- 主机请求发送:主机将
BIO引脚拉低,表示“数据已就绪在I/O端口0x0001”。 - DSP读取并应答:DSP检测到
BIO为低,立即从I/O端口0x0001读取数据,然后将XF引脚拉高,表示“数据已接收”。 - 主机释放请求:主机看到
XF变高后,将BIO拉高。 - DSP准备就绪:DSP检测到
BIO变高后,将XF拉低,表示“准备好接收下一个数据”。 - 循环:主机可以开始准备下一个字,然后再次拉低
BIO,重复上述过程。
这个协议保证了主机和DSP之间速度的匹配。同样,在XF上升沿和实际写内存操作之间,也有至少4个指令周期的延迟,为主机释放数据总线留出时间。
4.5 热启动模式(模式9)
热启动模式并不搬运任何代码。它适用于系统需要“软复位”的场景——即DSP核心复位,但外部RAM中的用户代码仍然保持有效。在此模式下,Bootloader只是简单地跳转到一个指定的入口地址。
入口地址:由BRS字中的6位ADDR_bb字段指定。与EPROM源地址计算类似,这6位作为高6位,低10位为零,形成入口地址。因此,入口地址必须是0x0400的整数倍(如0x0000, 0x0400, 0x0800等)。
使用场景:假设你的应用程序已经通过其他方式(如之前的EPROM启动或串口下载)加载到了外部RAM的0x9000地址并正在运行。此时,如果触发一个热复位(可能通过看门狗或软件命令),且配置为模式9,BRS字中ADDR_bb指向0x9000所在的页(例如0x9000 >> 10 = 0x24),那么Bootloader会直接跳转到0x9000执行,实现了快速复位而不需要重新加载代码。
注意事项:热启动模式依赖于一个存储在片内B2 RAM中的变量,该变量保存了上一次成功启动的入口地址。如果你的应用程序覆盖了这个内存位置,则热启动可能失败。此时,就必须依靠BRS字中指定的
ADDR_bb字段来提供正确的跳转地址。
5. 实战指南:从代码生成到烧录的完整流程
理解了原理,我们来看如何实际操作。这里以最常用的**C206 8位并行EPROM启动(模式7)**为例,梳理从编写代码到生成可烧录文件的完整流程。
5.1 开发环境与工具链准备
你需要以下工具:
- 代码编写与编译:TI的TMS320C2x/C5x汇编器/编译器。虽然古老,但仍有社区维护的版本或可在旧版CCS中找到。
- 链接器:用于将多个目标文件(.obj)合并成一个可执行文件(.out),并分配内存地址。
- Hex转换工具:如
hex6x.exe或DSPHEX,用于将COFF格式的.out文件转换为二进制(.bin)或Intel Hex(.hex)格式,供EPROM编程器使用。 - EPROM编程器:硬件设备,用于将二进制文件烧录到物理EPROM芯片中(如27C256, 29C010等)。
5.2 编写链接器命令文件(.cmd)
这是最关键的一步,它定义了代码和数据在内存中的“居住规划”。
/* 示例:my_boot.cmd */ MEMORY { PAGE 0: PROG (RWIX): origin = 0x9000, length = 0x2000 /* 外部程序RAM */ PAGE 1: DATA (RWIX): origin = 0x0800, length = 0x0800 /* 内部数据RAM B2 */ PAGE 0: VECT_REMAP (RWIX): origin = 0x8000, length = 0x0080 /* SARAM中的第二向量表 */ PAGE 0: BOOT_ROM (RIX): origin = 0x8000, length = 0x8000 /* EPROM在全局数据空间的映射地址 */ } SECTIONS { /* 第二级中断向量表:必须放在SARAM的0x8000 */ .vectors_remap: load = BOOT_ROM, run = VECT_REMAP, PAGE = 0 { /* 链接器会将vectors.obj中该段的内容放进来 */ } /* 主程序代码段 */ .text: load = BOOT_ROM, run = PROG, PAGE = 0 { /* 链接器会将各模块的.text段放进来 */ } /* 已初始化的全局/静态变量 */ .data: load = BOOT_ROM, run = DATA, PAGE = 1 { /* 链接器会将.data段放进来 */ } /* 未初始化的变量段 */ .bss: run = DATA, PAGE = 1 { /* .bss段只定义运行地址,加载时不占空间 */ } }关键点:
load地址:代码/数据在**EPROM(源)**中的存放位置。.vectors_remap和.text的load地址都在BOOT_ROM(0x8000起始),这意味着它们在生成的二进制文件中是连续的。run地址:代码/数据在**DSP内存(目标)**中的执行位置。.vectors_remap必须运行在0x8000(SARAM),.text运行在0x9000(外部RAM)。- Bootloader会按照我们在第4.3节描述的格式,从
load地址处读取数据,搬运到run地址。
5.3 编写启动代码(Startup Assembly)
你需要一个汇编文件(如startup.asm)来放置第二级中断向量表和最初始的引导代码。
; startup.asm .ref _c_int00 ; 引用C语言入口函数 .ref _timer_isr ; 引用定时器中断服务函数 .sect ".vectors_remap" RESET_RMP: B _c_int00 ; 复位向量,跳转到C入口 NOP NOP INT1_RMP: B _int1_isr ; 假设不使用,可跳转到空循环或错误处理 NOP NOP TINT_RMP: B _timer_isr ; 定时器中断向量 NOP NOP ; ... 初始化其他所有中断向量,即使不用也最好指向一个安全处理函数 .text _c_int00: ; 1. 初始化堆栈指针(SP) LDP #0 SPLK #0x100, SP ; 假设堆栈设在内部RAM高端 ; 2. 初始化.bss段(清零未初始化变量区) LAR AR1, #_bss_start LAR AR2, #_bss_end MAR *, AR1 BSS_LOOP: CMPR 2 ; 比较AR1和AR2 BCND BSS_DONE, GEQ ; 如果AR1 >= AR2,完成 SPLK #0, *+ ; 清零当前地址,AR1递增 B BSS_LOOP BSS_DONE: ; 3. 从.data段的加载地址拷贝到运行地址(如果需要) ; ... (此处代码略,取决于链接器是否自动处理.data拷贝) ; 4. 调用主函数 CALL _main ; 5. 主函数返回后,进入空闲循环 IDLE_LOOP: NOP B IDLE_LOOP _timer_isr: ; 定时器中断服务程序 ; ... 保护上下文、处理中断、清除中断标志、恢复上下文 RET实操心得:在C206的增强Bootloader模式下,Bootloader完成跳转后,并不会像C203那样遗留一个返回地址在堆栈中。因此,你的启动代码中不需要执行
POP指令来清理堆栈。这是与C203 Bootloader的一个重要区别。
5.4 编译、链接与Hex文件生成
- 编译:使用汇编器编译你的
.asm文件,使用C编译器编译你的.c文件,生成目标文件(.obj)。 - 链接:运行链接器,指定上一步创建的
.cmd文件,将所有.obj文件链接成一个完整的.out文件。lnk500 my_boot.cmd -o my_app.out - Hex转换:使用Hex转换工具,根据启动模式生成最终文件。对于并行EPROM模式,通常生成简单的二进制(.bin)文件。
其中hex6x my_app_hex.cmdmy_app_hex.cmd是给hex6x工具的配置文件:/* my_app_hex.cmd */ my_app.out /* 输入文件 */ -a /* 输出ASCII-Hex格式(可选,二进制更常用)*/ -o my_app.bin /* 输出二进制文件 */ -boot /* 启用Boot格式:在文件开头添加目标地址和长度 */ -bootorg 0x8000 /* 指定Bootloader的源起始地址(全局数据空间)*/ -memwidth 8 /* 源存储器宽度(EPROM是8位)*/ -romwidth 8 /* 目标ROM宽度 */ -order M /* 字节序:Motorola格式?需根据编程器要求调整,通常MS(高字节在前)*/-boot选项是关键,它会自动在生成的二进制文件开头,按照Bootloader期望的格式,插入目标地址和长度字。
5.5 硬件配置与烧录
- 配置硬件引脚:
- 将
MP/MC引脚接地(低电平),使能微计算机模式。 - 将
EXT8引脚通过电阻上拉到VCC(高电平),选择C206增强Bootloader。 - 根据选择的模式(如模式7),配置连接到I/O地址0x0000的DIP开关或硬件逻辑,使其在上电时输出
0xXX07(低8位为0x07)。XX部分对于EPROM模式是SRCE_AD字段,例如,如果EPROM接在全局数据空间0x8000,则SRCE_AD = 0x20,所以BRS字可能是0x2007。
- 将
- 连接EPROM:将8位EPROM的数据线(D0-D7)连接到DSP数据总线的低8位(D0-D7)。将EPROM的地址线连接到DSP的地址总线,并确保其片选信号被解码到全局数据空间的相应地址(如0x8000)。
- 烧录EPROM:使用编程器,将上一步生成的
my_app.bin文件烧录到EPROM中,起始地址对应编程器的0x0000(因为文件本身已包含Bootloader所需的头信息)。 - 上电测试:给系统上电,用示波器或逻辑分析仪观察DSP的地址总线、数据总线和控制信号(如PS、DS、R/W),应该能看到Bootloader从EPROM(0x8000等地址)读取数据,并向程序RAM(0x9000等地址)写入数据的活动。最终,程序应能跳转到你的代码入口开始执行。
6. 常见问题排查与调试技巧
在实际操作中,Bootloader失败是常见问题。以下是一些排查思路和技巧:
问题1:上电后DSP毫无动静,程序不运行。
- 检查电源、时钟和复位电路:这是最基本也是最重要的一步。确保电源电压稳定,晶振起振,复位引脚在上电后有正确的低-高跳变。
- 检查MP/MC和EXT8引脚:用万用表或示波器确认复位期间这两个引脚的电平状态是否符合你的预期模式。注意上拉/下拉电阻的阻值是否合适。
- 检查BRS Word:在I/O读周期,测量I/O地址0x0000的数据线电平,确认读到的值是否正确。注意总线的上拉/下拉。
- 逻辑分析仪抓取总线:这是最强大的调试手段。在复位释放后,立即抓取DSP的地址总线、数据总线、PS/DS/IS、R/W、READY等信号。
- 看第一步:DSP是否从0xFF00(Bootloader ROM)开始取指?如果不是,检查
MP/MC。 - 看后续:在增强模式下,DSP是否去读I/O地址0x0000?读出的数据是什么?是否符合你设置的BRS字?
- 看EPROM访问:在EPROM模式下,DSP是否按照预期访问全局数据空间(如0x8000, 0x8001...)?读出的数据是否正确(与你烧录的文件前几个字节对比)?
- 看RAM写入:DSP是否向你的程序RAM目标地址(如0x9000)执行写操作?写入的数据是否与EPROM读出的数据对应?
- 看第一步:DSP是否从0xFF00(Bootloader ROM)开始取指?如果不是,检查
问题2:程序似乎加载了,但运行行为异常或跑飞。
- 检查中断向量表:这是最常见的原因。确保第二级中断向量表(在SARAM的0x8000)被正确初始化。如果某个未使用的中断意外发生,而向量指向了随机地址或未初始化的内存,程序就会跑飞。一个安全做法是将所有未用的中断向量都指向一个统一的错误处理函数,该函数至少能点亮一个LED或保持在一个循环中。
- 检查堆栈指针(SP)初始化:在你的启动代码中,是否在C环境初始化前正确设置了SP?堆栈溢出或指向非法区域会导致不可预知的问题。
- 检查.data段拷贝:如果你的程序有已初始化的全局变量(在.data段),启动代码必须负责将它们的初始值从加载地址(ROM/EPROM)拷贝到运行地址(RAM)。遗漏这一步会导致变量初值错误。
- 核对内存地址:仔细检查链接器命令文件中的
load和run地址,确保它们与你的硬件设计(EPROM连接地址、RAM连接地址)以及Bootloader模式所允许的目标地址空间完全匹配。特别是要避开SARAM中保留的0x8000-0x807F区域(除非你妥善处理了向量表)。 - 检查代码对齐和字长:确保你的代码编译链接后是16位字对齐的。在8位模式下,Bootloader是连续读两个字节组成一个字,如果EPROM中代码的字节顺序不对,会导致组成的指令码完全错误。
问题3:UART Bootloader模式下,PC发送数据后DSP无反应。
- 检查‘a’字符:确认你在发送正式的Hex数据流之前,是否先发送了一个‘a’字符(ASCII 0x61)。DSP必须收到这个字符才会启动自动波特率检测和回显。
- 检查波特率:虽然DSP支持自动波特率检测,但对发送的‘a’字符的波形有要求。确保PC端串口工具的波特率设置是准确的,并且发送的‘a’字符波形稳定。可以尝试降低波特率(如9600)进行测试。
- 检查硬件连接:确认RS-232电平转换电路正确,TX/RX线没有接反,地线连接良好。
- 检查Hex文件格式:确认转换后的Hex文件开头有‘a’,末尾有‘0000’。可以用文本编辑器打开检查。
问题4:从EPROM启动正常,但热启动(模式9)失败。
- 检查BRS字配置:热启动时,BRS字中的
ADDR_bb字段必须正确指向用户代码的入口地址所在的页(地址高6位)。 - 检查内存保持:热复位时,保持用户代码的RAM(通常是外部SRAM)的电源不能断电,且片选和读写信号不能有毛刺导致数据丢失。
- 检查Bootloader保存的地址:热启动模式会尝试使用一个内部RAM变量中保存的上次加载地址。如果你的应用程序覆盖了这个变量(位于B2 RAM的特定位置,需查手册),则热启动会失败。此时应确保BRS字提供了正确的地址。
调试Bootloader是一个系统工程,需要结合软件、硬件和芯片文档进行综合分析。耐心和细致的信号测量往往是解决问题的关键。