☰
STM32CubeMX移植FatFs文件系统:SD卡挂载、读写与调试全攻略
2026/10/16 1:26:07 网站建设 项目流程

拿到一块带SD卡槽的新板子,插上卡,打开STM32CubeMX,勾上FatFs中间件,生成工程,然后f_mount()挂载,结果卡在FR_NO_FILESYSTEM——这几乎是每个刚开始接触“STM32CubeMX移植FatFs文件系统”的人都会遇到的第一课。我之前调SD卡日志功能时也在这里折腾过不少时间,后来把CubeMX侧的配置、FatFs底层的接口、以及调试顺序彻底理清楚之后,一切才顺起来。这篇文章就把我实测下来的一套流程完整写出来,目标是让同阶段的开发者能少走弯路:不但能把SD卡读写跑通,还能知道挂载失败、读写超时这类问题到底该从哪里查起。如果你正在给MCU项目加文件系统,用来做数据记录、参数存储或者固件升级文件缓存,这篇应该正好对口。

1. 开始之前,先想清楚这个移植到底要解决什么问题

1.1 文件系统在MCU项目里到底扮演什么角色

很多第一次接触FatFs的开发者会觉得,文件系统就是“能在SD卡上看到文件树”。这个理解没错,但放到嵌入式场景里,它真正解决的是数据怎么组织和怎么持久化的问题。

如果没有文件系统,你要往SD卡里写数据,通常的做法是直接按扇区偏移,比如第0扇区放编号,第100扇区放日志。听起来简单,但一旦日志长度不固定、文件需要增删、断电后要查上次写到哪里,这一套自己管理的逻辑会越来越复杂,而且很容易出错。文件系统的价值就在于把“底层扇区管理”“目录结构”“文件分配表”这些脏活接管了,你只需要调f_open、f_write、f_read就可以按文件名读写数据,完全不用关心这些数据到底落在卡的哪个物理位置。

在“STM32CubeMX移植FatFs文件系统”这个场景里,核心目标就是让SD卡上的存储变成“可见、可管理、可随时拔走”的文件。MCU把传感器数据写成log/2024-05-20.csv,用户拔出SD卡插到电脑上就能直接看,不需要任何上位机工具来解析裸扇区,这就是文件系统最直观的价值。

1.2 CubeMX在这个移植过程里帮我做了什么

STM32CubeMX给的不只是初始化代码,它在FatFs移植里的作用是“搭骨架”。手动移植FatFs的老流程是:去官网下载FatFs源码,复制ff.c、ff.h等一堆文件进工程,然后自己写diskio.c里的底层接口,再去对接SD卡的HAL驱动。这个流程容易出问题的地方在于:工程文件多、函数名容易冲突、底层对接需要手写卡检测和状态机,稍有不慎就编译不过。

而CubeMX的方式完全不同。你在图形界面里勾上FATFS中间件,选择SD卡接口,它会把三个部分一起处理好:外设驱动、中间件源码、底层适配文件。生成出来的工程里已经包含了FatFs的核心源码ff.c,也包含了和SDIO/SDMMC外设对接的适配层,你要做的不是从零搭一个文件系统,而是理解这个骨架、核对配置、在需要的地方做少量调整。这个模式对实际项目帮助很大,因为它显著降低了移植门槛,也保证了驱动和中间件之间的接口不会出现低级错位。

1.3 从SD卡到文件系统的完整数据通路

我建议你在动手前先画一遍数据通路,这个思路对后面排查太重要了。标准路径是这样的:FatFs应用层调用f_read/f_write,然后进入FatFs核心层,核心层把文件操作转换成扇区读写请求,接着调用底层的disk_read/disk_write,这两个函数再调用STM32的HAL外设接口,最终通过SDIO/SDMMC外设和SD卡上的控制逻辑交互。

