ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

嵌入式烧录与仿真调试全解析:从SWD原理到工程实践

嵌入式烧录与仿真调试全解析:从SWD原理到工程实践 我到现在还记得第一次把程序烧进STM32板子时的那种兴奋光标停在Keil的Download按钮上心跳加快点下去几秒钟后串口工具里蹦出“Hello World”那一瞬间感觉整个世界都亮了。后来带过不少实习生和新同事发现大家普遍卡在两个地方一是烧录下载总在“为什么连不上芯片、为什么烧不进去”之间反复横跳二是仿真调试以为只是暂停一下看个变量结果真遇到问题时完全不知道从哪里下手。这篇文章我就以嵌入式软件开发的日常为背景把烧录下载和仿真调试工具这整条链路掰开揉碎讲清楚覆盖原理、实操、翻车案例、调试思路和量产阶段的做法适合刚入门的新人也适合干了两三年但一直“只会点按钮”的朋友。嵌入式软件开发面试题里烧录失败排查和调试器的断点机制都是高频考点所以这篇文章顺带也能帮你把这两块知识串起来。1. 烧录下载从“点一下Download”到理解整个链路1.1 烧录这件事的本质是什么很多人把烧录想得太简单觉得就是“把文件发到芯片里”。实际上烧录包含三步擦除、编程、校验。顽固一点说烧录的本质是把PC上编译链接生成的二进制可执行映像通过调试器写入目标MCU的非易失存储通常是内部Flash然后在复位后让CPU从正确的地址开始执行。以STM32为例芯片复位后CPU会从0x00000000处取出栈顶指针MSP从0x00000004处取出复位向量然后跳转到SystemInit和main之前。所以烧录不是“传个文件到U盘里”那么随意烧录地址、向量表位置、Flash算法这些参数错一个程序都跑不起来。这也是为什么在工程配置里Flash起始地址和烧录算法必须与芯片型号严格匹配。很多新人烧录失败不是下载线坏了而是Flash算法的起始地址选错程序被写进了错误的扇区。1.2 从JTAG到SWD接口协议怎么选烧录和调试共用一套接口这里绕不开JTAG和SWD。JTAG是祖宗级的调试接口需要TCK、TMS、TDI、TDO等至少5根线支持菊花链串联多个器件在复杂板卡、FPGA调试、CPU仿真器领域仍被广泛使用。但嵌入式MCU调试场景它最大的问题是占用引脚多。SWDSerial Wire Debug是ARM专门为Cortex-M系列设计的精简调试接口只需要SWDIO和SWCLK两根线加上GND就能完成烧录和调试少了两根线哭声都小了一半。我个人的做法是只要是Cortex-M系列默认走SWD除非目标板硬件设计只引出了JTAG。顺便提一句BOOT/ISP启动方式很多STM32芯片的ROM里固化了BootLoader可以通过UART、USB、CAN烧录程序不需要调试器。量产阶段和现场升级经常用这个方式但在开发阶段它不能仿真调试还是乖乖接SWD方便。1.3 手边常用的烧录下载工具怎么选市面上的调试烧录工具种类很多我按实际场景给个相对实用的选型思路。工具类型典型产品适用场景核心优势需要注意的点通用调试器J-Link系列、ST-Link/V2开发调试、单板烧录生态完善、支持型号广、可仿真可打印RTT高仿和正版差距大尤其在速度稳定性和虚拟串口上开源调试器DAPLink、CMSIS-DAP、pyOCD预算有限、学生、开源项目便宜、协议开放、配合Python脚本灵活高端特性如RTT/trace支持不完整离线烧录器各家量产编程器产线批量烧录无需电脑、可脱机作业、支持工装联动价格高配置复杂需要维护烧录工程ISP/串口工具各芯片厂官方工具BootROM烧录、量产备选只需要串口成本极低不能仿真速度慢现场要设置BOOT模式如果你正在犹豫第一只调试器该买什么我的建议是先把预算花在J-Link的正版或兼容版上如果你用STM32为主ST-Link/V2也完全够用。等真的开始研究trace、时序分析时再考虑更高级的调试方案。1.4 烧录算法FLM到底是什么绝大多数人点Download时压根不会注意“Flash Download”页面里的Flash Algorithm列表但烧录失败十有八九和它有关。直接说结论CPU自身是没有Flash编程能力的。烧录Flash需要调用芯片厂商提供的烧录算法这个算法是一小段可执行程序调试器会先把它加载到芯片的内部RAM里然后让CPU执行这段程序去操作Flash控制器的寄存器完成擦除、编程、校验。你把工程里的Flash算法理解成“临时跑在芯片上的一段小助手程序”就行了。所以每次烧录你都经历了“下载算法到RAM——校验RAM——运行算法——擦除Flash——写入固件——校验写入结果”的全过程。这就是为什么工程选错芯片型号时Keil会提示“No Algorithm found”或者“Flash Download failed”因为调试器不知道该拿哪个算法去操作你的Flash。2. 仿真调试的工作原理调试器和芯片之间到底在聊什么2.1 调试器不是“看着”芯片而是“接管”芯片很多人以为调试器的USB线连到电脑就相当于电脑和芯片之间开了一条视频通道能实时看到芯片内部。真实机制远没有这么玄乎。Cortex-M内核内部有完整的CoreSight调试架构通过DAPDebug Access Port对外暴露调试接口。你接上J-Link本质上是通过SWD协议去访问芯片内部的调试寄存器然后通过AHB-APDebug访问内存的通道读写内存和外设寄存器。所以调试器能做的所有骚操作——暂停、单步、读变量、改寄存器——都是通过一组寄存器接口完成的。这也解释了为什么芯片进低功耗模式后调试器经常连不上因为调试时钟域可能被关了也解释了为什么调试时CPU跑到WFI/WFE指令后会出现“卡死”一样的现象。面试官最爱问的一个题是“芯片进入Stop模式后为什么调试器连不上”答案很简单调试接口的时钟域可能被关闭DAP失去访问能力。方法也简单要么用唤醒源先唤醒芯片要么把低功耗调试配置位DBGMCU-CR里的DBG_STOP/DBG_SLEEP位打开让调试时钟在低功耗模式下保持运行。2.2 断点机制断点为什么不是万能的断点有硬件断点、软件断点两种它们的实现原理完全不同。硬件断点靠芯片内部调试单元Cortex-M的FPB单元通常提供6个比较器实现。CPU执行指令时硬件会把当前PC地址和预设断点地址做比较命中就触发调试事件。硬件断点数量有限但可以在Flash中任意设置不修改用户代码。软件断点则是指令级替换调试器把目标地址的指令临时替换成一条BKPTBreakpoint指令CPU执行到BKPT时触发异常并进入调试状态。好处是断点数量几乎不受限制坏处很多——如果Flash是只读的软件断点在Flash里根本没法用在RAM中设置软件断点时如果程序依赖时序严格的代码或DMA操作临时替换指令可能引发不可预料的副作用。实际调试时我在Keil里设置大量断点时经常遇到提示“Not enough hardware breakpoints”这就是硬件断点资源的瓶颈。此时可以切换到RAM中运行代码来获得更充足的软件断点或者用“Flash Breakpoints”技术让J-Link先备份Flash内容、临时改写断点位置但这种操作会磨损Flash不建议在频繁调试的板子上滥用。2.3 单步和变量监视看着CPU一步步走单步执行本质是让CPU执行一条指令后自动触发一次调试事件。由于每条指令执行时间极短调试器在每步之后都会读取内核寄存器和内存数据更新IDE界面。你看着“貌似连贯”的单步过程实际上是一次次“暂停—读状态—恢复执行”的循环。这就带出一个变量监视的关键问题别相信你看到的每一个变量值。编译器开了优化后很多变量根本不驻留内存只存在于寄存器里甚至被彻底优化掉你在Watch窗口看到的地址往往是“最后写回内存时的值”并不代表当前真实状态。所以我在调试时有一条铁律不确定变量是否被优化时先在代码里临时加volatile修饰再重新编译。如果你看到Watch窗口里某个变量显示“ ”或者“optimized out”大概率就是被优化掉了。明白这个原理你才能理解为什么有些网上教程反复强调“调试时建议开-O0”。2.4 调试通道的新玩法SWO/ITM和RTT串口printf是嵌入式调试的祖传手艺但串口在时序分析、高频日志输出场景下很容易干扰程序运行节奏。现代调试工具提供了两个更优雅的方案。SWOSerial Wire Output是ARM调试接口里专用的一条单线输出通道CPU可以通过ITM模块往SWO引脚吐调试信息带宽高且几乎不干扰主程序执行调试器直接接收并显示不需要占用真实串口。不过SWO引脚不是所有板子都引出来了用之前要确认原理图。RTTReal-Time Transfer则是SEGGER搞出的更通用方案在目标RAM中开一块环形缓冲区固件往缓冲区里写日志J-Link通过调试接口轮询读取。它不需要额外引脚、不需要串口在调试阶段可以极快地打印大量数据。我在调一些实时性要求高的算法时基本都是RTT输出速度远超UART。这也是面试高频题“SWO/RTT和串口printf的区别是什么”标准答法就是串口printf要占用一个UART外设、有波特率上限、可能被中断和DMA抢占SWO/RTT通过调试接口走速度更快、干扰更小。3. 实战从Keil和VS Code里跑通一次完整烧录与调试3.1 Keil MDK侧的配置链路Debug页和Utilities页Keil里跟烧录和调试相关的配置其实分散在两个页面很多人搞混。Debug页负责“仿真调试”相关配置。在Options for Target - Debug里选择“Use J-LINK / J-Link Trace”点击Settings正常情况下能看到“SW Device”窗口中出现芯片IDCODE这说明调试器已经和芯片建立了连接。如果这里显示“No target connected”那就先不要点Download了先去查接线和供电。Utilities页负责“下载烧录”相关配置。勾选“Use Debug Driver”后点击Settings进入Flash Download页面你会看到Flash Programming Algorithm列表这里才是真正管理烧录算法的地方。必须确保列表里有和你芯片型号匹配的算法并且Start地址要填对。STM32F103的Flash起始地址是0x08000000如果在其他地方用的是0x08000000换了个页面还要保证没有把Flash大小和算法搞错。实操清单我通常这样做用SWD接线接好目标板连接线顺序SWDIO、SWCLK、GND不接VCC目标板独立供电。在Keil里打开工程确认芯片型号和Flash地址。Options for Target - Debug选“Use J-LINK”Settings里确认SW Device识别。Options for Target - Utilities勾选Use Debug DriverAdd正确的FLM算法。点击Download观察下载日志擦除、编程、校验三步都出现“OK”字样即为成功。按一下目标板复位键确认程序从main开始执行串口或LED有反应。3.2 VS Code Cortex-Debug的开源路线Keil不是万能的很多新兴芯片和开源社区项目更偏好VS Code OpenOCD/pyOCD的组合。我用这个组合调过不少项目虽然初始配置有些门槛但用顺了之后很舒服。以STM32F4 ST-Link为例用OpenOCD作为调试服务器需要准备一个最小配置文件内容大致如下source [find interface/stlink.cfg] transport select hla_swd source [find target/stm32f4x.cfg]第一行指定调试接口适配器第二行选择SWD传输方式第三行加载目标芯片的配置。之后在VS Code的launch.json里配置Cortex-Debug插件{ type: cortex-debug, request: launch, servertype: openocd, device: STM32F407VG, configFiles: [ interface/stlink.cfg, target/stm32f4x.cfg ], svdFile: ${workspaceFolder}/STM32F407.svd, executable: ${workspaceFolder}/build/app.elf, name: Cortex Debug STM32 }如果你用J-Link也可以直接用J-Link自己的GDB Server或者JLinkGDBServerCLI来替代OpenOCD配置思路一致。svdFile的作用很值得提一下它能让你在调试窗口里直接看到外设寄存器的含义比如RCC-CR的各个位代表什么不用频繁翻手册强烈建议配好。3.3 一次完整调试会话里发生了什么选好IDE之后你会发现点击“Start Debug Session”和点击“Download”的差别。Download只是把固件写进Flash就结束而Start Debug Session会经历“下载固件到Flash——复位芯片——停在main的第一行”的全流程。这在调试时很关键因为调试器帮你建立了“初始状态的一致性”每次调试会话开始程序都从同一个起点出发变量初值、外设状态都是可预期的。实际中你会遇到一种情况断点停在某行时程序看起来正常但一旦你单步往下走外设行为就异常了。这通常是因为单步执行改变了时间关系让中断处理、外设时序都不再真实——这就是很多老工程师说“单步调不出来问题必须跑起来看”的原因。3.4 正确认识调试窗口里的“寄存器重名”有个细节值得提Cortex-M内核的寄存器在IDE里显示为R0-R15、SP、LR、PC、xPSR但这些是内核寄存器不是芯片外设寄存器。Keil的Peripherals菜单下才是外设寄存器视图而Cortex-Debug插件里的Peripherals窗口需要配合SVD文件才能显示外设寄存器。很多新手在Watch窗口里直接写“GPIOA-ODR”发现显示不出来然后在Peripherals里翻找半天其实两者是不同层级的东西。内核寄存器管CPU执行状态外设寄存器管芯片功能模块调试时这两者要分开看不能混在一起。4. 烧录与调试翻车现场那些让你怀疑板子坏了的坑4.1 常见报错与第一反应先给个快速对照表都是我在不同板子上实际遇到过的报错。报错现象常见原因第一反应No target connected / No device found接线错误、供电异常、调试接口被占用查SWD接线顺序、确认目标板上电、降低SWD速度到100kHzRDDI-DAP Error芯片进入低功耗模式、调试时钟域关闭唤醒芯片或检查DBGMCU配置位Cannot access target. Shutting down debug sessionSWD引脚被复用成GPIO、软件已修改引脚功能拉高BOOT0进入ISP模式或按住复位键连调试器Flash Download failed - Cortex-M3FLM算法选错、Flash地址配置不对、芯片读保护核对芯片型号、Flash算法、Start地址检查RDP等级Error: Flash Download failed - Cannot access target目标板电压异常、调试器与目标板电平不匹配检查目标板供电参考电压确保VCC一致性Verification failedFlash编程后校验不一致可能是芯片写保护或VCC不稳重新擦除再下载检查Flash写保护设置4.2 一个“芯片连不上”的完整排查链路有一次同事拿来一块板子说“烧不进程序”现象是J-Link报“Cannot access target”。我没有急着换芯片而是按这个顺序排查第一步查接线。SWDIO、SWCLK、GND三根线有没有插反这个看似低级却是最高频错误。确认接法后依然无解进入第二步。第二步查供电。用万用表量目标板的VDD发现只有1.6V左右正常应该是3.3V。查了半天发现是纽扣电池供电电路里升压芯片电流不够换了稳压源后芯片立刻能被识别。这个案例说明芯片连不上先看电再看线不要一上来就怀疑芯片坏了。第三步如果供电和接线都正常把目标板完全断电再上电同时按住复位键不放然后点Download在松复位键的瞬间让调试器抢到连接。很多调试接口被软件配置成GPIO或者芯片进入低功耗模式的板子都可以靠这个“抢时序”的方法连上。第四步如果还是不行把BOOT0拉高让芯片从系统 BootROM启动再用ISP串口工具连接查看是否能读到芯片。如果串口ISP能读到芯片说明CPU是活的问题就在调试接口或Flash上。那个案例最后的原因很有意思上一版固件把SWD引脚重映射成了普通GPIO导致所有调试器都无法连接拉高BOOT0进入ISP模式用串口全片擦除后调试器才恢复连接。4.3 接线和电平层面的魔鬼细节SWD的两根线看起来简单但很多人烧录失败就败在连线上。最常见的是杜邦线过长超过20cm后信号反射导致调试不稳定SWDIO和SWCLK偶尔交叉GND没接导致电流回路异常。我的建议是尽量缩短SWD连线首选软排线或定制转接板。如果板上没有标准SWD接口需要飞线时尽量用短线并将目标板地、调试器地在最近处相连。降低SWD速度是万能的“降火”手段Keil里可以设置把SWD时钟从4MHz降到100kHz很多高频不稳定的板子会立刻稳定。还有一个坑是目标板VDD电平与调试器电平不匹配。比如目标板是1.8V内核调试器是3.3V直接连接就可能损坏芯片或导致连接失败。现代调试器很多能自动适配电压但你要是用老旧调试器务必确认双方电平兼容。4.4 读保护、写保护与“砖头”的救法STM32系列带RDPRead Protection等级机制。Level 0是无保护Level 1是禁止调试接口读Flash既能防调试器连不上也能防别人读固件Level 2是永久的、不可逆保护。如果你烧录时发现调试器能识别芯片但一执行Flash操作就报错或干脆连不上很可能是芯片被设置成了Level 1/2保护。Level 1有一个特性连不上调试器但可以通过串口ISP、拉高BOOT0后用官方工具执行“全片擦除”命令擦完之后RDP会回到Level 0芯片救活。Level 2则是彻底屏蔽所有调试接口连ISP都无法解除这种情况只能换芯片。所以我在量产配置里明确提醒不要轻易开启Level 2除非固件已经非常稳定且确认不再需要现场调试维护。另外ST-Link/J-Link在连接受保护芯片时可能会提示“Error connecting to the target”不要误判为芯片烧毁。合理使用“Connect under reset”功能比反复按复位键更靠谱。5. 调试不只是“暂停看变量”高级用法与调试思维5.1 从“暂停”到“抓凶手”条件断点和数据观察点很多人在调试时只会设普通断点全速运行到断点就停再手动看变量。遇到“变量被莫名改掉”这种问题时这种方式根本抓不到凶手。条件断点就是在断点基础上加触发条件比如“counter 100”。注意一点条件断点每个执行周期都会被评估一次程序运行速度会明显变慢但找bug时值得。我调试状态机时经常对这种条件断点加“log message”选项让调试器在条件满足时记录那一刻的寄存器值这种方式比停住看变量高效得多。数据观察点Watchpoint更重要它可以设置在某个内存地址上只要该地址的内容被修改调试器就暂停。经典案例是排查数组越界写坏相邻变量的bug你可以在某个被写坏的变量地址上设观察点跑全速程序会在越界写入的瞬间停下调用栈窗口会直接告诉你凶手是谁。Cortex-M的DWT单元通常提供4个观察点。如果你在RAM区设了观察点但一直没触发先确认写入是否经过DMA——DMA写内存是不经过CPU的普通硬件观察点拦不住。5.2 RTOS感知调试任务列表、死锁与HardFault栈回溯用FreeRTOS/RT-Thread这类系统时调试界面不能只停留在“看裸机寄存器”。Keil的RTX/FREE RTOS调试插件、VS Code的Cortex-Debug都支持展示当前任务列表。任务列表的意义在于死锁往往表现为整个系统卡死但你不知道卡在哪。打开任务列表窗口看哪些任务处于Running、Ready、Blocked状态如果所有任务都Blocked了基本就是死锁。再看Blocked在什么事件上就能快速定位是谁没释放信号量。HardFault是嵌入式老兵的噩梦。遇到HardFault后先看LR寄存器如果LR的值是0xFFFFFFF9说明是在线程模式下跑Fault发生在任务上下文如果是0xFFFFFFED则是在中断上下文。然后从Call Stack窗口看调用栈直接定位到出错的源文件和行号。如果栈被破坏了Call Stack不可信那就退而求其次查看xPSR里的异常类型位判断是总线错误、用法错误还是内存管理错误对应的CFSR寄存器里会有更详细的错误原因比如是否非法地址、是否除零。我踩过一个大坑HardFault后一看Call Stack窗口地址全是“0xDEADBEEF”这种情况说明栈已经被写得面目全非。后来我养成了习惯在开发阶段开启MPU对栈溢出进行监控并把这个思路写进了项目规范。5.3 时序类问题调试器看CPU逻辑分析仪看物理世界调试器能告诉你“CPU在执行什么”但不会告诉你“引脚上的电平到底是什么时候跳变的”。当你遇到SPI读写偶发失败、I2C从机不响应这类时序问题时光靠调试器断点是不能从根本上搞明白的你需要逻辑分析仪或示波器。我在调一个UART通信时调试器看到的串口寄存器值完全正常但接收端就是收不到数据。后来用逻辑分析仪抓了TX引脚波形发现波特率偏差高达3.8%原因是外部晶振频率和库函数里的HSE_VALUE不一致。这个bug纯粹靠调试器根本看不出来必须去看物理波形。所以我的工具箱是分层的调试器负责CPU视角逻辑分析仪负责时序视角万用表负责电气视角。三者缺一不可。5.4 别迷信调试器日志和断言在长夜里更可靠调试器并不是万能的特别是在中断风暴、DMA触发频繁、CPU高速运转的场景下每次断点都会改变程序的运行画像。这个时候一个完善的自研日志系统和断言机制往往比调试器更可靠。我习惯在固件里留一个环形日志缓冲区将关键运行点打点程序崩溃或异常复位后第一个启动代码就把日志通过串口dump出来。这样即使客户现场没有调试器我也能知道系统最后在做什么。配合断言能自动定位到“不变量被破坏”的源头。这算是一个长期主义的高性价比投资宁可多写几个日志点也不要让自己陷入“现场只有一块板子、没有调试器”的绝望境地。6. 从个人开发到量产交付把烧录下载这道工序工程化6.1 命令行烧录让“刷固件”成为可重复的一行命令手工点IDE按钮适合单板调试但在自动化、批量刷机、CI/CD场景里完全不适用。命令行烧录工具是必经之路。J-Link的命令行方式是这样的JLink.exe -device STM32F407VG -if SWD -speed 4000 -CommanderScript flash.jlink其中flash.jlink的内容大概是loadbin app.bin 0x08000000 r g q意思依次是把app.bin加载到0x08000000、复位、运行、退出。这套脚本可以很轻松地放进批处理或Makefile里。如果你使用pyOCD则一行搞定pyocd flash -t stm32f407vg -f 4000 app.binSTM32官方工具STM32_Programmer_CLI同样好用STM32_Programmer_CLI -c portSWD -w app.bin -v -rst命令行烧录的最大价值不是“省鼠标”而是可重复、可审计、可嵌入流水线。6.2 自动化冒烟测试烧录后自动验证串口输出项目稍微正规化之后我不会只靠“人工点Download”来验证固件。典型的做法是构建一个冒烟测试脚本编译完固件后自动烧录到测试板等待系统启动然后读取串口日志断言关键启动信息。一套非常简单但实用的Python脚本思路如下用pyserial打开串口通过subprocess调用pyOCD烧录然后等待日志中出现自定义的启动标志字符串。如果超时没等到测试即失败。这个脚本可以挂在GitLab CI里每次提交都会自动对测试板执行烧录和基础功能验证。我个人把这道工序称为“给固件打预防针”它能很有效地拦截那些编译能过但一上板就挂的问题尤其是启动流程、硬件初始化、Flash映射这类低级错误。6.3 量产烧录从J-Link到离线烧录器与工装量产阶段和开发阶段完全是两回事。产线上不能要求每个工位都配一个工程师专用的J-Link也不能让工人每次烧录前都检查一个“SW Device”窗口。大一点的量产系统一般用离线烧录器或PC端烧录工装。离线烧录器支持预先烧好一份母片然后脱机对目标板批量烧录支持一拖多、序列号自动递增、MAC地址写入、UID绑定等操作。量产烧录工程里需要额外关注几件事烧录后是否要配置RDP防止固件被读出是否要写入产品的唯一序列号用于后续追溯烧录工位是否需要防错机制防止刷错固件版本。简而言之量产的烧录下载不再是“开发者的工具”而是“生产流程的一环”。从这一步开始对烧录工具的要求从带宽和调试体验转移到了可追溯性和一致性上。6.4 固件版本管理与OTA的底线思维开发阶段的烧录是“开发者往芯片里写代码”而产品阶段的烧录是“用户从远端拿新固件”。这两者本质相同但工程基础设施完全不同。我的经验是提前建立固件版本管理规范每个发布版本用Git tag唯一标识编译产物bin/hex连同.commit哈希一起归档记录CRC或SHA256。拿开发板调新功能时烧录的固件必须是某个可复现版本的产物不要随手编译一个就上板。很多人“明明上次调好了这次重新编译后行为变了”就是因为烧录的固件和源码版本没有对应关系。OTA升级也需要基本的分区设计Bootloader区、App区、参数区、暂存区。升级流程本质上还是一个烧录下载过程只不过目标不再是调试器直连而是通过通信总线把固件先传输到暂存区再通过Bootloader把固件写入App区。这种思路既有开发期烧录的影子又多了很多可靠性设计。嵌入式软件开发做到最后你会发现烧录下载和仿真调试不是“点按钮”这种小事它们是从单板开发、到自动化测试、再到量产交付全链路里贯穿始终的方法论。调试器是你和硬件世界之间的“最直接的眼睛”烧录工具则是你让代码落地的“手”把这两样家伙吃透能让你在嵌入式这条路上少走很多弯路。最后再分享一个我自己的习惯每次拿到新开发板第一件事不是烧自己的程序而是先备份出厂固件。很多板子的出厂程序里带了自检、bootloader甚至官方校准数据贪图方便直接覆盖之后再想恢复就得费一番周折。备份一次不花几分钟但关键时刻能救命。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进