
1. 项目概述从.hex到.bin嵌入式开发的最后一步在DSP 28335的开发流程里CCSCode Composer Studio编译后默认生成的是.out文件这是TI自家的一种可执行文件格式包含了代码、数据和调试信息。但对于生产烧录、批量升级或者某些特定的Bootloader来说.out文件就显得有些“臃肿”和“专有”了。我们真正需要的是一个纯净的、只包含机器码和数据的二进制镜像文件也就是.bin文件。这个文件可以直接被烧录到Flash的指定地址上电后CPU就能从那里开始执行。很多刚接触28335或者从其他平台比如STM32的Keil/IAR转过来的朋友会困惑为什么我的CCS没有生成.bin文件其实CCS本身并没有一个像Keil那样的“一键生成Bin”的图形化按钮。这个功能被巧妙地隐藏在了“Post-build steps”构建后步骤里。你需要告诉CCS在编译链接完成后调用一个名为hex2000的转换工具把.out文件“翻译”成我们需要的.bin。这个过程不复杂但配置不对就死活出不来文件或者出来的文件地址错乱导致芯片无法运行。今天我就结合自己踩过的坑把CCS5及以上版本包括最新的CCS12为C2000系列DSP生成.bin文件的完整配置方法掰开揉碎了讲清楚。2. 核心工具与原理认识hex2000在配置之前我们必须先搞清楚核心工具hex2000是什么以及它和.out文件的关系。这样配置时才能心里有数出了问题也知道往哪个方向排查。2.1 .out文件与.bin文件的本质区别CCS编译后生成的.out文件通常是COFF或ELF格式它不仅仅包含最终要烧写到芯片Flash/RAM里的机器码和数据还包含了大量的“元数据”。这些元数据有段Section信息 比如.text段代码、.cinit段C初始化数据、.const段常量、.econst段扩展常量等各自的起始地址和长度。符号表 函数名、变量名及其地址用于调试。重定位信息 告诉链接器如何调整代码中的地址引用。调试信息 行号、变量类型等用于在IDE中单步调试。而.bin文件则是一种“扁平化”的纯二进制映像。它没有任何元数据就是按照指定的内存地址范围将对应地址的内容0和1依次排列成一个长串。烧录器或者Bootloader的工作很简单从某个起始地址比如Flash的0x3F8000开始把这个长串的二进制数据一字不差地“刻”进去。所以生成.bin的过程本质上是一个“提取”和“重组”的过程从.out文件中根据链接器命令文件.cmd定义的存储器映射提取出需要烧录到非易失性存储器通常是Flash的那些段的内容并按照地址顺序拼接成一个文件。2.2 hex2000工具的角色与参数解析hex2000就是TI提供的这个“提取和重组”工具。它通常位于CCS安装目录的ccs/tools/compiler/编译器版本/bin文件夹下。例如你的路径可能是C:\ti\ccs1240\ccs\tools\compiler\ti-cgt-c2000_22.6.2.LTS\bin\hex2000.exe。这个工具通过一个.hex格式的配置文件来工作。我们需要在Post-build步骤里调用它并告诉它两件最关键的事输入文件 你要转换哪个.out文件输出格式和选项 你想转换成什么格式Intel Hex, TI-Tagged Hex, 还是Binary输出文件的地址范围是什么生成.bin文件最常用的命令格式如下hex2000 --memwidth 16 --romwidth 16 --bin -o “输出.bin” “输入.out”这里有几个关键参数--memwidth和--romwidth 对于28335这类16位数据总线的DSP通常都设为16。它指定了内存和ROM的物理宽度单位是位。--bin 这是核心选项告诉hex2000输出二进制.bin格式。如果不加这个默认可能输出的是ASCII格式的Hex文件。-o 指定输出文件名。但仅仅这样是不够的最大的坑在于地址范围。默认情况下hex2000会转换整个.out文件涉及的所有地址空间包括RAM比如0x000000开始的M0 SARAM。如果你把这样的.bin文件烧进Flash芯片启动后Bootloader把本该加载到RAM的数据也复制到RAM地址可能会覆盖掉正在运行的代码导致不可预知的崩溃。所以我们必须通过一个额外的配置文件精确地告诉hex2000只转换那些需要烧写到Flash里的段。这就是下面要讲的.cmd文件协同和配置方法。3. 详细配置步骤从工程设置到文件生成理解了原理配置就是按图索骥。这里以CCS12.4为例步骤在CCS5到CCS12之间都是通用的。3.1 步骤一确认与准备链接器命令文件(.cmd)你的工程里肯定有一个或多个.cmd文件例如28335_RAM_lnk.cmd用于仿真调试和F28335.cmd用于Flash烧录。生成用于生产的.bin文件时必须确保工程当前激活的是Flash版本的链接器命令文件。打开你的Flash链接器文件如F28335.cmd找到MEMORY指令部分。这里定义了芯片的存储器映射。你需要重点关注Flash区域的定义例如MEMORY { PAGE 0: /* Program Memory */ ... FLASHA : origin 0x3F8000, length 0x002000 /* Sector A */ FLASHC : origin 0x3FA000, length 0x002000 /* Sector C */ ... PAGE 1: /* Data Memory */ ... }然后在SECTIONS指令部分查看哪些段被分配到了这些Flash区域。通常.text代码、.cinitC全局变量初始化表、.const常量、.econst扩展常量等段会被分配到PAGE 0的Flash中。例如SECTIONS { .text : FLASHA PAGE 0 .cinit : FLASHA PAGE 0 .const : FLASHC PAGE 0 ... }记下这些段的名称它们就是需要被包含进.bin文件的内容。3.2 步骤二创建hex2000的配置文件.hex在工程根目录下新建一个文本文件命名为bin_convert.hex名字任意。这个文件的内容用于指导hex2000进行转换。一个最基础、最常用的配置如下-b -a -image -map bin_convert.map -o MyApp.bin MyApp.out逐行解释-b 输出二进制格式Binary。这是生成.bin文件的关键。-a 输出绝对地址。生成的.bin文件数据会带有地址信息体现在文件布局上这对于需要指定烧录起始地址的烧录器很重要。-image 这个选项告诉hex2000生成一个连续的、完整的存储器映像。它会用默认值通常是0xFF填充段与段之间的地址空隙确保.bin文件是地址连续的。-map bin_convert.map 生成一个映射文件。这个文件非常有用它会详细列出输入.out文件中的各个段被转换到了输出.bin文件的什么位置。当你的.bin文件烧录后运行不正常时首先就该查这个map文件确认段地址是否正确。-o MyApp.bin 指定输出的.bin文件名。MyApp替换成你的工程名。MyApp.out 指定输入的.out文件名。同样需要替换成你的实际输出文件名通常和工程名一致。注意 网上有些教程会使用--memwidth等参数但在.hex配置文件中通常使用-b、-a、-image这种短格式选项。直接在Post-build命令行里调用时长短格式都可以。使用配置文件是更清晰、可管理的方式。3.3 步骤三配置CCS工程的Post-build Steps这是最关键的操作步骤。在CCS的“Project Explorer”视图中右键点击你的工程选择“Properties”。在属性对话框中导航到“Build” - “Steps”。在“Post-build steps”下方的命令行输入框中填入以下命令${CCS_INSTALL_ROOT}/tools/compiler/ti-cgt-c2000_${CG_TOOL_VERSION}/bin/hex2000.exe bin_convert.hex命令解析与变量说明${CCS_INSTALL_ROOT} 这是一个CCS内置的环境变量指向你的CCS安装根目录如C:\ti\ccs1240。使用它保证了命令在不同电脑上的可移植性。${CG_TOOL_VERSION} 这是编译器版本变量例如22.6.2.LTS。它确保了即使你升级了编译器这条命令也能自动找到正确路径下的hex2000.exe。bin_convert.hex 就是上一步我们创建的配置文件它需要放在工程的根目录或者相对于工程的正确路径下。更简洁的写法推荐如果你确认hex2000.exe所在的路径已经添加到了系统的环境变量PATH中也可以直接写hex2000 bin_convert.hex但使用绝对路径更稳妥能避免因环境变量问题导致的“找不到命令”错误。点击“Apply and Close”保存配置。3.4 步骤四编译并验证输出完成上述配置后点击CCS的编译按钮小锤子。编译过程结束后CCS会自动执行我们设置的Post-build步骤。如何验证是否成功查看“Console”视图。在编译输出的最后你应该能看到类似这样的信息表明hex2000被成功调用并执行Building file: ../bin_convert.hex Invoking: Hex2000 C:/ti/ccs1240/ccs/tools/compiler/ti-cgt-c2000_22.6.2.LTS/bin/hex2000.exe bin_convert.hex Finished building: ../bin_convert.hex在工程的根目录下或者你在.hex配置文件中-o参数指定的路径找到生成的.bin文件如MyApp.bin和可选的.map文件如bin_convert.map。用十六进制编辑器如HxD、UltraEdit打开生成的.bin文件。你可以看到全是十六进制数据没有多余的文本信息。再对比一下.out文件后者开头通常有可识别的文件头魔术字。4. 高级配置与疑难排查按照上述步骤大多数情况下都能成功生成.bin文件。但如果你的工程比较复杂或者遇到了问题下面这些高级技巧和排查方法能帮到你。4.1 只转换特定段到.bin文件有时候你可能不想把所有的Flash段都打包进去或者需要排除某些段。这时可以在.hex配置文件中使用-boot选项来指定一个“引导表”或者更精细地使用段过滤。一种常见的方法是在链接器命令文件.cmd中将需要生成到.bin文件中的段分配到一个专有的、易于识别的内存区域。然后在.hex配置文件中指定这个区域。例如在F28335.cmd中定义一个合并的Flash区域MEMORY { ... FLASH_ALL : origin 0x3F8000, length 0x008000 /* 将多个Flash Sector合并 */ ... } SECTIONS { .myFlashSections: { *(.text) *(.cinit) *(.const) *(.econst) } FLASH_ALL PAGE 0 ... }然后在bin_convert.hex配置文件中你可以尝试注意hex2000对段的直接过滤支持有限更可靠的方法是上述的合并段法-b -a -image -range FLASH_ALL -map bin_convert.map -o MyApp.bin MyApp.out-range选项可以指定只转换某个地址范围。但最根本的还是确保你的链接脚本只把需要持久化的内容分配到Flash地址。4.2 常见问题与解决方案实录这里记录了几个我亲自踩过并且帮同事解决过的典型问题。问题1编译成功但Console提示“hex2000 不是内部或外部命令...”原因 Post-build steps中的命令路径错误或者hex2000.exe确实不在PATH环境变量中。解决检查命令中的路径。最稳妥的方法是使用${CCS_INSTALL_ROOT}变量。你可以先在Console中尝试一个简单的命令如echo ${CCS_INSTALL_ROOT}看是否能正确回显路径。去CCS安装目录下手动找到hex2000.exe的完整路径将Post-build命令替换为绝对路径。例如C:/ti/ccs1240/ccs/tools/compiler/ti-cgt-c2000_22.6.2.LTS/bin/hex2000.exe bin_convert.hex。注意路径中的斜杠和空格如有空格需要整个路径用双引号括起来。问题2生成了.bin文件但烧录后程序不运行或运行异常原因 这是最棘手的情况。可能性很多地址错误 .bin文件包含的地址范围不对可能包含了RAM数据或者起始地址不是Flash起始地址0x3F8000。段缺失 某些关键段如.cinit它负责初始化全局变量没有被包含进.bin文件。烧录工具配置错误 烧录器软件中设置的烧录起始地址与.bin文件的实际起始地址不匹配。排查步骤首要检查bin_convert.map文件 这是hex2000生成的映射文件。打开它看“OUTPUT FILE”部分。它会列出输入文件中每个段被转换后的输出地址和长度。确认这些段是否都是你期望的Flash段如.text, .cinit等并且它们的起始地址是否正确如0x3F8000开始。对比链接器生成的.map文件 编译后CCS还会在输出目录如Debug生成一个MyApp.map文件。这是链接器的映射文件详细记录了所有段最终被分配到了哪个地址。将bin_convert.map的输出段列表与MyApp.map中的段分配进行对比看是否一致。检查烧录配置 打开你的烧录软件如TI的Uniflash或第三方烧录器确认你设置的“起始地址”、“加载地址”或“偏移量”是否与.bin文件的实际起始地址一致。通常对于从Flash启动的28335这个地址就是0x3F8000。问题3生成的.bin文件巨大几十MB明显不正常原因 这通常是因为使用了-image选项但没有正确限制地址范围导致hex2000从地址0开始一直填充到.out文件中出现的最大地址可能包含未使用的内存空间或仿真器地址并用0xFF填充了中间的巨大空隙。解决优先使用前面提到的“合并段”方法在链接阶段就控制好范围。如果不方便改链接脚本可以在.hex配置文件中尝试不使用-image而使用-byte和-zero选项组合来控制填充但这更复杂。最根本的解决方案还是优化链接脚本确保不需要烧录的内容如调试段、RAM段不被错误地引用到最终映像的地址空间。问题4Post-build步骤执行了但没看到.bin文件原因 输出路径可能不对。解决检查Post-build命令中-o参数指定的路径。如果是相对路径如-o MyApp.bin它通常相对于工程根目录生成。在CCS的“Project Explorer”中右键工程 - “Refresh”看看文件是否已经生成但没刷新显示。查看Console输出看hex2000是否有报错信息。4.3 与Keil/IAR生成bin文件的对比很多从ARM开发转过来的工程师会熟悉Keil或IAR的配置。这里简单对比一下加深理解Keil 在“User”选项卡下配置“After Build/Rebuild”的命令行调用fromelf.exe --bin -o工具进行转换。逻辑和CCS的hex2000几乎一模一样也是构建后调用转换工具。IAR 在“Output Converter”选项卡中直接勾选“Generate additional output”并选择“Binary”格式。IAR将其集成到了构建流程内部配置更图形化一些。CCS的方式更接近于Keil需要用户显式地配置构建后命令。这种方式虽然多了一步但灵活性更高可以方便地集成其他自定义脚本比如计算CRC校验和、自动重命名文件等。5. 自动化与集成提升效率的技巧当项目需要频繁生成用于测试和发布的.bin文件时手动操作或依赖IDE配置还不够高效。这里分享两个提升效率的实战技巧。5.1 使用批处理脚本进行一键生成你可以在工程目录下创建一个.batWindows或.shLinux/macOS脚本将编译和转换过程自动化。例如创建一个build_and_convert.batecho off REM 进入工程目录如果脚本不在工程根目录需要先cd REM 设置CCS编译命令的路径根据实际安装位置修改 set CCS_PATHC:\ti\ccs1240\ccs\eclipse\eclipsec.exe set WORKSPACEC:\my_workspace set PROJECT_NAMEMy28335Project echo 正在清理工程... %CCS_PATH% -noSplash -data %WORKSPACE% -application com.ti.ccstudio.apps.projectBuild -ccs.clean %PROJECT_NAME% echo 正在编译工程Release配置... %CCS_PATH% -noSplash -data %WORKSPACE% -application com.ti.ccstudio.apps.projectBuild -ccs.build %PROJECT_NAME% -buildConfig Release echo 正在生成BIN文件... REM 假设hex2000在PATH中且.hex配置文件在工程根目录 hex2000 -q bin_convert.hex echo 所有操作完成 pause这个脚本实现了自动清理、编译指定配置并调用hex2000生成.bin文件。你可以将其集成到持续集成CI工具中如Jenkins。5.2 在Post-build步骤中集成版本信息与CRC校验一个更专业的做法是在生成.bin文件后自动为其添加软件版本号、编译时间甚至计算CRC校验和。这可以通过在Post-build步骤中调用额外的脚本或小程序来实现。例如你的Post-build步骤可以改成两条命令${CCS_INSTALL_ROOT}/tools/compiler/ti-cgt-c2000_${CG_TOOL_VERSION}/bin/hex2000.exe bin_convert.hex python ${ProjDirPath}/scripts/add_version_crc.py ${ProjDirPath}/Debug/MyApp.bin第一条命令生成原始的.bin文件。第二条命令调用一个Python脚本这个脚本可以读取一个头文件如version.h中定义的版本号。获取当前系统时间作为编译时间。计算整个.bin文件的CRC32值。将这些信息打包成一个小的“信息头”预置在.bin文件的最开头注意这会改变文件的烧录起始地址Bootloader需要知道这个约定。或者将这些信息追加到文件末尾如果Bootloader协议支持。这样每次编译生成的.bin文件都自带“身份证”便于版本管理和现场问题追踪。实现这个Python脚本需要一些编程工作但它带来的工程管理收益是巨大的。最后关于烧录生成的.bin文件可以用于TI的Uniflash、第三方烧录器或者通过串口/USB等接口由你自己的Bootloader进行固件升级。烧录时最关键的就是确保烧录工具中设置的起始地址与.bin文件内容实际对应的起始地址通常是Flash起始地址0x3F8000完全一致。配置好这一步你的28335程序就能稳稳地跑在Flash里了。