SPI NOR Flash 烧录「找不到型号」解决方案
从识别机制到自建通用烧录器的六条路径 · 编制日 2026-08-29 | 编制日期 2026-10-14 | 版本号 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 字节地址等配置位)。
编制日 2026-08-29 · 适用于研发 / 维修 / 生产场景的 SPI NOR 烧录问题处置