☰
芯片烧录三种方式:ICP、ISP、IAP区别与IAP Bootloader实操
2026/9/29 21:05:39 网站建设 项目流程

做嵌入式开发这些年,我几乎每周都会碰到有人问“芯片烧录到底是什么”。很多新手把烧录理解成“把电脑里的代码传到板子上”,这个说法不算错,但太粗糙了。准确地说,烧录是把编译好的固件——也就是我们常说的hex文件、bin文件——写进芯片内部的非易失性存储区,典型的就是Flash。断电不丢,一上电芯片就从Flash里把指令一条条取出来执行,你的程序就跑起来了。

为什么这件事值得掰开揉碎讲清楚?因为“写进Flash”这个动作,在实际工程里有好几种完全不同的实现路径,而你做的每一个选择,都直接影响开发效率、量产成本和现场维护的难度。ISP、ICP、IAP这三个缩写,就是三种完全不同的“把程序写进芯片”的方式。很多新人在三个词面前一脸懵,网上资料又多又碎,所以我干脆把概念、原理、适用场景、实操坑串起来写一篇,争取让你一次看明白。

这篇文章适合谁?刚接触单片机的学生、正在做第一版产品的硬件工程师,以及想搞懂“产品到底怎么通过串口或网络升级固件”的爱好者,都能从里面找到自己需要的坐标系。基础概念我会讲透,实操部分以STM32为例展开,其他Cortex-M系列的单片机逻辑也是相通的。

1. 芯片烧录是什么:先在脑子里建立一个完整画面

1.1 烧录的本质不是一个“复制粘贴”动作

先纠正一个直觉误区。很多人以为烧录就是把文件复制到芯片里,像U盘拷文件一样。不完全对。Flash的写入需要按照严格的时序,由芯片内部控制器或者外部烧录器按协议把数据一页一页地擦除、再写入。而且写之前通常要先擦除,因为Flash物理特性决定了它的存储单元只能从1变成0,想把它从0变回1,只能整片或整页擦除。所以一个完整的烧录流程是“擦除+写入+校验”,三步缺一不可。

打个生活比方。Flash就像一块写满字的黑板,你想改一个字,没办法只擦那一个字,得把整行甚至整块擦掉重写。这就决定了烧录过程没法像普通文件复制那么简单,你需要一个“能写得进去的通道”。ICP、ISP、IAP三种方式,本质上是三种不同的“写入通道”,通道的开在哪、谁在控制、需要什么前提条件,都不一样。

1.2 芯片里到底烧了什么东西

烧进去的内容分两种常见格式:hex和bin。hex是带地址信息的文本格式,每一行都包含数据对应放的地址,烧录器根据这些地址把数据挨个放到位。bin是纯二进制数据,没有地址信息,烧录的时候必须由你告诉烧录器“从哪个地址开始放”。开发调试阶段一般拿hex说事,量产时的批量烧录文件经常是bin,因为产线只关心裸数据。

还有一个容易被忽略的点:一个完整的固件里不只是你的代码,还包括中断向量表、栈初始值、启动代码。芯片上电会先取栈指针,再跳转到复位向量,最后才进入main函数。这部分你平时看不见,但它真实存在,而且正因为它的存在,后面讲IAP跳转时才不能简单地把跳转理解成“一个函数调用”。

2. 三种烧录方式逐个拆解:ICP、ISP、IAP到底是什么

为什么会有三种方式?答案很现实:因为不同场景的需求差异太大了。开发阶段你想边改代码边调试,需要一个高速稳定的通道;产线阶段你希望操作简单、成本低、速度快;产品卖出去之后,你还想远程更新固件。没有任何一种方案能同时完美满足全部场景,于是三种方式并存。它们之间的关系不是互相替代,而是互相配合。

2.1 ICP:最“硬核”的烧录方式

ICP的全称是In-Circuit Programming,在电路编程。理解它有个关键词:调试器。这种方式通过JTAG或者SWD接口,用ST-Link、J-Link、DAP-Link这类调试器,直接和芯片内部的调试总线通信,控制内核把程序写进Flash。因为调试器直接操作芯片内核,所以它不光能烧录,还能在线调试、下断点、看变量、单步执行。

