多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

固件下载全程解析:从ISP到OTA,嵌入式烧录避坑指南

固件下载全程解析:从ISP到OTA,嵌入式烧录避坑指南 这一讲我们聊一个在嵌入式开发里天天做、却总被当成“小事”的环节固件与程序下载。见过太多人把下载理解成“点一下烧录按钮”等到量产或者刷机时才被各种奇怪问题折磨——串口连不上、校验失败、刷完黑屏、甚至直接变砖。这篇文章不教你怎么编译代码而是把固件和程序下载这件事从原理到工具、从文件格式到安全加密、从正常操作到翻车排错完整走一遍。如果你正在学STM32/GD32、玩ESP32或者喜欢折腾路由器固件这篇应该能帮你省下不少弯路。1. 下载前的最后一公里先弄清楚程序、固件和镜像1.1 程序、固件、镜像三者不能混着叫很多初学者会把程序、固件、镜像这三个词混用但它们其实是不同粒度的东西。程序是源代码编译出来的可执行代码可能只是一个函数集合固件是烧进非易失存储器里的完整运行环境至少包含启动代码、中断向量表、应用主体有时候还包括文件系统镜像则是最终写入Flash的二进制内容可以看成Flash某个区域的完整“照片”。举例来说STM32编译出来一个.hex它本身是程序如果加上Bootloader和配置文件一起打包就是一个固件包而把分区表、Bootloader、应用全部拼接出来的.bin就是镜像。理解这个区别很重要因为后续所有下载操作都要面对“你手上到底是什么文件”这个问题。GD32F303开发就是个典型场景。官方固件库只是程序库里面没有下载配置真正下载时你烧的是编译链接后的hex/bin。而像华为EC6110T这类安卓盒子刷机时网上说的“固件”通常是一个包含分区表、系统、引导的多文件包烧录方式和单片机完全不一样。如果你拿单片机那套“加载一个hex直接烧”的思维去处理盒子固件大概率会刷出问题。1.2 为什么“编译通过”不等于“下载后能跑”编译器只检查语法、类型、链接是否通过它不关心你下载到哪块Flash、起始地址对不对、时钟配没配好。我见过最典型的翻车代码在开发板上跑得好好的画了自己的PCB后下载却不运行查了半天发现是BOOT0引脚电平不一样芯片上电后直接进了ROM Bootloader而不是运行Flash里的程序。固件下载能不能成功依赖的是一整条链路PC端驱动、下载器固件、目标芯片供电、复位时序、下载算法、Flash地址、文件格式哪一环出问题都会失败。所以这一讲的核心思路就是把这整条链路拆开逐个环节对齐。还有一个容易忽略的点下载器本身也有固件。J-Link、ST-Link出厂时内部有固件用久了或者升级失败会导致识别异常这时候需要先升级、修复下载器自身的固件再去下载目标固件。很多人把目标板问题误判成下载器问题折腾半天其实先给下载器重新刷一遍固件就好了。2. 四条下载通道ISP、IAP、ICP、OTA选错一条就折腾半天2.1 四种通道到底有什么区别固件下载不是只有一种方式按烧录时机和介入方式划分主流是四种ICP、ISP、IAP、OTA。通道全称写入位置是否需要外部工具典型场景ICPIn-Circuit Programming通过JTAG/SWD调试口直接访问Flash需要下载器研发调试、小批量生产ISPIn-System Programming芯片出厂ROM Bootloader通过UART/USB/SPI接收数据只需串口/USB线批量产线、无需专用下载器IAPIn-Application Programming用户Bootloader在运行时接收升级数据不需要通常通过UART/CAN/网络现场升级、功能定制OTAOver-The-Air在IAP基础上通过网络/无线下载升级包不需要物联网设备远程升级表格里最关键的一点是ISP和ICP都发生在芯片“没有运行用户程序”的时候IAP和OTA则发生在用户程序运行的时候。这决定了你的下载流程怎么设计。比如STM32的ISP就是把BOOT0拉高、BOOT1拉低复位后芯片进入系统存储器运行出厂固化的ROM Bootloader然后通过USART1等接口接收数据把hex写入Flash。整个过程芯片里没有你的用户代码在跑。而IAP不同上电后先运行你自己写的BootloaderBootloader检查有没有升级指令有就接收新固件没有就跳转到App。2.2 Bootloader 是所有下载方案的隐藏主角不管是ISP还是IAP核心都是Bootloader。STM32出厂ROM里有一段固化Bootloader通过BOOT引脚选择启动模式可以用串口直接下载这就是ISP用户自己写的Bootloader放在Flash起始地址上电后先检查有无升级请求没有就跳转App这就是IAP。路由器领域的Breed、U-Boot其实就是更复杂的Bootloader。它们负责引导系统、提供Web刷机界面。可以这么理解Bootloader是设备下载固件时的“接货员”没有它外部数据不知道往哪放也不知道该放在哪个Flash分区。这里要特别提醒Bootloader一旦丢失设备基本就失去了后续所有下载通道。很多“刷成砖”的案例不是应用固件坏了而是Bootloader区域被误覆盖。所以刷任何大版本固件前先确认固件包是不是包含Bootloader分区包含的话更要谨慎。2.3 实际项目里怎么选看阶段不看喜好研发阶段SWD/JTAGICP优先下载速度快支持断点调试改了代码马上烧进去看现象。量产阶段ISP串口批量烧录或用离线烧录器。如果每台设备要写唯一序列号还要配合烧录脚本自动生成。现场升级OTA优先但必须设计版本回退机制否则一个坏固件推上去整个设备群都得跑现场救砖。选通道还要看成本。ICP的下载器每个工位都要配成本高ISP只要USB转串口成本低但速度慢OTA需要网络模块和服务端开发量大。没有绝对好坏只有当前阶段合不合适。我见过一些团队在研发阶段就急着做OTA结果Bootloader不稳定反而拖慢了进度也见过量产阶段还在用SWD逐个下载效率低得吓人。3. 工具链全景从 J-Flash 到 esptool每类工具都有脾气3.1 ARM Cortex-M 三件套J-Flash、STM32CubeProgrammer、OpenOCD以STM32/GD32为例最常用的下载工具就三款。J-Flash适合J-Link用户操作直观打开软件选择芯片型号加载hex/bin点Program它会自动完成擦除、写入、校验。J-Flash在量产场景里很好用可以批量加载配置文件还能写序列号到指定Flash地址。STM32CubeProgrammer是ST官方工具既能通过ST-Link下载也能通过串口ISP还可以设置读保护等级一个工具管全流程。它的UART模式对应STM32的ISP下载不需要额外下载器一条USB转串口线就能干活。OpenOCD适合命令行和Linux环境好处是脚本控制适合自动化测试。比如在CI流水线里跑一条openocd命令编译完自动烧录、自动读回校验。我的建议是开发时用IDE里的调试器量产时用J-Flash或CubeProgrammer服务器上用OpenOCD。补充一个容易踩的坑J-Flash里芯片型号选错或者加载的Flash算法文件不对会直接导致擦除失败。GD32和STM32引脚兼容但Flash算法不通用不能拿STM32的算法去烧GD32。芯片厂在兼容的同时都有自己的FLM文件选型时必须选对应厂商的。3.2 ESP32/ESP8266esptool.py 的地址是个技术活玩ESP系列的人应该都遇到过类似这样的命令esptool.py --port COM3 --baud 460800 write_flash 0x1000 bootloader.bin 0x8000 partitions.bin 0x10000 firmware.binESP的Flash不是整块只放一个固件而是分成Bootloader、分区表、应用镜像等多个区块地址必须对应分区表。这里的0x1000、0x8000、0x10000不是随便写的是由编译时生成的分区表决定的。如果ESP32的Bootloader在0x1000你非要从0x0000烧设备上电后一样没反应。合并烧录也很常见官方提供了合并bin的工具也可以用esptool的merge_bin命令把多个分区合并成一个文件方便产线一次写进去。最怕的是只烧了应用固件到0x10000忘了Bootloader和数据分区结果上电没反应。另一个常见问题是擦除整个Flashesptool.py erase_flash。如果是量产阶段这条命令会把出厂校准数据也擦掉Wi-Fi射频参数、MAC地址存储在Flash末尾的factory分区或NVS分区里整片擦除后信号变差或者MAC丢失这类问题很难查因为板子能跑但射频性能不对。3.3 路由器、机顶盒与安卓盒子线刷、卡刷和引导的关系到了路由器、机顶盒这类Linux设备下载逻辑和单片机完全不同。以斐讯K2P、小米CR8806为例刷机前通常要先进入Breed或U-Boot这类引导环境再通过Web界面或者TFTP上传固件。固件包往往是包含uImage、rootfs、dtb的完整镜像不是单个hex。这类设备下载最关键的是版本匹配。同一型号不同硬件版本固件不能混刷。比如K2P的A版和B版硬件方案不同A版用的是联发科方案B版用的是博通方案刷错固件十有八九变砖。所以刷机前第一件事不是找固件而是确认硬件版本号通常印在机身标签或者主板上。机顶盒更复杂比如B860AV1.1、S905L-B这些不同CPU方案线刷工具都不一样有的用短接进入maskrom模式有的用串口命令进入升级模式不能一概而论。关于电视盒子刷安卓9这类操作我的建议是如果刷第三方固件先确认固件来源可信、做过兼容测试并在刷机前完整备份原厂固件。没有备份之前不要动分区表。3.4 8051/AVR 老伙计们串口ISP与avrdude传统51单片机STC用串口ISP下载有个特殊的冷启动时序软件点下载后再给板上电。这个顺序不能反因为芯片上电时才会进入Bootloader窗口错过了就得重新断电再来。下载时波特率不宜设太高尤其是USB转串口质量一般时57600比115200可靠得多。Arduino/AVR系列通过avrdude下载既能写应用也能写Bootloader。命令一般长这样avrdude -c arduino -p m328p -U flash:w:main.hexAVR下载时USB转串口芯片质量影响很大CH340比某些劣质PL2303稳定得多。还有一个坑Arduino板子如果是老款需要手动复位avrdude会等复位信号如果复位电路时序不对提示“programmer is not responding”可以先试着手动按一下复位键看能否卡进下载窗口。4. 固件文件格式与烧录地址见 bin 就烧迟早要翻车4.1 HEX、BIN、ELF 三家各怀绝技固件文件格式是下载环节最容易忽略的一环但恰恰是坑最多的地方。Intel HEX是文本格式每一行都包含长度、地址、类型、数据和校验和烧录器能根据地址自动定位到Flash的对应位置不需要你手动指定起始地址。.hex文件是记事本打开能看到一串串“:”开头的数据这就是它的特征。BIN是纯二进制没有任何地址信息烧录时必须手动指定起始地址。它没有分段没有校验信息规格最小OTA升级包通常用bin格式传输。ELF是编译器生成的默认输出格式包含符号表和调试信息适合调试器做源码级调试但不适合量产和OTA因为它体积大、结构复杂还包含很多与运行无关的调试数据。格式是否含地址可读性典型用途HEX是文本可读串口/下载器通用BIN否不可读固件打包、OTAELF是含符号二进制调试符号调试器下载、源码级调试4.2 地址填错的典型翻车现场最经典的例子是带Bootloader的App下载。STM32的Flash从0x08000000开始假设Bootloader占前16KB0x08000000到0x08003FFFApp必须从0x08004000开始。如果直接拿App的bin文件从0x08000000烧后果就是Bootloader被覆盖设备变砖。用HEX文件时链接脚本已经写好地址一般不担心这个问题用BIN文件时地址全靠手动指定这就是为什么很多人“烧进去了但跑不起来”的根本原因。编译时链接脚本里的FLASH起始地址、长度信息和烧录时工具里填的起始地址必须完全对应。另一个翻车点是大小端。有些固件从一颗芯片导出再刷到另一颗同型号芯片时需要做字节序转换否则数据反了程序跑飞。尤其是从二进制镜像里提取配置参数时大小端搞错配置全部错乱。我自己的习惯是每次下载完都做一次读回校验Verify并把固件的MD5记录在批次单上。J-Flash默认有Verify很多串口ISP工具需要手动点一下。批量生产时这个动作能拦截掉大部分漏烧和错烧。5. 固件加密与安全下载防抄板和防篡改是两回事5.1 读保护到底在防谁固件加密不是一个动作而是一套组合。最常见的是读保护也叫RDP。以STM32为例选项字节里有读保护等级设置Level 0完全开放调试口可以随意读取Flash。Level 1禁止调试口通过SWD/JTAG读取Flash但芯片还能正常升级。Level 2彻底锁定调试接口直接禁用芯片后期基本没办法再用调试器操作。很多产品量产时只做到Level 0等于把固件裸奔在硬件上。竞争对手用J-Link连上OpenOCD一条命令就能把整个Flash读走抄板毫无成本。设了Level 1之后即使对方把芯片拆下来也读不到代码。GD32也有类似机制做法基本一致。但读保护防的是“读”防不了“改”。如果Bootloader没有校验App的签名攻击者可以把修改后的固件通过OTA通道下发设备照样执行。很多路由器刷第三方固件时遇到“校验失败”“固件不合法”就是同一套签名校验机制在起作用。5.2 实际项目里怎么组合使用如果只是防一般抄板量产时开Level 1就够了。如果产品有联网功能且需要OTA建议加签名验签固件用私钥签名Bootloader内置公钥升级包校验通过才写入。如果涉及支付、身份认证等高安全场景必须考虑安全启动和硬件加密引擎。开发阶段千万别开Level 2。我之前见过一个同事在调试时误开了Level 2芯片直接锁死只能换芯片。另外开启读保护后如果要重新下载调试得先解除保护而解除保护通常意味着整片Flash被擦除所以这个操作前一定要把固件备份好。GD32F303固件库开发时尤其注意官方固件库里有些例程会直接配置选项字节里面包含Flash读保护操作。新手把例程复制过来一运行下载器就再也连不上了。遇到这种问题先用串口ISP做全片擦除再重新下载一般都能救回来。6. 实测翻车现场驱动、供电、校验三类下载失败排错实录6.1 电脑识别不到下载器先查驱动再查线材最常见的下载失败不是固件问题而是电脑根本没识别到设备。插上USB转串口后设备管理器里看不到COM口这时先别急着换驱动按顺序做三个检查换一根数据线。很多Type-C线只能充电不能传数据这是头号元凶。直接插主板原生USB口不要用扩展坞和前置面板。确认USB转串口芯片型号。CH340、CP2102、FT232驱动不通用尤其PL2303老版本芯片在新系统上驱动兼容性很差。J-Link、ST-Link识别不到还要检查下载器自身的固件版本。有些下载器在低版本J-Flash里会被提示“The connected probe appears to be defective”这种通常不是硬件坏了而是固件需要升级。还有个容易被忽略的点USB口的供电能力。部分笔记本的USB口在睡眠唤醒后会进入省电模式导致下载器供电不足识别不稳定。遇到这种情况拔插一次往往能解决彻底解决进电源管理里关掉USB选择性暂停。6.2 下载到一半报校验错误时钟、Flash算法和供电三部曲下载到一半失败最常见的三个原因按概率排序供电不足、Flash算法不匹配、下载时钟过快。供电不足的现象很典型擦除Flash时电流大电压被拉低芯片复位下载中断。解决方法是外接独立供电别指望USB口那点电流能扛住所有负载。有些开发板带LED灯擦除瞬间可以看到灯明显变暗这就是供电撑不住的信号。Flash算法不匹配多出现在GD32/STM32兼容芯片混用场景。芯片本身能识别但擦除指令、页大小、超时时间都不一样必须选择对应厂商的FLM文件。下载时钟过快更隐蔽。SWD模式在短杜邦线和面包板上可能跑10MHz没问题换成长线或者排线干扰大了就失败。把SWD速率从10MHz降到1MHz往往问题就解决了。DSO138示波器升级FFT固件这类DIY项目很多人失败就是供电和波特率的问题。USB供电不稳时用手机充电头单独供电成功率会高很多。6.3 刷成砖之后怎么自救先分清软砖和硬砖先给一颗定心丸刷机失败不等于报废。所谓砖分软砖和硬砖。软砖是Bootloader还在只是系统起不来比如固件刷错、分区表被改。这种还能进Breed、U-Boot或者恢复模式重新刷。硬砖是Bootloader都没了比如误擦了引导分区需要JTAG/SWD飞线才能救。遇到变砖第一原则是先看串口日志别反复拔电重试。很多刷机工具在Bootloader里留了恢复入口断电时机不对反而错过。路由器刷坏后用TTL串口进U-Boot控制台通过tftp重新写固件基本都能救回来。单片机如果开了读保护又刷错固件用ISP全片擦除后一般都能恢复。最后分享一个习惯每次下载前我会把“文件格式、目标地址、芯片型号、下载通道”四个参数口头确认一遍确认无误再点烧录。听起来很傻但真的能减少低级错误。固件下载这个环节95%的失败都不是高深问题而是这四个参数里至少有一个不对。这一讲从概念到工具、从文件格式到安全加密、从正常操作到翻车排错基本都覆盖了。我个人在实际项目里的体会是固件下载不是简单动作而是一条完整的工程链路越早把它当成正经环节来对待后面的坑越少。如果你刚开始接触建议先在自己手头的开发板上用J-Flash和串口ISP各下载一次同一个hex感受一下两种通道的区别再试着把读保护打开、解除一次把这些操作都过一遍比看十篇教程都管用。
返回列表