注意中间这个“扇区读写”层就是diskio.c,它是整个移植的枢纽。CubeMX生成代码的时候已经把这个枢纽搭好了,但你仍然需要知道它内部做了什么。打个比方,FatFs核心层像一个记账员,只管文件目录和分配表;diskio.c像仓库管理员,记账员说“我要2号仓第5箱”,仓库管理员就去把货搬出来。记账员不关心仓库怎么开门,仓库管理员也不关心账本上写了什么。区分清楚这两层,后面看到FR_DISK_ERR这类错误时,你就知道问题是出在“账本逻辑”还是“仓库搬运”上。

2. CubeMX配置:三个关键设置决定后续代码能不能跑

2.1 外设选择:SDIO/SDMMC 4位模式、DMA和中断

CubeMX里配置SD卡接口时,首先确认你的板子SD卡走的是SDIO还是SDMMC。F1/F4系列大多数叫SDIO,F7/H7系列叫SDMMC,本质都是同一个类型的存储外设,下面表述统称SDMMC接口。如果你用的是带SD卡槽的开发板,原理图上通常标注了SDIO_D0~D3、SDIO_CK、SDIO_CMD这几根信号线,说明走的就是这个接口。如果板子上的SD模块是SPI接口的,那就不是走这里,而是要把SPI外设打开、FatFs中间件选SPI模式,别混为一谈。

在CubeMX的Connectivity分类下找到SDMMC,选择4-bit模式,然后它会自动把D0~D3、CK、CMD这几根引脚映射到对应GPIO。接下来打开两个东西:一是SDMMC的全局中断,在NVIC设置里使能;二是DMA请求,如果你的HAL库版本支持,建议把SDMMC_RX和SDMMC_TX两个DMA通道都加上,并把优先级设成高。原因很简单:FatFs底层读写扇区时,如果用轮询模式等外设完成,CPU会一直在那空转,而且遇到慢卡时可能因为超时算得不对而误报错误。用DMA加中断的方式是在传输过程中把CPU释放出来,卡慢一点也只是DMA等得久一点,不会卡死整个系统。

有个细节容易被忽略:CubeMX生成DMA配置后,DMA中断是要单独在NVIC那边勾选的,很多人只开了DMA通道,但忘了开DMA的中断向量,读卡时程序就卡在等待传输完成。这个现象后面排查心得里还会再提。

2.2 时钟树上那个48MHz为什么是分界点

SD卡的时钟频率不是越高越好。标准SD卡在初始化阶段时钟必须在400kHz以内,初始化完成后才能切换到高速模式;SDIO/SDMMC外设的时钟源通常是PLL生成的48MHz,然后外设内部再通过分频系数降到卡端时钟。CubeMX里配时钟树的时候,经常会看到Warning提示SDIOCLK must be <= 48MHz,这是它帮你把关的地方。

初学时我直接照默认时钟树走,发现SD卡初始化时好时坏,后来才明白问题不一定是代码,而是时钟没配好。以我常用的某型号MCU为例,系统主频跑到168MHz或180MHz,APB2总线84MHz,而SDMMC外设时钟必须从专门的PLL输出取,CubeMX会自动把它导向48MHz。如果你用的板子外部晶振不是8MHz而是25MHz,那PLL参数完全不一样,必须先在Clock Configuration里看SDMMC这个框的数值是不是在48MHz以内。

还有一点,初始化阶段那400kHz的时钟,CubeMX生成的HAL库和中间件会自己处理,不需要你去改分频系数。你只需要保证外设时钟源本身不超标。真有人为了追求速度把SDMMC时钟直接拉到50MHz以上,结果读取大量随机错误,这种问题基本属于自找麻烦。

2.3 FATS中间件选SD卡模式后,CubeMX替你生成了哪几层代码

中间件部分你要找的是Middleware下的FATFS选项。进去之后有个Mode选项,选择SD Card(有的版本叫SDMMC)。选完之后,CubeMX会把FatFs相关的文件加入工程:核心层是ff.c、ff.h、ffconf.h,磁盘I/O层是diskio.c、diskio.h。