ICP这个名字,最早强调的语境是“芯片已经焊在电路板上了,我还要给它烧程序”。相比早期先烧录再把芯片贴到板子上的做法,ICP的好处显而易见:板子焊完统一烧,产线流程好安排得多。后来SWD接口普及,只需要两根数据线加电源地就能完成烧录加调试,所以现在几乎所有Cortex-M芯片的开发板,默认都用SWD烧录。

ICP的优缺点也一并说清楚。优点是快、稳定、支持在线调试;缺点是硬件上必须预留调试接口,调试器本身要花钱,而且量产阶段如果一台一台接调试器下载,效率需要认真评估。我自己开发时天天用,但产线方案上不会让工人拿J-Link一个个点。

2.2 ISP:不需要调试器的烧录方式

ISP全称In-System Programming,在系统编程。这里的“系统”指的就是你的整块电路板。它主要利用芯片出厂前固化在系统存储区里的一段引导程序。芯片厂商在生产芯片时,会在内部专门放一段Bootloader,这段程序认识串口UART、USB等通信协议。你只需要把对应引脚接出来,通过USB转TTL或直接USB线连电脑,再用厂商提供的下载工具,把固件发给这段引导程序,由它替你写进Flash。

这里要说一个常见误区:很多人把STM32串口下载叫UART下载,把51单片机下载叫ISP,其实原理大同小异,都是靠出厂Bootloader。STC的51单片机出厂固化ISP引导程序,所以下载软件直接叫stc-isp;STM32的串口下载靠的是System Memory里的出厂Bootloader,通过BOOT0/BOOT1引脚选择进入。核心思想一模一样。

ISP最大的好处是省掉调试器,一条几块钱的USB转TTL串口线就能烧录,特别适合业余玩家、低成本开发和部分小批量产线。缺点也明显:速度通常不如ICP,不能在线调试,而且出厂Bootloader支持的通信协议是厂家定死的,灵活性有限。如果你只是想把程序下进去跑,不打算调试,ISP完全够用。

2.3 IAP:程序自己烧自己

IAP全称In-Application Programming,在应用编程。这是三种方式里最灵活、也最需要自己动手的一种。思路是这样:芯片出厂后,你自己在Flash里写一个简单的引导程序Bootloader,芯片上电先执行它。Bootloader判断自己需不需要升级,如果需要,就通过串口、USB、SPI、网络等任意通道把新固件收过来,写到Flash里另一块区域,然后跳过去运行新程序。这就是“在应用编程”——程序在运行过程中,自己完成了对自己所在芯片Flash的更新。

IAP最典型的落地场景是OTA升级。产品已经到用户手里了,不可能派人拆机接调试器。只要设备联网,服务器下发新固件,Bootloader收下来、校验通过、跳转运行,一个远程升级就完成了。stm32h750vbt6这类芯片经常被用在IAP方案里,因为它的片上Flash不大,但可以通过QSPI外挂大容量Flash,App放在外部Flash,升级逻辑更灵活。类似地,gd32f103、hc32l136这些国产芯片做IAP升级现在已经很常见,工控设备、物联网终端里到处都是。

IAP的难点在于:Bootloader和App都运行在同一颗芯片上,你得自己规划内存布局、写通信协议、处理固件校验和跳转细节,这也是本文第4部分要重点展开的原因。说白了,前两种烧录方式是厂家把路铺好了,你走就行;IAP需要你自己铺路。

2.4 一张表看清三者区别

对比项ICPISPIAP
全称In-Circuit ProgrammingIn-System ProgrammingIn-Application Programming
编程触发方外部调试器芯片出厂自带的Bootloader用户自己写的Bootloader
主要接口JTAG / SWDUART / USB / SPI等UART / USB / SPI / 网络等
是否需要调试器需要不需要不需要
是否需要用户写Bootloader不需要不需要需要
能否在线调试能不能一般不能
典型场景开发调试、产线烧录低成本开发、小批量产线现场升级、远程OTA
速度快中等取决于通道,通常中等

这张表记住一句话就够了:ICP靠外部调试器,ISP靠出厂Bootloader,IAP靠你自己的Bootloader。三个词的核心区别不在接口,在“谁在帮你写Flash”。

2.5 一个容易闹笑话的坑:ISP还有别的意思

