首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Keil中hex与bin的区别及一键生成bin配置指南
📅 2026/11/3 18:10:19
✍️ 爱科研究院
👁 阅读 3,247
1. 从一次真实的翻车现场说起前阵子帮一个学弟看他的STM32小项目现象很典型代码在Keil里仿真跑得一切正常串口能打印LED能闪但把文件发给烧录厂的兄弟帮忙批量生产时对方回了一句“你这文件烧进去没反应啊”。学弟一脸懵把工程里的.hex发过去对方说他们产线只吃.bin学弟又翻遍工程目录发现根本找不到.bin文件于是跑来问我“Keil不是编译完就有hex吗bin是啥为啥还要单独生成”这个问题其实一点都不小众。我见过太多人Keil用了两三年新建工程、写代码、点编译、下载仿真一条龙很熟练但一旦涉及到固件交付、OTA升级、产线烧录、Bootloader跳转这些稍微“离开IDE”的场景就立刻卡壳。核心原因就一个没搞清楚hex和bin到底有什么区别以及它们各自是怎么被“用”的。这篇文章就围绕这个点展开。我会把hex和bin从文件格式本质、生成方式、地址信息、适用场景、转换方法、常见坑这几个维度彻底讲透并且给出在Keil MDK里一键生成bin的完整配置步骤。不管你是刚学STM32的新手还是已经工作几年但一直没深究过固件格式的工程师看完都能直接上手操作。关键词就三个Keil、hex、bin但背后牵扯的是整个嵌入式固件交付链路的基本功。2. hex和bin到底差在哪先把本质讲清楚2.1 一个是“带地址的文本”一个是“纯二进制镜像”很多人以为hex和bin只是“两种格式的同一个东西”换个后缀而已。这个理解是错的而且错得很关键。.bin文件是纯二进制镜像。它里面只有字节一个接一个没有任何额外的描述信息。你可以把它想象成一张“从某个起始地址开始连续铺开的内存照片”。比如一个STM32F103的bin文件大小是20KB那它就是从Flash起始地址0x08000000开始连续20KB的机器码数据。烧录器拿到这个文件默认从约定的起始地址开始写写完就完事。.hex文件则完全不同它是Intel HEX格式是一种带地址信息的ASCII文本格式。你直接用记事本打开一个hex文件看到的是这样的内容:1000000000800020CD010008C1010008C5010008D4 :100010000000000000000000000000000000000000 :04002000C9010008B8 :00000001FF每一行都以冒号:开头后面是十六进制字符。这一行叫做一条记录Record里面包含了数据长度、目标地址、记录类型、数据本身和校验和。也就是说hex文件自己“知道”每一段数据应该被烧到哪个地址去。这就是两者最根本的区别bin是裸数据地址靠外部约定hex是自描述数据地址写在文件里。2.2 为什么会有这两种格式并存这就要从使用场景说起了。早期单片机烧录很多是用编程器直接对着芯片的地址空间操作编程器软件需要知道“这段数据写到哪”所以Intel HEX这种带地址的格式非常合适它甚至支持不连续的多段地址比如Flash里有一段EEPROM里有一段一个hex文件就能描述清楚。而bin格式的流行是因为Bootloader和OTA升级场景。Bootloader在跳转或搬运固件时它只关心“从Flash某个固定偏移开始有一坨连续的代码”它不需要解析地址记录直接按字节搬运效率最高。产线的批量烧录器、一些离线下载器也倾向于用bin因为解析简单、速度快。所以不是谁替代谁而是两种格式服务于不同的链路环节。一个完整的固件交付流程里经常是hex给仿真器和调试器用bin给产线和Bootloader用。2.3 一个容易被忽略的点hex里可能不止一段因为hex带地址所以它天然支持非连续地址。比如你的工程里代码段在0x08000000但有个配置数据段被链接到了0x08010000那么生成的hex文件里就会有两段地址不连续的记录。烧录器解析hex时会分别把这两段写到对应地址。而bin做不到这一点。bin只能表示“从起始地址开始的一段连续数据”。如果中间有空洞bin要么把空洞用0xFF填充导致文件变大要么就丢失信息。这也是为什么有些工程生成bin后大小比预期大很多——中间被填充了。提示如果你发现生成的bin文件异常大先检查链接脚本里是不是有地址不连续的段或者是不是把整个Flash空间都填充了。3. Keil里怎么生成bin默认为什么没有3.1 Keil默认只给hex这是设计使然Keil MDK在编译链接完成后默认调用fromelf工具生成.hex文件如果你勾选了“Create HEX File”。这个选项在Options for Target - Output里很多人新建工程时随手勾上就再也没管过。但Keil默认不生成bin因为bin的“起始地址”和“是否填充”这些参数需要用户明确指定IDE没法替你决定。比如你的固件是从0x08000000开始还是从0x08004000开始前面留给Bootloader这只有你自己知道。所以生成bin这件事需要你手动配置一个User Command在链接完成后调用fromelf来转换。3.2 fromelf是什么为什么用它fromelf.exe是ARM编译器工具链自带的一个镜像转换工具位于Keil安装目录下的ARM\ARMCC\bin或者ARM\ARMCLANG\bin里取决于你用的编译器版本。它的作用就是把链接器输出的.axf文件ELF格式带完整符号和段信息转换成各种格式包括hex、bin、以及带调试信息的文本。为什么不用其他工具因为fromelf最懂ARM的ELF文件它能正确处理段属性只取可加载的、可执行的段不会把调试段、符号表这些无关内容也塞进bin里。用第三方工具转很容易把不该烧的东西也烧进去。3.3 配置前必须确认的一件事起始地址在配置生成bin之前你必须先搞清楚你的固件从哪个地址开始。这个信息在Keil里可以这样看打开Options for Target - Target看IROM1的Start地址。对于STM32通常是0x08000000。如果你用了Bootloader应用固件的起始地址可能是0x08004000或0x08008000这个要跟你的Bootloader约定一致。这个地址决定了fromelf转换时要不要加--base参数。如果bin要从0x08004000开始而链接脚本里代码确实从0x08004000开始那直接转出来的bin就是从0x08004000开始的数据不需要额外偏移。但如果链接脚本从0x08000000开始你想让bin从0x08004000开始那就得用--base指定。注意--base参数只是改变bin文件的“起始地址标记”它不会帮你做地址重定位。如果你的代码本身链接在0x08000000却用--base 0x08004000生成bin烧到0x08004000后代码里的绝对跳转全会错。正确做法是链接脚本和bin起始地址保持一致。4. 手把手配置Keil一键生成bin4.1 找到User配置入口打开Options for Target切到User标签页。这里有两个区域Before Build/Rebuild和After Build/Rebuild。我们要用的是After Build/Rebuild因为bin必须在链接完成、axf文件生成之后才能转换。勾选Run #1然后在后面的输入框里填入命令。命令的写法有讲究下面详细说。4.2 命令怎么写逐段拆解一个典型的命令是这样的fromelf --bin --output.\Objects\app.bin .\Objects\app.axf逐段解释fromelf工具名。如果Keil的PATH环境变量没配好可能需要写全路径比如C:\Keil_v5\ARM\ARMCLANG\bin\fromelf.exe。但通常Keil执行User Command时会自动带上工具链路径直接写fromelf就行。--bin指定输出格式为纯二进制。--output.\Objects\app.bin指定输出文件路径和名字。.\表示工程目录Objects是Keil默认的输出文件夹app.bin是你要生成的文件名可以自己改。.\Objects\app.axf输入的axf文件路径。这个名字要跟Options for Target - Output里的Name of Executable一致。如果你想让bin从特定地址开始加上--base参数fromelf --bin --base0x08004000 --output.\Objects\app.bin .\Objects\app.axf4.3 一个更稳妥的写法用Keil内置变量直接写死路径有个问题如果你改了工程名或者输出目录命令就失效了。Keil提供了一些内置变量可以让命令更通用fromelf --bin --output$LL.bin #L这里的$LL和#L是Keil的Key Sequence#L代表链接器输出的axf文件完整路径。$LL代表axf文件所在目录加上文件名不含扩展名再拼上.bin。这样写的好处是不管你工程叫什么、输出到哪命令都能正确找到文件并生成同名的bin。实测下来这个写法最省心推荐直接用。4.4 验证是否生成成功配置完点一次Rebuild然后去Objects文件夹看应该能看到.bin文件。如果没看到检查两个地方User标签页的Run #1有没有勾上。编译输出窗口Build Output里有没有报错。如果fromelf找不到会提示fromelf 不是内部或外部命令这时候需要写全路径。另外可以对比一下hex和bin的大小。通常bin会比hex小因为hex是文本格式每个字节用两个ASCII字符表示还有地址和校验开销。比如一个20KB的固件hex可能40KB以上bin就是20KB左右。5. hex和bin的互转以及那些年踩过的坑5.1 hex转bin注意地址偏移有时候你手头只有hex但需要bin。这时候可以用fromelf反过来转吗不行fromelf的输入必须是axf或elf。hex转bin需要用其他工具比如hex2bin或者用Python脚本自己解析。用hex2bin的典型命令hex2bin -s 0x08000000 app.hex-s指定起始地址。这里有个大坑如果hex里的地址不是从你指定的起始地址开始hex2bin会在前面填充。比如hex从0x08004000开始你指定-s 0x08000000那生成的bin前面会有16KB的填充数据通常是0xFF文件会变大而且烧录时如果烧录器从0x08000000开始写就会把填充数据也写进去可能覆盖掉Bootloader。所以转之前一定要确认hex的实际起始地址。用记事本打开hex看第一条数据记录的地址字段。比如:1000000000800020...这里的0000是数据长度0000是地址高位0000是地址低位合起来地址就是0x0000。但这是相对地址实际地址要看链接时的基地址。更可靠的方法是用fromelf --text -c把axf转成文本看每个段的起始地址。5.2 bin转hex需要指定地址反过来bin转hex也需要指定起始地址因为bin本身不带地址。可以用srec_cat工具srec_cat app.bin -binary -offset 0x08000000 -o app.hex -intel-offset指定bin数据在地址空间中的偏移。这个命令会把bin的内容放到0x08000000开始的位置生成Intel HEX格式。5.3 常见问题速查表问题现象可能原因排查方法Keil编译后没有bin文件User Command没配置或没勾选检查Options for Target - Userbin文件异常大链接脚本有地址不连续的段被填充用fromelf --text查看段分布bin烧进去不运行起始地址不对或bin包含了不该有的数据确认链接地址和烧录地址一致hex转bin后前面多出很多0xFFhex起始地址高于指定的-s地址用hex2bin时-s设为hex实际起始地址fromelf报错找不到文件路径写错或axf名字不对用#L和$LL变量替代硬编码路径生成的bin比hex还大不太可能除非hex是压缩过的检查是否误把调试段也转进去了5.4 一个真实踩坑案例之前有个项目Bootloader在0x08000000应用在0x08004000。应用工程链接地址设的是0x08004000生成bin时没加--base直接fromelf --bin。结果bin文件确实是从0x08004000开始的数据大小也正常。但烧录厂的兄弟习惯性地把bin烧到0x08000000导致Bootloader被覆盖整个板子变砖。问题出在沟通上产线不知道这个bin是给应用区的他们默认从Flash起始地址烧。后来我们在bin文件名里加了_app_0x08004000后缀并且在交付文档里明确写了烧录地址才避免再次出错。所以记住bin文件本身不携带地址信息交付时必须明确告知烧录地址。这是bin最大的“坑”也是它和hex最本质的区别。6. 不同场景下该用hex还是bin6.1 仿真调试用hex或axf在Keil里点Debug仿真时其实用的是axf文件因为axf带完整的调试信息符号、行号、变量类型。hex只是给外部烧录器用的。如果你用ST-Link Utility或J-Flash烧录它们通常支持hex、bin、axf多种格式但hex最通用因为带地址不容易烧错位置。6.2 产线批量烧录看烧录器支持产线的离线烧录器有的只支持bin有的只支持hex。如果支持hex优先给hex因为地址信息明确不容易出错。如果只支持bin那必须提供烧录地址说明最好在文件名里体现。6.3 OTA升级几乎都用binOTA升级时Bootloader从服务器下载固件然后写入Flash的应用区。这个过程通常用bin因为Bootloader只需要按字节搬运不需要解析hex的地址记录。而且bin体积小传输快。但OTA用bin有个前提Bootloader和APP必须约定好固定的起始地址和大小。比如约定APP从0x08004000开始最大0x0003C000字节。Bootloader收到bin后直接从这个地址开始写写完校验然后跳转。6.4 多目标工程每个target单独配置Keil支持多Target比如一个工程里同时有Bootloader和APP两个Target。这时候每个Target的User Command要单独配置输出不同的bin文件名。比如Bootloader输出boot.binAPP输出app.bin。可以用$LL变量自动区分因为不同Target的输出文件名不同。7. 几个进阶话题值得多看一眼7.1 hex里的扩展地址记录Intel HEX格式支持扩展线性地址记录Type 04和扩展段地址记录Type 02。当固件地址超过64KB时就需要这些记录来扩展地址范围。比如STM32F4的Flash从0x08000000开始这个地址已经超过16位所以hex里会出现Type 04记录来指定高16位地址。如果你自己写脚本解析hex必须处理这两种记录否则地址会算错。这也是为什么推荐用成熟工具如hex2bin、srec_cat而不是自己手搓解析器的原因。7.2 bin的填充与对齐有些烧录器要求bin文件按特定大小对齐比如4KB或16KB。如果bin大小不是对齐的烧录器可能会报错。这时候可以用工具在bin末尾填充0xFF到对齐边界。fromelf本身不提供填充选项但可以用--pad参数部分版本支持或者用Python脚本处理。7.3 校验和与完整性hex文件每条记录都有校验和能检测传输错误。bin没有校验和所以OTA升级时通常会在bin末尾附加一个CRC32值或者用单独的校验文件。Bootloader收到bin后先算CRC跟预期值比对一致才写入Flash。7.4 从hex反推C语言别想了热搜词里有个“hex文件反编译成c语言”这里顺带说一句hex是机器码的十六进制表示反编译成汇编还有可能用IDA等工具反编译成可读的C语言几乎不可能尤其是经过优化的代码。变量名、函数名、注释全丢了控制流也可能被优化得面目全非。所以别指望从hex还原出源码老老实实保管好工程文件。8. 我个人的几条实操建议第一新建工程时就把bin生成配置好。不要等到要交付了才临时加很容易漏。配置一次以后所有编译都自动生成省心。第二bin文件名带上地址信息。比如app_0x08004000.bin这样即使文件被转发多次烧录的人也能一眼看出该烧到哪。第三hex和bin都保留。hex给调试和通用烧录器bin给产线和OTA。两个都生成不冲突多占一点磁盘空间而已。第四交付固件时写清楚三件事文件格式、烧录起始地址、固件大小。最好再附一个CRC32值。这三件事写清楚能避免90%的“烧进去没反应”问题。第五定期用fromelf --text检查段分布。命令是fromelf --text -c -o output.txt app.axf生成的文本里能看到每个段的名称、起始地址、大小。如果发现有不连续的段或者意外的段被包含进来及时调整链接脚本。最后再分享一个小技巧如果你用的是Keil MDK 5.38以上版本fromelf的路径可能在ARM\ARMCLANG\bin下而不是老的ARM\ARMCC\bin。如果User Command报找不到fromelf去这个目录下确认一下或者直接把全路径写进命令里。这个细节很小但卡住过不少人。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/11/3 18:10:19
3111S单通道直流电机驱动芯片:选型、控制与保护实战指南
2026/11/3 18:10:19
PIC18LF45K80与MAX14431实现SPI隔离通信设计
2026/11/3 18:10:19
工业SPI隔离实战:用数字隔离器MAX14431与PIC24HJ设计稳定通信链路
2026/11/3 23:27:41
CAP定理与分布式系统设计:从理论到实践
2026/11/3 23:27:41
Java抢票程序拆解:GOHOME自动购票工具的实现与边界
2026/11/3 23:27:41
CAD夹点编辑技巧:提升设计效率的5种核心模式
2026/11/3 23:27:41
DeepSeek推理模型提示语实战:从选型到模板固化
2026/11/3 23:27:41
反正切函数求导全解析:从公式推导到高频错误清单
2026/11/3 23:22:40
Paperxie:一站式论文写作工具的功能解析与使用技巧
2026/11/3 0:03:28
淘宝商品详情API开发实战与性能优化指南
2026/11/3 0:03:28
Unity Package Manager刷新错误排查与解决方案
2026/11/3 0:03:28
Java record 规范构造器能否被反序列化或反射绕过
2026/11/3 4:05:41
面向复杂光照与角度的宠物面部识别鲁棒性增强策略
2026/11/3 4:05:35
DRV8818PWPR与R7FA8M1AHECBD工业步进驱动实战指南
2026/11/3 4:05:30
STM32与DRV8818双极步进电机驱动方案:从选型到调试全流程解析
2026/11/3 4:05:41
面向复杂光照与角度的宠物面部识别鲁棒性增强策略
2026/11/3 4:05:35
DRV8818PWPR与R7FA8M1AHECBD工业步进驱动实战指南
2026/11/3 4:05:30
STM32与DRV8818双极步进电机驱动方案:从选型到调试全流程解析