芯片烧录这个词,很多刚入行的朋友第一次听到会以为是某种焊接工艺,或者觉得是产线工人干的活,跟自己写代码、调板子没多大关系。实际上,只要你做过单片机开发、嵌入式产品量产,或者自己焊过一块带MCU的小板子,烧录就是绕不开的一环。我做了十多年硬件和嵌入式项目,从最早的并口编程器到现在的在线仿真器、量产离线烧录器,踩过的坑能写满一个笔记本。今天我就把ISP、ICP、IAP这三个最容易混淆的概念彻底讲清楚,顺便把芯片烧录这件事从原理到实操给你捋一遍,不管你是刚学STM32的学生,还是正在做量产方案的工程师,看完都能直接上手。
1. 芯片烧录到底在烧什么
1.1 从空白芯片到能跑程序,中间发生了什么
一颗刚从封装厂出来的MCU,内部Flash是空的,或者只有出厂测试用的校验数据。所谓烧录,就是把编译好的机器码、配置字、校验信息写进芯片的非易失存储区,让芯片上电后能从指定地址取指执行。这个过程听起来简单,但涉及几个关键概念:存储介质、编程接口、引导程序、时钟与电压。
存储介质方面,主流MCU内部用的是Flash,也有部分老型号用OTP(一次性可编程)或者EEPROM。Flash的好处是可擦可写,擦除单位通常是扇区或页,写入单位是字或半字。比如STM32F1系列,Flash页大小1KB或2KB,擦除一页需要几十毫秒,写入一个字大概几十微秒。这些参数直接决定了烧录速度,量产时差几毫秒就是几秒钟的差距。
编程接口方面,芯片厂商会在硅片里固化一段Bootloader,放在系统存储区,用户代码区是另一块。Bootloader负责接收外部数据并写入用户区。根据Bootloader的激活方式和通信协议不同,就衍生出了ISP、ICP、IAP这三种模式。很多人分不清它们,其实核心区别就一句话:谁在控制写入过程,以及写入通道是什么。
1.2 烧录的本质:三个地址空间的切换
要理解烧录,得先明白MCU内部至少有三个地址空间:系统存储区(System Memory)、用户Flash区(User Flash)、SRAM。系统存储区里放的是厂商固化的Bootloader,用户不能改。用户Flash区放你写的程序。SRAM是运行时内存。
上电或复位时,芯片根据BOOT引脚的电平决定从哪个地址取指。如果从系统存储区启动,就运行厂商Bootloader,这时候如果外部有正确的通信请求,Bootloader就会把数据写进用户Flash,这就是ISP。如果从用户Flash启动,就运行你自己的程序,你的程序里如果集成了接收新固件并写入Flash的功能,那就是IAP。
ICP则不太一样,它通常指通过调试接口(如SWD、JTAG)直接访问Flash控制器,不经过Bootloader,也不依赖用户程序。你可以把它理解为“芯片级的直接写入”,速度最快,但需要调试器。
注意:不同厂商对ISP和ICP的定义有细微差别。比如ST把通过UART/CAN/USB等外设烧录叫ISP,把通过SWD/JTAG烧录叫ICP。而有些厂商把JTAG烧录也叫ISP。本文以ST和多数ARM Cortex-M厂商的通用叫法为准。
2. ISP、ICP、IAP到底怎么选
2.1 ISP:靠厂商Bootloader吃饭
ISP的全称是In-System Programming,中文叫“在系统编程”。它的核心是芯片出厂时固化在系统存储区的Bootloader。你只需要把BOOT引脚拉到指定电平,复位,芯片就进入Bootloader模式,然后通过UART、USB、CAN、SPI等接口把固件传进去。
以STM32为例,BOOT0拉高、BOOT1拉低,复位后从系统存储区启动,默认使用UART1,波特率通常自适应或固定为115200。上位机用STM32CubeProgrammer或者Flash Loader Demonstrator,选好串口,点下载就行。整个过程不需要调试器,只需要一个USB转TTL模块,成本极低。
ISP的优势很明显:便宜、简单、不需要额外硬件。产线上可以用一个工装夹具同时给多块板子烧录。但缺点也突出:速度慢,受串口波特率限制;占用一个串口;Bootloader版本固定,有些老芯片的Bootloader有bug,比如擦除大容量Flash时超时;另外,如果BOOT引脚被产品外壳挡住,量产时操作不方便。
我做过一个项目,板子装进防水壳后BOOT0被盖住了,结果返修时必须拆壳才能烧录。后来改成IAP方案,通过无线模块升级,才解决了这个问题。所以选ISP之前,一定要确认BOOT引脚是否容易操作。
2.2 ICP:调试器的直接通道
ICP是In-Circuit Programming,中文叫“在电路编程”。它通过SWD或JTAG接口,直接操作芯片内部的Flash控制器。你用的ST-Link、J-Link、DAPLink都属于这一类。ICP不依赖Bootloader,也不依赖用户程序,只要芯片没被读保护锁死,就能烧录。
ICP的速度比ISP快很多。以STM32H750为例,SWD时钟可以跑到10MHz以上,烧录几百KB的固件只需要几秒钟。而且ICP还能在线调试、读寄存器、设断点,开发阶段离不开它。
但ICP在量产时有个麻烦:需要给每块板子留出SWD接口,至少4根线(VCC、GND、SWDIO、SWCLK),有些还要复位线。如果板子空间紧张,或者产品已经组装好,SWD接口可能被遮挡。另外,J-Link等商用调试器价格不便宜,量产时不可能给每个工位配一个。
我通常的做法是:开发阶段用ICP,量产阶段如果板子上有SWD焊盘,就用离线烧录器配合烧录座;如果没有,就切到ISP或IAP。这里有个经验:SWDIO和SWCLK走线尽量短,不要跟高频信号平行走,否则烧录时容易掉线。我遇到过一块板子,SWD线走了10厘米,旁边是DCDC的开关节点,结果烧录成功率只有70%,后来加了地线屏蔽才稳定。
2.3 IAP:让程序自己升级自己
IAP是In-Application Programming,中文叫“在应用编程”。它的思路是:用户程序里包含一段升级逻辑,通过任意通信接口(串口、CAN、以太网、无线)接收新固件,然后自己擦写Flash。IAP不依赖厂商Bootloader,也不依赖调试器,完全由你的代码控制。
IAP的典型结构是:Flash分成两部分,一部分是Bootloader(你自己写的),一部分是应用程序。上电后先跑Bootloader,检查是否需要升级。如果需要,就接收新固件并写入应用程序区;如果不需要,就跳转到应用程序区执行。
IAP的难点在于:Bootloader和应用程序的地址分配、中断向量表重映射、跳转前的现场清理、升级过程中的断电保护。我见过很多新手在这里翻车。比如,应用程序里开了中断,跳转前没关总中断,结果跳过去就HardFault。再比如,Bootloader里定义的全局变量,跳转到应用程序后,这些变量的值会怎样?答案是:如果跳转时没有复位,这些变量还在SRAM里,但应用程序的启动代码会重新初始化.data段和.bss段,所以Bootloader的变量值会被覆盖。如果你希望某些标志位在跳转后保留,必须放在特定的no-init段,或者写到备份寄存器里。
提示:IAP升级时,一定要先擦除再写入,而且擦除前要确保新固件完整接收并校验通过。我习惯在外部Flash或SD卡里先存一份完整固件,校验CRC后再擦内部Flash,这样即使升级中断,也能重新来过。
2.4 三种方式对比与选型建议
| 对比项 | ISP | ICP | IAP |
|---|---|---|---|
| 控制方 | 厂商Bootloader | 调试器 | 用户程序 |
| 接口 | UART/USB/CAN等 | SWD/JTAG | 任意通信接口 |
| 需要调试器 | 否 | 是 | 否 |
| 速度 | 慢 | 快 | 中等 |
| 量产成本 | 低 | 中 | 低 |
| 现场升级 | 不方便 | 不方便 | 方便 |
| 依赖BOOT引脚 | 是 | 否 | 否 |
| 典型场景 | 小批量、开发板 | 开发调试、小批量 | 量产、远程升级 |
选型逻辑很简单:开发阶段用ICP,方便调试;量产阶段如果板子有SWD焊盘且数量不大,可以用离线烧录器;如果量大且现场升级需求强,用IAP;如果芯片没焊之前就想烧,用离线编程器(座烧)。实际项目中,我经常把ISP和IAP结合:出厂时用ISP烧一个带IAP功能的Bootloader,后续升级全走IAP。
3. 手把手实操:从零烧录一颗STM32
3.1 硬件准备与接线
以STM32F103C8T6最小系统板为例,准备USB转TTL模块(CH340或CP2102)、杜邦线若干。接线如下:
- TTL模块的TX接STM32的PA10(USART1_RX)
- TTL模块的RX接STM32的PA9(USART1_TX)
- TTL模块的GND接STM32的GND
- TTL模块的3.3V接STM32的3.3V(如果板子已供电,可不接)
- BOOT0用跳线帽接高电平(3.3V),BOOT1接低电平(GND)
接好后,按一下复位键,芯片进入ISP模式。这时候如果串口有数据发出,说明Bootloader已经运行。
注意:有些CH340模块的TX电平是5V,虽然STM32的IO大多兼容5V,但为了安全,最好用3.3V电平的模块。我烧过一块板子,因为TTL模块漏电,把PA10打坏了,后来换了个带隔离的模块才解决。
3.2 软件配置与烧录步骤
打开STM32CubeProgrammer,选择UART模式,端口选CH340对应的COM口,波特率选115200,奇偶校验Even,数据位8,停止位1。点击Connect,如果接线正确,会显示芯片型号和Flash大小。
然后选择要下载的hex或bin文件。如果是bin文件,需要手动填写起始地址,通常是0x08000000。点击Download,等待进度条走完。烧录完成后,把BOOT0跳回低电平,复位,程序就开始运行了。
这里有个细节:STM32CubeProgrammer默认会全片擦除,如果你只想擦除部分扇区,可以在Erasing选项里选“Erase necessary sectors only”。全片擦除会清掉选项字节,如果之前设了读保护,全片擦除后读保护也会解除,但会触发一次Mass Erase,时间较长。
3.3 用ICP方式烧录的对比
同样的板子,如果用ST-Link,接线是:SWDIO接PA13,SWCLK接PA14,GND接GND,3.3V接3.3V。在STM32CubeProgrammer里选ST-LINK模式,点击Connect,然后选文件下载。速度明显快很多,而且不需要动BOOT引脚。
我实测过,烧录一个64KB的固件,ISP方式大约需要8秒,ICP方式只要1.5秒。如果产线每天烧1000片,ISP方式光烧录就要2个多小时,ICP只要25分钟。所以量产时,如果条件允许,优先用ICP或离线烧录器。
3.4 IAP升级的代码实现要点
IAP的Bootloader代码结构大致如下:
#define APP_ADDR 0x08008000 #define APP_SIZE 0x00010000 typedef void (*pFunction)(void); void jump_to_app(void) { uint32_t app_stack = *(volatile uint32_t*)APP_ADDR; pFunction app_entry = (pFunction)(*(volatile uint32_t*)(APP_ADDR + 4)); __disable_irq(); HAL_DeInit(); SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0; SCB->VTOR = APP_ADDR; __set_MSP(app_stack); app_entry(); }这段代码的关键点:关总中断、反初始化外设、关SysTick、重设向量表偏移、设置主堆栈指针、跳转。少一步都可能出问题。
应用程序那边,需要在链接脚本里把起始地址改成0x08008000,并且在main函数开头设置SCB->VTOR = 0x08008000。如果用Keil,可以在Target选项里改IROM1的Start地址;如果用GCC,改链接脚本的FLASH起始地址。
提示:IAP升级时,如果新固件比旧固件大,而Flash剩余空间不够,需要先擦除旧固件。但擦除过程中如果断电,芯片就变砖了。我的做法是:在Flash末尾留一个备份区,先把新固件写到备份区,校验通过后再擦除应用程序区并搬运过去。这样即使断电,Bootloader还能从备份区恢复。
4. 烧录中常见的坑与排查方法
4.1 连接失败:从硬件到软件逐层排查
烧录时最常见的报错就是“No target connected”或“Cannot connect to target”。排查顺序如下:
- 检查供电:用万用表量芯片VDD引脚,确保在2.0V到3.6V之间。有些板子用USB供电,但USB线电阻大,实际到芯片只有2.8V,导致烧录不稳定。
- 检查接线:SWDIO和SWCLK不要接反,GND一定要共地。我见过有人把SWDIO接PA14、SWCLK接PA13,结果死活连不上。
- 检查BOOT引脚:ISP模式下BOOT0必须为高,ICP模式下BOOT0可以为低,但如果芯片里跑了禁用SWD的代码,需要把BOOT0拉高,从系统存储区启动,再用ICP连接。
- 检查复位电路:有些板子的复位电容太大,导致复位脉冲太宽,调试器无法同步。可以尝试把复位线接到调试器,用调试器控制复位。
- 检查读保护:如果芯片被设了读保护,ICP连接会失败,需要先解除保护。解除保护会全片擦除,数据不保。
4.2 烧录成功但不运行:向量表和时钟问题
有时候烧录显示成功,但程序不跑。常见原因:
- 中断向量表没重映射。IAP方案中,应用程序的向量表偏移没设对,导致中断触发后跳到Bootloader的向量表,直接HardFault。
- 时钟配置错误。比如外部晶振没起振,程序却切到外部时钟,导致死机。可以在SystemInit里加超时检测,起振失败就回退到内部RC。
- 堆栈溢出。应用程序的栈顶设置到了非法地址,一运行就进HardFault。检查链接脚本里的栈顶地址是否在SRAM范围内。
- Flash等待周期不对。高主频下需要插入等待周期,如果没设,取指会出错。STM32H7系列尤其要注意,不同频率对应不同的等待周期。
4.3 量产烧录的效率优化
量产时,烧录速度直接影响产能。几个优化方向:
- 提高通信速率:ISP模式下,如果Bootloader支持,可以把波特率提到921600甚至更高。ICP模式下,SWD时钟可以设到10MHz以上。
- 使用离线烧录器:把固件存在烧录器里,产线工人只需要按一下按钮,不需要电脑。离线烧录器通常支持多路同时烧录,比如一拖四、一拖八。
- 优化固件大小:去掉不必要的调试信息,开启编译优化,减小bin文件体积。我见过一个项目,固件里带了完整的printf库,bin文件多了30KB,烧录时间多了好几秒。
- 并行烧录:用夹具同时给多块板子烧录。注意每块板子的电源要独立,否则一块短路会影响全部。
注意:量产烧录前一定要做小批量验证,至少烧10片,跑24小时老化。我吃过亏,有一批板子烧录后测试都正常,但客户用了两周后陆续出现死机,最后查出来是Flash某个扇区擦除不干净,导致程序跑飞。后来加了烧录后校验和老化测试才解决。
4.4 常见问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 连接失败 | 供电不足、接线错误、BOOT引脚不对 | 量电压、查接线、拉高BOOT0 |
| 烧录超时 | 波特率太高、Flash擦除慢 | 降低波特率、增加超时时间 |
| 校验失败 | Flash坏块、电压不稳 | 换芯片、加滤波电容 |
| 烧录后不运行 | 向量表偏移错、时钟配置错 | 检查VTOR、检查晶振 |
| IAP升级失败 | 中断未关、跳转地址错 | 关总中断、检查APP_ADDR |
| 读保护无法解除 | 选项字节被锁 | 用Mass Erase或专用工具解锁 |
5. 进阶话题:ISP Pipeline与图像处理中的ISP
这里要特别说明一下,嵌入式烧录领域的ISP和图像处理领域的ISP完全是两码事。图像ISP是Image Signal Processor的缩写,负责把传感器输出的RAW数据转换成RGB图像,包含去马赛克、白平衡、伽马校正、降噪等步骤。最近热搜里的“isp pipeline”和“stc isp去弹窗”指的就是图像处理方向。
如果你在搜索ISP时看到这些内容,不要混淆。芯片烧录的ISP是In-System Programming,图像ISP是Image Signal Processor。两者缩写相同,但领域完全不同。我在面试新人时,经常有人把这两个搞混,问“ISP去弹窗是不是烧录时弹窗”,这就闹笑话了。
图像ISP的Pipeline通常包括:黑电平校正、镜头阴影校正、坏点校正、去马赛克、白平衡、色彩校正、伽马校正、锐化、降噪。每一步都有大量参数需要调试。而芯片烧录的ISP只关心数据传输和Flash写入,不涉及图像算法。
提示:如果你在做摄像头项目,调试图像ISP时发现画面有弹窗或闪烁,那通常是曝光策略或去噪参数的问题,跟烧录无关。STC系列单片机也有ISP烧录功能,但“STC ISP去弹窗”这个热搜词,我查了一下,更多是指STC-ISP烧录软件在Windows下弹窗报错的问题,比如串口被占用、驱动未签名等。解决方法通常是换USB口、更新驱动、以管理员身份运行。
6. 关于IAP变量复位后的行为
热搜里有个问题:“iap boot里面定义的变量复位后会怎样”。这个问题很典型,我专门说一下。
IAP Bootloader里定义的全局变量,比如一个标志位uint8_t upgrade_flag,在复位后,启动代码会执行.data段拷贝和.bss段清零。所以upgrade_flag会被初始化为0(如果它在.bss段)或者你指定的初值(如果它在.data段)。但是,如果你是从Bootloader跳转到应用程序,没有经过复位,那么SRAM里的内容不会被重新初始化,upgrade_flag的值会保留。然而,应用程序的启动代码同样会执行.data和.bss初始化,如果应用程序的链接脚本覆盖了Bootloader的变量地址,那这些变量的值就会被覆盖。
所以,如果你希望某个变量在跳转后保留,必须把它放在一个不被应用程序启动代码初始化的段里,比如:
__attribute__((section(".noinit"))) uint32_t magic_flag;然后在链接脚本里把.noinit段放在SRAM的特定位置,并且确保应用程序的.data和.bss不覆盖这个区域。另一种方法是写到备份寄存器(BKP)或者RTC备份域,这些区域在复位后不会丢失。
我实际项目中,通常用备份寄存器存升级标志。STM32的BKP寄存器在VBAT供电下,即使主电源掉电也能保持。写之前要先使能PWR和BKP时钟,解除备份域写保护。
7. 工具选型与个人经验
烧录工具这块,我按场景推荐:
- 开发调试:ST-Link V3、J-Link EDU、DAPLink。ST-Link便宜,兼容性好;J-Link速度快,支持芯片多,但价格高。
- 小批量量产:离线烧录器,如正点原子的ATK-ISP、野火的Fire-DAP。可以脱机烧录,支持一拖多。
- 大批量量产:专用烧录机,如河洛、昂科的自动化烧录设备。配合夹具,每小时可以烧几百到上千片。
- 现场升级:IAP方案,配合无线模块或SD卡。我做过一个项目,通过4G模块远程升级,设备装在野外,不用派人去现场。
个人经验方面,有几个点值得强调:
第一,烧录前一定要备份原始固件。尤其是读保护解除后,全片擦除不可逆。我习惯在烧录前用ICP读一次全片,存成bin文件,万一后面发现版本不对,还能回滚。
第二,烧录后一定要校验。不要相信“Download Success”的提示,有些工具只是写完了,没校验。手动点Verify,或者用CRC校验。我遇到过Flash写入成功但读出来不一致的情况,原因是电源纹波太大,导致写入时序出错。
第三,量产烧录的工装要定期维护。烧录探针用久了会氧化,接触电阻变大,导致烧录失败率上升。我一般每两周用酒精清洗一次探针,每三个月更换一次。
第四,IAP升级的固件要加版本号和CRC。Bootloader收到固件后,先校验CRC,再检查版本号是否比当前版本新,避免降级或重复升级。
第五,注意芯片的寿命。Flash擦写次数有限,通常10万次左右。如果IAP升级频繁,比如每天升级一次,几年后Flash可能损坏。可以在Bootloader里加一个擦写计数,超过阈值就报警。
最后再分享一个小技巧:如果你用Keil,可以在Options for Target -> User里添加编译后自动生成bin文件的命令,比如fromelf --bin --output=@L.bin !L。这样每次编译完直接得到bin文件,不用手动转换。烧录时直接选bin,比hex快一点,因为bin没有地址信息,工具不需要解析。
这个内容后续还可以这样扩展:比如讲一讲多芯片联合烧录的同步问题,或者安全启动与固件签名,再或者离线烧录器的文件系统设计。如果你在做相关项目,欢迎一起交流。