以我用的某一版环境为例,生成出来的SD卡适配文件叫fatfs_sd.c,里面提供了disk_initialize、disk_status、disk_read、disk_write、disk_ioctl这几个函数,还有平台初始化相关的逻辑。另一部分工程里会生成user_diskio.c之类的文件,用来连接FatFs的通用接口。不同CubeMX版本生成的文件名可能不同,但职责一样:一个是“底层对接层”,一个是“平台模块初始化和卡检测”。你在理解代码时不要死记文件名,而是认准这几个函数的接口语义。

生成代码之后,打开fatfs.h和fatfs_sd.c看一眼。尤其注意那个SDPath变量,它定义了FatFs的盘符路径。有的版本是const char *SDPath = "0:/",有的版本是数组形式。后面挂载文件系统时要引用它,如果直接复制网上的旧代码用""挂载,在CubeMX生成的工程里不一定编译过。

3. 生成代码之后,文件系统的配置和底层接口还差什么

3.1 ffconf.h里值得改的几项配置

ffconf.h是FatFs的配置文件,CubeMX生成时会按默认模板放一份进来。默认值能用,但不一定适合你的项目。我最常改动的配置项列出来供参考:

  • FF_USE_LFN:长文件名支持。0表示只支持短文件名,1表示用静态缓冲区支持长文件名,2表示用堆分配缓冲区。SD卡数据记录场景强烈建议设成1或2。否则你创建temperature_2024.csv这种名字,系统会直接拒绝或者自动截断成老式8.3短文件名,后面程序里对文件名做判断时很容易对不上。
  • FF_USE_STRFUNC:设为2,这样就能用f_printf往文件里格式化写数据,做日志功能非常方便,不用手动拼字符串再调f_write。
  • FF_USE_MKFS:设为1,才能调用f_mkfs格式化SD卡。调试阶段这块几乎是必须的,不然卡里没有FAT文件系统时,你没有快速初始化手段。
  • FF_MAX_SS:扇区大小上限。大多数SD卡是512字节,新卡有些是4KB物理扇区。如果你的卡格式化成4K扇区,FF_MAX_SS还只设512,读取会失败。拿不准就设成4096,代价是缓冲内存略微变大。
  • FF_FS_READONLY:默认0,保持可写就行,不用动。
  • FF_CODE_PAGE:涉及字符编码。如果你要写中文文件内容或中文文件名,需要选简体中文对应代码页,具体数值查FatFs文档即可;纯英文日志用默认437就够。

这些配置项改完以后,重新生成代码会覆盖ffconf.h,要注意每次生成完检查一遍,别指望全靠CubeMX记住你的配置。我的习惯是在工程模板里保存一份ffconf.h备份,CubeMX重新生成后直接比对差异或覆盖回去。

3.2 diskio底层函数在CubeMX产物里的正确打开方式

理解了diskio.c里每个函数的语义,才算真正掌握了移植。以disk_read为例,它接收的参数包括扇区号sector、字节缓冲buff、以及要读的扇区数count。这个函数要做的事就是把请求转成SD外设的具体操作。CubeMX生成的代码里,这个函数通常长这样:

DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { if (pdrv != 0) return RES_PARERR; /* 确保卡准备好了再读 */ if (!card_ready()) return RES_NOTRDY; /* 调HAL库的阻塞读接口 */ if (HAL_SD_ReadBlocks(&hsd, buff, sector, count, HAL_MAX_DELAY) != HAL_OK) return RES_ERROR; return RES_OK; }

这里有几个坑。第一,buff指针必须是4字节对齐的,因为DMA访问要求对齐;如果你传入一个未对齐的局部数组,可能会在DMA传输时出现总线错误。第二,阻塞式读传了HAL_MAX_DELAY,虽然理论上会等到完成,但如果函数返回后你直接操作buff,要确认HAL内部已经处理完数据。在F4等不带D-Cache的芯片上还好,如果换到H7这种带D-Cache的芯片,问题就来了:DMA会把数据写进SRAM,但CPU可能还读取到Cached旧数据,必须在读完后做Cache一致性处理。

