FatFs在小内存单片机上跑不起来主因是默认配置功能冗余,需裁剪ffconf.h:关FF_USE_STRFUNC、FF_FS_EXFAT、FF_FS_NORTC,设FF_VOLUMES=1、扇区大小为512;SD初始化需稳压、降速、真实读OCR;disk_read缓冲区须4字节对齐且避免栈溢出;f_write需启用FF_FS_TINY=1并检查*bg写入量。

FatFs 为什么在小内存单片机上跑不起来
不是 FatFs 本身写得差,是默认配置把所有功能都编进去了——哪怕你只读一个 config.txt,它也准备好了长文件名、多分区、Unicode、磁盘缓存、格式化……全塞进 RAM。STM32F030 或 GD32F130 这类只有 6KB SRAM 的芯片,光一个 f_mount() 就可能触发 HardFault。
关键裁剪点不在代码删减,而在 ffconf.h 里关掉「不碰就不该占内存」的功能:
-
FF_USE_STRFUNC设为0:禁用f_printf()和字符串转换,省下几百字节栈空间 -
FF_FS_EXFAT和FF_FS_NORTC必须为0:exFAT 解析吃内存,无 RTC 模式反而会偷偷开缓冲区 -
FF_VOLUMES设为1:只支持一个逻辑驱动器(0:),避免卷管理开销 -
FF_MIN_SS和FF_MAX_SS统一设为512:跳过扇区大小自适应逻辑,省掉判断分支和临时 buffer
SD 卡初始化失败卡在 disk_initialize()
常见现象是返回 STA_NOINIT,但实际原因五花八门:SPI 速率过高、CS 电平没拉低、SD 卡未上电稳定、甚至 SD 卡本身是伪装成 SD 的 eMMC(不兼容 SPI 模式)。FatFs 不报具体错,只甩个状态码,容易误判。
实操时盯住三件事:
- 确保
disk_initialize()前,SD 卡电源已稳压 ≥ 100ms,且CS引脚被拉高(非选中态)至少 1ms - SPI 时钟先降到
100kHz(disk_ioctl(DISK_IOCTL_INIT)阶段必须 ≤ 400kHz),初始化成功后再用CTRL_SYNC切到高速 - 重写
disk_status():不要直接返回全局状态变量,每次调用都真实读 SD 的 OCR 寄存器,否则热插拔后状态不同步
示例片段:
if (send_cmd(CMD1, 0) == 0) { // 成功响应 R1=0x00 表示已就绪
return 0;
}
return STA_NOINIT;
f_open() 返回 FR_DISK_ERR 但 SD 卡能初始化
这通常不是硬件问题,而是 FatFs 在找 FAT 表或根目录时读到了非法扇区——根源常出在底层 disk_read() 实现:没处理好 DMA 传输完成标志、SPI 接收缓冲区未清空、或对齐访问越界(比如向非字节对齐地址拷贝 512 字节)。
尤其注意两个细节:
-
disk_read()的buff参数必须是 4 字节对齐(__align(4)),否则 Cortex-M0/M3 的 unaligned access fault 会被静默吞掉,返回随机数据 - 别用局部数组当读缓冲,例如
uint8_t tmp[512]放在函数栈里——小内存单片机栈空间紧张,极易溢出;改用静态缓冲或static uint8_t s_sd_rx_buf[512] - 检查
FF_FS_READONLY是否误设为1:只读模式下f_open(..., FA_WRITE)也会返回FR_DISK_ERR,而非更明确的FR_DENIED
如何让 f_write() 不卡死在小内存环境
默认 FatFs 会把整个簇(通常 4KB)读进内存再修改写回,对 6KB RAM 芯片就是灾难。即使你只写 1 字节,它也可能触发两次 512 字节读 + 一次 512 字节写 + FAT 更新。
真正轻量的写法是关掉缓存、直写扇区:
-
FF_FS_TINY设为1:强制 FatFs 用最小内存模型,放弃文件缓冲区,所有操作直通disk_read()/disk_write() -
FF_USE_FASTSEEK设为0:禁用簇链预读,避免额外扇区读取 - 写前手动调用
f_lseek(fp, offset)定位,比反复f_write()更可控;定位后检查fp->fptr是否真更新,有些移植漏实现lseek逻辑
最易忽略的一点:f_write() 的第三个参数 *bw 是输出实际写入字节数,必须检查是否等于期望值——SPI 写超时或 SD 卡忙时它可能只写了一半,但返回 FR_OK。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











