STM32驱动贴片式SD NAND:硬件接线与FatFS读写实测
2026/9/7 22:04:12 网站建设 项目流程

去年给一个小批量设备做数据记录功能时,被TF卡座坑惨了:振动环境下卡座接触不良,日志写到一半掉卡,现场返修了好几台。后来换用了MK SD NAND,情况一下子干净了。手头正好有正点原子的STM32F103ZET6精英板,我顺手把这块“贴片式SD卡”跑通了,整个过程其实可以压缩成三步:焊接接线、配置工程、读写验证。这篇文章就把这套流程完整记录下来,给打算用SD NAND做存储方案的你一个可以直接抄作业的参考。

MK SD NAND本质上是把SD控制器和NAND Flash封装在一颗芯片里,对外接口完全兼容SD协议,所以STM32的SDIO外设可以直接驱动它,上层再配上FatFS就能像操作普通文件一样读写。相比TF卡,它不用卡座、不怕振动、适合机器贴片量产;相比eMMC,它只需要SDIO或SPI接口,不用额外买eMMC控制器,STM32全系基本都能带动。这篇文章适合三类人:一是正点原子用户想扩展存储空间,二是做产品原型需要可靠存储方案,三是准备把TF卡方案升级成贴片式存储的嵌入式工程师。

1. 为什么是MK SD NAND:三种存储方案的取舍

1.1 MK SD NAND到底是什么

很多人第一次听到“SD NAND”会误以为是某种特殊Flash,其实把它的名字拆开看就清楚了:SD是指接口协议,NAND是指底层存储介质,合起来就是一颗自带SD控制器的NAND Flash芯片。

它和TF卡最大的区别在于形态。TF卡是把SD控制器和NAND颗粒做成一张卡片,靠卡座的金手指接触导电;SD NAND则是把同样的东西封装成一个LGA-8的贴片IC,直接焊在PCB上。这带来的直接好处就是:没有接触不良的问题,没有卡座弹簧疲劳的问题,也没有运输途中卡片脱落的问题。

我是在一次做设备振动摸底测试时意识到这个差距的。TF卡在静态环境跑得好好的,一上振动台就偶发SD_Init失败,后来查来查去就是卡座触点瞬间断开导致的。换成SD NAND之后,同样的振动测试跑了三天,读写全程零故障。从那以后,但凡要交付到现场的设备,我基本都用SD NAND方案。

1.2 关键对比:TF卡、eMMC与MK SD NAND

为了说清楚选型逻辑,我把三类方案放在一起对比过,直接看表格。

对比项TF卡 + 卡座eMMCMK SD NAND
接口协议SDIO / SPIeMMC专用接口SDIO / SPI
STM32驱动难度高(需要eMMC控制器或时序模拟)低(等同驱动SD卡)
焊接要求无需焊接BGA封装,一般要回流焊LGA-8封装,手工烙铁可焊
机械可靠性差(触点接触)好(贴片固定)好(贴片固定)
长期供货稳定性卡商经常换方案稳定稳定
单颗成本低,但卡座+结构件拉高成本中高
典型容量1GB~512GB4GB~256GB1GB~16GB

从表格能看出,SD NAND和eMMC在可靠性上属于同一档次,但SD NAND的驱动门槛低得多。STM32F103这类M3内核芯片本身没有eMMC控制器,如果硬上eMMC,需要额外扩展方案,不划算。而SD NAND直接吃SDIO或SPI,正点原子精英板的例程里本身就带了SDIO驱动和FatFS,几乎是无缝对接。

1.3 选型注意事项

MK SD NAND具体型号时要注意两点。

第一是容量。当前市面上常见的是1GB、4GB、8GB,对嵌入式应用来说4GB是比较平衡的选择,存日志、存图片素材、存固件升级包都够用。如果容量需求超过16GB,SD NAND的性价比优势会减弱,这种情况不如考虑其他方案。

第二是引脚定义。不同品牌、不同系列的SD NAND引脚排列不完全一样,焊接前一定去官网下对应型号的datasheet确认。以我用的MK系列4GB型号为例,LGA-8封装的引脚功能大致是:VCC、GND、CMD、CLK、DAT0、DAT1、DAT2、DAT3,其中DAT3/CS和CMD/DI在SPI模式下还承担片选和数据输入输出。这个布局和SD卡协议是对应的,好在正点原子精英板上的SDIO引脚资源刚好能覆盖。