disk_write类似,但它多了一步写保护检查。很多卡套和卡槽有写保护开关,写保护状态下直接调HAL_SD_WriteBlocks会失败。disk_status返回STA_NOINIT或STA_PROTECT,如果卡没插好,这里很容易被调用到。CubeMX生成的代码里通常有一个FindDrive或者类似的函数处理卡在位检查,如果卡检测引脚没接,这个函数的表现就是永远找不到卡,这个时候需要直接改底层,把“卡检测”做成始终有效,否则挂载永远不成功。

3.3 内存、堆栈和缓冲区对齐的隐藏要求

FatFs需要的运行内存比很多人想象的大。文件系统的工作区、文件对象的缓存、以及读写的扇区缓冲,都会占用RAM。CubeMX生成工程时默认的堆栈不一定够,尤其是开了长文件名、用了f_print这类功能后,栈容易爆。我一般会把工程启动文件里Stack_Size和Heap_Size都上调,栈设成0x1200、堆设成0x1000都不算大。栈太小会导致程序跑着跑着卡在异常里,而且问题不好复现,因为只有访问深度到某个程度才触发。

缓冲区对齐的问题单独说。除了disk_read接收的buff要对齐,你自己使用的文件数据缓冲也应这样声明:

static uint8_t file_buffer[512] __attribute__((aligned(4)));

这行声明能避免很多麻烦。如果底层用的是DMA方式读写,建议直接对齐到__attribute__((aligned(8))),在高性能芯片上更稳。另外,在堆空间比较大的时候,配合FF_USE_LFN=2让FatFs从堆里分配长文件名缓冲,可以省下不小的静态内存。

这部分我踩过的坑记忆很深刻:开局调好一切,文件创建正常,但读回来的数据一开始是好的,后面会偶尔错乱,查了两天才意识到是DMA和Cache一致性问题。所以如果你用的是H7,按下面方法处理:给FatFs操作的RAM区配置成non-cacheable,或者每次disk_read之后调用SCB_InvalidateDCache_by_Addr,disk_write之前调用SCB_CleanDCache_by_Addr。具体哪个方案看项目内存布局,但这条一定要记得。

4. 从格式化到读写文件:一条可以照抄的最小实现

4.1 工程里从CubeMX到能挂载文件系统的最小步骤

如果你不想纠结版本细节,直接按下面这套参考流程走,适用于主流STM32F4/F7/H7系列。我拿某个实际测试过的工程配置来说,步骤没有省略:

  1. 新建工程,选一个带SDMMC外设的芯片型号。以某F4系列的芯片为例,外部时钟配置成24MHz或25MHz晶振,Clock Configuration里让HCLK到168MHz左右,确认SDIO时钟标称值在48MHz以内。
  2. 在Connectivity里使能SDMMC,选4-bit模式,检查引脚映射和原理图一致。通常默认映射是可用的,如果板子设计不同,你需要手动重新分配。
  3. 在NVIC里打开SDMMC中断,DMA设置里增加SDMMC_RX和SDMMC_TX两个请求,并把对应的DMA中断也打开。
  4. 到Middleware里打开FATFS,Mode选SD Card。
  5. 资源管理里确认栈和堆够用,我建议栈0x1000以上,堆0x800以上,具体大小参考你开的长文件名配置。
  6. 选择你的工具链,生成代码。

生成完直接编译,正常情况下会通过。然后你打开主循环,写一个测试函数,调几个FatFs接口。第一步是挂载文件系统。CubeMX生成的工程里会提供一个文件系统对象和路径,直接引用过来用就行:

