简介本资源面向嵌入式开发初学者与进阶工程师提供在STM32平台上通过SPI接口驱动SD卡、移植FATFS文件系统并生成CSV数据文件的完整工程。项目以STM32F429高性能型号为硬件基础涵盖SPI主模式配置、FATFS工作区与扇区参数设定以及f_open、f_printf、f_close等API的读写流程同时涉及文件打开失败、内存不足、写保护等错误处理与资源优化策略。压缩包共1482个文件以629个C源文件、382个头文件、144个工程描述文件及144个编译中间文件为主另含库文件、启动代码、IOC配置与HEX固件等整体约59.43MB目录结构完整便于直接编译与二次开发。目前已有1113人学习下载。读者可从中掌握STM32文件系统集成方法、CSV格式数据记录技巧及嵌入式存储方案设计思路适合作为课程设计、数据采集项目或自学练手的参考工程。1. STM32 挂 SD 卡跑 FatFs 并生成 .csv一条被低估的数据落盘链路做数据采集的嵌入式项目绕不开一个朴素需求把传感器读数按时间顺序存成一张表插到电脑上双击就能用 Excel 打开。STM32 SD 卡 FatFs .csv 这套组合就是满足这个需求最稳、成本最低的方案。它解决的不是能不能存的问题而是存出来的文件能不能被 PC 直接识别、能不能边采边写不丢数据、断电后文件还能不能读这三个真问题。适合谁做环境监测、电机参数记录、电池充放电曲线、毕业设计里要交数据表的人。很多人卡在 f_write 返回 FR_OK 但文件是空的或者写了几百行后卡死本质是没搞懂 FatFs 的缓冲机制和 SD 卡的块写入特性。这篇按我实际调过的路径从 CubeMX 配置讲到 csv 格式化写入把参数和坑一次说清。2. 先搞懂 FatFs 在 STM32 上到底怎么跑起来2.1 三层结构SD 卡驱动、磁盘 IO 层、FatFs 内核FatFs 不是一个能独立运行的库它是一套文件系统逻辑底下必须挂一个磁盘 IO 层diskio.c再底下才是真正的 SD 卡物理驱动。这三层的关系决定了你排错时该看哪一层。最底层是 SD 卡通信。STM32 接 SD 卡有两种模式SPI 模式和 SDIO 模式。SPI 模式接线简单CS、SCK、MISO、MOSI 四根线速率一般能到几 Mbps 到十几 Mbps适合中低速采集SDIO 模式是 4 位并行速率能到 20MB/s 以上适合高速连续写。新手我建议先用 SPI 跑通因为 SPI 的时序问题好抓逻辑分析仪一挂就看得见SDIO 一旦初始化失败报错信息很含糊。中间层是 diskio.cFatFs 通过 disk_initialize、disk_read、disk_write、disk_ioctl 这四个函数跟硬件打交道。你要做的就是把 SD 卡的块读写函数填进去。注意一个关键点FatFs 读写的最小单位是扇区通常 512 字节disk_write 一次可能要求写多个扇区你的驱动必须支持连续多块写不能只写一块。最上层才是 f_open、f_write、f_close 这些 API。FatFs 内部有窗口缓冲默认 512 字节你调 f_write 写 20 字节它不一定立刻落到卡上而是先攒在缓冲里等攒满一个扇区或者你调 f_sync 才真正写卡。这就是为什么写完不关文件拔卡一看是空的——数据还在 RAM 缓冲里。2.2 CubeMX 里把 SDIO/SPI 和 FatFs 勾上以 SDIO 模式为例CubeMX 里的操作路径是Connectivity 里选 SDIO模式选 SD 4-bit 或 1-bitMiddleware 里选 FATFS勾上 SD Card。生成代码后CubeMX 会自动帮你把 diskio.c 里的 SD 卡读写接到 HAL_SD 驱动上省掉大量手写工作。几个必须确认的参数配置项推荐值说明SDIO 时钟分频初始化阶段用低速跑通后再提分频系数太小会导致初始化失败Bus Width4-bit硬件支持时1-bit 更稳但慢FATFS 扇区大小512与 SD 卡物理扇区一致_USE_LFN1 或 2需要长文件名就开占 Flash_FS_TINY0用独立窗口缓冲性能更好_FS_REENTRANT0单任务裸机不用开生成后先别急着写 csv先跑一个挂载 列目录的最小验证FATFS fs; FIL fil; FRESULT res; UINT bw; res f_mount(fs, 0:, 1); // 1 表示立即挂载 if (res ! FR_OK) { printf(mount fail: %d\r\n, res); // 常见 3FR_NOT_READY, 13FR_NO_FILESYSTEM return; } printf(mount ok\r\n);这段代码的逻辑是f_mount 的第三个参数为 1 时会立刻调用 disk_initialize 和读引导扇区。如果返回 FR_NOT_READY值为 3说明卡没初始化成功去查 SDIO 时钟和供电如果返回 FR_NO_FILESYSTEM值为 13说明卡上没有 FatFs 能识别的分区表需要用电脑重新格式化成 FAT32。这一步过了后面才有意义。2.3 从 f_open 到 f_write一次完整落盘的最小闭环挂载成功后写文件的标准流程是 open → write → close中间可以穿插 f_sync。下面是一个能直接抄的最小例子res f_open(fil, 0:/data.csv, FA_CREATE_ALWAYS | FA_WRITE); if (res ! FR_OK) { printf(open fail: %d\r\n, res); return; } char header[] timestamp,temp,voltage\r\n; res f_write(fil, header, sizeof(header) - 1, bw); if (res ! FR_OK || bw ! sizeof(header) - 1) { printf(write header fail\r\n); } char line[64]; int n snprintf(line, sizeof(line), %lu,%.2f,%.3f\r\n, HAL_GetTick(), 25.6f, 3.72f); res f_write(fil, line, n, bw); f_sync(fil); // 强制把缓冲刷到卡上 f_close(fil); // 关闭并更新目录项逻辑说明FA_CREATE_ALWAYS 表示文件存在就覆盖适合每次上电重新记录如果你要追加用 FA_OPEN_ALWAYS 并把读写指针移到末尾f_lseek(fil, f_size(fil))。f_write 的第四个参数 bw 是实际写入字节数必须检查它等于你期望的长度否则说明卡写满了或出错。f_sync 是关键它把 FatFs 内部缓冲和目录项都刷到物理卡上代价是每次调用都会触发实际写操作频繁调用会拖慢速度。参数上snprintf 的缓冲区大小要留够一行 csv 一般不超过 128 字节。浮点用 %.2f 格式化注意有些精简版 newlib 默认不带浮点 printf需要在工程里开 Use float with printf 选项否则打出来是空的或者乱码这个坑我踩过。3. 把传感器数据写成标准 .csv 的几个关键动作3.1 csv 格式规范逗号、换行、转义csv 看着简单但要让 Excel 和 pandas 都能正确解析有几条硬规则。字段之间用英文逗号分隔行尾用 \r\nWindows 风格兼容性最好纯 \n 在部分 Excel 版本里会全部挤在一行。如果某个字段本身含逗号或引号要用双引号把整个字段包起来内部的双引号写成两个。传感器数据一般是数字不涉及转义但时间戳如果格式化成 2024-01-01 12:00:00 这种带空格的建议也加引号。表头建议第一行就写清楚列名别省。我见过有人为了省空间不写表头结果数据交上去没人看得懂哪列是什么。表头用英文或拼音避免中文编码问题——FatFs 默认不做 UTF-8 BOM 处理中文表头在 Excel 里可能乱码。3.2 用 snprintf 拼行避免 f_printf 的坑FatFs 提供了 f_printf用起来像 printf但它内部实现比较简陋对浮点和长整型的支持依赖配置而且每次调用都有函数开销。我一般用 snprintf 先拼到内存缓冲再一次性 f_write这样既可控又快。typedef struct { uint32_t ts; float temp; float voltage; } Sample_t; void write_sample(FIL *fp, Sample_t *s) { char buf[80]; int len snprintf(buf, sizeof(buf), %lu,%.2f,%.3f\r\n, (unsigned long)s-ts, s-temp, s-voltage); if (len 0 || len sizeof(buf)) return; // 格式化失败或截断 UINT bw; FRESULT r f_write(fp, buf, len, bw); if (r ! FR_OK || bw ! len) { // 记录错误考虑重试或停止采集 } }参数说明buf 大小按最长行估算80 字节够存时间戳加两个浮点。snprintf 返回的是本该写入的长度如果它大于等于 buf 大小说明被截断了这行数据不完整应该丢弃或扩大缓冲。f_write 之后检查 bw 是必须的SD 卡写失败比如接触不良时 bw 会小于 len。3.3 采集频率和缓冲策略多久 sync 一次这是性能和可靠性的核心权衡。f_sync 太频繁每次都要写卡SD 卡单次写有毫秒级延迟1kHz 采集根本扛不住f_sync 太少断电丢的数据多。我的经验值采集频率 10Hz 以下每行写完就 sync简单可靠10Hz 到 100Hz攒 10 到 50 行 sync 一次100Hz 以上用双缓冲加 DMA采集和写卡分离写卡任务按扇区批量写。裸机环境下可以用一个环形缓冲主循环里判断缓冲里攒够 N 行就批量 f_write 一次再定期 f_sync。#define FLUSH_EVERY 20 static int cnt 0; void maybe_flush(FIL *fp) { if (cnt FLUSH_EVERY) { f_sync(fp); // 每 20 行刷一次 cnt 0; } }注意 f_sync 返回 FR_OK 才代表真的落盘了如果返回错误说明卡可能已经掉了这时候继续写只会一直失败应该报警并停止。4. 避坑与排查那些让文件写不出来的真凶4.1 现象f_write 返回 FR_OK 但文件是 0 字节原因数据还在 FatFs 的窗口缓冲里没调 f_sync 也没 f_close或者调了 f_close 但没检查返回值。另一个可能是 f_open 用了 FA_READ 却去写。解决写完必须 f_close或者中途 f_sync。检查 f_open 的模式标志里有没有 FA_WRITE。用 f_size 查一下当前文件大小确认。4.2 现象写几百行后卡死f_write 一直返回 FR_DISK_ERR原因SD 卡写块时出现超时常见于 SPI 模式下时钟太快、卡质量差、或者供电不足。也可能是 FatFs 的 _MAX_SS 配置和实际扇区不匹配。解决降低 SPI/SDIO 时钟初始化阶段用 400kHz 以下稳定后再提。给 SD 卡供电加 100uF 以上电容。检查 diskio.c 里 disk_write 的超时返回值别让它死等。4.3 现象文件在电脑上打不开提示文件系统损坏原因写卡过程中直接拔卡或断电目录项FAT 表没更新完整。FatFs 的目录项是在 f_close 或 f_sync 时才写的。解决采集结束前一定走正常 f_close 流程。如果必须断电至少保证定期 f_sync。可以在文件末尾写一个结束标记上位机解析时校验。4.4 现象csv 里浮点数变成 0.00 或乱码原因工程没开浮点 printf 支持snprintf 对 %f 处理不了。或者 float 在传参时被隐式转成 double栈空间不够。解决在 IDE 的链接选项里勾选支持浮点的 printfKeil 里是 Use MicroLIB 加 float 选项或标准库加 --fpmode。或者干脆把浮点乘 100 转成整数再格式化避免依赖。4.5 现象f_mount 返回 FR_NO_FILESYSTEM原因卡是 exFAT 或 NTFS 格式FatFs 默认只认 FAT12/16/32。或者卡根本没分区表。解决用电脑把卡格式化成 FAT32分配单元大小选 4096 或 512。大容量卡64G 以上Windows 默认给 exFAT需要用工具强制格成 FAT32。5. 进阶让 csv 落盘更快、更稳、更好用跑通基础流程后真正拉开差距的是吞吐和可靠性。我一般会做三件事。第一把写卡从主循环里挪出去。用一个环形缓冲采集中断或定时器往里塞数据主循环或 DMA 完成回调里批量取出来写卡。这样采集频率和写卡速度解耦1kHz 采样配 100ms 批量写完全可行。环形缓冲的大小按最坏情况下写卡阻塞时间 × 采集速率估算比如写一次卡最多 50ms1kHz 采集那缓冲至少要能存 50 个样本留一倍余量取 128。第二文件名带时间戳或序号避免覆盖。用 RTC 取日期拼成 log_20240101_120000.csv或者用序号递增。FatFs 支持 f_stat 检查文件是否存在存在就换名。第三加一层数据校验。每行末尾可以加一个简单的累加和或者 CRC上位机解析时校验能发现传输或存储过程中的位翻转。对可靠性要求高的场景还可以写两遍或者用日志式追加。验证方法上我习惯用两个手段一是写完把卡插电脑用 Excel 和 Python 的 pandas 各读一遍确认行列对齐、数值范围合理二是故意在写入过程中断电重启后看文件能不能打开、已 sync 的数据在不在。这个断电测试最能暴露缓冲策略的问题。最后说个我自己的习惯每次新板子第一次跑 SD 卡我一定先用示波器看 SDIO 的 CLK 和 CMD 线确认初始化时序正常再去看 FatFs 的返回值。因为 FatFs 的报错永远是上层现象真正的根因八成在物理层。这套链路调通一次后面换传感器、换采样率都是改改格式化字符串的事。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
51单片机超声波测距系统Proteus仿真与实现 简介:这是一份基于51单片机的超声波测距系统仿真资源,面向单片机初学者及课程设计人员,帮助掌握HCSR04超声波测距原理与LCD1602显示驱动的完整实现。压缩包共31个文件,约102KB,包括C源码(main.c、hcsr04.c等… · 2026/11/3 7:52:01
51单片机超声波测距系统仿真:HC-SR04与LCD1602完整实现 简介:基于51单片机的超声波测距系统仿真工程包,面向51单片机入门学习者与电子爱好者,以HCSR04超声传感器完成距离检测,并用LCD1602显示屏实时呈现测量数值。配套Keil开发工程与Proteus仿真电路,可在虚拟环境下验证程序… · 2026/11/3 7:52:01
“臻”未来招聘主题项目:从口号到落地全流程解析 “臻”未来——等你改变世界,这句话放在招聘页面上,第一眼看上去像一句宏大的口号。但干过招聘的人都懂,越是这种带点理想主义的文案,背后越是要有实打实的东西托底,否则候选人面试完出门就会觉得被骗了。我过去几年参… · 2026/11/3 7:51:01
宏智树AI如何革新学术研究流程 1. 项目概述:重新定义AI辅助学术研究的边界 去年帮导师带本科生论文时,发现一个有趣现象:学生们使用AI工具时普遍存在两极分化——要么完全依赖生成内容导致学术事故,要么因担心抄袭彻底拒绝技术辅助。这促使我系统测试了市面上12… · 2026/11/3 15:56:52
OIF-CEI-05.2电气契约解析:高速SerDes互操作的物理层底线 简介:本资源为光学互联论坛(OIF)于2024年1月正式发布的最新版CEI高速电气接口标准——OIF-CEI-05.2实施协议官方PDF文档,面向芯片设计、高速互连系统开发、数据中心架构及光模块研发等领域的工程师与标准研究者。该标准统一规范了… · 2026/11/3 15:56:52
AI学术研究助手:从文献检索到论文写作的全流程解决方案 1. 项目概述:重新定义AI在学术研究中的角色"数字科研伙伴"这个概念正在颠覆传统认知中AI辅助写作的工具属性。作为一名在学术写作和AI交叉领域深耕多年的研究者,我见证了从早期简单的语法检查工具到如今具备全流程研究辅助能力的智能系统演进过… · 2026/11/3 15:56:52
nerf-slam复现编译排错:依赖、CMake与CUDA/torch 简介:面向希望从零复现 NeRF-SLAM 的研究者与三维重建方向的学习者,这份 docx 文档以实操记录加原理解读的方式,梳理了神经辐射场与 SLAM 结合的完整链路。包内共 1 个 docx 文件,约 11.01MB,内容兼顾公式推导与工程细… · 2026/11/3 15:56:52
CSP-J/S第一轮分数线深度解析:省际差异、划线逻辑与预估方法 每年到了第一轮成绩公布那几天,后台总会被同一类问题刷屏:某个省今年大概多少分能进第二轮、为什么隔壁省分数差那么多、孩子考了多少分到底稳不稳。这类问题看着简单,真要回答清楚,得把分数线背后的东西掰开揉碎讲。2019年那一届… · 2026/11/3 15:56:52
链表操作精要:快慢指针与回文判断实战 1. 链表基础与问题概述 链表作为一种基础数据结构,在实际编程和算法面试中出现频率极高。今天我想和大家分享两个经典的链表问题:寻找链表的中间节点(876题)和判断链表是否为回文(234题)。这两个问题看似简… · 2026/11/3 15:55:51
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46