这里必须插一段自嘲式的提醒。我入行不久时上网查ISP资料,一搜出来一堆图像处理工程师在聊“ISP pipeline”,当时非常困惑。后来才搞清楚,芯片烧录里的ISP是In-System Programming,而图像处理里的ISP是Image Signal Processor,图像信号处理器。还有做三维视觉的同学,一听到ICP,第一反应是Iterative Closest Point,迭代最近点配准算法,跟烧录完全不沾边。FPGA的“ISP”则是另一种在系统配置方式,层级又不一样。

所以我现在的习惯是:跟人沟通涉及这些缩写,一定带上场景,比如说“串口下载”“SWD烧录”“远程OTA升级”,或者直接讲全称。否则大家很可能在鸡同鸭讲,搞半天发现讨论的根本不是同一个东西。

3. 开发、量产、现场升级:三种方式如何配合使用

理解了概念,接下来就是实际工程里到底怎么选。这些年我经手的项目,几乎没有只靠一种烧录方式的,都是组合使用。从开发到量产再到售后维护,每个阶段的最优解不同,下面分开讲。

3.1 开发调试阶段:ICP几乎是默认答案

如果项目处于软件调试阶段,每天要改代码、烧录、看现象,SWD调试器加ICP这种方式最顺手。原因有两个。第一,速度快,代码稍微改动,点一下下载,几秒钟烧完。第二,也是更重要的,它支持在线调试。你可以在代码里打断点、看变量、单步执行,出问题能很快定位。ISP和IAP都不具备这个能力,因为它们的烧录过程不经过调试总线,自然也没法把调试器挂上去。

有人问预算紧张,用串口ISP行不行。当然可以,我早期没有调试器时也这么干,但你需要忍受两件事:一是每改一次代码,要手动切换BOOT引脚进入下载模式,二是一旦程序跑飞,只能靠串口打印慢慢排查。等复杂度上来,效率非常吃亏。省了一百块调试器,耽误的时间远超这个数。

3.2 量产烧录:效率和成本之间的平衡

量产阶段,产线工人不关心你代码怎么写,只关心“烧得准、烧得快、操作傻瓜化”。这时ICP和ISP各有一席之地。产量不大,几百片级别,用一台脱机烧录器加烧录夹具,按一下按钮烧一片,完全够用。产量上万,一般考虑两条路:找烧录厂在贴片前用烧录座集中烧录,或者在产线上用多路烧录器,一次同时烧好几片。

这里有一个特别想提醒的点:量产烧录一定要重视校验环节。我见过不少电路板到用户手里才出问题,查到最后是烧录数据有个别字节错了,产线签收时根本看不出来。所以不管用哪种方式烧录,都要确保烧录工具开启了CRC校验或者回读校验,宁可烧录时间多几秒,也不能让坏数据出厂。这个习惯要刻在骨子里。

3.3 现场升级:IAP是绕不开的方案

产品到了现场,不管是工业设备还是消费电子,都不可能让用户拆开外壳去接调试器。哪怕硬件预留了烧录接口,普通用户也不会操作。所以只能走IAP:设备通过网络、串口、USB甚至蓝牙把固件接收进来,由Bootloader完成自我更新。

这几年国产MCU的IAP应用明显增加,gd32f103的IAP升级在工控和物联网设备里大量出现,hc32l136这类低功耗MCU也常配合IAP做远程维护。我自己做IAP的体会是:不要把IAP当成“加一个功能”,它本质上是在设计一个“最小引导系统”。要考虑升级失败时的兜底策略,否则设备很可能变成砖。这个第4部分细讲。

4. IAP核心实操:以STM32为例写一个最简单的Bootloader

讲概念不实操都是耍流氓。这一节带大家走一遍最简IAP固件升级流程,以STM32F103为例。GD32、HC32等国产芯片寄存器配置会有差异,但思路完全一致,学会一套能迁移到很多平台上。

4.1 第一步:规划Flash分区

IAP的第一件事是给Flash做分区。以STM32F103C8T6为例,它只有64KB Flash,地址范围0x08000000到0x0800FFFF。要把这块空间分成两个区:Boot区放引导程序,App区放真正的应用。

我常用的分法:Boot区分配16KB,地址0x08000000到0x08003FFF;App区分配剩下的48KB,从0x08004000开始。为什么Boot要16KB?如果Boot里只做串口接收和Flash写入,这个大小足够,但以后想加加密、日志存储、升级失败回滚,16KB还能容纳。App的起始地址必须按Flash页对齐,STM32F103的Flash页大小是1KB,0x08004000刚好对齐,没问题。