extern FATFS SDFatFS; extern const char *SDPath; void fs_init_test(void) { FRESULT res = f_mount(&SDFatFS, SDPath, 1); if (res != FR_OK) { /* 挂载失败,注意打印错误码 */ } }

f_mount的第三个参数1表示挂载时立即尝试访问磁盘,0表示延迟到第一次文件操作时才真正访问。调试阶段强烈建议用1,因为这样挂载失败的时机可控,能立刻定位问题。

4.2 建目录、写日志、同步落盘的最小代码片段

挂载成功之后,就可以开始做实际功能了。以写一个传感器日志文件为例,代码量很少:

void write_log_demo(void) { FATFS SDFatFS; FIL file; FRESULT res; UINT bytes_written; res = f_mount(&SDFatFS, SDPath, 1); if (res != FR_OK) { return; } res = f_open(&file, "0:/sensor.csv", FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) { return; } f_printf(&file, "timestamp,temperature,humidity\r\n"); for (int i = 0; i < 100; i++) { f_printf(&file, "%d,%d,%d\r\n", i, i * 3, i * 5); } f_sync(&file); /* 确保数据真正落盘 */ f_close(&file); }

看到f_sync了吗?很多人会忽略这步。f_write之后数据可能还在FatFs的缓冲区里,直接f_close虽然也会做一部分同步,但在断电前调用f_sync更保险。如果你边采集边写日志,建议每写满一批数据就f_sync一次,格式比如每50条记录同步一次。频率太高会频繁擦写SD卡,降低寿命;频率太低,一旦系统断电,最后一段数据大概率丢失。日志型应用中我一般把缓冲区定在512字节到4KB之间,写满就同步,这个策略兼顾了速度和可靠性。

目录创建也是一样:

f_mkdir("0:/log"); res = f_open(&file, "0:/log/run_001.csv", FA_CREATE_ALWAYS | FA_WRITE);

FatFs的f_mkdir只能一次创建一级目录,如果log/2024/05这种多级目录不存在,需要逐级调用。这个细节经常导致目录创建失败,排查时要注意是FR_NO_PATH。

4.3 快速验证:写一串数据读回来对比

测试写文件是否成功,很多人写完就拔卡插电脑看,但这样效率低,尤其是你人在工位、电脑离板子远的时候。我建议在单片机里直接把文件读回来做对比,这样能同时验证读写通路:

void verify_log_demo(void) { FIL file; char buf[64]; UINT bytes_read; FRESULT res; res = f_open(&file, "0:/sensor.csv", FA_READ); if (res != FR_OK) { return; } while (f_gets(buf, sizeof(buf), &file) != NULL) { /* 把 buf 通过串口打印出来 */ } f_close(&file); }

这里需要注意f_gets读取的是文本行,前提是你用f_printf写入的是文本内容。如果文件里是二进制数据,f_read配合bytes_read返回值来做循环更合适。如果你在串口上看到读回来的内容和写入的一致,这个移植基本就通了。

5. 移植与调试中高频出现的问题,以及我常用的排查顺序

5.1 f_mount不成功,先查卡物、再查初始化、最后查时钟

f_mount返回非FR_OK的排查顺序,我建议严格按这个来:硬件连接,卡初始化,最后才是时钟和代码逻辑。

第一步,把SD卡插电脑上格式化成FAT32,再用读卡器确认卡本身能用。别笑,这一步我做过太多次,很多所谓“移植问题”最后发现是卡早就坏了,或者卡是NTFS格式而FatFs根本不支持。FatFs兼容FAT12/FAT16/FAT32,exFAT需要额外配置且不一定在你的中间件版本里默认打开。NTFS别考虑了,直接不能用。

第二步,确认初始化是否成功。在disk_initialize函数里加串口打印,或者直接调一次HAL_SD_Init(&hsd)并判断返回值。如果初始化返回HAL_ERROR,多半是卡没插到位、卡座接触不良、或者SDMMC引脚映射错了。很多廉价卡座的D3引脚本来就悬空,四线模式会出问题,可以在CubeMX里改成1-bit模式试一下,这样只需要D0、CMD、CLK三根线,能快速排除接线因素。