2. 硬件连接:让芯片“长”在精英板上

2.1 先看懂精英板的TF卡座原理图

正点原子精英板的板载TF卡座,如果手上没有原理图,可以直接看开发板TF卡座附近丝印。卡座引脚通常引出6路信号:SDIO_CK、SDIO_CMD、SDIO_D0、SDIO_D1、SDIO_D2、SDIO_D3,外加VCC和GND。这些信号对应到STM32F103ZET6的具体引脚是:

信号STM32引脚
SDIO_CKPC12
SDIO_CMDPD2
SDIO_D0PC8
SDIO_D1PC9
SDIO_D2PC10
SDIO_D3PC11
卡检测CDPA8

这就是为什么用MK SD NAND替换TF卡如此顺滑:卡座上的信号线和SD NAND的引脚是一一对应的,SDIO 4位模式正好全部用上。只要把SD NAND各引脚飞线到卡座对应焊盘,硬件层面就等于“插了一张永远不掉的TF卡”。

2.2 三种接线方式的推荐与实操

我试下来有几种接线方案,按推荐程度排序:

方式一、飞线到TF卡座焊盘

这是最省事、也最接近量产形态的做法。把MK SD NAND放在靠近TF卡座的位置,用杜邦线或者漆包线把芯片引脚逐根连到卡座焊盘。注意飞线尽量等长、间距合理,信号线不要贴着长距离并行走,避免干扰。这种方式下正点原子的SDIO例程几乎不用改,只处理CD检测逻辑即可。

方式二、通过转接板直接插到卡座

如果只是快速验证,可以买现成的SD NAND转TF卡转接板,把芯片焊在转接板上,然后像TF卡一样插进卡座。优点是零改动、随时拔插,缺点是转接板增加了接触点,只适合测试不适合交付。

方式三、SPI模式接到任意IO口

如果不想占用SDIO外设,可以用SPI模式驱动SD NAND。这要求芯片支持SPI协议,接线只需要VCC、GND、SCLK、MOSI、MISO、CS六根线,但FatFS底层驱动要换成SPI版本,速度也远不如SDIO 4位模式。一般只在SDIO被其他功能占用的场景才考虑。

我在精英板上最终用的是方式一,飞线焊接后用一个塑料支架固定芯片,散热和抗振都更好。

2.3 上拉电阻、去耦电容与CD检测的处理

硬件连接里有三个容易踩的细节,我单独拿出来说。

第一是上拉电阻。SD卡协议要求在CMD线和DAT0~DAT3线上有上拉电阻到VCC,正点原子精英板原理图里TF卡座部分已经加了相关上拉,所以直接接卡座焊盘不需要额外处理。但如果你是走SPI模式接到其他IO,或者自己画板子,千万别省这4个上拉电阻,我见过不少初始化失败就是数据线上没上拉导致的。

第二是电源去耦。MK SD NAND在读写瞬间电流变化明显,我习惯在芯片VCC和GND之间靠近放一个100nF陶瓷电容,条件允许再并一个10μF钽电容。这个动作在开发板上不太明显,但到了产品PCB上能显著降低写入时偶发失败的概率。

第三是CD检测引脚的处理,这是正点原子例程最容易卡住的地方。正点原子的SD卡实验代码通常会先读PA8,通过卡座的CD引脚判断有没有插入TF卡,没有卡就直接报“SD卡错误”。MK SD NAND是焊在板子上的,卡座孔位里没有实体卡,CD引脚状态永远是无卡,于是例程会一直停在“未检测到卡”。

解决办法有两种,任选其一:

  • 把PA8引脚用一个跳线直接接到GND,模拟“卡已插入”的信号;
  • 在代码里把检测逻辑屏蔽,或者直接把判断表达式强制为真。

我图省事是直接把PA8焊了一根线到GND,这样正点原子例程原封不动就能继续跑,连代码都不用改。

3. 软件配置:CubeMX加上FatFS,伪代码与关键逻辑

3.1 用CubeMX快速生成SDIO+FatFS工程

正点原子精英板是STM32F103ZET6,CubeMX配置不算复杂,按下面几步走:

打开STM32CubeMX,新建工程选择芯片STM32F103ZET6,在Pinout视图里配置:

  • RCC,选择外部晶振Crystal/Ceramic Resonator,时钟树配置为72MHz主频;
  • SDIO,勾选SD 4 bits Wide bus;
  • DMA,添加SDIO_RX和SDIO_TX的DMA通道,方向分别是PeripheralToMemory和MemoryToPeripheral,模式选Normal;
  • USART1,异步模式,115200波特率;
  • Middleware → FATFS,选择SDIO接口,建议打开长文件名支持,代码页按自己需求选437或936;
  • Project Manager里生成MDK-ARM V5工程。