App编译时有一件关键的事:在工程里把链接地址改成0x08004000,中断向量表偏移也要设置。否则编译出来的App仍认为自己从0x08000000开始,跳过去必然乱套。在Keil里就是修改Target选项卡的IROM1起始地址和大小,在IAR里改链接配置,同时代码中通过SCB->VTOR重映射中断向量表。

4.2 第二步:Boot怎么判断是否需要升级

Boot上电第一件事,检查“升级标志”。这个标志可以放在Flash固定地址、备份寄存器里,甚至外部引脚电平上。最简方案:在Flash某个固定地址(比如0x08003F00,位于Boot区内部)存一个固定值,比如0xA5A5A5A5,表示“收到新固件,需要升级”。没有这个标志,Boot直接跳转App。

升级标志怎么产生?App收到升级指令后,先写标志到Flash,然后软复位。复位后Boot看到标志,进入升级模式,等待接收新固件。接收方式先用最简单的串口:约定帧头、命令、起始地址、长度、数据,每包数据带CRC32校验。收到完整固件并校验通过后,写入App区,清除标志,软复位,跳转App。

整个流程串起来是这样一个链路:设备正常运行App→App收到升级指令→写入升级标志→软复位→Boot启动发现标志→进入升级模式→串口接收固件→校验→写入App区→清除标志→软复位→Boot无标志→跳转App→新版固件开始运行。

4.3 第三步:从Boot跳转到App的三个关键细节

跳转是IAP里最容易出问题的环节。千万别写一句“跳到一个函数地址”就完事,必须处理三件事:切换栈指针、重映射中断向量表、确认跳转目标地址有效。

栈指针:App向量表的第一个字是初始栈顶地址,也就是MSP初始值。跳转前先把App区起始地址处的第一个字读出来,赋给MSP。向量表第二个字是复位向量,把它当成函数指针跳过去。中断向量表重映射:Cortex-M3/M4通过设置SCB->VTOR寄存器,让中断表指向App区。跳转目标有效判断:检查App区首字是否落在SRAM地址范围,第二个字是否是合法的Flash地址,防止跳到空白区域。

