ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Jlink读MCU固件全流程:从SWD连接到Flash读取的实战指南

Jlink读MCU固件全流程:从SWD连接到Flash读取的实战指南 接到过不少类似的活儿板子放在面前没有源码只有一颗MCU客户要求把里面的固件完整读出来备份或者拿去做逆向分析。手头最顺手的工具就是Jlink加J-Flash本来以为是个简单操作真走一遍才发现硬件连接、芯片型号选择、读取起始地址、Flash保护状态任何一个环节卡住都读不出东西来。这篇把我完整踩过的流程和坑都记下来给同样需要干这活的朋友当个参考。先说清楚适用范围本文的操作对象是Cortex-M系列MCU也就是STM32、GD32、NXP LPC、瑞萨RA等主流ARM内核单片机。这类芯片绝大多数支持SWD接口只需要四根线就能连上Jlink读取速度通常能跑到几兆赫兹相比老式JTAG动辄十几根线要方便太多。如果你的板子是51、AVR、PIC这类非ARM架构或者是完全加密并烧断调试口的芯片本文的流程只做参考后面会单独说明限制。1. 读固件前的几个概念想清楚再动手1.1 固件到底存在哪里我们说的MCU固件本质上就是一段烧录在芯片内部存储器里的二进制机器码。ARM内核的MCU一般有两类存储介质第一种是Flash也就是你平时用J-Flash或者Keil下载程序时写进去的那种存储器掉电不丢失容量从几十KB到几MB不等第二种是OTP ROM或者ROM Bootloader区域这类存储区域出厂时固化或者只能写一次正常读固件不会涉及。面向绝大多数场景我们读的就是Flash区域。以STM32F103为例它的Flash起始地址是0x08000000容量从64KB到512KB不等NXP的LPC1768则是从0x00000000开始Flash大小是512KB。这个起始地址和容量非常关键在J-Flash里选错起始地址或者容量配置错读出来的固件要么缺失要么全是空数据。后面我会详细讲怎么确认这两个参数。1.2 芯片的读保护机制读书人绕不开的坎读固件这件事最怕遇到的就是芯片开了读保护。ARM Cortex-M芯片几乎都有一个叫读保护RDPRead Protection的机制不同厂商叫法略有不同STM32叫RDPGD32也有对应的类似功能NXP叫Code Read ProtectionCRP。RDP通常分成几个等级Level 0是无保护可以随便读Level 1是禁止通过调试接口直接读Flash内容Level 2直接永久禁用调试接口连恢复的机会都没有。关键点来了如果芯片处于Level 0你用J-Flash直接读就行完全没问题。如果处于Level 1J-Flash也能连上芯片但读取Flash时会返回错误或者读出的全是F。这时如果确实要读固件内容只能做芯片全擦除来解除保护代价是固件本身也没了。所以我的建议是拿到一块板子先别急着读固件先用Jlink Commander或者J-Flash里读一下芯片的ID和选项字节确认保护状态再动手省得白忙活。1.3 Jlink能做什么不能做什么Jlink本质上是一个JTAG/SWD调试探针。它通过调试接口访问芯片内部的AHB-AP总线进而访问Flash控制器和Flash存储阵列。换句话说Jlink读固件的原理就是借助芯片自己的调试硬件接口来读取存储器内容不是物理直接去抠Flash。这个特性决定了Jlink读固件有边界如果芯片把调试接口物理锁死RDP Level 2或者完全没有调试接口某些超低端MCU型号没有SWD只有厂家专用烧录口Jlink就无能为力。另外Jlink通过SWD读取的速度受限于线材质量、目标板布局和芯片型号不是想跑多快就跑多快实践中我通常先选4000kHz连不上再降速非常稳。提示读固件前先自查合法性。只能读自己拥有或者被合法授权读取的设备不要拿这套流程去做侵犯他人知识产权的操作。这个边界希望大家心里有数。2. 硬件准备与连接别在这翻车2.1 Jlink版本选择与固件状态市面上的Jlink种类很多正版、国内正版授权的、山寨的、各种V8/V9/V10/V11性能区别很大。读取固件这种操作其实并不挑版本只要是Jlink ARM都能干这个活但如果你的Jlink是那种几十年老古董的V8或者克隆版建议先确认它的固件状态正常。很多克隆版Jlink有个毛病连接目标芯片时报错“The connected probe is a J-Link clone”虽然通过一系列操作可以绕过但我个人的经验是干正经事别用不靠谱的工具花两三百块钱买个入门级正版或者授权版一次到位。对大多数MCU来说J-Link BASE、J-Link PLUS、J-Link EDU这些型号都够用。我用的是J-Link PLUSSWD模式下稳定跑到4000kHz没问题。如果你平时还要配合调试复杂的Cortex-A芯片或者FPGA那得上更高端的型号但那不是本文讨论的范围。2.2 接口定义与接线方法Jlink的接口定义是标准化的20-pin的JTAG排针里SWD模式只用到几个关键引脚。实用的引脚如下表Jlink引脚信号名SWD模式下对应功能连接到目标板1VTref参考电压检测目标板3.3V/1.8V供电端7SWDIO数据输入输出目标板SWDIO9SWCLK时钟信号目标板SWCLK4GND地目标板GND19RESET复位信号目标板NRST建议接如果你手头的是那种没有20-pin全排针的Jlink板上自带杜邦线接口那你对照丝印把SWDIO、SWCLK、GND、VCC、RESET对应接上就行。这里有一个关键细节VTref就是Jlink读取目标板电平的参考电压它同时用来判断目标板是否供电。如果VTref没接或者电压异常Jlink会报错“Cannot connect to target”而且此时目标板的参考电压过低会让Jlink认为芯片没有上电。所以不管你的目标板是3.3V还是5V供电VTref必须接到目标板的电源正极。RESET这根线经常被忽略。很多旧代码和教程说SWD只需三根线SWDIO、SWCLK、GND就能连这个说法在多数情况下成立但在目标板固件把SWD引脚复用为GPIO、或者芯片处于休眠状态时没有RESET线会导致无法连接。强烈建议一开始就把RESET接上它的另一个作用是当芯片死机或者看门狗疯狂复位时J-Flash可以通过硬件复位来控制芯片进入调试模式。2.3 供电问题经常让人头大Jlink的20-pin接口上通常会有一个VCC pin有的叫VTref或Target Supply但它能不能给目标板供电不同型号策略不一样。老式的J-Link V9在板子上有一个跳线或者开关可以设置为“目标板由Jlink供电”V10之后很多型号这个功能被去掉了因为官方更推荐让目标板用自己的电源供电。我的经验是读固件这种操作必须给目标板独立供电不要让Jlink去带板子。原因很简单很多MCU板子尤其是传感器板、电机驱动板上除了MCU还有外设一块电机驱动板启动瞬间电流几百毫安让Jlink那点输出能力去带瞬间电压跌落就可能导致MCU掉电重启调试连接直接断掉。接法上目标板供电比如USB口或电源适配器和Jlink的GND必须共地电源和Jlink的地都接到目标板的GND网络上不能有压差。2.4 引脚被复用的情况怎么办热词里有一个搜索特别多“mcu没有usb差分信号数据引脚怎么办”。这个其实是两类问题一类是MCU引脚本身没有USB差分信号能力却想用USB功能另一类是SWD调试引脚被固件复用成普通IO了。和读固件相关的是第二种。SWD引脚PA13/PA14STM32或者对应芯片的SWDIO/SWCLK在复位之后默认是调试功能没有特殊设置前是可以连接的。但一旦固件跑起来并且把这两个引脚配置成了GPIO输出或者输入模式Jlink再连就会出现“Cannot connect to target”或者“DLL error”。遇到这种情况处理方法就是硬件复位时序先按住目标板的复位键不放打开J-Flash的连接在连接命令发出的瞬间松开复位键。更稳定的办法是接上RESET线之后在J-Flash里勾选“Connect under Reset”工具会强制芯片保持复位状态发起调试连接在芯片启动固件之前抓住调试接口。这是对付固件里禁用调试口最有效的常规手段。3. 软件环境搭建驱动和工具版本匹配是门学问3.1 Jlink驱动安装软件方面首先需要安装Jlink驱动官方名称叫“J-Link Software and Documentation Pack”。下载时注意一定要到Jlink官网www.segger.com下载最新版不要从乱七八糟的下载站拿。Jlink驱动自带USB驱动、J-Link Commander、J-Flash等全套工具装一个驱动包基本就齐了。装驱动时有个容易踩的坑如果系统里以前装过旧版驱动或者盗版Jlink的驱动新驱动安装后可能会提示设备无法识别。Windows下需要进设备管理器把未知的“J-Link”设备卸载勾选“删除此设备的驱动程序软件”然后再插上Jlink让系统重新安装驱动。另外新版Jlink驱动有个“向后兼容”配置它会检查Jlink固件版本如果固件太老会提示升级。这里我要提醒一句盗版或者克隆版Jlink千万别乱升级固件一升级可能就直接变砖了正版和授权版随便升升坏了还能靠bootloader恢复。3.2 J-Flash版本与界面J-Flash装好之后打开第一眼是欢迎界面让你选择工作模式一般选“Create new project”。它的主界面分成三大块左侧是项目树显示目标芯片型号、接口类型、连接速度这些配置中间主区域是Flash数据缓冲区读取出来的固件会以hex/bin形式显示在这里下方是日志输出窗口连接过程、命令交互、错误信息都会打在这里。日志窗口非常重要。我肉眼判断连接是否正常很多时候不看弹窗提示直接看日志输出。比如正常的连接日志会打印出芯片ID、Flash大小、制造商信息等。一旦有错误日志里会给出比较明确的错误码排查起来事半功倍。3.3 驱动和J-Flash版本最好配套我见过不少人装了最新版J-Flash但Jlink是好几年前的老固件连接目标芯片时报“Communication error”或者干脆弹窗让升级固件。实际上Jlink驱动软件和Jlink固件是配套的SEGGER在新版软件里会要求新固件才支持某些功能。对于老设备官方也保留了兼容性选项。在“J-Link Commander”或者安装目录下的“JLinkConfig”工具里你可以配置“Use legacy driver for old J-Links”之类的选项。我自己遇到老克隆版Jlink没法用新版软件时会直接把软件回退到旧版本比如J-Link Software V6.40配合老Jlink用起来顺滑得多。这里没有“越新越好”的说法工具匹配才最重要。4. J-Flash读取固件完整实操跟着点就行4.1 新建项目并选择芯片型号打开J-Flash点击菜单栏“File - New project”软件会弹出新建项目向导第一步就是选择目标设备类型。这里通过“Select Device”按钮进入芯片选择界面界面里可以按厂商过滤也可以直接搜索关键词。比如STM32F103C8T6你在搜索框输入“STM32F103C8”下面会列出一堆带不同后缀的型号。选择时注意看后面的Flash容量标注比如64KB或128KB选错容量会直接影响后面读取长度和地址范围。如果列表里找不到你的芯片还有一个通用办法选择同厂商同Flash控制器的替代型号或者选择Generic类型的Cortex-M3/M4设备但那样J-Flash不会知道Flash的具体参数需要手动设置新手不建议这么干。选完型号后调整连接参数接口类型选SWD速度默认可能是4000kHz或者5000kHz。如果你用的是线材比较差的杜邦线建议手动降到2000kHz或者1000kHz如果板子布线特别烂或者线序很长甚至可以降到500kHz稳定性远比速度重要。4.2 连接目标芯片日志是唯一真相菜单栏“Target - Connect”软件就开始向目标芯片发起连接请求。成功的标志有两个主界面底部状态栏变成绿色或显示Connected同时日志窗口输出目标芯片的IDCODE和Flash信息。我第一次顺利连接时日志大概是这样的Connecting to target via SWD Found SW-DP with ID 0x1BA01477 Found Cortex-M4 r0p1, Little endian. FPUnit: 4 code (BP) slots and 2 literal slots CoreSight components: ROMTbl[0] E00FF000 ... FlashID 0x001C0040FlashID这行信息很关键它表示Jlink识别到了Flash的制造商ID和设备ID只有FlashID正确识别后面读写Flash才有基础。如果连接失败日志会给出具体原因。最常见的报错就几种目标板没上电、SWD接线不对、芯片型号选错、芯片被读保护锁了。连接成功之后可以先做一步验证操作点击“Target - Read Back - Read ID”或者直接查看“MCB”和“Device”信息确认芯片的IDCODE和你选择的型号匹配。这一步虽然不产生固件内容但能避免你对着一个错误型号反复折腾。4.3 设置读取地址范围这是最容易忽略的一步在J-Flash主界面Flash数据缓冲区默认显示的地址一般就是芯片Flash的起始地址。但读取之前必须要做一件事确认“Options - Project Settings”里的Flash配置看Base Addr是否为该芯片的Flash起始地址。还是以STM32F103为例Flash起始地址必须是0x08000000容量如果选64KB那地址范围就是0x08000000到0x0800FFFF。如果你在缓冲区里看到Flash起始还是0x00000000就说明设备配置有问题必须修正后重新连接。这一步出错的结果很直观你信心满满地读了一个“固件”打开bin文件一看全是F或者内容错位根本没法用。所以读取前花30秒核对地址范围和容量能避免后续浪费大量时间。4.4 执行读操作保存固件为bin或hex连接正常、配置无误后点击“Target - Read Back - Read Entire Flash Memory”J-Flash就会把整个Flash内容读入中间的缓冲区。这一步的速度取决于Flash容量、SWD速度和ESD保护情况64KB的Cortex-M0片子通常一两秒钟就搞定大容量的比如1MB的Cortex-M4虽然耗时久一些也就十几秒。读完后缓冲区中显示的是一个个地址行和对应的十六进制数据。这时候要检查一下数据分布如果一大片区域全是0xFFFFFFFF说明这部分是空白区没有程序正常的如果起始地址处就已经全是0xFF那肯定有问题可能是读保护导致读出了无效数据也可能是读取地址错了。保存固件时点击“File - Save Data File As”在保存对话框里选择类型。这里有两种选择bin格式或hex格式。bin文件是纯粹的二进制镜像起始地址隐含在内部你必须在保存前知道或者记下读取起始地址hex文件则自带地址信息ExternTool或者后续烧录更方便。我个人保存时习惯两个都存先存一个bin“STM32F103_backup.bin”再存一个hex“STM32F103_backup.hex”。bin用于直接对比、分析、拆解hex用于重新烧录时不怕地址错。4.5 读取校验防止读到一份坏数据固件读出后如果不做校验等于白忙。最简单的校验方法就是读取两遍然后比对哈希值。J-Flash本身没有直接提供哈希比对功能但你可以在保存完第一遍后重新读第二遍分别保存为两个bin文件然后用Beyond Compare或者开发用的十六进制编辑器来对比内容。更专业的做法是直接对两遍读取结果在命令行做MD5或SHA256比对md5sum backup_first.bin backup_second.bin如果两个bin文件的哈希值完全一致那么基本可以确定读取过程稳定可靠如果哈希不一致第一件事就是降速重读。我遇到过一次2000kHz读一遍和1000kHz读一遍结果不一致最后排查发现是一根杜邦线接触不良换线之后问题消失。注意如果芯片已经被读保护锁定连接时Jlink会提示“Cannot read protected target”读取操作会报错或者读出一片0xFF。此时想读固件必须先解锁但解锁的唯一方式是全片擦除固件也会一并消失。所以拿板子之前先问清楚这块芯片有没有开锁免得做无用功。5. 读取行为分析这些数据说明了什么5.1 从固件内容反推程序结构当你把固件成功读出来之后光有一堆bin文件还不够。很多情况下我们读固件是为了确认程序版本、查找关键配置、或者分析某段逻辑。我这里简单介绍一下从固件内容反推程序结构的技巧。首先是确认中断向量表。ARM Cortex-M的启动文件放在Flash起始位置它的第一个32位值地址偏移0x000是初始栈指针第二个32位值偏移0x004是复位中断处理函数地址。用十六进制编辑器打开bin文件前4个字节通常是类似0x20000800这种值这说明程序从SRAM起始处开始用栈紧接着的4个字节是复位向量地址一般落在Flash区间的后半段。其次是查找字符串。固件里的字符串常量比如日志输出、版本号、菜单文本一般以ASCII码形式连续存储在Flash里。用字符串提取工具配合bin文件扫描经常能发现不少有价值的信息。比如我在分析一台设备固件时扫描字符串直接看到了配置服务器的地址和固件版本号这对后续升级和排查问题非常有帮助。然后是函数入口点分析。更深入的分析需要用到反汇编工具比如Ghidra、IDA Pro或者针对Cortex-M的专用分析脚本。这些工具可以把bin文件解析成汇编指令标注出函数边界和调用关系。不过这一步依赖前一步的起始地址正确如果你保存bin时不知道起始地址反汇编时也要手动指定这一步错了后面全乱。5.2 固件合法性自查读自己设备固件之前可以先做个基本功确认这个固件是不是真的完整。比如一个正常的STM32程序其栈指针值通常是0x2000xxxxRAM区复位向量落在Flash区间。如果你发现栈指针是0x00000000或者复位向量直接是0xFFFFFFFF那读出来的多半是垃圾数据不用浪费时间分析了。另外可以检查bin文件大小是否和Flash容量有对应关系。一个64KB的芯片如果bin文件大小远小于64KB说明程序本身就很小如果读出来恰好是64KB但后面很多是0xFF说明程序实际占用明显小于Flash容量这是编译时常见情况不能凭大小判断固件缺陷。5.3 固件加密与防护的一种视角很多人在搜索“固件加密”、“固件安全”这其实对着两个方向一是开发者如何防止自己的固件被读走二是分析者如何看待固件的防护手段。从我们做读取的视角来看开发者最高效的防线是开启读保护和代码区加密。而分析者的思路则是寻找调试接口是否还在、引导程序是否可直接交互、以及是否因为没有开保护而暴露了关键逻辑。我个人早几年的习惯是先把读保护Level 1开起来这样既能防住绝大多数新手读取尝试又保留了日后通过全片擦除重新烧录的余地。如果是有保密需求的产品还可以配合代码混淆、校验和验证等方式。不过必须强调任何加密手段都是把普通用户挡在门外真正的攻击者面前没有绝对安全。这里不展开讲对抗性内容只想提醒读完本文的人你用这套流程能读出来固件同样意味着别人也能读出来。产品上市前该做的保护措施还是要做。至少把调试口引脚禁用、开RDP Level 1这两步成本极低、防得掉80%的业余尝试。6. 实战中常见问题与排查技巧6.1 识别不到单片机的几个排查方向Jlink识别不到单片机基本可以沿着下面的排查思路走先看硬件再看连接最后看配置。硬件层面目标板是否上电VTref是否检测到目标电压Jlink的USB连接是否正常。连接层面SWDIO和SWCLK是否接反GND是否共地线材是否过长或接触不良。配置层面芯片是不是被RDP锁了接口模式选的是SWD还是JTAG但线序接成了JTAG速度是不是设得太高。我实际遇到过最离奇的一次芯片型号选成了STM32F103RC512KB而实际板子上是STM32F103C864KBJlink连上后提示“Cannot connect to target”。原因是我在J-Flash里按错了容量导致Jlink初始化Flash控制器时失败。换成C8型号后一次成功。所以识别不到时别急着怀疑硬件先从软件配置排查。6.2 连接失败常见报错及对策报错信息日志中的典型片断可能原因对策No target connected / Cant find SWD device目标板没上电、接线错误检查供电、VTref电压、SWDIO/SWCLK接线SWD Communication failure线材质量差、速度太高降速到1000kHz或500kHz换短线Cannot connect to target under reset复位引脚没接或者固件禁用调试口接上RESET线勾选Connect under ResetTarget has read protection enabledRDP Level 1/2Level 1可考虑全片擦除Level 2则基本放弃DLL error / Dll version mismatchJlink驱动和软件版本不匹配统一Jlink软件版本或换旧版驱动6.3 升级固件后Jlink变砖了怎么办克隆版Jlink最怕的就是升级固件。官方包里有“J-Link Updater”工具如果你点了一下然后发现Jlink的灯不亮了设备也认不出来那大概率是固件写入失败或者固件不合法。正版Jlink有bootloader保护重新跑一遍Updater能恢复克隆版就麻烦一点。我见过的能救回来的方法拆壳找到Jlink主板上的SWD调试口用另一块好的Jlink去连它重新烧写bootloader和固件文件。但这个操作对普通用户门槛很高动手能力不够的人大概率直接换新。所以这里再次强调盗版Jlink不要升级固件不要升级固件不要升级固件。能用就行。6.4 读取固件之后怎么快速验证可用性读完固件后不是马上交付就完事我强烈建议做一次完整的“连接烧录验证”。做法是把读出来的bin/hex文件用J-Flash重新烧录到一块同样型号且已清空的芯片里然后上电看功能是否正常。如果正常说明固件完整可用如果跑不起来十有八九是读取时丢数据、地址配置错了或者固件本身有依赖外部Flash等其他因素。一次真实经历我从一块跑着采集功能的板子上读出了固件重新烧录到新片子发现功能正常但过一段时间就复位。排查到最后发现是固件初始化时检查了内部RTC备份寄存器的某个标志位没有那个标志就进入自复位逻辑。这类固件的可执行内容没问题但依赖上下文状态不是读取失败导致的。所以验证时要把“固件完整”和“设备能跑”分开理解。7. 工具链扩展与后续操作建议7.1 不止J-FlashJlink全家桶还有其他工具读固件只是Jlink能力的一小部分JLink Commander和JLink RTT在实际项目里也很有用。JLink Commander是命令行工具适合脚本化操作。比如我想快速读取一段Flash区间的数据可以用如下命令JLinkExe -device STM32F103C8 -if SWD -speed 4000 -autoconnect 1进入命令行后输入savebin C:\backup_fw.bin 0x08000000 0x10000这条命令的意思是把从0x08000000开始、长度0x1000064KB的Flash内容保存成bin文件。相比打开J-Flash一顿点命令行的优势是快、可重复、适合量产环境中快速备份。JLink RTT则是调试利器。如果目标芯片运行了启用RTT的程序你可以用Jlink实时读取日志输出还能写交互命令。对固件分析场景来说RTT能给出一扇观察固件运行时行为的窗口有时候比盲读Flash更有价值。7.2 手头没有原厂芯片资料时怎么办偶尔会遇到冷门芯片J-Flash的设备选择列表翻遍了也没有。这种情况可以试试两个思路思路一用同公司同系列的近似型号。很多芯片的Flash控制器是通用的比如ST公司的F0系列、F1系列、F3系列在Jlink的支持库里经常共用一套驱动选个引脚数接近的同型号通常能连上。思路二使用“Generic”设备类型。J-Flash允许你手动配置设备参数包括内核类型Cortex-M0/M3/M4、Flash起始地址、擦除命令等。这需要从芯片数据手册里查Flash寄存器地址和擦除算法门槛不低。一般我优先建议找官方支持列表实在没辙才考虑Generic方案。7.3 备份固件这件事建议做成常态对于还在维护的存量设备我特别建议项目组把“固件备份”纳入开发流程常态。具体做法就是每次发布量产版本前用本文的流程把编译生成的hex文件通过J-Flash烧进去再用J-Flash读出来保存到备份目录。这样既能验证烧录是否可靠也能在源码意外丢失时多一层保险。我经历过一次真实事故客户那边源码放在共享服务器上结果某天服务器磁盘坏了备份也没恢复成功。后来对方靠产品线上一台还没交付的样机用它当母版读出了固件拿我的bin文件配了一个烧录工装硬是把产品线救了回来。代码丢了但固件还在这就是读固件技能最值得的地方。7.4 读固件之后的进一步分析路线拿到固件后的下一步按需求不同有几种路线只想做备份和再烧录保存成hex/bin、记录芯片型号和起始地址写进项目文档就行。要做固件对比比如确认两个设备的版本差把两边的bin文件做二进制diff通常是拿Beyond Compare比hex内容先肉眼查看差异分布再看差异集中在哪段地址。要做功能分析建议先用binwalk等工具做字符串和文件结构扫描再加载进Ghidra做反汇编结合硬件原理图定位关键逻辑函数。这条路线每一步都有大量细节我也是踩了几年坑才摸顺。以后单独写一篇。写在最后一次摸索的真实体会Jlink读MCU固件这件事操作本身不复杂真正考验人的是对芯片、调试接口和保护机制的理解。J-Flash按钮就那几个但为什么选这个型号、为什么设这个地址、为什么降速重试背后全是一环扣一环的硬件原理。我一开始也是照着网上教程一股脑点读了无数遍空数据才意识到起始地址没设对。后来养成了固件读出来先看栈指针和向量表的习惯又养成了同一块芯片至少读两遍验证的强迫症才慢慢不再翻车。再分享一个我经常用的小技巧读固件前先拍一张板子的高清照片把芯片丝印、外部Flash型号、PCB版本都清清楚楚留下来。这个习惯帮我解决过好多次“过了几个月拿出来看忘了当初读的是哪块板子”的尴尬。做硬件这行细节和记录往往比工具本身更值钱。
返回列表