ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ISP、ICP、IAP三种芯片烧录方式详解:原理、区别与实操

ISP、ICP、IAP三种芯片烧录方式详解:原理、区别与实操 刚入行做嵌入式那会儿我第一次听到“芯片烧录”四个字脑子里第一反应是烧拿什么烧后来才知道烧录就是把编译好的固件写入芯片内部的Flash存储区本质上就是一次数据写入操作。真正让我头疼的不是烧录本身而是项目里动不动冒出来的ISP、ICP、IAP三个缩写——它们长得像三胞胎实际却对应完全不同的烧录方式选错一个轻则下载失败重则芯片锁死。这篇文章我按从业者的视角把这些概念彻底拆一遍。适合三类人看刚接触单片机的学生和初学者、想从“能跑代码”进阶到“理解原理”的开发者、以及做量产和产品售后升级的工程师。1. ISP / ICP / IAP 到底在讲什么1.1 烧录的本质给芯片“装系统”芯片烧录英文叫Programming或者Flashing核心动作就是把编译生成的二进制文件常见的是.hex或.bin格式写入芯片内部的程序存储区。对绝大多数MCU来说这个存储区就是Flash掉电不丢上电后CPU从里面取指令执行。为什么叫“烧”这得追溯到EPROM时代。早期的可编程只读存储器需要用紫外线擦除编程时靠高电压把内部熔丝“烧断”或让浮栅电荷注入物理上真有“烧”的感觉。发展到今天Flash的写入原理虽然还是电荷注入但大家习惯了继续叫“烧录”或者叫“下载程序”“灌程序”。反正意思都一样把代码放进去。有个基础概念要分清编译和烧录是两个独立环节。编译是把你写的C代码翻译成机器指令生成一个文件烧录是这个文件通过某种物理通道、某种协议被写进芯片。搞不清这个后面看ISP、ICP、IAP的区别就容易晕。1.2 三种方式的本质区别时机、通道、谁来写ISP、ICP、IAP这三个缩写区别的核心可以浓缩成三件事什么时候烧、用什么通道烧、由谁来触发写入。我用一个“往家里搬东西”的类比你一遍就能记住ISPIn-System Programming在系统编程芯片已经焊在电路板上了你用预留出来的串口或SPI接口通过芯片出厂固化的引导程序把新程序“从门缝塞进去”。你不用拆芯片但需要芯片自己愿意开门——这个“开门动作”就是让芯片进入出厂Bootloader。ICPIn-Circuit Programming在线电路编程芯片同样在电路板上但你手里有一把“万能钥匙”——调试器ST-Link、J-Link这类硬件通过JTAG或SWD接口绕过芯片里的所有软件直接操作芯片内核把Flash写了。门是你用钥匙直接打开的走的是专用通道。IAPIn-Application Programming在应用编程芯片自己运行着你写的程序程序在跑的过程中通过串口、USB、网口甚至无线收到新固件然后调用芯片内部的Flash写入函数把新程序写到指定区域最后跳转执行。整个过程是“芯片自己给自己换脑子”不需要额外工具。为了更直观我做了个对比表建议收藏对比项ISPICPIAP触发方芯片出厂固化的Bootloader外部调试器硬件用户自己写的应用程序物理通道UART、SPI、I2C等串行接口JTAG、SWD调试接口任何通信接口甚至OTA是否需要额外硬件只需要串口线或USB转TTL必须用调试器完全不需要额外硬件典型阶段量产烧录、产线批量写入开发调试、故障恢复产品升级、远程维护能否在线调试基本不能可以断点调试升级过程自己控制占用芯片资源ROM里固化的Bootloader调试引脚Flash分区要预留程序空间2. 三种烧录方式逐个拆透2.1 ISP量产线上的主力靠的是芯片厂家的“出厂程序”ISP能工作的前提是芯片出厂时ROM里固化了一段特殊的引导程序。以STM32为例芯片出厂时System Memory里就有一段Bootloader你只要把BOOT0引脚拉高、BOOT1拉低芯片上电或复位后就会跑这段引导代码。它通过USART1不同型号可能不一样接收上位机发来的数据自己调用Flash编程接口把数据写进去。写入完成后把BOOT0拉低复位后正常跑你的程序。STC单片机是另一个典型。大家常说的“STC下载”用的就是串口ISP方式——STC芯片出厂自带一段引导程序PC端用STC-ISP软件通过串口跟它通信就能把程序灌进去。很多老工程师吐槽STC-ISP软件界面老派、弹窗不少但它的下载稳定性确实靠得住这正是因为它走的是芯片出厂时就设计好的引导通道基本不依赖外部调试器。使用ISP要注意几个点第一目标板必须有可靠的供电串口线的电平要和芯片IO电平匹配现在很多是3.3V的MCU直接用USB转TTL模块要把跳线调到3.3V否则可能烧坏IO第二TX和RX一定要交叉连接上位机的TX接芯片的RX上位机的RX接芯片的TX共地也是必须的第三ISP烧录速度普遍不快尤其是高波特率下容易出错量产时千万别盲目追求最高波特率。ISP最厉害的地方是不需要任何调试器一根串口线就能搞定产线成本极低。缺点是依赖芯片出厂Bootloader而且通常只能烧录不能在线调试出了问题你还是要靠调试器去查。2.2 ICP开发调试的利器直接“硬写”FlashICP是开发阶段用得最多的一种方式。它的原理是调试器通过JTAG或SWD接口直接访问芯片内核的调试端口Debug Port然后通过调试端口间接操作Flash控制器完成擦除、编程、校验等动作。整个过程不依赖芯片里有没有程序也不需要设置BOOT引脚哪怕芯片里的程序已经跑飞了只要内核还能响应调试请求就能把Flash救回来。STM32的SWD接口只需要四根线SWDIO、SWCLK、GND、VCC3.3V。很多工程师图省事不接VCC让调试器判断目标板电压但我建议最好接上尤其是给低功耗板子调试时电压检测不准会直接导致连接失败。ST-Link、J-Link、DAP-Link都是常见的调试器其中DAP-Link是开源的几十块钱就能买到配合Keil或STM32CubeProgrammer日常开发完全够用。ICP的速度通常比ISP快很多。SWD接口跑个4MHz甚至更高的时钟很常见写几百KB的固件也就几秒钟。更关键的是ICP支持在线调试——你可以设断点、单步执行、查看变量和寄存器值。程序出问题的时候这是排查逻辑错误最有效的手段没有之一。用ICP最忌讳的一点就是乱勾读保护。很多人看教程说要开RDPReadout Protection结果开了又不记得密码芯片直接被锁死JTAG/SWD全部失联。真遇到了也别慌部分STM32可以用“Connect under reset”模式碰碰运气或者用ST-Link Utility里的全擦除选项尝试恢复但如果是高等级读保护Level 2基本就是换芯片的命了。2.3 IAP应用自己升级自己OTA的命根子IAP的思路跟ISP和ICP都不一样。前面两种都依赖外部东西——要么是出厂Bootloader要么是调试器硬件。IAP则是你程序里自己写了一套升级逻辑Bootloader引导程序负责“接货”把收到的新固件写入Flash的另一块区域然后跳转过去执行应用代码App负责“跑业务”它可以在运行过程中接收新版本固件存下来然后复位进入到Bootloader由Bootloader完成最终写入。为什么非IAP不可因为产品一旦发出去售后升级是绕不开的需求。你不可能让用户拆开设备接调试器更不可能每个设备都拉一根串口线。有了IAP你可以让设备通过串口、CAN、USB、以太网甚至Wi-Fi接收新固件实现远程升级。大家经常刷到的各种 MCU 远程升级方案底层全是IAP。具体到芯片型号上GD32F103、HC32L136、STM32H750VBT6这些热门MCU的IAP方案都很成熟。比如GD32F103把Flash分成两段前32KB放Bootloader后面的放AppBootloader里做串口接收和Flash写入App启动时检查一个升级标志位决定是跳去跑业务还是进入升级流程。HC32L136这类国产芯的IAP做法也差不多核心都是“Bootloader App分区 跳转”。很多初学者对IAP有个误解以为要写完整个操作系统。其实IAP不依赖操作系统裸机也能做核心就是三件事Flash分区、引导程序跳转、中断向量表重映射。后面实操部分我会用代码演示。顺带提一句FPGA里的“ISP”概念跟MCU不太一样。FPGA常说的在线配置是把位流文件通过JTAG或SPI Flash接口写进配置芯片强调的是“可重新配置”的硬件灵活性思路和MCU的ISP神似但实现机制完全不同别混为一谈。3. 实操全流程以STM32为例把三种方式各走一遍3.1 ISP实操串口烧录全程记录先说硬件准备。准备一块STM32F103C8T6最小系统板一个USB转TTL模块几根杜邦线。接线关系是USB转TTL模块的TX接芯片的PA10USART1_RXRX接PA9USART1_TXGND接GND。注意这是交叉接法很多人第一次烧录失败就是栽在这——TX和RX接反了数据根本送不进去。然后设置启动方式。STM32F103有两个启动引脚BOOT0和BOOT1。ISP模式下BOOT0接高电平接到3.3VBOOT1接低电平接地。这句话务必记牢BOOT01、BOOT10芯片才会进入系统存储器启动模式才会运行出厂Bootloader。如果你只想跑正常用户程序BOOT00、BOOT1随意即可。给目标板上电之前先检查电源。USB转TTL模块如果带3.3V输出可以直接给板子供电但前提是板子上没有其他电源同时供电否则两套电源“打架”轻则烧稳压重则烧芯片。稳妥做法是目标板用USB线单独供电USB转TTL只接TX、RX、GND三根线把VCC留空。软件方面STM32官方推荐的STM32CubeProgrammer就很好用免费、跨平台。打开后选UART接口选择对应的串口号波特率默认115200如果是57600能提高成功率。先点“Connect”如果串口没有异常软件会识别出芯片型号和Flash大小然后就能加载.hex或.bin文件点“Start Programming”了。下载过程中有两个细节值得说。第一注意软件里的“Verify programming”选项建议保持勾选写入后逐字校验能及时抓出通信错误导致的数据错位。第二如果连接失败先检查BOOT0是不是真的被拉高了然后按一下复位键再点Connect因为很多ISP引导程序是上电才开始跑不手动复位一次它可能还停在原来的程序里。烧录完成后先把BOOT0跳线恢复到低电平再按一次复位程序就开始跑了。如果你发现程序没跑起来先查BOOT0是不是还放在高电平这是ISP流程里最经典的“烧录成功但不能运行”的原因。3.2 ICP实操用ST-Link加SWD实现烧录与调试ICP的方式就从容多了完全不用碰BOOT引脚。拿ST-Link/V2举例SWD四根线接法是SWDIO接芯片PA13SWCLK接PA14GND接GND3.3V接板子的3.3V电源。部分板子把SWD接口做成独立的四针排针直接插就行。烧录工具我用STM32CubeProgrammer的ST-LINK模式。插上ST-Link后点“Connect”工具会自动读取芯片的ID、Flash大小和读保护等级。如果芯片目前没有加密就能正常连接。然后加载固件设置好烧录起始地址STM32内部Flash默认从0x08000000开始点“Start Programming”即可。为什么推荐新手优先掌握ICP因为它的容错率高。ISP模式下芯片里跑着奇怪的程序、串口被占用、引脚被复用都可能导致失败ICP直接走调试端口即使Flash里的程序完全错乱你依然能连上芯片重新烧录。更不用说它还能在线调试在代码里打断点看变量这是新手排查逻辑错误的“外挂”。这里我把SWD的进阶技巧一并说了。如果遇到“No ST-LINK detected”或者“Cannot connect to target”先别急着怀疑芯片坏了。第一检查驱动是否安装ST-Link在Windows下需要装驱动STM32CubeProgrammer自动安装依赖时会带上第二检查接线SWDIO和SWCLK不要接反GND必须共地第三试着在软件里勾选“Connect under reset”——有些场景下目标芯片运行了低功耗代码正常运行模式下内核已经睡死调试器喊不醒它必须在复位信号拉低期间建立连接趁机把芯片“按住”再接管。J-Link用户对应这个功能的选项叫“Connect during Reset”原理一样。调试器模式下还会遇到一个经典问题目标板电压不稳。很多板子用LDO稳压调试器上电瞬间电压爬升慢调试器检测不到目标电压就拒绝工作。解决办法是先给目标板独立上电再插调试器或者干脆把调试器3.3V和板子电源分开只用GND、SWDIO、SWCLK三根线通信。3.3 IAP实操Bootloader与App跳转的核心代码IAP实现起来比前两种烧录方式更“软”因为它完全是软件层面的设计。我以STM32F103为例演示一个最小可用的Bootloader App方案。第一步规划Flash分区。STM32F103C8T6有64KB Flash起始地址0x08000000。我习惯把前8KB0x08000000 - 0x08002000留给BootloaderApp从0x08002000开始用户程序最大可用56KB。Bootloader代码量一般很小几百字节到几KB就够8KB留足了余量。第二步Bootloader负责串口接收和Flash写入。启动后先初始化USART1然后判断是否进入升级模式。简单做法是App运行期间收到升级指令后往Flash末尾或者备份寄存器写一个“升级请求”标志然后复位Bootloader上电后检查这个标志如果为真就走升级流程否则直接跳转到App。跳转函数是IAP的灵魂。核心代码长这样typedef void (*pFunction)(void); void jump_to_app(uint32_t app_addr) { uint32_t app_stack *(volatile uint32_t *)app_addr; // 取App的初始栈顶指针 pFunction app_entry (pFunction)*(volatile uint32_t *)(app_addr 4); // 取App的复位向量 if ((app_stack 0x2FFE0000) ! 0x20000000) { return; // 检查栈顶指针是否落在SRAM合法范围内非法就拒绝跳转 } __disable_irq(); // 跳转前一定要关闭全局中断 SCB-VTOR app_addr; // 重设中断向量表偏移 __set_MSP(app_stack); // 设置主栈指针 app_entry(); // 跳转到App的复位向量 }这段代码有一个关键检查app_stack必须是合法的SRAM地址。很多芯片跳转跑飞不是因为地址算错了而是因为你把App的地址当成栈顶用App第一句代码就把栈指针指向了无效内存一调用函数就出事。加这个检查至少能在开发早期帮你排除一半问题。第三步App工程要改两个地方。一是启动文件里或main最前面设置中断向量表偏移SCB-VTOR 0x08002000;不改这行App里一旦发生中断CPU会从0x08000000也就是Bootloader的向量表去找中断函数跑的就是Bootloader里的旧处理逻辑调试时经常出现“中断进去了但感觉不对”的灵异现象。另外App在Keil里的烧录起始地址要改成0x08002000用IAP方式时不要直接烧到0x08000000否则会把Bootloader覆盖掉。第四步App收到新版固件后通常先存到外部Flash或者内部空闲区校验完整体性和CRC再置升级标志、复位。Bootloader收到数据后按扇区擦除、写入全部写完再做一次整体校验通过后清除升级标志再跳转。热词里有个很具体的问题“IAP boot里面定义的变量复位后会怎样”。我直接回答Bootloader里定义的普通全局变量在复位后几乎肯定会被清掉因为App的启动代码会重新初始化RAM区覆盖掉整个.bss和.data段。如果你真的需要在软复位后跨Bootloader和App保留一个标志位有两条路一是把变量定义在“NoInit”段链接脚本里指定一段不参与初始化的RAM地址二是用备份寄存器或RTC后备域比如STM32的BKP寄存器只要备用电池有电数据就一直保留。量产的升级设备里用备份寄存器做升级标志非常普遍。4. 常见问题与排查技巧实录4.1 下载失败、连接不上的典型场景速查实际项目中我踩过的坑不少这里列一个速查表基本都是我或身边同事亲测过的场景。现象大概率原因解决办法ISP串口连不上上位机报超时BOOT0没拉高TX/RX接反共地缺失重新确认BOOT01、BOOT10交叉接线GND必须相连ISP下载到一半报校验错误波特率太高串口线质量差干扰大降到57600或38400换短线给目标板独立供电SWD连接失败提示No target接线错误、供电不稳、芯片进入低功耗检查SWDIO/SWCLK/GND先给板子单独上电勾选Connect under reset芯片烧过一次后再也连不上了误开了读保护RDP Level 1用ST-Link Utility的“Full chip erase”恢复Level 2只能换芯片程序烧录成功但开发板上电没反应BOOT0仍处于高电平晶振没起振启动文件选错BOOT0拉低后复位检查外部晶振和电路核对启动文件是否匹配芯片型号IAP跳转后App跑飞仿真时卡在HardFault中断向量表没偏移栈顶指针非法App链接地址没改启动早期设置SCB-VTOR用栈顶指针合法性检查确认App工程地址从App起始处开始App里用全局变量做升级标志复位后丢失Bootloader和App共用同一段RAMApp启动时清零用NoInit段或者改用备份寄存器/RTC后备域这里补一个很多人忽略的点下载算法Flash Algorithm。用Keil或IAR烧录时工具会根据芯片型号自动选择对应的Flash算法文件如果你在工程设置里选错了芯片型号比如实际是F103C8选了F103RB擦写地址范围、扇区大小都会对不上最终表现就是烧录失败、校验失败。遇到莫名奇妙的烧录问题先查工程芯片型号和调试器配置别一味怀疑硬件。4.2 排查IAP升级失败的一条完整思路做IAP升级十个问题里八个出在跳转环节。我总结了一个固定的排查顺序建议按这个顺序来别跳步骤。第一步确认分区链接是否正确。打开App工程的MAP文件看有没有把代码链接到0x08002000。如果还是0x08000000这板子上烧的App跟Bootloader重叠了Bootloader早就被覆盖一切异常都正常。第二步确认中断向量表偏移生效。在App的main函数第一行打断点查看SCB-VTOR的值它应该等于0x08002000。如果值是0说明偏移代码没执行中断一来必跑飞。第三步确认跳转前的栈顶指针。在跳转函数里打印或观察app_stack的值它应该落在0x20000000开始的SRAM区间。很多国产MCU比如说GD32的SRAM起始地址和STM32不同直接抄STM32的代码时忘了改这个检查地址就会误判栈指针非法拒绝跳转。这也是GD32F103 IAP升级踩得最多的坑。第四步关闭全局中断再跳转。跳转前不关中断跳转瞬间外设触发中断而此时App的向量表可能还没设好CPU去读了一个地址不合法的异常向量直接HardFault。最好在跳转函数里加__disable_irq()并在App启动后重新初始化所有外设时再开中断。第五步区分软复位跳转和长跳转。很多人IAP用软复位NVIC_SystemReset方式App收到升级包写标志复位Bootloader读标志。软复位后整个系统重新初始化外设状态干净推荐。但要注意如果升级标志是用普通全局变量存的必须用前面说的备份寄存器或NoInit段否则复位后标志就没了。有一种情况很隐蔽Bootloader烧进去后能跑App也能独立烧进指定地址跑但是Bootloader一调app_entry()就死。这时候很大概率是App的启动文件把中断向量表又覆盖了——有些启动文件里会重新设置SCB-VTOR或者Keil工程里勾选了“Use MicroLIB”影响了SP初始化。我遇到过两次最后都是把SCB-VTOR设置在App工程启动文件里提前执行同时去掉启动文件里冲突的写法才解决。4.3 别被同名缩写带偏ISP/ICP在不同领域的含义写到这里我必须多说一句因为不少新手在搜索“ISP”时会被各种信息绕晕。搜索引擎里热门的“ISP pipeline”“ISP图像处理”“富瀚ISP”说的是图像信号处理器Image Signal Processor是摄像头传感器后端处理图像的一颗芯片或IP跟“在系统编程”完全是两个世界。手机拍照效果的ISP算法、安防监控的富瀚ISP方案虽然缩写一样但语境差着十万八千里。搜索时建议带“MCU”“烧录”“单片机”等限定词能省很多时间。“ICP”同样有两个常见含义。芯片领域是In-Circuit Programming在线电路编程用调试器烧录但三维视觉和机器人领域有个热词叫“ICP配准”Iterative Closest Point是点云拼接、SLAM建图里常用的迭代最近点算法。你如果搜“ICP配准”出来的全是激光雷达点云对齐的内容那不是我们说的芯片烧录ICP擦亮眼睛看好行业前缀。连“IAP”都有一语双关的情况。嵌入式领域是In-Application Programming但苹果的App Store应用内购买也叫IAPIn-App Purchase。所以当你搜“IAP方案”看到一堆iOS应用商店付费教程别奇怪直接在前面加“MCU”“Bootloader”“固件升级”就好。这个“缩写撞车”的问题提醒我们学习嵌入式概念时一定要先锁定领域语境。芯片烧录、ISP/ICP/IAP这套名词都是在半导体、嵌入式软件领域内部的约定俗成。多看看芯片参考手册里的“Programming”章节比在网上零散搜索效率高得多。最后分享一点经验烧录方式这件事困扰过我很久甚至一度把三个词背混。后来我自己做量产和售后维护才彻底想明白这三个词不是三个并列的选项而是一条产品生命周期上的三个节点。开发阶段用ICP因为要调试、要抓bug产线阶段用ISP因为便宜高效、不用调试器产品交付后靠IAP因为要远程升级、持续迭代。你不需要一开始就把三条路都走通但至少要知道它们的存在知道每到一个阶段该掏出哪把钥匙。给新手一个明确的起步建议从ICP/SWD方式入门买一个兼容DAP-Link的调试器先学会烧录和打断点。把这条路走顺了再回头看ISP的启动引脚配置和IAP的分区跳转理解成本会低很多。等你亲手写过一次Bootloader、看着App从调试串口升级成功你会回来感谢当年那个愿意把概念拆开弄明白的自己。
返回列表