资讯动态

CCS下TMS320F28335生成hex与bin文件的完整教程及避坑指南

发布时间:2026/9/25 9:01:24 来源:尧图企业网站定制
前阵子帮同事处理量产固件发现很多人卡在同一个地方CCS里编译只出.out对着仿真器烧没问题一到产线要用离线编程器、要用串口Bootloader升级立刻抓瞎。所以把我在CCS 12.2下、TMS320F28335工程里生成.bin和.hex文件的完整过程整理出来重点是那些不实际踩一遍根本不知道的坑。这篇文章适合正在做28335开发的工程师尤其是要接触量产烧录、Bootloader远程升级、或者需要把固件交给第三方工具处理的人。如果你是刚开始学C2000还没搞清楚.out和烧录文件的关系这篇也能帮你把工具链的完整链路理清楚。1. 先搞清楚.out、.hex、.bin 到底差在哪1.1 三种文件的本质从“调试产物”到“烧录产物”我见过不少新手把.out文件当万能固件直接发给产线然后产线反馈“打不开”。这不是产线工具的问题而是文件格式压根不对。.out是CCS编译器直接生成的链接产物。它内部是COFF格式新版本也可能用ELF除了代码数据还带着完整的符号表、重定位信息、调试信息。仿真器加载.out的时候就是靠这些信息把变量名对应到具体地址这就是为什么CCS调试时能看变量、能打断点。但离线烧录器、产线工具不需要这些它们只关心“哪个地址写什么数据”。.hex是Intel HEX格式本质上是一个纯文本文件。把任意一个hex文件用记事本打开你会看到大量冒号开头的行例如:20000000开头的一串ASCII字符。每一行都包含长度、地址、记录类型、数据、校验和所以它自带地址映射和校验信息烧录器照着地址逐个写Flash就行。.bin是最纯粹的二进制镜像一段连续的数据从某个起始地址开始排列不包含地址、不包含校验、不包含任何元信息。烧录.bin前你必须明确知道它的起始地址。这也是很多人在用.bin时栽跟头的地方——文件本身不告诉你它该放在哪。打个比方.out是带着施工图纸的全套工程资料.hex是一份带门牌号的货物清单.bin就是货物本身。你给快递员货物清单他能准确投递你直接把货物堆门口他根本不知道往哪送。1.2 哪些场景必须用 .hex 或 .bin如果你只用XDS100/XDS110仿真器在CCS里调试和烧录那么一直用.out没有任何问题。但只要是下面几个场景就必须把固件转成.hex或.bin我实际接触最多的就是量产烧录。产线上的离线烧录器、自动烧录工装绝大多数只支持Intel HEX或纯bin没人会去解析COFF。还有一个高频场景是Bootloader升级28335的串口引导模式SCI Boot需要的是特定的镜像通过Bootloader搬运固件时普通.out完全无法使用。再就是跨工具协作到了客户现场对方经常只收“固件文件”你扔一个.out过去他连打开都费劲更别说二次处理。1.3 转换工具为什么是 hex2000 而不是别的CCS的C2000编译器自带一个工具叫hex2000位于编译器安装目录的bin文件夹下。这个工具专门负责把COFF/ELF格式的输出文件转换成各种烧录格式Intel HEX、TI-TXT、二进制、引导表等。为什么主力是它因为它是TI官方工具与编译器版本完全匹配能正确处理C2000系列16位存储器的数据宽度和字节序别的第三方工具还真不一定靠谱。2. 在 CCS 12.2 里自动生成 .hex 文件2.1 最容易上手的方案Post-build Steps 一条命令搞定如果你不想每次编译完都手动敲命令行最稳妥的做法是在CCS工程属性里配置构建后步骤让编译完成后自动执行转换命令。具体操作路径右键工程 - Properties - Build - Steps在 Post-build steps 的文本框中填入转换命令。这里先给一个最稳、全路径版本的命令不会受到变量展开问题影响C:\ti\ccs12xx\ccs\tools\compiler\ti-cgt-c2000_22.6.x\bin\hex2000.exe --intel --memwidth 16 --romwidth 16 -o D:\work\28335_app\Debug\28335_app.hex D:\work\28335_app\Debug\28335_app.out注意我上面写的是一个示意路径实际目录要根据你的CCS安装位置和工程路径来替换。通常情况下CCS 12.2的安装路径会在C:\ti\ccs12xx下编译器文件夹以ti-cgt-c2000_开头版本不同文件夹名也不同建议你在文件管理器里直接搜一下hex2000.exe把实际路径填进来。如果你更在意可移植性比如代码要交给同事、换电脑也要能一键构建可以用CCS的构建变量来替代${CG_TOOL_HEX} --intel --memwidth 16 --romwidth 16 -o ${PROJECT_LOC}/Debug/${ProjName}.hex ${PROJECT_LOC}/Debug/${ProjName}.out使用变量版有两个前提一是确认你当前CCS版本支持${CG_TOOL_HEX}这个变量如果命令执行时提示找不到程序就老老实实写绝对路径二是确认工程的构建输出目录确实是Debug如果你用的是Release配置要把路径里配置名一起换掉。配置完成后正常点击Build锤子图标。编译结束后CCS会自动执行Post-build步骤控制台会打印hex2000的执行信息。没有报错的话在Debug目录下就能看到.hex文件。2.2 命令行手工转换与完整参数对照有些时候我不想配置工程属性或者想批量转换一批.out文件就会直接用命令行操作。手动转换的思路和自动配置完全一致只是省去在CCS界面里点来点去。进入编译器bin目录后执行hex2000.exe --intel --memwidth 16 --romwidth 16 -o output.hex input.out这几个参数我逐个说一下因为它们决定了生成结果是否正确--intel指定输出Intel HEX格式。这个格式通用性最好UniFlash、C2Prog、各种离线烧录器都认它。--memwidth 16表示目标系统的存储器宽度是16位。TMS320F28335是32位CPU但外部Flash是16位宽这里必须指定为16让工具按16位数据宽度来处理地址和数据。--romwidth 16表示ROM的物理宽度是16位C2000的片上Flash基本就是16位跟memwidth保持一致就对了。-o后面跟输出文件名一般把输入文件的.out后缀改成.hex即可。你可能看到网上有些教程会加上--boot和--bootorg参数这个我在后面生成bin的章节单独展开。这里先明确一个概念如果你的目标是把程序烧进Flash并正常启动用--intel生成普通hex就够了别去加boot参数。加了反而会让hex变成一个Boot Table格式直接烧进Flash以后程序大概率跑不起来。2.3 如何确认生成的 HEX 是可用的生成hex只是第一步关键是确认它真的能被正确烧录。我每次转完文件都会做三个快速检查。第一个检查是看文件大小是否合理。用文件管理器看一眼hex大小如果只有几十字节那肯定不对多半是链接时没把段放进去或者CMD文件定位错误。第二个检查是打开hex文件看首行的地址跟CMD里Flash起始地址是否一致。比如你的CMD把FLASH段定义在0x003F8000hex里第一条数据记录的处理地址应该与此对应。第三个检查是针对会用脚本的上位机开发人员Intel HEX每行开头是冒号第二个字节是数据长度紧接着是16位地址再往后是记录类型00代表数据记录01代表文件结束04代表扩展线性地址记录。如果你想写脚本把hex转成十进制数组记得每两个十六进制字符转成一个字节不要整行直接转。我在实际项目里做过一个简单的自动校验工具用Python逐行读取hex文件提取所有00类型记录按地址拼接成一个字节映射表再比对我预期的固件版本号。对量产来说这种自动化检查比人工看靠谱得多。3. 生成 .bin 文件的三种路径与各自的大坑3.1 最直接但最容易翻车hex2000 的 --binary生成.bin最直接的方式就是给hex2000加--binary参数hex2000.exe --binary --fill0xFF --memwidth 16 --romwidth 16 -o app.bin app.out这个命令背后的逻辑是把.out中所有具有加载地址load address的段提取出来按地址从低到高连续排列形成一个二进制流。如果地址中间有空洞--fill0xFF会把这些空洞填上0xFF。28335的Flash空白区域本来就是0xFF所以这样填充被认为是安全的。但这里藏着一个巨坑bin文件的长度取决于.out中所有可加载段的最小地址和最大地址之差。如果你的CMD文件把程序入口放在0x003F8000同时又有中断向量段放在0x003F8000附近那么bin的起始地址就是整个可加载区域的最低地址不是0x003F8000。如果工程里某个数据段被放到了0x00338000Flash区域的较低地址那么生成的bin就会从0x00338000开始中间几千字节全部是0xFF填充文件体积瞬间膨胀。我在一个工程里就碰到过这种情况一个实际只有20KB代码量的程序转出来的bin有近200KB。产线同事还以为是程序写错了检查下来完全没问题就是地址空洞闹的。后续的规避方案我放在3.4小节详细说。3.2 Bootloader远程升级时用 --boot 生成引导表如果你在做Bootloader相关的工作场景就完全不一样了。28335复位后内部Boot ROM会根据GPIO引脚状态决定进入哪种引导模式。如果进入SCI Boot模式Boot ROM会通过串口接收一段特殊格式的数据——Boot Table。这个Boot Table包含引导关键字、数据块起始地址、数据块长度、数据内容、校验和。它和普通hex完全是两回事。此时就需要用hex2000的Boot Table生成模式。常见命令形式hex2000.exe --boot --serial8 --binary --memwidth 16 --romwidth 16 -o app_boot.bin app.out--boot告诉工具按Bootloader可识别的格式输出--serial8表示面向SCI引导按8位串行方式打包数据。生成的app_boot.bin可以直接通过串口发送给Boot ROM。网上的老教程里经常出现--bootorg 0x003F7FF6这种写法我提醒一句这个地址不是硬性规定它表示Boot Table在内存中的存放位置具体值要和你实际使用的Bootloader约定一致。不同CMD文件、不同Flash起始地址需要的--bootorg都不一样。直接抄网上的命令很可能翻车。更稳妥的做法是先看你的Bootloader源代码确认它期望从哪个地址读取Boot Table再据此设置--bootorg。3.3 用脚本做裁剪和校验一个简单的Python方案当hex2000的--binary因为地址空洞导致bin文件过大或者你需要对hex内容做定制化处理时可以写一段小脚本来完成。这里分享一个我常用的Intel HEX解析思路足够覆盖大多数场景import sys def parse_hex(path): data {} base 0 for line in open(path, r): line line.strip() if not line.startswith(:): continue length int(line[1:3], 16) addr int(line[3:7], 16) rectype int(line[7:9], 16) payload bytes.fromhex(line[9:9 length * 2]) if rectype 0x04: # 扩展线性地址 base int.from_bytes(payload, big) 16 elif rectype 0x00: # 数据记录 for i, b in enumerate(payload): data[base addr i] b elif rectype 0x01: # 文件结束 break return data def to_bin(data, start_addr, end_addr, fill0xff): result bytearray() for addr in range(start_addr, end_addr 1): result.append(data.get(addr, fill)) return bytes(result) if __name__ __main__: data_map parse_hex(app.hex) bin_data to_bin(data_map, 0x003F8000, 0x003FBFFF) open(app_cut.bin, wb).write(bin_data)这段代码的逻辑很清晰先把hex文件解析成一个地址到字节值的映射然后按你需要的地址区间提取数据空洞部分填入fill指定的默认值。通过这种方式产线使用的小体积bin文件就出来了而且起始地址完全可控。如果你要做固件合并、版本标识写入、CRC校验追加也是在这种映射基础上操作最方便。3.4 关于文件大小的隐藏问题地址空洞与CMD布局我在3.1节提到的bin文件膨胀问题本质上不是hex2000的锅而是CMD文件布局造成的。所以要让生成的bin体积可控最根本的办法是调整CMD文件的段定位。一种常见的做法是把应用程序的Flash段统一放到较高地址区域比如从0x003F8000开始和Bootloader程序占用的低地址区域隔离开。这样应用程序单独生成bin时起始地址就是0x003F8000结束地址就是代码区最大地址中间不会有低地址段和它混在一起。前提是你需要清楚哪些段是可加载段loadable哪些只是符号定义。如果CMD文件里有多个PAGE0的Flash区域被标记成可加载那么hex2000都会把它们纳入输出哪怕这些段之间隔了几十KB的空洞。另外提醒一个细节不要指望单纯用--fill选项把空洞填成0除非你有明确理由。对Flash而言空白区域保持0xFF是天然状态填0会改变未使用区间的电平某些场景下可能引发误判。所以我在默认命令里写的是--fill0xFF这是跟Flash特性对齐的选择。4. 我实际踩过的坑与排查实录4.1 常见报错速查表下面这张表是我用CCS 12.2处理28335工程时遇到过的典型报错以及排查思路报错信息实际原因处理办法createprocess failed或 系统找不到指定的文件hex2000.exe路径错误或路径含空格且没有加引号核对hex2000实际路径用双引号包住完整路径Error: Cant find input file xxx.outPost-build步骤的工作目录和实际构建输出目录不一致使用${PROJECT_LOC}/Debug/xxx.out这样的绝对化路径生成的hex文件是空的CMD文件里没有正确分配可加载段或链接阶段没有代码段输出检查Build控制台是否有warning确认FLASH段是否分配生成的bin巨大几MB.out里包含低地址段导致地址空洞被填充调整CMD布局或用脚本裁剪地址区间用C2Prog烧写hex后程序不跑使用了带--boot的Boot Table格式替换了普通烧录hex烧Flash用--intelBoot Table只用于Bootloader搬运场景hex2000不是内部或外部命令当前终端PATH里没有编译器bin目录或工具名写错在命令中写全路径不要依赖PATH我提一句CCS 12.2的Post-build步骤报错时控制台输出的信息可能比较模糊经常是“Build Failed”加上一行状态码。这时候最好的方法是把Post-build里的命令复制到CMD终端里手动执行逐字核对。命令行能跑通多半是变量没有正确展开命令行也报错就是命令本身的问题。4.2 烧录阶段的隐性坑地址、密码区、字节序hex和bin生成出来了烧录阶段还有几个坑不会立刻暴露但出了问题会让你排查到崩溃。第一个是密码区。TMS320F28335的Flash有一个CSM密码区域位于0x003F7FF8到0x003F7FFF。如果这个区域写入全0芯片的Flash就会被锁死之后连CCS都无法通过JTAG访问。所以在量产前一定要检查生成的hex或bin文件在这个地址区间内的数据确认不是全0。如果工程里没有专门处理密码区逻辑最安全的做法是让这段保持0xFF也就是不写入任何密码。第二个是字节序。虽然28335是小端处理器但Flash的16位数据在文件中的字节排列方式不同烧录器可能有不同预期。生成hex时使用--memwidth 16 --romwidth 16能保证绝大多数情况下的正确排列。如果你遇到烧录成功但程序运行结果错乱先怀疑字节序查看hex文件中一个16位常量的字节顺序和反汇编结果做对比。第三个是烧录工具的地址解析方式。UniFlash这类TI官方工具对hex文件处理得很规范但有些第三方产线工具在解析涉及Flash起始地址偏移时可能不够严谨导致写错位置。遇到这种情况我的经验是先烧一个只有LED翻转的测试程序确认烧录工具出的地址完全正确再烧正式固件。4.3 工具选型实测官方工具、UniFlash、脚本、第三方围绕bin和hex的生成市面上可选的工具不少但我实测下来各有取舍。首选的仍然是TI官方工具链。hex2000已经在大量生产项目中验证过和编译器同源对COFF/ELF支持最完善参数虽然多但逻辑清晰。缺点是纯命令行新手需要一点学习成本。UniFlash的CLI模式也很实用它能直接导入.out再导出bin或hex。对不喜欢命令行的人来说UniFlash图形界面操作更直观。它的缺点是自动化集成不如Post-build步骤方便适合单次操作或小批量生产。基于Python的自研脚本则是我的兜底方案特别是需要裁剪地址空洞、拼接多段固件、校验CRC时脚本的控制粒度是前两者做不到的。缺点是需要维护测试覆盖不足时容易引入新问题。至于各种在线转换网站我不是特别推荐。自己能本地生成的情况下没必要把固件传到第三方平台而且在线工具对TI的COFF格式支持参差不齐很容易出现段信息丢失、地址漂移这类问题量产环节承担不起这种风险。在我当前的工程实践里标准流程是CCS的Post-build步骤同时输出普通Intel Hex和裁剪后的bin再用一个本地Python脚本做CRC校验和版本号注入最后把成品hex/bin归档到量产目录。整个链路全部自动化人为干预越少产线出错的概率就越低。这个流程跑了一年多基本没再为固件格式问题返过工。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