ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Keil生成bin文件完全攻略:C51与ARM工程实践

Keil生成bin文件完全攻略:C51与ARM工程实践 1. 为什么非要用bin文件三句话讲清楚先说个我最近踩坑的真实场景。产品要做OTA升级Bootloader和App分区各占一块Flash。调试阶段我用HEX文件烧录一切正常等到真正做升级功能时发现App区必须直接烧裸的二进制镜像HEX连用都用不上。被迫临时去翻工具链现场折腾了半小时才把bin生成出来。这就是我写这篇攻略的起因bin文件这东西平时不觉得多重要等你要做Bootloader、做量产、做远程升级的时候它就是刚需绕不开。网上讲Keil生成bin的教程不少但多数只讲一种编译器要么只讲C51要么只讲ARM。实际情况是很多单片机工程师手头同时维护着几款8位和32位平台的项目一块板子是STC89C52另一块就是STM32F103两边都得能出bin。所以这篇我把C51和ARM两条路线都完整梳理一遍包括工具命令、配置位置、路径坑位、批量用法一次说透。1.1 HEX和bin到底差在哪搞懂bin之前先得把HEX文件那层窗户纸捅破。HEX文件本质是文本文件每一行都带地址和校验信息是给人看、给调试器解析的。比如Keil输出的标准Intel HEX格式一行长这样:020000040800F2 :10000000F0F000F8F0F000F80000000000000000 :0400000508000000ED冒号开头第一个字节是记录长度后面是地址、记录类型、数据段、校验和。烧录器读到这些行按地址把数据填进Flash。好处是中途断掉可以续传、单个块损坏能发现这对调试阶段非常友好。bin文件则是纯二进制镜像里面没有任何格式信息连起始地址都没有。0x00000000地址上的内容是什么文件第一个字节就是什么。它忠实反映存储器的最终状态。打个比方HEX像快递单写了收件人、地址、物品清单bin像裸包装的货物本身你知道这箱东西要搬到某栋楼但它不会告诉你楼在哪搬运工如果不知道地址就抓瞎。这个区别直接导致了两件事。第一bin必须烧到指定地址才有意义烧错位置白烧第二bin文件大小就是固件在Flash里的实际占用空间如果不考虑填充对齐的话。对Bootloader升级来说把bin文件按固定偏移搬进Flash就是最简单直接的操作。1.2 三个无法绕开的场景哪三种情况最需要bin文件我用自己的项目经历来举例。第一种Bootloader App架构。这是考研级、面试级、项目级的老三样。Bootloader区存放引导程序App区存放应用代码升级时Bootloader通过串口或无线收到bin文件写入App区然后跳转。这个过程里HEX几乎帮不上忙——你总不能把HEX的文本内容按行解析在单片机内部再算一遍校验吧。直接搬bin才是常规做法。第二种产线量产烧录。产线不希望工人拿着调试器也不希望依赖某个IDE环境。更常见的做法是用专用的脱机烧录器或全自动烧录机把bin文件直接灌到单片机里。文件越简单越好没有任何多余格式烧录器不需要解析地址和类型效率最高。第三种给客户或同事发固件。很多嵌入式公司交付固件时不会直接给HEX或AXFAXF里还带调试符号相当于把源码底裤都扒了给人看而是给bin文件。体积小、容易加密处理、内容干净对接收方来说也友好省得对方自己用Keil打开工程重新编译。所以bin文件不是“高级玩家才用的东西”而是嵌入式开发日常绕不开的基础产物。下面进入正题先讲C51侧的做法。2. C51工程生成bin文件从OH51到Hex2BinC51工程生成bin文件经历过两个典型阶段老派做法是用Keil自带的OH51工具现在更顺手的是用Hex2Bin这种独立小工具。两者我都用过各有各的坑。2.1 最容易踩坑的OH51方案OH51是Keil C51自带的一个命令行工具全称是Object-Hex Converter对应文件是OH51.exe一般在C:\Keil_v5\C51\BIN\目录下。它的作用是把编译生成的OMF文件转换为二进制格式或HEX格式。命令大概是OH51 project.omf执行后默认会生成一个和工程同名的bin文件基本够用。但OH51有两个大坑我是实实在在踩过。坑一地址偏移导致bin体积炸裂。OH51转换时如果代码不是从地址0开始链接它会按绝对地址把前面的“空洞”全部用0xFF填充。假设我把代码放在0x8000开头的区域实际代码只有8KB但OH51生成的bin可能直接从0x0000写到0x9FFF看起来是40KB。这个bin文件烧进Flash倒是没错但传到Bootloader做升级时数据量大得离谱传输效率低到让人崩溃。坑二OH51的输入是OMF文件。如果你在Keil里没有勾选“Generate OMF output”编译后只有AXF或HEXOH51就拿不到输入文件。现在很多新版Keil C51默认输出HEX格式但OMF有独立的开关很多人根本没注意OH51就会报“input file not found”之类的话。所以OH51这招适合代码从0地址link的简单小项目一旦涉及Bootloader偏移、App区偏移它就是给自己挖坑。2.2 Hex2Bin实战配置五步把它焊进Keil我现在的C51工程几乎不用OH51改用Hex2Bin.exe这个小工具。它的核心能力是把Intel HEX格式转换成纯bin文件而且能正确处理地址信息不会把空洞无脑填满体积控制得干净利落。Hex2Bin.exe本身是Keil C51老版本里自带过的一个小工具后来在MDK里不再默认附带需要单独准备一份。很多单片机论坛和代码托管平台上都有人存过注意找正版、无捆绑的版本就好。它就是个命令行工具我在工程里放了一份一直用到现在没出过幺蛾子。第一步确认编译输出HEX。Keil C51工程在Options for Target → Output页面勾选“Create HEX File”。这一步不勾后面所有步骤都白搭。C51默认是勾的但有时候别人给的工程模板会把这项关掉新手尤其容易漏。第二步找到Hex2Bin.exe。我习惯在工程根目录建一个tools文件夹把Hex2Bin.exe放进去路径相对稳定换电脑也不至于找不到。也可以放在Keil安装目录的C51\BIN里这样能直接用环境变量找到但我不太推荐——Keil一升级就可能被清掉。第三步配置User标签页。进入Options for Target → User页面找到After Build/Rebuild区域勾选Run #1然后在输入框里写命令。注意分清After Build和After Rebuild的区别After Build每次编译Build后执行After Rebuild每次全部重新编译Rebuild后执行如果你只填了After Build哪天点了Rebuild命令是不会跑的。稳妥做法是两个都勾上。第四步写命令。我常用的写法..\tools\Hex2Bin.exe ..\obj\project.hex ..\obj\project.bin这里假设hex文件在obj目录下工程文件在上一级目录。如果你的工程结构和我的不一样把相对路径调整成你自己的即可。也可以直接用绝对路径但相对路径的好处是工程整个文件夹挪到任何电脑上命令都不失效。第五步编译验证。执行一次Build看Build Output窗口里的输出信息应该能看到Hex2Bin.exe的执行结果同时在obj目录下多出一个project.bin。打开这个bin文件确认大小和预期Flash占用一致。注意如果你用绝对路径路径里有中文或空格很可能导致命令行解析失败。Keil对这类情况的处理有时相当迷惑建议要么用相对路径要么保证路径全英文、无空格。2.3 不愿装工具就写个脚本有时候环境受限不好找现成的Hex2Bin.exe。我自己写过一段Python脚本专用于把标准Intel HEX转成bin放在CI持续集成环境里也能跑非常方便。核心思路就是解析HEX文本行按记录类型处理import sys def hex2bin(hex_path, bin_path): with open(hex_path, r) as f: lines f.readlines() buf {} base_addr 0 max_addr 0 for line in lines: line line.strip() if not line or line[0] ! :: continue byte_len int(line[1:3], 16) addr int(line[3:7], 16) rec_type int(line[7:9], 16) data line[9:9 byte_len * 2] if rec_type 0x04: # Extended Linear Address base_addr int(data, 16) 16 elif rec_type 0x00: # Data for i in range(0, byte_len * 2, 2): buf[base_addr addr i // 2] int(data[i:i 2], 16) if base_addr addr byte_len max_addr: max_addr base_addr addr byte_len with open(bin_path, wb) as f: for offset in range(max_addr): f.write(bytes([buf.get(offset, 0xFF)])) print(fgenerated {bin_path}, size {max_addr} bytes) if __name__ __main__: hex2bin(sys.argv[1], sys.argv[2])这段脚本能处理包含扩展线性地址记录的HEX文件Flash地址超过64KB的情况也覆盖了。把它存成hex2bin.py命令行执行python hex2bin.py project.hex project.bin效果和Hex2Bin.exe基本一致。如果没有Python环境也可以用其他脚本语言改写逻辑都一样。3. ARM工程生成bin文件fromelf一条命令搞定到了ARM这一侧事情反而简单了。ARMCC编译器自带一个fromelf工具专门负责把ELF格式的文件转换成各种烧录格式包括bin、Hex、Motorola等。不需要额外找工具只要装了Keil MDKfromelf一定躺在你硬盘里。3.1 fromelf核心用法参数顺序要记牢fromelf的命令格式我用熟之后就一句话fromelf --bin --outputxxx.bin xxx.axf--bin是转换目标格式--output指定输出文件名放在末尾的是输入的AXF文件。注意AXF是链接后生成的ELF文件在Keil工程的Listings或Objects目录下能找到。不要用.hex做输入fromelf不认。实际项目中我常这么干fromelf --bin --output.\Output\project.bin .\Output\project.axf生成成功后在Output目录下就能看到project.bin。这个bin的大小对应的就是AXF里加载段load region的实际大小不会有C51地址空洞填充那种诡异问题ARM的fromelf处理得规规矩矩。fromelf不只支持bin它还能输出i32格式Intel Hex的32位变体和m32格式Motorola S-Record的32位变体。这些格式在特殊烧录器或者Bootloader需求里偶尔用得到知道有这个能力就行日常用bin足矣。注意fromelf的--bin参数是区分大小写的写成--BIN或大写缩写的工具解析不了报错信息还很绕半天看不出来哪里错。我第一次用AC6时把命令写成了--Bin卡了好久。3.2 把命令焊死在Keil里User页配置命令行能用但没人愿意每次编译后手动敲一遍。正确姿势是配置到Keil的User页面让每次Build后自动执行。进入Options for Target → User在After Build/Rebuild区域勾选Run #1填上fromelf命令。这里有个和C51不一样的细节C51那条命令里需要写Hex2Bin.exe的全名但ARM这条fromelf命令一般不用写完整路径因为Keil会自动把ARM编译器目录加入PATH。我常用的配置是这样fromelf --bin --output.\Output\project.bin .\Output\project.axf注意一下输出目录。Keil的默认目录结构是工程目录下有个Output文件夹取决于Target Options里的设置。相对路径的写法和你在哪个目录编译有关但Keil会以工程文件所在目录为基准解析相对路径我实测这个规则在AC5和AC6上是一致的。配置完后随便改几行代码执行Build。Build Output窗口里如果出现类似这样的信息.\Output\project.bin: 16384 bytes就说明转换成功了。3.3 AC5与AC6的路径差异ARM Compiler 5AC5和ARM Compiler 6AC6的fromelf都叫fromelf但存放位置完全不一样。我本机的路径经验是这样的AC5ARMCCC:\Keil_v5\ARM\ARMCC\bin\fromelf.exeAC6ARMCLANGC:\Keil_v5\ARM\ARMCLANG\bin\fromelf.exe如果你在命令行里直接敲fromelf系统实际调用的是哪个版本取决于Keil当前工程的编译器选择。在Options for Target → Target页面看“ARM Compiler”下拉框选的是V5还是V6就用对应目录下的fromelf。我在一个工程上遇到过这种情况工程默认AC6我从旧电脑复制了一段AC5的fromelf命令路径里写的是ARMCC\bin结果Keil直接报找不到工具。后来改成不写路径让Keil自动找问题就消失了。所以我的建议很简单User页面里不要硬编码fromelf的绝对路径直接写fromelf命令名让Keil根据当前编译器环境自动解析。跨电脑、跨编译器版本都不容易踩坑。3.4 从map文件验证bin的对不对生成bin之后不少人心里犯嘀咕这个bin到底对不对我一般习惯配合map文件做二次确认。map文件就是编译生成的链接映射文件里面详细记录了每个段region的起始地址和大小。打开map文件找到类似这样的内容Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00004000, Max: 0x00080000, Absolute) Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00004000, Max: 0x00080000, Absolute)这里Size是0x4000也就是16384字节。把生成的bin文件大小和这个Size对比一下如果一致那基本没问题。不一致说明链接脚本或代码段布局和你的预期有出入需要仔细排查。这个“map文件bin大小对照”的小习惯我每次发固件前都会做一遍成本极低却能拦住大量低级错误。4. 工具下载与版本问题的正确打开方式标题里写了“附工具下载”那我就把工具获取这件事说透。不少人一听说需要Hex2Bin.exe就从各个论坛乱找下载一个被改过、带捆绑的版本最后给自己电脑装了一堆垃圾。这个环节要慎重。4.1 Keil自带工具清单先分清哪些是Keil官方自带、不需要额外下载的工具用途位置fromelf.exeARM参数转换工具输出bin/hexARM\ARMCC\bin 或 ARM\ARMCLANG\binOH51.exeC51工程老式OMF转换工具C51\BINobjhex.exeC51旧版Hex转换工具C51\BIN这三兄弟里fromelf是ARM工程生成bin的主力全流程可靠OH51和objhex是C51老工具能用但有限制。如果你只在MDK里做ARM开发完全不需要额外下载任何工具。4.2 Hex2Bin与AC5/6的获取建议Hex2Bin.exe不是Keil官方当前版本随包附带的但它知名度高找起来不难。我的建议是优先从你手头的Keil安装目录里翻一翻有时候老版本的MDK会在C51\BIN下自带它。没有就在可信的代码托管平台搜“Hex2Bin”关键字下载前多看几个star和issues。实在找不到就用我上面给的Python脚本方案自己动手丰衣足食。ARM Compiler 5.06和6.x版本官方更新包在MDK的Pack Installer里就能获取也可以从官网的下载中心找历史组件。但这里有个大前提你需要有对应的MDK授权否则某些版本的编译器组件下载后无法在Keil中激活使用。这部分涉及授权与合规不建议走任何捷径各家用各家的授权属于行业基本常识。4.3 工具的合法性提醒工具下载这档子事我放句话在这里尽量用官方渠道和可信的代码托管平台远离来路不明的注册机、破解版、绿色版。嵌入式开发工具链是吃饭的家伙在工具链里埋雷是不明智且不划算的。如果你的Hex2Bin.exe是从某个小论坛来的跑之前先杀个毒Windows Defender扫一遍心里有数再用。另外很多工具允许个人免费使用但商业项目要仔细看EULA。别为了省一个工具的授权费在客户审计时翻车。5. 实测高频问题与排查实录把工具串起来以后最容易出问题的反而不是命令本身而是路径、大小写、执行顺序这些边角料。我把这些年见过的、自己也踩过的问题集中列一下都是真实案例。5.1 CreateProcess failed多半是路径的锅Keil的Build Output窗口报错最常见的一句是*** Error: CreateProcess failed, Command: C:\Keil_v5\ARM\ARMCC\bin\fromelf...这个报错我一看到就有三个怀疑对象第一fromelf.exe路径不对特别是把AC5和AC6的工具路径混用了第二命令行的参数写错或者路径有中文空格导致解析失败第三User页面的命令勾选了两行但其中一行配置有问题。排查思路按顺序来。先看fromelf.exe是否真的存在于报错路径下不存在就去正确的目录复制路径然后检查命令行里是否有多余的引号或反斜杠最后把参数简化到最简形式比如先在cmd里手动执行一次确认能转出bin再来配置Keil。5.2 bin跑到“上一级目录”去了Keil的User页命令里相对路径的基准并不总是你想象的“工程文件目录”。如果你在Output页面改了输出目录比如输出到了..\Output\那么命令里的相对路径也要基于最终的目录关系调整。我自己就吃过一次亏把输出目录设置成.\Output\然后在User命令里写..\Output\project.axf结果编译一执行fromelf抱怨找不到AXF文件。原因是我把基准目录记错了多写了一层跳转。解决办法很土但很有效先在cmd里cd到工程目录然后手工敲一遍命令看看到底哪一级目录才是正确的。确认之后再把命令写进Keil。不要凭感觉写相对路径跑一次就露馅。5.3 生成了但地址全错有的工程生成bin后发现文件开头全是0xFF真实数据出现在文件末尾。这个问题几乎都是同一个原因链接脚本里代码基地址不是从0开始但转换工具却按地址0开始布局。C51侧这个现象特别明显。ARM侧fromelf处理得较好它会把加载区load region的地址规范化不会把空洞全填0xFF。但如果你用某些第三方转换脚本比如从网上找的Hex转Bin工具很可能遇到这个问题。我的建议是尽量用官方fromelf或者用我上面提供的脚本——脚本逻辑里已经考虑了基地址偏移不会无脑填充空洞。顺带说一句如果Bootloader和App有固定偏移bin文件烧录地址应该以链接后的实际加载地址为准。写升级逻辑时App的bin烧录起点要和map文件里的加载区地址一致这是另一个容易踩的大坑。5.4 常见问题速查表把高频问题整理成一张表方便日常工作快速查阅现象原因解决办法CreateProcess failed提示fromelf路径AC5/AC6工具路径混用或缺失不写绝对路径直接用fromelf命令名生成的bin在“上级目录”相对路径基准判断错误在cmd里手工执行一次定位正确路径bin开头大量0xFFC51的OH51按绝对地址填充空洞改用Hex2Bin.exe或Python脚本fromelf报参数不识别--bin大小写写错统一用小写--binUser命令不执行只勾了After Build没勾After Rebuild两个都勾上文件路径有中文或空格导致失败命令行解析困难项目目录统一使用英文路径C51输出没有HEX文件未勾选Create HEX FileOutput页面勾选该选项OH51报输入文件找不到OMF开关没打开在Output页面勾选Generate OMF output这张表我直接贴在工位旁边每次给不同平台生成bin时扫一眼能少踩很多重复的坑。6. 最后再分享两个我常用的组合拳正经步骤都讲完了最后说点我日常实际在用的扩展玩法。这些细节写文档的人往往不会告诉你但用熟了真的能提升效率。组合拳一编译后自动检查bin大小。我在User页里除了fromelf命令还会再加一条命令行检查语句在Windows上可以用批处理的for循环读取bin大小超出Flash容量就报警。不过Keil的User页面执行多行命令有些局限我更推荐的方式是在CI脚本里做这件事。我的Jenkins流水线里编译后用Python脚本读取bin文件大小和Flash容量比对超过预期就直接fail整个构建。这个习惯帮我提前拦下了好几次固件超容量的问题比每次手动看bin体积省心太多。组合拳二Bootloader升级时给bin加校验。如果bin要用于OTA升级裸的bin文件是没有校验机制的。我一般会在打包阶段用脚本给bin末尾追加4字节CRC32校验值升级协议里约定好校验收尾。这样传输损坏能在写入前被识别而不是刷完才发现启动不起来。这段逻辑不复杂但能明显提升升级流程的可靠性。你们可以根据自己协议里的约定把bin文件的校验、加密、分包处理串成一条工具链一次性把待升级文件准备好。我个人在实际操作中的体会是工具永远不嫌简单关键在于组合和流程化。把生成bin、校验、大小检查这些琐碎步骤焊进自动化流程里你的精力才能从复制粘贴命令中解脱出来真正放到业务逻辑上。希望这篇攻略能让你的C51和ARM工程都能在一条命令里干干净净地产出bin。
返回列表