做嵌入式存储方案这几年,我经手最多的东西之一就是 SD NAND Flash。它长得像一颗普通的贴片 IC,管脚少、外围电路简单,可一旦测试不到位,等到整机老化或者出货后才开始丢数据,那真是灾难现场。所以这篇就把 Flash 闪存的基础原理和 SD NAND Flash 的测试方法一起盘一盘,适合想快速入门存储方案的硬件工程师,也适合已经在用但总被偶发读写失败折磨的朋友。我会从原理讲到实际操作,再落到问题排查,尽量让文章可以直接对着抄。
1. 先从Flash闪存说起:为什么SD NAND能省掉一堆麻烦
1.1 NAND的核心存储原理
Flash 闪存有 NOR 和 NAND 两种主要形态,SD NAND 内部用的基本都是 NAND Flash。它的存储单元本质是一个浮栅晶体管,通过向浮栅注入电子或移除电子来改变晶体管的阈值电压,从而记录 0 和 1。往浮栅里注入电子就叫"写",把电子拉出来就叫"擦除",读的时候只需要检测阈值电压的变化,不需要动浮栅里的电荷。
这个机制决定了 NAND 的很多"怪脾气"。第一,它不能像普通内存那样按字节覆盖写,要先擦除、后写入,而且擦除的最小单位是块(Block),写入的最小单位是页(Page)。一块通常包含几十上百个页,比如一个 512KB 的块,里面可能有 128 个 4KB 的页。第二,每个块都有擦写次数上限,SLC 一般在 5 万到 10 万次,MLC 在 3000 到 10000 次,TLC 更低。擦写次数到了,这个块就会变成坏块。第三,NAND 出厂时本身就允许存在少量坏块,厂家会把坏块信息标记在特定位置,主控要用的时候得先扫出来避开。
这些特性单独看都还好,但合在一起,就对上层的存储软件提出了非常高的要求。如果你直接拿一颗裸 NAND 接在单片机 SPI 接口上读写,会发现根本没法用:写了一个页,下次再写同一页要先整块擦除;数据存储一段时间后出现 bit flip,没人帮你纠错;某几个块寿命耗尽后,整个分区可能就挂掉了。这就是为什么很多工程师第一次碰裸 NAND 时会崩溃。
1.2 裸NAND为什么难用
裸 NAND 要稳定工作,至少需要一整套 FTL(Flash Translation Layer)算法来兜底。FTL 要做的事情包括逻辑地址到物理地址的映射、坏块管理、磨损均衡、垃圾回收、掉电保护、ECC 校验。你可以把 FTL 理解成 NAND 的"物业公司":业主(文件系统/应用)只负责报门牌号(逻辑地址),物业要把具体电表水表(物理页)分配好,还要定期检修(磨损均衡)、处理水管爆裂(坏块替换)、防小偷(掉电保护)。
这套东西在 Linux 内核里有现成的 MTD 层和 UBIFS 可以跑,但你要用一颗低成本的 MCU 来做,那工作量就非常大了。而且普通 MCU 的 RAM 和 Flash 资源有限,跑复杂的 FTL 很容易资源不足。更麻烦的是,不同 NAND 厂商、不同制程、不同料号,坏块标记位置、页大小、块大小、时序参数都可能不同,固件要跟着适配,改一次方案就得重新调一次。
所以,除了本身就在做存储主控芯片的团队,绝大多数嵌入式开发者都不应该直接和裸 NAND 硬刚。这就要说到更实用的方案了。
1.3 SD NAND:把控制器和Flash封在一起
SD NAND Flash 的思路很粗暴:把一颗 NAND Flash 和一个 SD 控制器封装在一起,对外只暴露 SDIO 接口,管脚就 8 个,焊接方式和普通贴片 IC 一样。控制器内部把 FTL、坏块管理、磨损均衡、ECC 全做完了,上层 MCU 只需要像读写 TF 卡一样,走 SD 协议或者 SPI 协议就行。
它和 TF 卡最大的区别在于形态和可靠性。TF 卡依靠金属触点接触,插在卡座里,时间久了会氧化、接触不良、震动后松动,而且卡座本身的高度也占了 PCB 空间。SD NAND 是直接焊在板子上的,没有接触点,抗震动、抗腐蚀,可靠性高出很多。和 eMMC 相比,SD NAND 的协议更简单,MCU 只要支持 SDIO 或 SPI 就能驱动,不需要 MMC 控制器,也不需要处理 boot partition 这些复杂概念。
所以实际项目里,如果你需要一颗 1Gbit 到 8Gbit 左右的存储芯片,又不想忍受裸 NAND 的开发成本和坏块风险,SD NAND 是很常见的选择。它把最复杂的存储管理问题挡在芯片内部,让你可以专心写业务逻辑。但话说回来,正因为内部藏了一个控制器,它的测试就不能只看"能不能识别",还要看控制器的稳定性、固件策略是否靠谱,这就引出了后面的主题。
2. 测试SD NAND前的准备工作:平台、硬件和初始化
2.1 测试平台怎么选:读卡器、MCU还是开发板
测试 SD NAND 第一步是选平台。根据测试目的不同,大概有三种方案。
第一种是拿读卡器或开发板直接测,适合快速验证芯片好坏。用一个 USB 读卡器,把 SD NAND 通过转接板插进去,在电脑上就能读容量、跑 ATTO/CrystalDiskMark,甚至用 H2testw 做全盘校验。这个方法上手最快,但要注意:读卡器的主控芯片本身也做了很多处理,它测出来的速度只能代表"在这个读卡器上的表现",不能代表你的 MCU 系统实际能跑多快。
第二种是用 MCU 开发板,比如 STM32、GD32、ESP32 这类,通过 SDIO 或 SPI 接口去驱动。这种方式最接近量产环境,因为你在量产板上大概率也是这么接的。测试结果能反映真实系统下的性能,而且你可以把测试代码直接演进成量产固件里的自检程序。推荐用带 SDIO 外设、支持 DMA 的型号,比如 STM32F4 以上,跑起来效率高很多。
第三种是全自动测试台,适合产线和来料质检。用 PC 上位机通过串口或者 USB 连接测试板,测试板上固定好一颗待测 SD NAND,PC 端自动下发测试命令,收集测试结果。这种方案前期投入大,但好处是测试项目和判定标准可以统一,数据可追溯,哪个批次有问题一眼就能看出来。
我个人建议,刚接触 SD NAND 的工程师先用第二种方案,把 MCU 驱动调通,再考虑要不要搭自动化产测。原因很简单:MCU 平台的问题排查手段更多,逻辑分析仪、示波器都能直接挂上去看协议波形,出了问题你知道该查哪里。
2.2 硬件连接的关键细节和避坑点
SD NAND 的硬件连接不复杂,但有几个关键细节会影响后续测试是否顺利。
第一是供电。SD NAND 的 VDD 一般是 3.3V,写操作瞬间电流会比读操作大不少,尤其 FIFO 写满时,电流尖峰可能到几百毫安。所以芯片旁边的去耦电容不能省,通常要放一个 10uF 钽电容或陶瓷电容,再并一个 0.1uF 高频电容,电源走线要尽量短粗。供电不稳,测试时会出现诡异的速度波动和数据校验失败。
第二是上拉电阻。SD 协议里的 CMD 和 DAT0-DAT3 线,在空闲状态下是靠上拉电阻维持高电平的。很多 SD NAND 芯片内部已经集成了上拉电阻,但为了让信号更稳定,尤其是走线较长或者主控 IO 驱动能力比较弱的时候,外部再各加一个 10k 到 47k 的上拉电阻会更稳妥。特别是 DAT3,在 SD 模式下还要承担卡检测的功能,上拉不能省。
第三是时钟频率和 IO 电平。SD NAND 支持的最高时钟频率取决于具体型号,但测试时千万别一上来就跑到几十兆赫兹。上电初始化阶段,建议把 SDIO_CLK 拉到 400kHz 以下,等初始化完成后再切换高速时钟,这是 SD 协议的标准要求,也是排查问题时的经典套路。IO 电平要匹配,主控如果工作在 1.8V 或者 2.5V,就不能直接接 3.3V 的 SD NAND,必须加电平转换芯片,否则初始化大概率失败。
第四是热设计。SD NAND 虽然封装小,但控制器在高负载下也会发热,PCB 上要给它留出散热过孔和铜皮。尤其是做老化测试时,芯片长时间在高速度下读写,热量累计起来很可观,不加散热会导致控制器内部温度过高,写入速度下降甚至触发过温保护。
2.3 从CMD0到CSD:认识一颗新芯片的第一步
不管是做测试还是量产,第一步都是让 SD NAND 完成初始化。SD 协议初始化流程是固定的,大致是这样:
- 发送 CMD0,让卡进入 IDLE 状态。
- 发送 CMD8(SD_SEND_IF_COND),确认卡的工作电压范围和版本。
- 发送 ACMD41(SD_APP_OP_COND),让卡完成内部初始化,同时可以读出 OCR 寄存器,判断是否支持 3.3V。
- 发送 CMD2(ALL_SEND_CID),读出一串 128 位的 CID 信息,里面有厂商 ID、产品名、序列号、生产日期等。
- 发送 CMD3(SEND_RELATIVE_ADDR),获取卡的 RCA(相对地址)。
- 然后发送 CMD9(SEND_CSD),读出 CSD 寄存器,这里面存着容量、最大读速率、是否支持 4 位模式等关键参数。
以 STM32 HAL 库为例,用 SDIO 外设初始化 SD NAND 的核心代码可以简化成下面这样。注意这是伪代码级别的简化,实际工程里还要加状态机处理超时和错误重试。
// 初始化 SD 卡 HAL_SD_Init(&hsd); // 底层会用 400kHz 时钟走 CMD0/CMD8/ACMD41 HAL_SD_GetCardStatus(&hsd, &status); // 读取卡状态,确认初始化成功 HAL_SD_GetCardInfo(&hsd, &cardInfo); // 读取 CSD,得到卡容量信息 // 切换为 4 位模式并提高时钟 hsd.Init.ClockDiv = 4; // 提高 SDIO_CK 频率,具体值取决于主频 HAL_SD_Init(&hsd);读完 CSD 后,容量信息在cardInfo.BlockNbr和cardInfo.BlockSize里,实际总字节数就是BlockNbr * BlockSize。比如 BlockNbr 是 1,953,000,BlockSize 是 512,那容量就是 1.95GB 左右。如果读出来的容量和标称值差太多,那就要警惕芯片是不是被改过容量标称的"货",这个在后面的测试环节要重点查。
初始化这个步骤,很多人觉得简单就跳过,但恰恰是翻车最多的地方。驱动写好后,我建议用逻辑分析仪或者示波器抓一次 CMD 和 DATA 线的波形,确认命令是否得到了有效响应。如果 CMD0 之后卡没回应答,先降时钟、查上拉、查供电,别急着改代码,绝大多数初始化失败都是硬件问题,不是时序问题。
3. 核心测试项目实操:从功能到可靠性的完整打靶
3.1 容量识别和信息校验:先确认是不是一颗好芯片
SD NAND 测试的第一个项目,是确认芯片身份和容量,这一步属于"验明正身"。
先通过 CSD 寄存器算出标称容量,再和芯片丝印标称对比。比如一颗标注 4Gbit 即 512MB 的 SD NAND,读出来 BlockNbr 应该是大约 1,000,000 个 512 字节扇区。如果读出只有 500MB 不到,或者差别超过 10%,那就有问题了。有一种情况要区分:SD NAND 的控制器会占用一部分空间做内部固件、映射表、坏块替换区域,所以实际用户可用容量会比裸 NAND 的物理容量少一点,但通常不会少太多。如果差得离谱,就要怀疑是不是翻新片或者黑片重新打标。
接下来要看 CID 和 CSD 里的字段。CID 里的 Manufacturer ID 可以帮你确认芯片的原厂信息,比如某些主流 SD NAND 方案的厂商 ID 是固定在协议里的。还要检查生产日期,如果一颗新买的芯片生产日期是五六年前,那很可能是拆机片或者积压库存,寿命和可靠性都有风险。
全盘扫描也是必做的。拿一个预先填好的数据模式,比如 0xA5,把整个用户可用区域写一遍,再读回来比对。如果某几个扇区读回数据不对,说明控制器的主控有坏块处理漏洞,或者芯片本身有物理坏块没被屏蔽掉。这种芯片绝对不能进量产,否则就是你产品里的定时炸弹。全盘扫描耗时较长,1GB 大概在 SDIO 高速模式下需要几分钟,但这一步省不得。
3.2 读写速度测试与数据一致性校验
速度是 SD NAND 测试的重头戏,但也是误解最多的部分。测速度要区分顺序读、顺序写、随机读、随机写,不同场景下表现差异巨大。对大多数嵌入式应用来说,顺序读写更常见,所以先从顺序读写开始测。
写速度测试的思路很简单:准备好一块缓冲,填充固定数据模式,然后从逻辑地址 0 开始连续写,每写完一块记录当前块号,最后用总写入字节数除以耗时。这里要注意,SD NAND 是有内部缓存和 FTL 的,连续写的时候可能先快后慢,因为后面的写操作会触发垃圾回收。所以测试时要写满至少整个容量,不能只写几百 KB 就下结论。
以 STM32 HAL 库为例,顺序写一个 512MB 的 SD NAND,核心循环大概是这样的:
uint8_t buffer[512] __attribute__((aligned(4))); uint32_t total_blocks = 1024 * 1024; // 512MB / 512B per block uint32_t i; uint32_t tick_start = HAL_GetTick(); for (i = 0; i < total_blocks; i++) { if (HAL_SD_WriteBlocks(&hsd, buffer, i, 1, 0xFFFFFFFF) != HAL_OK) { printf("write error at block %lu\n", i); break; } } uint32_t tick_end = HAL_GetTick(); float mbps = (float)(i * 512) / 1024.0f / ((tick_end - tick_start) / 1000.0f); printf("write speed: %.2f MB/s\n", mbps);读速度测试同理,区别是调用 HAL_SD_ReadBlocks,然后可以再加一步数据比对。数据模式不能只用一种 0xA5,要覆盖几种典型情况:
- 全 0x00,检查 VCC 短路或者数据线固定为低的问题;
- 全 0xFF,检查空片或者数据线固定为高的问题;
- 0x55 / 0xAA 交替,检查相邻位之间的干扰;
- 伪随机数据,模拟真实应用的数据特征。
数据一致性校验要跑多轮,至少写一遍再读一遍,最好连续读写 10 轮以上,每一轮都用不同数据模式。我见过一颗 SD NAND 用 0xA5 读写 20 遍全通过,换成伪随机数据就出错,暴露出来的是内部数据线信号完整性问题。如果只测一种模式,这种问题根本测不出来。
另外要留意写缓存带来的假象。SD 协议规定写命令在数据进入卡内部缓存后就可以返回成功,但数据真正落到 NAND 上可能是在后台。如果测试刚写完就立刻断电,下次上电发现数据丢了,这不一定是 SD NAND 的错,可能是测试方法的问题。正确的做法是:写完一批数据后,先发 CMD12(STOP_TRANSMISSION)或者等待写总线空闲,再执行一次标准的"读回校验",以读回的数据为准。
3.3 掉电测试:最容易暴露问题的环节
掉电测试是 SD NAND 可靠性验证里最刺激也最关键的一环。因为应用跑着跑着突然断电是不可控的,如果 SD NAND 的 FTL 固件在掉电时没有做好一致性保护,就会出现映射表损坏、部分数据丢失、甚至整卡无法识别。
掉电测试怎么做?我需要一个可控的电源开关,最简单的方式是用一个继电器或固态继电器控制 SD NAND 的 VDD 通断,用 MCU 的一个 GPIO 去控制继电器吸合断开。测试脚本逻辑:
- 上电初始化 SD NAND。
- 生成一个固定大小的测试数据段,比如 64MB,循环写入。
- 在写入过程中随机延时几十到几百毫秒,然后控制继电器断开 VDD。
- 等待 1 到 2 秒让电容放完电。
- 重新上电,初始化 SD NAND。
- 扫描之前写入的区域,看数据是否完整。如果读取失败,记录失败扇区。
- 重复以上循环 100 次到 500 次。
掉电测试的坑在于,你不能只在一个固定的写入位置断电。FTL 的映射表更新可能发生在某一个页写入后、擦除前、擦除后的不同时刻,不同断电点对应不同的一致性问题。所以断电点要尽量随机化,覆盖写入的不同阶段。我习惯在测试固件里用一个伪随机数生成器决定每次写入的数据量和断电时间点,这样能在有限次数内覆盖更多种情况。
如果掉电测试中发现数据丢失,不一定全是 SD NAND 的问题。我的经验是,控制器本身做掉电保护做得比较成熟,真正容易丢数据的是文件系统层。比如你在用 FATFS,它在写文件时会有目录项更新和 FAT 表更新的多步操作,断电发生在两步之间,文件系统就可能损坏。所以掉电测试要分层测:先裸块读写测 SD NAND 控制器的一致性,再挂文件系统测应用层的一致性。这两个问题要分开定位。
3.4 老化与高低温测试:把风险提前暴露在实验室
老化测试的目的是压榨芯片,让早期失效在出货前就暴露出来。SD NAND 的早期失效通常表现为读写错误率升高、初始化失败、速度明显下降。老化测试的经典做法是持续读写循环:写满、校验、擦除、再写满,如此循环若干轮,连续运行 8 到 24 小时。
温度是最能放大芯片问题的因素,所以高低温测试基本是必选项。SD NAND 一般有商业级(0℃ 到 70℃)和工业级(-40℃ 到 85℃)之分,选型时就要确认你的应用环境需要哪个等级。测试时使用高低温试验箱,让芯片在高温 85℃ 和低温 -40℃ 两个极端温度点分别跑读、写、校验的循环测试。
实际测试中经常遇到的情况是:常温下 100 轮读写全通过,一到高温 85℃ 就开始偶发校验错误。这通常和 NAND 的电荷保持能力下降、控制器在高热下的错误重试机制触发有关。遇到这种问题,先确认是不是超出了芯片的温度规格,如果确实在规格范围内,那就要找原厂技术支持的麻烦,或者考虑换一颗更稳妥的型号。
还有一个容易被忽略的是温变速率。快升温快降温会让封装内部产生热应力,焊点可能出现细微裂纹导致接触不良。如果做温循测试,要关注测试结束后的功能仍然正常,不能只看温度稳定时的测试结果。有一次我碰到客户成板老化后偶发"找不到 SD NAND",最后定位到是 LGA 焊盘在热应力下产生了微小虚焊,重新规范回流焊曲线之后问题就消失了。
4. 测试中最高频的翻车现场:问题排查实录
4.1 初始化失败:卡在CMD0或ACMD41
这是 SD NAND 测试里遇到最多的报错。确认卡在哪个阶段很关键:卡在 CMD0 说明卡根本没进入 IDLE 状态,优先查硬件;卡在 ACMD41 说明 CMD0 已经正常应答但卡内部初始化没完成,多半是电压或者时钟问题。
我梳理过一个排查顺序,照着走能省很多时间:
- 先量 VDD 电压和纹波,确保上电时电压不掉下 3.0V;
- 确认 SDIO_CLK 初始化阶段是 400kHz 以下;
- 检查 CMD、DAT0-3 的上拉电阻有没有漏焊、虚焊;
- 用示波器抓 CMD0 到 CMD1 的命令序列,看卡有没有回 0x01;
- 换个主控板或者交叉测试,排除主控外设配置错误。
如果在 SPI 模式下初始化,要注意 SPI 模式下的初始化时序和 SDIO 模式不完全一样,CMD0 前要先拉高 CS,再发送至少 74 个时钟周期的同步序列。很多人会忽略这个同步时钟,导致卡在初始化。
4.2 读写速度上不去:别急着怪芯片
速度上不去的原因多种多样。先看通信模式:是不是真的跑在 4 位 SDIO 而不是 1 位模式?很多 MCU 的 HAL 库默认初始化 SDIO 为 1 位模式,要在初始化之后切到 4 位模式。再看 DMA:如果没有开启 DMA,CPU 每次处理一个扇区都要等中断,速度会掉一个数量级。
数据对齐也很关键。SD 协议要求扇区地址和数据缓冲地址都按 512 字节对齐,如果你在 STM32 上用了未对齐的缓冲,DMA 会直接报错,或者被 HAL 库内部做了一次拷贝,拖慢速度。还有一个常见的坑是时钟分频系数配置不合理,比如主频 168MHz,分频后实际上只能跑到 12MHz 甚至更低,这时候如果怀疑芯片慢,先查查实际时钟频率。
如果硬件和配置都没问题,速度还是上不去,可以跑一颗同型号的信任样品交叉对比。如果信任样品的速度也差不多,那就是这颗芯片本身的标称速度区间就是这样,需要调整测试预期或者换更高速档位的型号。
4.3 数据校验错误与容量异常
数据校验错误要区分是偶发还是固定位置。固定位置出错,优先怀疑坏块管理有漏洞或者地址线信号问题;偶发错误,可能是电源不稳、时序裕量不足或者高温电磁干扰。把错误日志打出来,看错误扇区是否随机分布,是定位思路的关键一步。
容量异常有两种情况,一种是读出来的容量比标称少很多,这通常是控制器的保留区域占比较大,属于正常现象,但要在选型时了解这个占比,避免量产时"容量不够用";另一种是容量比标称大,这种反而是危险信号,极有可能是改版片或白片打磨后再标记,绝对不能用。用 H2testw 全盘写校验是最有效的验证手段,能顺便把坏扇区都找出来。
4.4 高温或者长时间运行后不稳定
长时间运行后出现卡死、初始化失败,先看芯片温度有没有超标。用手摸外壳只能大概判断,最好是给 PCB 上贴一个热电偶,把温度实测记录下来。如果温度在规格范围内但还是出问题,就要考虑是不是电迁移、信号完整性随温度变化引起的,这时可以适当降低 SDIO 时钟频率做交叉验证。
还有一种容易被忽略的情况:SD NAND 长时间写入后触发内部垃圾回收,此时如果上层继续发写命令,芯片可能进入忙等状态。如果主控没有正确处理忙等待,等待超时后就会判定超时。解决方法是利用 SDIO 的 DAT0 线判断卡是否 busy,在发送命令前轮询卡的状态,或者使用 CMD13 查询状态。
5. 最后说几句实在话
测试 SD NAND 这事,看着简单,真正较真起来工作量不小。我做项目的习惯是,把测试脚本固化下来,不管是来料质检、研发验证还是产线抽检,都用同一套流程跑。数据记录必须有:每颗芯片的 CID、固件版本、测试日期、测试项目、通过/失败。这样批次一多,哪个供应商、哪个批次容易出问题,数据一拉就能看到。
如果你的产品和数据可靠性强相关,我强烈建议留出老化测试的时间,新批次到货先抽三到五颗,跑一轮 8 小时持续读写加掉电测试再决定是否批量使用。这个成本看着高,但比起整机出货后客户反馈丢数据、返工、赔款,便宜太多了。
最后一个小技巧:测试代码里千万不要只用上电初始化成功作为"测试通过"的判断,一定要有完整的数据回读校验。初始化成功只能说明卡认出来了,不代表它存储数据是可靠的。很多翻车现场,前面初始化看起来都很正常,数据写到一半或者过几天再读就出错。做存储方案,玄学越少越好。