1. 芯片烧录到底在烧什么?先把概念理清楚
刚入行那会儿,我第一次听到“烧录”这个词,脑子里浮现的是拿烙铁往电路板上怼的画面。后来才知道,这跟焊接半毛钱关系没有,烧录说的是把固件代码写进芯片里。你可以把它理解成给芯片“装系统”——芯片出厂时里面是空的,跟一块没装系统的硬盘差不多,得把程序灌进去它才能干活。
芯片烧录的本质,是通过特定的硬件接口和通信协议,将编译好的二进制固件写入芯片的Flash或ROM中。这个过程涉及三个关键要素:烧录器(负责产生时序信号和电压)、通信接口(比如SWD、JTAG、UART、SPI等)、目标芯片(接收数据并写入存储区)。三者缺一不可,任何一个环节出问题,烧录都会失败。
那为什么会有ISP、ICP、IAP这三个词?说白了,它们描述的是“在什么阶段、用什么方式把固件写进去”的三种不同场景。ISP是芯片焊到板子上之前或之后,通过芯片自带的BootROM程序来烧;ICP是直接用调试器通过SWD/JTAG接口烧;IAP是芯片已经在运行了,通过程序自己把自己更新掉。三者的核心区别在于谁来执行烧录动作——是芯片内部的固化程序、外部调试器、还是用户自己的应用程序。
这三个概念之所以容易混淆,是因为它们经常出现在同一块板子的不同开发阶段。比如你打样回来第一块板子,可能先用ICP把Bootloader烧进去,然后产线量产时用ISP批量烧录,产品出厂后需要升级时再用IAP远程更新。一个产品完整生命周期里,三种方式可能都会用到。
注意:很多人把“烧录”和“下载”混着用,严格来说下载是把固件从电脑传到烧录器,烧录是烧录器把固件写进芯片,中间隔了一层。但在日常交流中,大家基本混着说,不影响理解。
2. ISP、ICP、IAP三种方式的核心差异
2.1 ISP:靠芯片自带的“引导程序”吃饭
ISP全称In-System Programming,翻译过来叫“在系统编程”。这里的“在系统”指的是芯片已经焊在电路板上了,你不需要把它拆下来。ISP的核心依赖是芯片出厂时固化在BootROM里的一段引导程序,这段程序芯片厂商写死了,你改不了,但它支持通过特定接口(通常是UART、USB、CAN等)接收外部数据并写入用户Flash区。
我拿STM32举例,它的BootROM里就有一段ISP程序,你只需要把BOOT0引脚拉高、BOOT1拉低,复位后芯片就会进入ISP模式,等待上位机通过UART发送固件。这时候你用官方的Flash Loader Demonstrator或者开源的stm32flash工具,选好串口号和波特率,就能把hex或bin文件写进去。
ISP最大的好处是不需要额外的调试器。产线上你只需要一个USB转串口模块,几块钱的成本,就能批量烧录。而且因为用的是芯片自带的程序,不依赖外部工具链,稳定性很高。但缺点也很明显:速度慢。UART波特率一般也就115200到921600,烧个大一点的固件要等好几十秒。另外不同厂商的ISP协议不一样,工具也不通用,ST的不能烧NXP的,换芯片就得换工具。
还有一个坑:有些芯片的ISP模式需要特定引脚上电时序配合,比如某些国产MCU要求在上电瞬间某个引脚保持低电平,晚了或早了都进不去。我第一次用某款国产芯片时就栽在这上面,后来拿示波器抓时序才发现是上电复位时间不够。
2.2 ICP:调试器直接怼,快准狠
ICP全称In-Circuit Programming,叫“在电路编程”。它跟ISP最大的区别是:不依赖芯片自带的BootROM,而是直接用外部调试器通过SWD或JTAG接口访问芯片的调试模块,进而操作Flash控制器。你可以把它理解成“走后门”——调试接口本来就是芯片设计时留给开发者的,权限最高,能直接读写内存和Flash。
ICP的典型工具是J-Link、ST-Link、DAPLink这些调试器。以J-Link为例,你接上SWDIO、SWCLK、GND、VCC四根线,打开J-Flash软件,选好芯片型号,加载固件,点“Program”就完事了。速度比ISP快得多,因为SWD时钟可以跑到几MHz甚至几十MHz,烧个几百KB的固件也就一两秒。
ICP的优势在于速度快、通用性强。同一款J-Link可以烧ARM Cortex-M全系列,换芯片只需要在软件里改型号。而且ICP不占用芯片的通信外设,你UART、SPI该干嘛干嘛,互不影响。产线上如果产量不大,用ICP是最省事的方案。
但ICP也有它的局限。首先你得有调试器,J-Link正版不便宜,虽然国产替代很多,但稳定性和兼容性参差不齐。其次ICP需要引出调试接口,SWD至少占两个引脚,对于引脚资源紧张的芯片来说是个负担。更关键的是,ICP无法在芯片运行时进行——你必须让芯片停下来,调试器才能接管。所以产品出厂后想远程升级,ICP就无能为力了。
实操心得:用ICP烧录时,如果遇到“Cannot connect to target”的报错,先检查三件事——芯片供电是否正常、复位引脚是否被拉低、SWDIO/SWCLK有没有接反。我遇到过好几次是因为板子上电后程序立刻把SWD引脚复用成了GPIO,导致调试器连不上。解决办法是按住复位键再点烧录,或者用“Connect under reset”模式。
2.3 IAP:程序自己给自己“动手术”
IAP全称In-Application Programming,叫“在应用编程”。这个最有意思——芯片已经在跑程序了,程序自己决定要不要更新自己。具体做法是:Flash里分成两块,一块是Bootloader(引导区),一块是Application(应用区)。Bootloader负责检查有没有新固件、要不要更新,Application才是真正干活的业务代码。
当需要升级时,Bootloader通过某种通信方式(UART、CAN、以太网、无线等)接收新固件,把它写到Application区,然后跳转过去执行。整个过程不需要拆机、不需要接调试器,甚至不需要人到现场。这就是为什么智能家居设备能通过手机App推送固件更新——背后就是IAP在干活。
IAP的关键在于Bootloader的设计。你得考虑:固件怎么分包传输、怎么校验完整性(CRC32还是MD5)、升级失败怎么回滚、断电了怎么办。我见过太多项目因为Bootloader没写好,升级到一半断电直接变砖。正确的做法是双区备份——新固件先写到备份区,校验通过后再拷贝到运行区,这样即使拷贝过程中断电,Bootloader下次上电还能从备份区恢复。
还有一个容易忽略的点:中断向量表的偏移。ARM Cortex-M芯片的中断向量表默认在Flash起始地址,但Application区通常不在0x08000000,所以需要在Application的启动代码里设置SCB->VTOR寄存器,把向量表偏移到Application的起始地址。忘了这一步,中断就会跳到Bootloader的向量表里去,程序跑飞。
2.4 三种方式对比速查
| 对比维度 | ISP | ICP | IAP |
|---|---|---|---|
| 执行主体 | 芯片自带BootROM | 外部调试器 | 用户Bootloader |
| 接口 | UART/USB/CAN | SWD/JTAG | 任意通信接口 |
| 速度 | 慢(百KB级需数十秒) | 快(百KB级1-2秒) | 取决于通信带宽 |
| 是否需要调试器 | 否 | 是 | 否 |
| 能否远程升级 | 否 | 否 | 是 |
| 占用引脚 | 通信引脚 | SWDIO+SWCLK | 通信引脚 |
| 典型场景 | 产线量产 | 研发调试 | 现场升级 |
3. 从零搭建一套完整的烧录方案
3.1 硬件准备与接线要点
假设你手头有一块STM32F103的板子,想把这三种方式都跑一遍。你需要准备:USB转TTL模块(CH340就行)、ST-Link V2调试器、杜邦线若干、目标板。
先看ISP接线。STM32F103的ISP通过UART1,引脚是PA9(TX)和PA10(RX)。把USB转TTL的TX接PA10,RX接PA9,GND对GND。然后BOOT0拉高到3.3V,BOOT1拉低到GND。注意BOOT0不能悬空,悬空时电平不确定,可能进不去ISP。我一般直接用一个跳线帽把BOOT0接到3.3V排针上。
ICP接线更简单。ST-Link V2的SWDIO接PA13,SWCLK接PA14,GND接GND,3.3V接3.3V。四根线搞定。注意有些板子的SWD引脚被外部电路拉了上拉或下拉电阻,阻值太小会影响信号质量,一般4.7K到10K没问题,1K以下就可能出问题。
IAP不需要额外接线,用你Application里选定的通信接口就行。比如用UART做IAP,那还是PA9/PA10,但这次不需要动BOOT引脚,因为Bootloader是你自己写的,跳转逻辑由你控制。
注意:接线时务必断电操作,带电插拔杜邦线容易打火,严重时可能烧掉芯片的IO口。我亲眼见过同事带电插拔把STM32的PA13烧了,SWD再也连不上,只能换芯片。
3.2 ISP烧录实操:以STM32为例
先装好STM32CubeProgrammer,这是ST官方工具,支持UART、USB、SWD多种方式。打开软件,选择UART模式,端口选CH340对应的COM号,波特率选115200,奇偶校验Even,数据位8,停止位1。这些参数是STM32 BootROM规定的,不能随便改。
然后板子上电,BOOT0保持高电平。点击“Connect”,如果一切正常,软件会读出芯片型号和Flash大小。这时候你点“Open file”加载编译好的hex文件,再点“Download”就开始烧录了。烧录完成后,把BOOT0跳回GND,复位,程序就跑起来了。
整个过程最常遇到的问题就是连不上。排查顺序:先确认COM口选对了没有,设备管理器里看看CH340驱动装没装;再确认BOOT0电平对不对,拿万用表量一下;然后检查TX/RX有没有接反,很多人在这里翻车。如果还不行,试试降低波特率到9600,有些CH340模块在高波特率下不稳定。
3.3 ICP烧录实操:J-Link与STM32CubeProgrammer配合
ICP我用得最多的是STM32CubeProgrammer加ST-Link,或者J-Flash加J-Link。以STM32CubeProgrammer为例,选择SWD模式,频率默认4MHz,点击Connect。如果芯片之前跑的程序把SWD引脚复用了,就连不上,这时候需要按住复位键,点Connect,等软件开始连接了再松开复位键。
连接成功后,界面会显示Flash内容。你可以直接加载hex文件然后点Download,也可以先Erase再Program。我一般习惯先全片擦除再烧,避免旧数据残留导致奇怪的问题。烧录速度方面,STM32F103的64KB Flash,SWD 4MHz下大概1.5秒烧完,比ISP快了一个数量级。
J-Flash的用法类似,但J-Link的兼容性更好,支持芯片型号更多。如果你用的是国产MCU,比如GD32、APM32,J-Link可能需要手动添加芯片支持包,或者选一个兼容的STM32型号凑合用。我试过用J-Link烧GD32F103,选STM32F103的配置也能烧进去,但Flash算法可能不完全匹配,烧录大文件时偶尔会出错。
3.4 IAP实现:手把手写一个Bootloader
IAP的Bootloader我写过不下十个版本,从最简单的UART升级到带AES加密的无线升级都做过。这里给一个最精简的UART IAP框架,基于STM32 HAL库。
Flash分区规划:Bootloader占0x08000000到0x08007FFF(32KB),Application占0x08008000到0x0801FFFF(96KB)。Bootloader启动后先检查一个标志位,如果标志位是0x5A5A5A5A,就进入升级模式,否则跳转到Application。
#define APP_ADDR 0x08008000 #define UPGRADE_FLAG_ADDR 0x08007FF0 #define UPGRADE_FLAG_VALUE 0x5A5A5A5A void jump_to_app(void) { uint32_t app_stack = *(volatile uint32_t*)APP_ADDR; if ((app_stack & 0x2FFE0000) == 0x20000000) { __set_MSP(app_stack); void (*app_entry)(void) = (void*)(*(volatile uint32_t*)(APP_ADDR + 4)); SCB->VTOR = APP_ADDR; app_entry(); } }升级流程:上位机通过UART发送固件,每包256字节,带包序号和CRC16校验。Bootloader收到一包就写一包,写之前先擦除对应扇区。全部收完后计算整体CRC32,跟上位机发过来的比对,一致就置标志位并复位,不一致就报错重传。
这里有个关键细节:擦除Flash时CPU会暂停。STM32的Flash擦除是按扇区来的,一个扇区1KB或2KB,擦除时间大概20到40毫秒。这期间如果来了中断,中断向量表还在Flash里,CPU取不到指令就会HardFault。所以擦写Flash时要么关中断,要么把中断向量表搬到RAM里。我一般选择关中断,简单粗暴,反正升级过程中也不需要响应什么中断。
实操心得:IAP升级最怕断电。我早期做的版本没有备份区,升级到一半断电,Bootloader标志位已经置了但Application没写完,结果上电后Bootloader跳转到空的Application区,直接HardFault。后来改成双区备份,新固件先写到备份区,校验通过后再拷贝到运行区,拷贝前先擦运行区,拷贝完再清标志位。这样即使拷贝中断电,下次上电Bootloader发现标志位还在,会重新从备份区拷贝。
4. 烧录过程中那些让人抓狂的问题
4.1 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| ISP连不上 | BOOT引脚电平不对 | 万用表量BOOT0 | 确保BOOT0=3.3V,BOOT1=GND |
| ISP连不上 | TX/RX接反 | 交换TX/RX试试 | 交叉接线 |
| ICP连不上 | SWD引脚被复用 | 看原理图确认 | 用Connect under reset |
| ICP连不上 | 芯片供电不足 | 量VDD电压 | 确保3.3V稳定,电流够 |
| IAP跳转后跑飞 | 向量表没偏移 | 检查SCB->VTOR | 在Application里设置VTOR |
| IAP升级失败 | Flash擦除未完成 | 看返回值 | 擦除后等待BSY位清零 |
| 烧录后程序不跑 | 复位电路问题 | 量NRST引脚 | 检查复位电容和上拉电阻 |
4.2 那些年我踩过的坑
第一个坑:Flash写保护。有些芯片出厂时默认开启了读保护或写保护,你烧录时会报“Flash protected”之类的错误。STM32的话需要用STM32CubeProgrammer解除保护,但注意解除读保护会全片擦除,数据全丢。我有个项目量产时忘了解除写保护,产线烧了500片全部失败,返工花了两天。
第二个坑:时钟配置错误导致烧录后不运行。ICP烧录时调试器用的是芯片内部RC时钟,跟你程序里配置的时钟没关系。但程序跑起来后如果时钟配置错了,比如外部晶振没起振但程序里等着HSE就绪,就会卡在时钟初始化里。现象就是烧录成功但程序不跑。解决办法是用示波器量晶振引脚,或者临时改成内部时钟测试。
第三个坑:IAP升级包大小超过分区。我见过一个项目,Application区规划了64KB,结果固件编译出来65KB,升级时写到一半发现越界了,把Bootloader区给覆盖了。芯片直接变砖,只能拆下来用编程器重新烧。所以规划分区时一定要留余量,至少留10%到20%的缓冲。
第四个坑:CRC校验算法不一致。上位机用CRC32,下位机用CRC16,或者多项式不一样,导致校验永远不过。这种问题最隐蔽,因为数据传输本身没问题,就是校验对不上。解决办法是两边用同一份代码,或者用标准算法比如CRC-32/ISO-HDLC,多项式0x04C11DB7,初始值0xFFFFFFFF,结果异或0xFFFFFFFF。
4.3 产线烧录的效率优化
如果你要做小批量生产,比如一次烧几十片,用ICP一个个烧太慢了。我一般会做一个烧录治具,把板子放上去,探针自动接触SWDIO、SWCLK、GND、VCC,然后用一个多路烧录器同时烧多片。市面上有那种一拖四、一拖八的烧录器,原理就是内部集成了多个J-Link或者DAPLink。
软件方面,J-Flash支持命令行模式,你可以写个批处理脚本,自动加载固件、烧录、校验、记录序列号。我之前的产线脚本大概长这样:
JFlash.exe -openprj"stm32f103.jflash" -open"firmware.hex" -connect -erasechip -program -verify -startapp -exit配合一个扫码枪,扫一个序列号就烧一片,烧录记录自动存到数据库里。这样效率比手动点鼠标高多了,而且不会漏烧或重复烧。
对于超大批量,比如几万片,那就直接找芯片原厂或者烧录厂做OTP烧录,他们在芯片封装阶段就把固件写进去了,你拿到手就是成品。不过OTP只能烧一次,改不了,适合固件已经冻结的量产项目。
5. ISP Pipeline与图像处理中的ISP是两码事
搜“ISP”的时候你会发现大量关于“ISP Pipeline”和“ISP图像处理”的内容,这跟芯片烧录里的ISP完全是两个东西。图像处理里的ISP是Image Signal Processor,图像信号处理器,负责把传感器输出的RAW数据转换成RGB图像。它内部有去马赛克、白平衡、伽马校正、降噪等一堆算法模块,这些模块串起来就是ISP Pipeline。
为什么会有这个混淆?因为缩写一样。芯片烧录的ISP是In-System Programming,图像处理的ISP是Image Signal Processor。你在搜索资料时如果看到“ISP去弹窗”、“ISP调试”之类的词,那说的是图像处理,跟烧录没关系。我刚开始做嵌入式时也被绕进去过,后来才分清楚。
如果你正好在做摄像头项目,需要调试ISP Pipeline,那跟烧录唯一的交集是:你可能需要把ISP的配置文件(比如IQ文件)烧录到芯片外挂的Flash里。这时候用的还是ICP或ISP方式,只是烧的内容不是固件而是参数文件。本质上没区别,都是往Flash里写数据。
提示:STC ISP去弹窗这个热搜词,说的是STC单片机用ISP方式烧录时,上位机软件会弹广告窗口,有人做了去弹窗的版本。这跟技术本身无关,纯粹是工具软件的问题。如果你用STC芯片,建议找官方最新版工具,或者用命令行模式避免弹窗干扰。
6. 选型建议:什么时候用哪种方式
如果你在研发阶段,手头有调试器,那ICP是最优解。速度快、通用性强、不挑芯片,J-Link一插,什么STM32、GD32、NXP都能烧。而且ICP支持在线调试,打断点、看变量、单步执行,这些是ISP和IAP做不到的。
如果你在产线量产,产量不大且芯片支持ISP,那ISP更划算。一个USB转串口模块才十几块钱,比J-Link便宜多了。而且ISP不占用调试引脚,产线工人操作也简单,跳线帽一插一拔的事。但前提是芯片的ISP协议要稳定,有些国产芯片的ISP工具做得稀烂,经常连不上,那还是老老实实用ICP。
如果你的产品已经出货,需要现场升级,那IAP是唯一选择。但IAP的开发成本最高,你得写Bootloader、设计通信协议、处理异常回滚。我建议IAP的Bootloader尽量做简单,只负责收数据和写Flash,复杂的逻辑放到Application里。Bootloader越简单越不容易出bug,而且一旦Bootloader出问题,整个设备就变砖了,只能返厂。
还有一个混合方案:ICP烧Bootloader + IAP升级Application。产线用ICP把Bootloader烧进去,Bootloader里包含IAP功能。产品出厂后通过IAP升级Application。这样产线效率高,现场升级也方便。我大部分项目都是这个套路,实测很稳。
最后说一个容易被忽略的点:烧录文件的格式。hex文件带地址信息,bin文件是纯二进制。ICP和ISP工具一般两种都支持,但IAP通常用bin,因为Bootloader需要知道每个字节写到哪个地址,hex解析起来麻烦。编译时记得勾选生成bin文件,Keil里在User选项卡的After Build里加一行fromelf --bin -o "$L@L.bin" "#L"就行。