第三步,确认时钟。初始化阶段要求低速,如果你把SDMMC外设时钟配得过高,或者PLL配置异常,卡初始化就是不稳定。拿个示波器或者逻辑分析仪看SDMMC_CK有没有输出,没有输出就回到时钟树那里查。

如果查完这些都正常,f_mount还是返回FR_NO_FILESYSTEM,那基本可以确定是卡里没有文件系统。直接在代码里调用f_mkfs格式化一次:

static uint8_t work_buffer[4096] __attribute__((aligned(8))); res = f_mkfs(SDPath, FM_FAT32, 0, work_buffer, sizeof(work_buffer)); if (res != FR_OK) { /* 格式化失败,多见于写保护或底层写入失败 */ }

注意:格式化会清空卡里所有数据,调试时无所谓,量产的时候绝不能随便执行。

5.2 能挂载但读写出错,DMA与缓存对齐是重灾区

文件系统挂载成功了,说明底层初始化和读取第一个扇区没有问题。但如果f_read返回FR_DISK_ERR,或者数据错乱,重点检查下面三件事。

第一件,缓冲区是否对齐。FatFs传给disk_read的缓冲有时来自FIL对象内部缓冲区,有时候来自用户手动指定。如果你直接在函数栈上声明了一个BYTE buffer[512],然后调f_read,编译器不会保证这个数组对齐到4字节,在启用DMA的外设上就可能出错。解决方式是显式指定对齐属性,像前面代码块里写过的那样。

第二件,DMA传输是否真的完成了。CubeMX生成的底层代码如果走DMA,disk_read里的HAL_SD_ReadBlocks_DMA是异步发起的,紧接着必须等HAL_SD_ReadBlocks_IT或通过信号量等待传输完成回调。如果代码里没有等待,disk_read可能会返回RES_OK,但实际上DMA还没搬完数据,后面的数据自然不对。看到这里,你打开fatfs_sd.c看一眼它究竟是阻塞调用还是DMA调用,就能判断问题在哪。

第三件,Cache一致性。如果你用的是Cortex-M7内核芯片,SDMMC DMA写回内存之后,CPU读到的可能是Cache中过期的数据。在disk_read完成之后调用SCB_InvalidateDCache_by_Addr这一句,能让大部分诡异问题消失。写过H7的人应该对这个非常有共鸣。

5.3 掉电丢数据:很多人没意识到的f_sync问题

我调试时遇到过一种情况:写了很多条日志,程序也正常运行,但断电重启后最后几十条数据凭空消失。追了很久的代码,最后定位到根因是f_write之后没有及时f_sync。

FatFs为了提高写入效率,会在内存里保留一个扇区缓冲。当你调用f_write把数据交给FatFs,数据可能还在这个缓冲里,还没真正搬到SD卡上。只有等缓冲满了、或者手动调用f_sync、或者f_close的时候,数据才真正落盘。但f_close也要谨慎理解,它是写完后再同步。如果你的程序在等待状态下突然断电,中间那些没满缓冲的数据自然就丢了。

实际项目里,如果日志实时性要求高,最佳做法是写完一个数据块立刻f_sync。如果为了SD卡寿命考虑,可以写满一个扇区再同步,但你要能接受最多丢一个扇区大小的数据。这是需要在可靠性和寿命之间做取舍的典型场景。我在做法上两档可选:普通日志写满512字节同步,关键事件数据单条同步。

5.4 一张速查表解决80%的移植问题

把常见现象、判断思路、处置方法整理成一张表,排查问题时直接对照,效率会高很多。

