SPI NOR Flash 烧录「找不到型号」解决方案
从识别机制到自建通用烧录器的六条路径 · 编制日 2026-08-29 | 编制日期 2026-09-21 | 版本号 Rev 1.0
量产或维修时,编程器软件里搜不到手上这颗 SPI NOR 的型号,出不了片、刷不了固件。文档先把「编程器认型号」这件事拆到底层机制,再给出六条按耗时从短到长排列的解决路径,每条都落到可执行的具体操作:改哪个配置文件、填哪些字段、发什么命令、怎么判断成功。
型号只是参数表的别名。只要能把这颗芯片的容量、擦除粒度与命令码、页编程、地址宽度、状态寄存器与保护/Quad 使能位这五组参数搞对,用什么型号名去烧都无所谓——这就是所有替代方案的共同原理。
1. 问题本质:编程器到底在认什么
把「认型号」还原成「查参数表」,问题就从「库里没有这个名」变成了「我能不能自己给出这组参数」。这一章是后面所有路径的共同基础。
1.1 型号库只是一张 ID → 参数的映射表
编程器软件里那个几万条的器件列表,本质是一张二维表:左边是 JEDEC ID,右边挂着这颗芯片的操作参数与时序。选中型号,软件只是把右边那组参数读出来,按它去发 SPI 命令。型号字符串本身不参与通信。所以「找不到型号」的真实含义是:软件手里没有一个已知 ID 能对上这颗芯片,因此没人告诉它该发什么命令。
1.2 编程器真正需要的那组参数
无论哪条路径,最终都要落到下面 8 项。做兼容替换或手填参数时,就是逐项确认这 8 项。
| # | 参数 | 典型值 / 命令 | 填错会怎样 |
|---|---|---|---|
| 1 | 总容量 | 512Kb – 2Gb(64KB – 256MB) | 读写出界;校验报地址越界或尾部数据丢失 |
| 2 | 页大小 Page | 256 B(少数 512 B / AAI 字节写) | 跨页回绕:末尾数据覆盖本页开头而不报错 |
| 3 | 擦除粒度与命令 | 4KB / 0x20,32KB / 0x52,64KB / 0xD8 | 命令不支持则擦不掉;粒度填大则误擦相邻数据 |
| 4 | 页编程命令 | 0x02(Dual 0xA2、Quad 0x32) | 写入无响应,校验全 0xFF 或全 0x00 |
| 5 | 地址宽度 | ≤16MB 用 3 字节;>16MB 需 4 字节 | 高地址区读写回绕到低地址,固件跑飞 |
| 6 | 读命令 + dummy 周期 | 0x03 / 0x0B(8 dummy)/ 0xEB | 读回数据与写入不符,但芯片内容可能其实是对的 |
| 7 | 状态寄存器与保护位 | SR1/SR2/SR3,BP[3:0]、CMP、TB、SEC、SRP | 写保护没解除 → 写入静默失败或报保护错误 |
| 8 | 特殊解锁 / 时序 | WEL(0x06)、4B 模式 0xB7/0xE9、QE 位、复位 0x66/0x99 | 卡在某一步不动;或 Quad 模式读不到数据 |
说明:上表为通用 NOR 的通行约定,具体以器件 datasheet 为准;命令码见附录 A。
1.3 JEDEC ID:三字节里装了什么
主机发 0x9F(RDID)后,芯片回 3 字节:厂商 ID + 存储器类型 + 容量码。这是所有编程器识别的起点,也是手工建型号时必须填准的字段。
| 字节 | 含义 | 示例(W25Q128JV) | 说明 |
|---|---|---|---|
| 第 1 字节 | Manufacturer ID 厂商 | 0xEF(Winbond) | 由 JEDEC 分配;部分国产厂商存在复用/自定义,见附录 B |
| 第 2 字节 | Memory Type 类型 | 0x40 | 常编码了电压/系列/代次,不同厂商定义不一 |
| 第 3 字节 | Capacity 容量码 | 0x18 → 16 MB | 近似 2^n 关系,但不能反推,必须查表 |
不用管软件认不认:把编程器切到 GENERIC / SPI 通用识别模式读一次 ID,或用逻辑分析仪抓一次 0x9F 的响应。拿到 3 字节 ID 后,再去比对手册里的 ID 表,就能从印字模糊的料片反推出真实型号。
1.4 SFDP:芯片自己带的一张参数表
JEDEC 的 JESD216 标准规定:现代 SPI NOR 内部固化了一张只读参数表,叫 SFDP(Serial Flash Discoverable Parameters),用命令 0x5A 读取(3 字节地址 + 8 个 dummy 时钟,之后连续读出)。芯片出厂时就把容量、页大小、支持哪几种擦除命令、每种擦多大写在这张表里了。
这是最理想的情况:参数由芯片自报,主机压根不需要认识这颗料。flashrom、NXP LinkServer 的通用 flash 驱动、RT-Thread 的 SFUD 走的都是这条路。
遇到未知型号,第一步永远是先试 SFDP:能读出来就直接用,不用找替代型号、不用改配置文件。只有 SFDP 读不出来时才往后走其他路径。
1.5 找不到型号的四种真实原因
| 原因 | 典型表现 | 对应路径 |
|---|---|---|
| 软件版本太旧 | 同厂同系列的新料号搜不到,老料号能搜到 | 升级软件/器件库;P1 |
| 国产或小厂新料 | 印字是陌生品牌(XTX、PUYA、XM25、PY25、HK25Q 等) | P1 / P2 / P5 |
| 印字不是型号 | 丝印只有 logo + 两三码缩写,查不到对应 datasheet | 先读 ID 反查;P3 |
| ID 冲突或厂商自定义 | 识别出型号但容量不对、擦除报错 | P2 手填 ID 与参数;P5 |
2. 六条解决路径总览
按「多久能出片」排序。前两条通常当天就能解决,第三条是原理上最干净的,第四条绕过编程器,第五条是长期兜底,第六条是彻底但慢。
sf 命令、或调试器(J-Link/OpenOCD)
把固件写进 flash。适用于芯片已焊在板上且不打算拆的场景。| 路径 | 适用场景 | 前置条件 | 出片耗时 | 长期可用性 |
|---|---|---|---|---|
| P1 兼容顶替 | 有 datasheet,能找到参数相同型号 | datasheet + 库里有相近型号 | ★ 最快 | 中 |
| P2 手动建型号 | 没有完全一致的,但算法可挂靠 | 知道用哪颗型号的算法 | ★★ | 中高 |
| P3 SFDP | 2015 年后的主流料,支持 JESD216 | 工具支持 SFDP(flashrom 等) | ★ 最快 | 高 |
| P4 主控侧烧 | 已贴片、有串口/USB/JTAG 下载口 | bootloader 可用 | ★★★ | 高 |
| P5 自建烧录器 | 型号杂、经常换料、需要可控 | 会接线 + 命令行基础 | ★★★★ | 最高 |
| P6 厂商加型号 | 量产定型料号 | 能联系到厂商/FAE | ★★★★★ | 高 |
3. 路径一:同参数兼容型号顶替
成本最低、见效最快。核心动作是「比对」而不是「猜」——逐项核对 5 组关键参数,确认无误后用库里已有的型号名去烧。
3.1 五组必查参数与容忍度
下表是判定「能不能顶替」的依据。左列任一项不满足,就不许顶替;右列不满足,必须回到 datasheet 确认或换路径。
| 参数组 | 必须一致(不可容忍差异) | 可容忍的差异 | 去 datasheet 哪里查 |
|---|---|---|---|
| 容量 | 总容量必须相同(或选库里更大的同系列并手动限定烧录长度) | 无 | Feature / Ordering Information 的密度表 |
| 擦除粒度与命令 | 扇区擦 0x20、块擦 0x52/0xD8 的命令码与粒度必须一致 | 整片擦除命令 0xC7/0x60 差异无关紧要 | Instruction Set Table 中 Erase 组 |
| 页编程 | 页大小必须一致(主流 256B) | Dual/Quad 编程命令(0xA2/0x32)有无不影响单线烧录 | Page Program 时序与 AC 特性表 |
| 地址宽度 | ≤16MB 用 3 字节;>16MB 必须支持 4 字节(0xB7 或 4B 命令) | 4 字节的进入/退出方式可不同(0xB7 或专用 4B 命令) | Addressing / 4-Byte Address Mode 章节 |
| 状态寄存器与保护 | 写使能 0x06、状态寄存器读 0x05 必须一致 | QE 位位置可以不同——离线单线烧录时不进 Quad 模式,用不到 | Status Register 与 Quad Enable 章节 |
Quad Enable 位的位置各厂各代次都不一样(有的在状态寄存器 2 的 bit1,有的在状态寄存器 1 的 bit6,还有的放在易失配置寄存器里)。但离线单线烧录根本用不到 QE 位——只有在 Quad/DTR 模式下读写才需要它。所以做兼容替换时,QE 位不一致不影响烧录本身,只影响「烧进去的固件能不能在目标板上以 Quad 模式启动」。这一项要分开判断,别混为一谈。
3.2 找基准型号的实操顺序
- 先认厂:读 0x9F 拿厂商 ID,对照附录 B 确定原厂。同厂同系列优先,跨厂次之。
- 再定容量:按容量码或印字确定密度,在库里筛出同容量的一批候选。
- 查代次:同容量下优先选同一代工艺的型号(如 GD25QxxE 对 GD25QxxE,不要拿老 GD25Qxx 的算法去烧新 E 代)。
- 比对命令集:打开两份 datasheet 的 Instruction Set 表,逐行核 0x20/0x52/0xD8/0x02/0x03/0x0B/0x05/0x01/0x06。
- 建条目并小范围试:不要一上来就整片擦写。先读回原片存档,再只擦一个扇区、写几十字节、读回比对。
- 通过后放量:小范围验证 OK 后,再整片烧录并做全片校验。
3.3 闭环验证:五个动作缺一不可
| 步骤 | 动作 | 判定标准 | 失败时说明什么 |
|---|---|---|---|
| ① 存档 | 整片读回,存成 bin 并备份 | 文件大小 == 标称容量 | 容量填错,或接触不良 |
| ② 空检 | Blank Check(或读回看是否全 0xFF) | 空白片应全 FF | 前次数据没擦净,或擦除命令不生效 |
| ③ 擦除 | 执行 Chip Erase | 不报错,读回全 FF | 写保护未解除,或擦除命令码不对 |
| ④ 写入 + 校验 | 写固件并自动读回比对 | 提示 VERIFIED / 校验通过 | 页大小或编程命令错;跨页回绕 |
| ⑤ 装机实跑 | 焊回板上通电运行 | 系统正常启动 | 参数对但配置位(QE/4B/保护)不对 |
前面四步全过,只说明数据被正确地写进去了,不代表目标板能以 Quad 模式读到它。批产前必须装机实跑至少一片:验证 QE 位、4 字节地址模式、状态寄存器非易失位等配置是否符合主板要求。跳过这步,量产线上会成片地「烧录 100% 通过、上电全不启动」。
4. 路径二:手动建型号(主流编程器实操)
库里没有,就自己补一条。原理统一:ID 用真值(让软件认得这颗料),算法挂靠一颗参数相同的已知型号(让软件知道怎么操作)。
4.1 RT809H / RT809F:编辑 DEVICE.INI
RT809 系列一直支持用户自行扩充器件库。安装目录下找到 DEVICE.INI(默认 D:\RT809H\DEVICE.INI),按 [ChipN] 的编号逐个递增追加条目,序号不能与已有条目重复,否则无效。
| 字段 | 含义 | 怎么填 |
|---|---|---|
User | 作者/备注信息 | 任意,可留空或写自己名字 |
ManuName | 厂商名称 | 按印字或 ID 反查的原厂名,如 XTX、PUYA |
ChipName | 型号/印字 | 芯片表面实际印字,方便以后在列表里搜到 |
ChipSize | 容量 | 按文件内注释给定的单位填写(示例文件中有说明) |
ChipID | 核心字段 | 以 0x 开头 + 6 位十六进制 = 0x9F 读回的 3 字节,如 0x0B4018 |
AlgoRef | 核心字段 | 填库里已有、参数兼容的型号名,算法直接复用它的 |
VCC / VCCIO | 供电 / IO 电平 | 1.8V 器件必须在这里写对,写 3.3V 会打穿芯片 |
PinVal | 引脚接触检测值 | 不确定可留空;填错会导致误报接触不良 |
VPP / TSCE / TPROG | 高压 / 擦除时间 / 编程时间 | 一般留空,让软件沿用 AlgoRef 的默认值 |
完整示例:给一颗 1.8V 的 16MB 国产料建条目
; —— 追加在 DEVICE.INI 末尾,序号接在最后一个 [ChipN] 之后 ——
[Chip1]
User = YourName
ManuName = XTX
ChipName = XT25F128B
ChipSize = 16384
ChipID = 0x0B4018
AlgoRef = W25Q128JV
PinVal =
VCC = 1.8
VPP =
VCCIO = 1.8
TSCE =
TPROG =
官方给出的经验是:W25X 与 MX25L 系列的算法通用性最好,优先考虑;而 SST、PCT 这类指令集比较特殊的,只能用同一厂商的型号来挂,跨厂大概率失败。实操中就是先用 W25Q 系列试,不行换 MX25L,再不行回同厂同系列。
DEVICE.INI 是纯文本,改错一个字符可能导致软件启动异常。动手前先复制一份原文件,改完重启软件,在器件列表里搜自己写的 ChipName 确认能出现。
4.2 XGecu(T48 / T56 / T76):用型号支持列表先确认
XGecu 各机型的器件支持列表是公开的,遇到问题先在官方列表里核对一次,确认是不是真的没有——很多时候只是丝印与列表里的正式型号写法不同导致的误判。
| 机型 | 能力范围 | 支持列表 |
|---|---|---|
| T48 | I2C / SPI / LPC / FWH / 并行 / eMMC | xgecu.com/MiniPro/T48_List.txt |
| T56 | 同上,含 eMMC | xgecu.com/MiniPro/T56_List.txt |
| T76 | 更高速,面向量产 | 见官网 MiniPro 目录 |
社区反馈:EZP 与部分 XGecu 机型对 8Mb(1MB)及以上的 ROM 自动识别不太准,建议手动指定型号而不是依赖自动识别;若读写报错,先把速度档位调低再试。这一条同样适用于其他品牌的国产编程器。
4.3 CH341A 系(AsProgrammer / NeoProgrammer)
这两个是 CH341A 编程器最常用的上位机,都允许往芯片库里加条目。做法一致:找到安装目录下的芯片定义文件(按软件版本不同,可能是 XML 或文本格式的库文件,具体以该版本 README 为准),照着同厂同容量的一条复制,改掉这几个关键字段即可。
| 字段(常见命名) | 填什么 | 说明 |
|---|---|---|
name | 自定义型号名 | 建议写成「印字 + 容量」,方便以后搜索 |
id | 3 字节 JEDEC ID | 厂商+类型+容量,十六进制连写,如 0B4018 |
size | 总容量 | 注意单位(KB 或字节),填错会导致读写截断 |
pagesize | 页大小 | 主流 256 |
sector / block | 擦除粒度 | 分别填 4096 与 65536(4K/64K) |
给 CH341A 加型号之前,请务必先读 第 7 章。黑色版 CH341A 的 IO 电平是 5V,而它给芯片供的是 3.3V——长期超规格使用会损伤 3.3V 的 SPI NOR,1.8V 器件更是一次就可能打穿。先改造或加电平转换,再谈烧录。
4.4 flashrom:往 flashchips.c 加一条
如果走 P5 自建烧录器(用 flashrom),加型号是改源码里的 flashchips.c,在对应厂商段追加一条结构体。字段含义如下,照抄一条最接近的再改关键项即可。
{
.vendor = "XTX",
.name = "XT25F128B",
.bustype = BUS_SPI,
.manufacture_id = 0x0B, /* 厂商 ID:0x9F 第 1 字节 */
.model_id = 0x4018, /* 类型+容量:第 2、3 字节 */
.total_size = 16384, /* 容量,单位 kB → 16MB */
.page_size = 256,
.feature_bits = FEATURE_WRSR_WREN,
.tested = TEST_UNTESTED,
.probe = PROBE_SPI_RDID,
.probe_timing = TIMING_ZERO,
.block_erasers = {
{
.eraseblocks = { {4 * 1024, 4096} }, /* 4096 个 4KB 扇区 */
.block_erase = SPI_BLOCK_ERASE_20, /* 命令 0x20 */
}, {
.eraseblocks = { {64 * 1024, 256} }, /* 256 个 64KB 块 */
.block_erase = SPI_BLOCK_ERASE_D8, /* 命令 0xD8 */
}, {
.eraseblocks = { {16 * 1024 * 1024, 1} }, /* 整片 */
.block_erase = SPI_BLOCK_ERASE_60,
}
},
.printlock = SPI_PRETTYPRINT_STATUS_REGISTER_PLAIN,
.unlock = SPI_DISABLE_BLOCKPROTECT, /* 写前自动解保护 */
.write = SPI_CHIP_WRITE256, /* 256B 页编程 */
.read = SPI_CHIP_READ,
.voltage = {1650, 2000}, /* 1.8V 器件! */
},
flashrom 会先跑 SFDP 探测。只要芯片支持 SFDP,即使 flashchips.c 里没有它,flashrom 也会把它识别成 “Unknown SFDP-capable chip” 并自动解析出容量与擦除命令,读/擦/写/校验全部可用——直接 -c "SFDP-capable chip" 就能干活。改源码只在芯片不支持 SFDP 时才必要。
4.5 嵌入式侧:SFUD 加一条
如果目标是让设备自己的 MCU 驱动这颗 flash(而不是用编程器烧),RT-Thread 的 SFUD(Serial Flash Universal Driver)在 sfud_flash_def.h 里维护了一张静态表,不支持 SFDP 的老芯片靠它兜底。加一条的字段格式:
/* {"型号名", 厂商ID, 类型ID, 容量ID, 容量(字节), 写模式, 擦除粒度, 擦除命令} */
{"XT25F128B", 0x0B, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},
SFUD 的识别是三级回退:先 SFDP → 再查静态表 → 最后用手动写死的参数。三条路都失败才初始化失败。所以给 SFUD 加条目的唯一场景是「既没有 SFDP、表里又没有」的老料。
4.6 建完怎么验:先小后大
- 重启软件,搜索自定义型号名,确认能出现在列表里且 ID 显示为你填的值。
- 先只读不写:整片读回存档。读不出来说明 ID 或接触有问题,别往下走。
- 单扇区试擦:只擦一个 4KB 扇区,读回确认该扇区变全 FF、其余区域未受影响。
- 小数据试写:往刚擦的扇区写几十字节,读回比对。
- 全片烧录 + 校验:通过后整片写入并自动校验,确认出现 VERIFIED。
- 装机实跑:焊回目标板上电,确认能正常启动(验证 QE 位、4 字节地址等配置位)。
5. 路径三:SFDP 自动识别(首选方案)
如果这颗芯片支持 SFDP,前面所有「找替代型号、改配置文件」的功夫都可以省掉——参数由芯片自己报出来,工具照单执行即可。
5.1 怎么判断它支不支持
| 方法 | 操作 | 判定 |
|---|---|---|
| 工具直接看 | flashrom 加 -VV 跑一次探测 | 日志出现 SFDP revision、解析出容量与擦除命令,即支持 |
| 发命令验 | 用任意 SPI 主机发 0x5A + 0x000000 + 8 dummy,读 4 字节 | 回 53 46 44 50(ASCII “SFDP”)即支持 |
| 逻辑分析仪 | 抓片选后的第一段通信 | 看到 0x5A 命令且有连续数据返回,即支持 |
| 查手册 | datasheet 搜 SFDP / JESD216 | 标注 “Supports SFDP (JESD216)” 即支持 |
2015 年之前的老料、部分超低功耗小容量料、以及少数厂商的早期 Quad 产品不支持 SFDP。越新的国产料支持率反而越高(这是行业趋势,也是新建项目的选型加分项)。采购时把「支持 JESD216 SFDP」写进选型条件,能省掉后面大量麻烦。
5.2 SFDP 里到底装了什么
来源:JEDEC JESD216
最核心的是 Basic Flash Parameter Table(ID 0xFF00,必需),它给出容量、页大小、支持哪几种擦除命令及各自粒度、dummy 周期、地址字节数。可选的 Sector Map 描述非均匀扇区,4-Byte Address Instruction Table 给大容量器件进入 4 字节地址模式所需的命令集。
5.3 flashrom 实操:从输出到可用
下面是 flashrom 探测一颗未被收录芯片时的真实输出形态(以 XT25F128B 为例,日志已精简)。重点看它自动解析出了什么:
$ flashrom -p ch341a_spi -VV
Probing for Unknown SFDP-capable chip, 0 kB:
SFDP revision = 1.0
SFDP number of parameter headers is 2 (NPH = 1).
SFDP parameter table header 0/1:
ID 0x00, version 1.0 Length 36 B, Parameter Table Pointer 0x000030
Parsing JEDEC flash parameter table...
3-Byte only addressing.
Write chunk size is at least 64 B.
Flash chip size is 16384 kB.
Block eraser 0: 4096 x 4096 B with opcode 0x20
Block eraser 1: 512 x 32768 B with opcode 0x52
Block eraser 2: 256 x 65536 B with opcode 0xd8
done.
=== SFDP has autodetected a flash chip which is not natively supported by
flashrom yet. All standard operations (read, verify, erase and write)
should work, but to support all possible features we need to add them
manually. ===
Found Unknown flash chip "SFDP-capable chip" (16384 kB, SPI) on ch341a_spi.
日志里那句 “All standard operations (read, verify, erase and write) should work” 是官方原话:读、校验、擦、写全都可用。缺的只是 OTP、特殊解锁序列这类非标特性。此时直接用 -c "SFDP-capable chip" 指定即可烧录。
常用命令速查
# 探测(不操作,看能不能认出来)
flashrom -p ch341a_spi
# 列出工具支持的所有芯片(找替代型号时用)
flashrom -L
# 强制指定用哪个型号条目去操作
flashrom -p ch341a_spi -c "W25Q128.V" -r backup.bin
flashrom -p ch341a_spi -c "SFDP-capable chip" -w firmware.bin
# 完整流程:先备份 → 擦 → 写(flashrom 写入时自带擦除与校验)
flashrom -p ch341a_spi -r backup.bin # 备份,永远第一步
flashrom -p ch341a_spi -E # 整片擦除
flashrom -p ch341a_spi -w firmware.bin # 写入(自动含校验)
flashrom -p ch341a_spi -v firmware.bin # 单独再校验一次
flashrom 写入完成后,输出末尾必须出现 VERIFIED 才算成功。没有这个词就是没通过校验,此时不要断电、不要装机,检查接线后重试。另外,写入的文件大小必须与芯片容量一致:目标文件小了要先补齐(用 0xFF 填充到等容量),否则尾部扇区不会被处理。
5.4 SFDP 的能力边界(它不告诉你什么)
| SFDP 能给出 | SFDP 给不出 · 需另行确认 |
|---|---|
| 总容量、页大小(写 chunk 大小) | QE(Quad Enable)位在哪个寄存器的哪一位 |
| 每种擦除命令及其粒度 | 进入/退出 4 字节地址模式的厂商自定义方式 |
| 地址字节数(3B / 3B+4B) | 状态寄存器是否为非易失、写状态寄存器是否需要先 WEL/WREN |
| Fast Read 的 dummy 周期数 | OTP / Security Register 区域的大小与操作命令 |
| 基础时序参数 | 块保护的具体解锁序列(部分厂商自定义) |
| 是否支持 DTR / xSPI 等(新版本表) | Unique ID、厂商自定义扩展寄存器的读写方式 |
SFDP 能保证把数据正确地写进去、正确地读出来;但不能保证目标板能以 Quad 模式把它跑起来。所以即便走了 SFDP 这条路,「装机实跑」这一步依然不能省。另外 flashrom 明确提示:SFDP 探测阶段对状态寄存器写使能要求的判断是「假设」而非确认,遇到解保护失败的芯片要回到 datasheet 人工确认。
编制日 2026-08-29 · 适用于研发 / 维修 / 生产场景的 SPI NOR 烧录问题处置