1. 从HEX到BIN:为什么28335项目需要这个转换?
如果你是从CCS3.3或者更老的版本迁移到CCS5、CCS6甚至CCS12的工程师,大概率会遇到一个“小”问题:以前工程编译后,在Debug或Release文件夹里,那个可以直接用于串口、CAN或者以太网升级的.out文件旁边,常常会有一个同名的.bin文件。但升级到新版本CCS后,你发现只有.out文件孤零零地躺在那里,那个熟悉的.bin文件不见了。对于TMS320F28335这类DSP芯片,.out文件是带有调试信息、符号表的COFF格式可执行文件,主要用于JTAG仿真调试。而.bin文件是纯粹的二进制映像,只包含需要烧录到Flash或RAM中的机器码和数据,是量产烧录、远程升级的“标准口粮”。
这个变化不是TI的疏忽,而是一种设计上的“职责分离”。CCS(Code Composer Studio)的核心定位是一个强大的集成开发环境,它的主要任务是编译、链接和调试。生成纯净的二进制烧录文件,被视为“构建后”的一个可选步骤。因此,从CCS v5开始,TI将生成.bin(或.hex)文件的功能,从编译器的默认流程中剥离出来,交给了“Post-build steps”(构建后步骤)这个强大的自定义接口。你需要明确地告诉CCS:“编译链接完成后,请再帮我执行一个转换命令。” 这乍看增加了步骤,实则给予了工程师更大的灵活性,比如你可以在生成.bin后,自动计算CRC校验和、附加版本信息头,甚至调用外部脚本进行自动化测试。
所以,当你面对一个只有.out文件的28335工程时,别慌,不是你配置错了,而是你需要手动点亮这个“技能树”。下面,我就以CCS5及以上版本(其配置方法在CCS6、CCS8、CCS12中几乎完全通用)为背景,手把手带你完成这个关键配置,并深入聊聊其中的门道和容易踩的坑。
2. 核心工具链:ofd6x、hex6x与tiobj2bin的抉择
在配置构建后步骤之前,我们必须先搞清楚CCS用什么工具来生成.bin文件。这里通常有两条技术路线,它们背后的工具链不同,适用场景也略有差异。
2.1 官方推荐路径:使用tiobj2bin工具
这是最经典、最直接的方法。tiobj2bin是一个独立的命令行工具,它随CCS安装包一起提供。它的作用非常专一:将COFF格式的.out文件转换为纯粹的二进制.bin文件。
它的工作原理是:解析.out文件的段(Section)信息,特别是那些需要加载到目标存储器(如Flash)的初始化段(比如.text,.cinit等),提取出其中的纯二进制数据,按照存储器地址顺序拼接成一个连续的文件。对于未初始化的段(如.bss),它不会包含在.bin文件中,因为这些段的内容是在程序运行时由启动代码(Bootloader或c_int00)进行清零初始化的。
在CCS的安装目录下,你可以找到它。例如,在CCS 12.8中,路径可能类似于:C:\ti\ccs1240\ccs\utils\compiler\tiobj2bin\tiobj2bin.exe。它的使用命令很简单:
tiobj2bin <input_coff_file.out> <output_bin_file.bin> <optional_options>但是,这里有一个至关重要的细节:tiobj2bin工具在CCS v9之后的某些版本中,可能不再默认包含,或者其依赖的库文件路径发生了变化。如果你在较新版本的CCS中直接调用tiobj2bin,可能会遇到“找不到指定模块”或“无法启动此程序”等运行时错误。这是因为它的运行依赖于一些旧的动态链接库(DLL)。因此,虽然它是历史最悠久的方案,但在新版CCS中可能需要额外处理依赖,稳定性稍逊。
2.2 更现代的路径:使用编译器自带的hex6x工具
这是TI当前更推荐、也更稳健的方法。TI的C2000编译器套件中,包含一个名为hex6x的工具。顾名思义,它最初是用于生成各种十六进制格式文件(如TI-TXT, Intel Hex)的。但鲜为人知的是,hex6x通过指定合适的输出格式选项,完全可以生成标准的二进制文件。
这条路径实际上分两步走:
ofd6x(Object File Display):这个工具用于从.out文件中提取出需要转换的段信息,并生成一个“转换命令文件”(通常是一个.cmd文件,但内容是指令,不是链接命令)。你可以把它理解为一个“段信息提取器”。hex6x:接收ofd6x生成的命令文件,根据指令将指定的段内容,以二进制格式输出。
为什么这条路径更可靠?因为ofd6x和hex6x是编译器cl6x的核心组成部分,与你的编译器版本严格绑定,环境变量和依赖关系都是自动配置好的。只要你的CCS工程能正常编译,这两个工具就一定可用,几乎不会出现路径或依赖问题。它的命令组合看起来更复杂,但一劳永逸。
对于F28335项目,我们通常采用第二种方法,因为它兼容性最好。接下来,我们就基于ofd6x+hex6x的方案,进行详细配置。
3. 一步步配置CCS工程的Post-build Steps
现在,我们进入实操环节。请打开你的CCS工程,这里以CCS 12.8为例,其他版本界面高度相似。
3.1 定位配置入口
- 在CCS的“Project Explorer”视图中,右键点击你的28335工程名称。
- 选择最底部的“Properties”(属性)。
- 在弹出的属性对话框中,在左侧导航树中找到“Build” -> “Steps”。
- 你会看到右侧有一个重要的文本框:“Post-build steps”。我们所有的工作都将在这里进行。
3.2 编写构建后命令
在“Post-build steps”的文本框中,你需要输入一串命令。这串命令的本质是:在CCS内部调用系统命令行(cmd或bash),在编译链接完成后,执行你指定的命令。
下面是一个完整、通用且带有详细注释的配置示例。你可以直接复制,然后根据你的实际路径进行微调。
echo 开始生成BIN文件... # 步骤1:使用ofd6x生成转换指令文件 "${CG_TOOL_ROOT}/bin/ofd6x" --obj_format=coff -o "${ProjName}.out.xdl" "${ProjName}.out" # 步骤2:创建一个.hex.cmd文件,指导hex6x如何生成bin echo "${ProjName}.out" > "${ProjName}_hex.cmd" echo -a >> "${ProjName}_hex.cmd" echo -image >> "${ProjName}_hex.cmd" echo -o "${ProjName}.bin" >> "${ProjName}_hex.cmd" # 步骤3:使用hex6x执行转换 "${CG_TOOL_ROOT}/bin/hex6x" "${ProjName}_hex.cmd" "${ProjName}.out.xdl" # 步骤4:清理临时文件(可选,建议保留以便调试) # del "${ProjName}.out.xdl" "${ProjName}_hex.cmd" echo BIN文件生成完毕: ${ProjName}.bin让我们逐行拆解这个命令的意图和关键变量:
echo命令:用于在CCS的“Console”输出信息,方便你观察构建后步骤的执行进度。${CG_TOOL_ROOT}:这是CCS内置的一个非常重要的环境变量。它指向当前工程所使用的编译器工具链的根目录。例如,对于C2000编译器,它可能是C:\ti\ccs1240\ccs\tools\compiler\ti-cgt-c2000_22.6.2.LTS。使用这个变量,可以确保无论你的CCS或编译器安装在哪,命令都能找到正确的ofd6x和hex6x工具,这是实现配置可移植性的关键。${ProjName}:这是另一个CCS内置变量,代表当前工程的名称。使用它,意味着你的命令适用于任何工程,无需硬编码文件名。ofd6x命令详解:--obj_format=coff:指定输入对象文件格式为COFF(这是.out文件的格式)。-o "${ProjName}.out.xdl":指定输出文件为.xdl格式,这个文件包含了.out文件中各段的布局信息。- 最后输入
"${ProjName}.out",即要处理的文件。
- 创建
_hex.cmd文件:这几行echo命令是在动态生成一个给hex6x使用的命令文件。其内容通常如下:MyProject.out -a -image -o MyProject.bin- 第一行:指定输入的
.out文件名。 -a:输出格式为“ASCII-Hex”,这是生成二进制格式的基础。-image:关键选项!它告诉hex6x生成一个连续的、基于存储映像的二进制文件。没有这个选项,可能会生成按段分割的多个文件。-o:指定输出的二进制文件名。
- 第一行:指定输入的
hex6x命令:它接收上面生成的_hex.cmd指令文件和ofd6x生成的.xdl文件,最终输出我们想要的.bin文件。- 清理临时文件:被注释掉的
del命令。在调试阶段,建议先注释掉,保留.xdl和_hex.cmd文件,如果转换失败,可以检查这两个文件的内容来排查问题。稳定后可以取消注释以保持目录清洁。
3.3 配置的验证与测试
- 将上述命令(根据你的需要是否取消清理步骤)粘贴到“Post-build steps”文本框。
- 点击“Apply and Close”。
- 在CCS中,对工程执行一次完整的“Rebuild Project”(建议使用重建,而非仅构建,以确保从头开始)。
- 观察“Console”视图的输出。你应该能看到在正常的编译链接信息之后,出现你写的
echo信息,以及工具的执行日志。 - 如果一切顺利,最后会显示“BIN文件生成完毕”。此时,去你的工程输出目录(通常是
Debug或Release),除了.out文件,你应该能看到一个同名的.bin文件。
注意:如果遇到“命令语法不正确”或“找不到文件”的错误,请首先检查:
- 路径中是否有空格或中文?确保CCS和工程路径是全英文的。
- 变量名是否拼写正确?
${CG_TOOL_ROOT}和${ProjName}是大小写敏感的。- 可以尝试先在系统的命令行中,手动切换到工程目录,并替换变量为实际值后执行命令,看具体报错。
4. 进阶:解决多段与非连续地址生成的BIN文件空洞问题
上面的基础命令能解决大部分情况,但当你遇到更复杂的链接器配置时,可能会踩到一个大坑:生成的.bin文件巨大无比(比如几百MB),但实际程序可能只有几十KB。用十六进制编辑器打开一看,里面充斥着大量的FF或00。这就是“地址空洞”问题。
问题根源:你的链接器命令文件(.cmd)将不同的代码/数据段分配到了非连续的、跨度很大的存储器地址空间。例如,.text段在0x80000,.cinit段在0x90000。hex6x在-image模式下,会生成一个从最低地址到最高地址的连续二进制映像。对于两个段之间的空白区域(0x80000到0x90000之间的64KB),它会用填充值(默认是0xFF)填满,导致.bin文件包含大量无效数据。
解决方案:我们需要告诉hex6x,只提取那些需要烧录的、已初始化的段,并且不要填充地址间隙。这需要通过修改给hex6x的指令文件来实现。
一个更健壮的_hex.cmd文件内容应该是这样的(你可以通过修改Post-build步骤中的echo命令来生成这个文件):
MyProject.out --memwidth=16 --romwidth=16 --order=MS --binary --outfile=MyProject.bin关键选项解析:
--memwidth=16和--romwidth=16:指定存储器和ROM的位宽为16位(对于C2000系列DSP是常见的)。这确保字节序正确。--order=MS:指定字节序为“Most Significant”优先,即大端序。注意:TMS320F28335是小端(Little-Endian)处理器,但TI的hex6x工具在生成二进制输出时,使用--order=MS配合--binary选项,能正确地处理小端格式的输入并生成标准的二进制流。这是一个容易混淆但正确的用法。--binary:这是核心!直接输出二进制格式,而不是ASCII-Hex。在此模式下,hex6x会智能地处理多个段,只为包含实际数据的地址范围生成输出,自动跳过地址空洞。它不会在段与段之间填充0xFF。--outfile=:指定输出文件名。
因此,你的Post-build步骤可以优化为:
echo 开始生成紧凑BIN文件... "${CG_TOOL_ROOT}/bin/ofd6x" --obj_format=coff -o "${ProjName}.out.xdl" "${ProjName}.out" echo 生成高级hex命令文件... ( echo --memwidth=16 echo --romwidth=16 echo --order=MS echo --binary echo --outfile=${ProjName}.bin echo ${ProjName}.out ) > "${ProjName}_advanced_hex.cmd" "${CG_TOOL_ROOT}/bin/hex6x" "${ProjName}_advanced_hex.cmd" "${ProjName}.out.xdl" echo 紧凑BIN文件生成完毕: ${ProjName}.bin使用这种方法生成的.bin文件,其大小将接近于你的程序实际数据量,非常适合用于网络传输和烧录。
5. 避坑指南与实战经验分享
配置本身不复杂,但实际项目中,以下几个细节决定了成败:
5.1 调试版与发布版的分别配置你的工程很可能有Debug和Release两种配置。它们的输出目录不同。上述命令中使用的${ProjName}.out是相对于工程根目录的。CCS在执行Post-build步骤时,当前工作目录就是活动的构建配置的输出目录(如Debug)。因此,直接使用${ProjName}.out就能找到刚编译出的文件。无需担心路径问题。但如果你在命令中硬编码了路径,就需要为Debug和Release分别配置属性。
5.2 如何验证BIN文件的内容是正确的?生成.bin文件后,不要直接烧录了事。建议用一个小工具进行校验:
- 使用十六进制编辑器(如HxD, WinHex)打开生成的
.bin文件,查看开头和结尾。通常,有效的程序开头会有特定的指令模式(对于C2000,程序入口点附近常有0x28B1等跳转指令)。文件中间不应有大片的FF或00(除非是空白填充段)。 - 与.out文件对比:使用TI的
ofd6x工具直接查看.out文件的段信息:"${CG_TOOL_ROOT}/bin/ofd6x" --obj_format=coff --section_details ${ProjName}.out。查看.text等段的起始地址和大小。然后计算.bin文件的大小,看是否与主要段的总和大致相符(略小,因为去除了符号表等调试信息)。
5.3 集成到自动化脚本或CI/CD流程对于团队协作或持续集成,你可能需要在命令行环境(而非CCS GUI)中完成编译和BIN文件生成。这时,你需要使用TI提供的make工具(通常是gmake)。CCS工程本质上是一个Eclipse工程,其构建命令是公开的。你可以:
- 在CCS中,通过“View” -> “Other…” -> “Scripting” -> “Scripting Console”,可以录制构建过程,得到底层的命令行。
- 更直接的方法是,使用
${CG_TOOL_ROOT}/bin/cl6x等编译器命令和上述的ofd6x、hex6x命令,自己编写Makefile或批处理脚本,实现从源码到.bin的全流程自动化。这脱离了CCS环境,但给了你最大的控制权。
5.4 关于Flash API和二次引导加载器(Bootloader)如果你的28335程序需要使用TI的Flash API库(F28335_Flash_API.lib)来在运行时对自身Flash进行编程(即IAP功能),或者你需要为串口/CAN Bootloader准备.bin文件,那么.bin文件的生成地址就至关重要。
- Bootloader场景:你的应用程序的链接地址必须避开Bootloader占用的空间(例如,从
0x80000开始)。生成的.bin文件的内容,就是从0x80000开始的二进制流。Bootloader会原封不动地将这个流写入Flash的对应位置。 - 校验和:有些Bootloader协议要求
.bin文件包含校验和(如CRC32)。这无法通过hex6x直接实现。你需要在Post-build步骤中再添加一行,调用一个外部的小程序(可以用Python、C等编写)来计算整个.bin文件的CRC,并将其追加到文件末尾,或者生成一个单独的校验文件。
配置完成后,每次点击编译,.bin文件就会自动出现在输出文件夹里。这个看似微小的自动化步骤,在实际的研发、测试和生产烧录环节中,能节省大量的手动操作时间,并减少因忘记转换格式而导致的错误。对于嵌入式开发,尤其是需要频繁烧录和升级的DSP项目,这是一项值得投入十分钟配置,却能长期受益的基础设施。