现象常见原因排查顺序
f_mount返回FR_NO_FILESYSTEM卡未格式化,或格式不是FAT/FAT32插电脑看格式,或f_mkfs格式化
f_mount返回FR_NOT_ENABLED底层初始化未完成,盘符路径不对检查disk_initialize返回,核对SDPath
f_mount返回FR_INVALID_DRIVE盘符路径或pdrv参数不对检查传入的路径是否以“0:/”开头
卡初始化时好时坏时钟偏快或接触不良调低SDMMC时钟,检查卡座
f_read返回FR_DISK_ERRDMA未完成、缓存不同步、扇区格式错检查缓冲对齐,做Cache操作,确认扇区大小
写文件后断电丢数据没调f_sync关键数据写后同步
创建目录失败父目录不存在逐级f_mkdir
文件名带中文乱码代码页配置不对修改FF_CODE_PAGE
能读但读出的数据大小不对用错了f_read的返回值,未循环读取用bytes_read循环直到读完
程序运行中死机栈溢出或堆耗尽增大Stack/Heap,检查长文件名分配方式

这个表不能覆盖所有情况,但覆盖了大部分实际开发里遇到的低频又难查的问题。每次遇到问题如果对不上表,我都会先回到数据通路图里问自己一句:是在哪一层断的?断了以后是上层报错还是底层报错?这样定位通常不会跑偏太多。

6. 移植完之后的扩展思路:文件系统能承载的不只是日志

6.1 参数导入导出

文件系统一旦跑通,下一个价值点就是参数导入导出。以前调试设备参数都是直接改代码重新烧录,有了SD卡文件系统,可以做一个“配置导入”功能:SD卡里放一个config.ini或config.csv,程序启动时挂载文件系统,读取每一行配置,解析成对应的变量。

举个例子,设备校准参数、PID系数、采集间隔这些内容,以前只能写在单片机常量区,改动一次就要重新编译烧录。现在只要打开文件改几行,插回板子重启,参数就自动更新。这个功能对样机调试和小批量生产尤其实用。解析文本配置的时候,我推荐用f_gets逐行读取加sscanf解析,不要在MCU上直接引入复杂文本解析库,资源有限时得不偿失。

6.2 固件升级文件的暂存

另一个常见方向是固件升级。很多设备没有联网能力,但用户手里有一张SD卡。设计人员把新固件文件放在SD卡指定目录,设备上电时检测到该文件,完成校验后直接把固件拷贝到内部Flash中的应用分区,然后重启进入新程序。文件系统在这条链路里的作用是验证文件完整性,以及把升级流程做得更贴近“拷贝文件”这种用户直觉。

用FatFs做升级文件搬运时,要特别注意文件读取的缓冲区大小。固件文件通常上百KB,甚至几MB,单片机的RAM不可能一次装下整个文件,所以会按块读取、按块写入内部Flash。块大小一般选和扇区缓冲区一致,读一段写一段,避免一次性申请大块内存。

6.3 在应用设计里给FatFs和SD卡留好余量

如果项目对可靠性要求高,比如数据记录仪、车载设备,我会建议再往前想一步。文件系统部署好后,最好给SD卡损坏、热插拔、低电压这几个场景做防御。硬件设计上加卡检测引脚,软件上在disk_status里多判断一步;逻辑上加“写文件失败后自动重试”或者“文件损坏后自动换新文件”的机制。别觉得医院那些总控设备怎么那么矫情,数据存下来拿不到就是事故。我自己最常用的做法是双日志文件策略:一个文件写满了就关闭,切换到另一个,同时保留最近的版本,这样即使某一个文件因为异常断电损坏,另一个还能作为数据备份。这套设计在稳定性要求高的项目里能节省大量售后排查时间。

移植FatFs的工程量本身不大,真正决定系统稳不稳定的,是你对底层接口的理解、对CubeMX配置的把控、以及对可靠性的取舍。希望这篇分享能帮你在“STM32CubeMX移植FatFs文件系统”这条路上少花几天无意义的搓板时间。

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

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

立即咨询