这里有一个关键参数:SDIO的Clock Div。初始化时SD协议要求时钟不超过400kHz,CubeMX的HAL库初始化阶段会自动用较低频率识别卡,之后才切到用户设定的Clock Div。我习惯把Clock Div设为4,对应9MHz左右的数据时钟,在精英板上跑得很稳。如果想保守一点,可以调到8甚至16,速度会降,但兼容性更好。

3.2 应用层代码:挂载、格式化、读写一个文件

CubeMX生成的FatFS底层驱动会通过sd_diskio.c把磁盘操作和HAL库的SDIO驱动对接好,应用层只需要在main.c里写逻辑。完整流程是挂载文件系统、判断是否需要格式化、创建文件、写入内容、读回验证。

以下是一段经过验证的测试代码,串口会打印每一步的返回值。

#include "ff.h" #include <string.h> #include <stdio.h> FATFS fs; FIL file; UINT bw, br; char write_buf[] = "Hello MK SD NAND!\r\n"; char read_buf[64]; FRESULT res; void SD_NAND_Test(void) { res = f_mount(&fs, "", 1); printf("f_mount = %d\r\n", res); if (res != FR_OK) { /* 如果返回 FR_NO_FILESYSTEM,说明芯片没有文件系统,需要格式化 */ if (res == FR_NO_FILESYSTEM) { printf("No filesystem, formatting...\r\n"); f_mount(NULL, "", 0); res = f_mkfs("", 0, 0, 0); printf("f_mkfs = %d\r\n", res); res = f_mount(&fs, "", 1); printf("f_mount again = %d\r\n", res); } if (res != FR_OK) return; } res = f_open(&file, "test.txt", FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) { printf("f_open write error %d\r\n", res); return; } f_write(&file, write_buf, strlen(write_buf), &bw); f_close(&file); printf("wrote %d bytes\r\n", bw); res = f_open(&file, "test.txt", FA_READ); if (res != FR_OK) { printf("f_open read error %d\r\n", res); return; } memset(read_buf, 0, sizeof(read_buf)); f_read(&file, read_buf, sizeof(read_buf), &br); f_close(&file); printf("read %d bytes: %s\r\n", br, read_buf); f_mount(NULL, "", 0); }

注意这段代码里f_mkfs("", 0, 0, 0)的参数格式在不同FatFS版本里略有差异,老版本常用f_mkfs("", 0, 0),新版是带了一个对齐配置参数。编译报错的话查一下头文件里的函数原型,按对应版本调整即可。

3.3 串口验证与实测效果

串口重定向走USART1,需要在代码里重写fputc:

int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); return ch; }

另外记得在Keil工程里勾选MicroLIB,否则printf重定向容易出问题。

我在精英板上实测,挂载、格式化、写文件、读文件全程不到一秒,串口输出如下:

f_mount = 3 No filesystem, formatting... f_mkfs = 0 f_mount again = 0 wrote 21 bytes read 21 bytes: Hello MK SD NAND!

第一次运行时返回3,对应FR_NO_FILESYSTEM,格式化后再次挂载返回0,说明文件系统正常。这个流程跑通了,后续任何文件读写操作都只是换文件名和路径的问题。

如果要验证掉电保存效果,可以在写文件后调用f_sync(&file),或者在关闭文件后断电重启,重新挂载并检查文件内容。我实测多次断电重启,数据都完整保留,这也是SD NAND相对RAM缓存方案最核心的卖点。

4. 踩坑记录:从“no target found”到写入异常

4.1 第一步可能挡路的调试器报错

很多人在配置完工程后,第一件事不是下载SD NAND代码,而是发现Keil里下载程序直接报错。最常见的错误是:

error: no stm32 target found! if your product embeds debug authentication, pl...