typedef void (*JumpFunc)(void); void JumpToApp(uint32_t appAddr) { uint32_t spVal = *(volatile uint32_t *)appAddr; uint32_t pcVal = *(volatile uint32_t *)(appAddr + 4); if ((spVal & 0xFFF00000) == 0x20000000) { // 关闭全局中断,避免跳转过程被中断打扰 __disable_irq(); SCB->VTOR = appAddr; __set_MSP(spVal); ((JumpFunc)pcVal)(); } }

注意,某些系列芯片强制设置VTOR可能需要先关闭全局中断,而GD32F103的写法与STM32F103基本一致。我写代码时习惯用0x20000000判断栈地址是否落在SRAM区,这个检查能挡掉很多“跳转到空白Flash”的坑。如果你要做在线调试下的跳转,还要考虑调试器的干扰,不过那属于进阶内容了。

4.4 第四步:App端要配合做什么

App不是被动地被跳转进来的,它要主动配合。第一,中断服务函数要正常工作,一进main就要重新设置SCB->VTOR为App的起始地址0x08004000。哪怕Boot跳转前设置过了,App里再设一次也是保险做法,因为有些库函数会动到这个寄存器。第二,App收到升级指令后要有明确的处理逻辑:确认升级意图、上报当前版本、请求下发固件。第三,内存布局上,如果App使用了Flash末尾区域做参数存储,要认真规划,避免覆盖Boot区或者升级过程要写的临时区。

还有一个必须强调的点:升级过程不能中途断电。最稳妥做法是接收数据时先写到临时缓冲区或另一个Flash区域,全部接收完并校验通过后再一次性擦写目标区。当然这对Flash空间有要求,如果芯片Flash很紧张,只能边接收边写,那就要在协议层做强化的断点续传和重传机制。我把这个原则叫做“要么不写,要么写完整”,IAP翻车大多翻在这里。

5. 常见问题与排查技巧实录

这一节分享一些我实际踩过的坑,每一条都有真金白银的代价,也顺便覆盖几个大家经常在群里问的问题。

5.1 芯片连不上烧录器:先查这5个地方

  • 供电:目标板要独立供电,调试器的3.3V通常只是电平参考,电流能力很弱,别指望靠它带动整板。
  • BOOT引脚:用串口ISP时BOOT0必须拉高,下载完记得拉回,否则设备永远进不了App。
  • SWD引脚被复用:程序里如果把SWDIO、SWCLK对应的引脚本用了,烧录时连不上。解决办法是按住复位键再点下载,或者在程序里用串口ISP先擦除芯片。
  • 接线距离:SWD时钟线太长,线上寄生电容会导致高频率握手失败,适当降速或者缩短杜邦线。
  • 芯片被读保护锁死:程序开了读保护,调试器连上后无法正常操作,可以先全擦除,部分芯片需要配合工具解锁。这个操作要谨慎,会把Flash内容全部清掉。

5.2 STC ISP联机弹窗怎么处理

STC的老用户都懂,stc-isp下载软件联机时偶尔弹广告窗口,非常影响产线操作节奏。处理思路很简单:选一个自己用惯的稳定版本,别一味追新;下载过程中弹窗,不要急着关,等下载完成再用任务管理器关掉。另外,STC部分新芯片必须用新版软件才支持,旧版根本不认,所以“关弹窗”这件事要根据芯片型号权衡。真到了量产环境,我更推荐用脱机烧录器,干净利落,彻底远离这些杂七杂八的弹窗问题。

5.3 IAP跳转后程序跑飞

跑飞的原因几乎逃不出三个:中断向量表没重映射、栈指针设置错误、App链接地址与实际存放地址不一致。排查方法比较机械:先在Boot跳转前把App区的头四个字打印出来,确认栈地址和复位向量对不对;再用LED翻转来验证是否进入App主循环;最后用一个定时器中断验证VTOR是否生效。如果跳转后一打开某个中断就死机,九成是VTOR没设对。这个排查顺序我用了很多年,很管用。

5.4 Boot里定义的全局变量,复位后到底会不会保留

这个问题常有人问,我认真答一下。Boot和App本质上是两个独立工程,被编译成两个独立固件。Boot的全局变量在Boot复位后由启动代码统一清零或初始化,数值不会保留。如果希望Boot和App之间传递信息,比如升级标志、错误码,应该把数据存到备份寄存器、Flash特定区域,或者固定在RAM中的某个不被启动代码清零的地址段。一句话总结:普通全局变量复位后不会保留,跨区通信不能用普通变量实现。

5.5 GD32、HC32等国产芯片做IAP的差异

GD32F103和STM32F103硬件兼容性很好,IAP思路一致,但Flash擦写时序、中断向量表偏移的寄存器在某些型号上略有区别。HC32L136这种低功耗MCU,Flash分区和BOOT引脚配置要看对应参考手册。我的建议是:不管用哪家芯片,先看用户手册里的“系统存储器”和“Flash编程”两个章节,对照着手册写Bootloader,比从网上抄代码靠谱得多。国产芯片厂商的寄存器习惯各成一家,底层这些坑只能靠手册和数据手册自己趟。

6. 烧录相关的几个实战心得

文章写到这里,我估计大家最想知道的是“我该优先掌握哪一种”。我的建议是:新手先从ICP开始,把Keil、MDK的下载调试搞得明明白白,因为调试能力是基本功;然后用ISP走一遍串口下载,你会真正理解Bootloader的价值;等要做产品化了,再深入学IAP,把内存布局、跳转、升级协议这套东西吃透。这条路我走过,回头看是最流畅的学习顺序。

还有两件事想提醒大家。第一,量产烧录前一定要规划好烧录工装,包括测试点、串口座、探针夹具,这些直接影响产线效率。第二,不管用哪种烧录方式,工程要做好备份和版本管理,固件文件名里带上版本号、日期、校验值。烧录环节看起来不起眼,但它一旦出问题,往往是整批产品的问题。把流程钉死,比任何高级技巧都重要。

最后,如果手里正在做需要远程升级的设备,一个最实在的小建议:第一次做IAP时,在开发板上把“老固件升级到新固件”和“新固件回滚到老固件”两个方向都测一遍,确认双向都能成功再进产线。我因为只测了单向升级,结果现场回滚失败,连夜出差修过一次。这种经验,写多少字都换不来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询