看到这行提示先别慌,和SD NAND没有关系,通常是调试器没连上芯片。排查顺序如下:

  1. 检查ST-Link与板子的SWD接线,SWDIO、SWCLK、GND三根线必须对应,有些便宜的ST-Link和杜邦线接触不稳定,重新插拔几次;
  2. 确认板子已经供电,精英板USB线连接后要看到电源指示灯亮;
  3. 在Keil的Options → Debug → Settings里查看是否能识别到设备ID,如果显示No target,检查一下Debug里选择的调试器类型是否和实际一致;
  4. 降低SWD时钟频率,把Settings里默认的4MHz降到400kHz,时序余量大更容易连上;
  5. 如果芯片之前烧过读保护程序,先用STM32CubeProgrammer选择Connect Under Reset方式连接,解除读保护后再回到Keil。

从我的经验看,80%的“no target found”都是接线松动或者没选对调试器,剩下20%才是芯片读保护。因为这块和存储芯片完全无关,很多人会绕圈子排查,所以单独提醒一下。

4.2 文件系统挂载不上、格式化失败怎么破

文件系统挂载失败是接触SD NAND后最普遍的软件问题,具体表现是f_mount返回FR_NO_FILESYSTEM,或者直接卡死在SDIO初始化。

从底层往上逐项排查,按这个顺序:

  1. 供电电压。用万用表量芯片VCC和GND之间电压,必须在3.3V左右。如果焊完后电压被拉低到3V以下,大概率是焊接短路或者电源走线有问题;
  2. 焊接质量。LGA-8封装引脚在芯片底部,手工焊接最容易出现连锡和虚焊。我用放大镜检查每对相邻引脚,再用万用表蜂鸣档确认相邻引脚不导通、对应引脚和飞线另一端导通;
  3. 时钟频率。把Clock Div从4改大到16或32,降到4MHz以下再试,只要能初始化就说明是速率余量问题;
  4. 上拉电阻。如果方案里省掉了CMD和DAT线上的上拉,初始化卡在CMD响应超时一点不奇怪;
  5. CD检测干扰。如果你用的正点原子例程,记得PA8要接地或屏蔽检测逻辑,否则初始化流程直接就不往下走。

如果f_mount提示没有文件系统,第一次使用SD NAND非常正常。记住先卸载再格式化,否则格式化可能返回错误。上面代码里已经做了这个处理。

4.3 数据写错、速度慢、掉电丢文件的真凶

读写正常之后,还会遇到几类隐蔽的异常,我展开说。

第一个是缓冲区对齐问题。STM32F103的SDIO走DMA时对内存地址有对齐要求,如果用局部数组当读写缓冲,很可能因为栈上地址没对齐导致DMA传输出错,表现出来就是写入几百个字节后偶尔读回几个错数据。解决办法是定义全局缓冲区,或者加__attribute__((aligned(4)))声明:

__attribute__((aligned(4))) char read_buf[64];

第二个是掉电丢文件。很多人测试时写完文件直接断电,再次上电发现文件大小不对或内容丢了一半。这不是SD NAND坏了,而是文件系统的写缓存还没刷到Flash里。FatFS在写操作后需要f_sync(&file)或者f_close(&file)才会真正落盘,测试掉电保存时记得先调同步函数。

第三个是写入速度异常。SD NAND的写入速度天然低于读取速度,如果感觉慢得离谱,检查是不是在SPI模式下跑。SPI模式字节位宽只有1位,和SDIO 4位宽差别很大,同样容量的写入耗时能差好几倍。如果必须用高速模式,确认DMA配的是Normal模式,并且HAL_SD_WriteBlocks_DMA完成后等待的中断回调没有被其他中断挤掉。

第四个是文件系统损坏。频繁断电或格式化过程中异常掉电,偶尔会导致分区表损坏,现象是插到电脑上提示需要格式化。遇到这种情况直接在程序里重新f_mkfs即可,不过这会清空全部数据。所以我的习惯是产品代码里单独做一个隐藏的“格式化入口”,只有维修模式才触发,避免误操作。

我个人在实际操作中的体会是,MK SD NAND这类芯片最大的价值是把“存储模块”变成了“存储焊点”,省掉卡座、外壳开孔、结构固定这些乱七八糟的事,产品BOM做起来清爽多了。最后再分享一个焊接小技巧:LGA封装底部是金属焊盘,手工放芯片时容易被助焊剂表面张力带偏,我的办法是先给PCB焊盘镀一层薄锡,涂助焊剂,然后芯片放上去,热风枪先低温吹到锡融化,芯片会在表面张力作用下自动“归正”到焊盘中心,这个技巧对LGA和QFN都很管用。整个项目跑通之后,后面再换到其它STM32系列只是改一下引脚和CubeMX配置的问题,存储这块代码基本可以跟着走。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询