[STM32U5] 开启数据缓存后 FATFS 的 f_open 失败

[复制链接]
171|25
solty 发表于 2026-9-6 13:44 | 显示全部楼层
你加了修复后还没完整验证,建议重点测试f_mkdir和f_unlink,这两个操作会读写FAT表,涉及的缓冲区偏移更大,很可能也会触发同样的问题。
spicy 发表于 2026-9-3 13:44 | 显示全部楼层
根本解决之道是放弃在sd_diskio.c里做地址运算,改用HAL_SD_ReadBlocks_DSB方式,在DMA完成回调里直接用DSB指令同步数据,比SCB函数更底层也更可控。
Bowclad 发表于 2026-9-4 12:14 | 显示全部楼层
直接用HAL库的DMA功能,在回调里进行数据同步,避免直接操作缓存。
遗忘领域 发表于 2026-9-5 13:30 | 显示全部楼层
DMA读前加Clean操作是对的,但你得确保整个缓冲区32字节对齐,包括FATFS占用的部分。可以试试将FATFS缓冲区定义为全局静态变量,并用__attribute__((aligned(32)))确保对齐。
斧王FUWANG 发表于 2026-9-6 15:05 | 显示全部楼层
这个问题太经典了,STM32U5开D-Cache后跑FATFS基本都会踩这个坑。核心原因就是DMA和Cache的一致性没处理好。你用的SCB_InvalidateDCache_by_Addr确实要求32字节对齐,如果传入的buff地址稍微偏一点,就会把相邻的有效数据给 invalidate 掉,导致文件系统元数据损坏,f_open自然就挂了
发GV第几啊 发表于 2026-9-7 08:06 | 显示全部楼层
其实除了手动算对齐地址,还有个更稳妥的办法。在定义FATFS的工作缓冲区(比如FIL或者FATFS结构体)时,直接用编译器属性强制对齐。比如在GCC下用__attribute__((aligned(32))),Keil下用__align(32)。这样不管FATFS内部怎么偏移,起始地址***是对齐的,能省去很多麻烦
感觉很反感mva 发表于 2026-9-9 10:14 | 显示全部楼层
关于那个地址偏移问题,我觉得根源可能在FATFS的底层实现上。它在调用disk_read的时候,有时候为了对齐扇区,会自己做一个指针偏移。建议你在sd_diskio.c里加个断言或者打印,看看传进来的buff地址到底变成了多少。如果是奇数或者非32倍数,那肯定是要出事的
kzlzqi 发表于 2026-9-11 10:14 | 显示全部楼层
还有一个容易被忽视的点:MPU配置。如果你开启了MPU,一定要把SDRAM或者SRAM中用于DMA缓冲区的区域配置为Non-cacheable或者Write-through策略。虽然这样会牺牲一点性能,但能彻底避免Cache一致性问题,对于调试阶段来说是最省心的
一点点0321 发表于 2026-9-13 09:15 | 显示全部楼层
还有一个容易被忽视的点:MPU配置。如果你开启了MPU,一定要把SDRAM或者SRAM中用于DMA缓冲区的区域配置为Non-cacheable或者Write-through策略。虽然这样会牺牲一点性能,但能彻底避免Cache一致性问题,对于调试阶段来说是最省心的
喂什么玩意 发表于 2026-9-14 11:17 | 显示全部楼层
楼主你只处理了Read的情况,Write操作其实更危险。写的时候如果Cache里有脏数据,DMA直接搬内存里的旧数据写到SD卡,文件就坏了。必须在写之前调用SCB_CleanDCache_by_Addr把脏数据刷下去。而且写完之后,如果CPU马上要读这块区域验证,还得再Invalidate一下,防止读到Cache里的残留
突然下起雨 发表于 2026-9-16 10:20 | 显示全部楼层
这种对齐问题在多线程环境下会更严重。如果一个任务正在做DMA传输,另一个任务刚好操作了相邻的内存变量,一旦触发整行Cache失效,那个变量的值就丢了。建议给DMA缓冲区单独开辟一块内存池,专门做32字节对齐分配,别跟普通变量混在一起
怎么总是重复啊 发表于 2026-9-19 09:02 | 显示全部楼层
其实ST官方后来出的HAL库版本里,针对这个问题有个补丁函数叫BSP_SD_ReadBlocks_DMA的改进版,里面自动处理了对齐逻辑。你可以检查一下你的固件包版本,如果是老版本的CubeU5,建议升级一下,或者去官网找找有没有针对FATFS Cache的Application Note
lxs0026 发表于 2026-9-20 10:22 | 显示全部楼层
对于f_open失败,还有一种可能是文件系统本身的元数据(比如FAT表)被破坏了。因为开启Cache后,如果没及时刷回,断电或者复位时数据还没落盘。建议在每次关键操作后手动调用SCB_CleanDCache,或者干脆把整个缓冲区设为Non-cacheable,虽然慢点,但数据安全第一
大鹏2365 发表于 2026-9-20 11:25 | 显示全部楼层
楼主的代码里alignedAddr计算逻辑有点复杂,容易出错。其实可以用标准库里的函数,比如CMSIS提供的ARM_CACHE_CLEAN_INVALIDATE_BY_ADDR之类的宏(如果有的话),或者自己封装一个安全的Wrapper函数,把对齐检查、边界计算都包进去,别在主逻辑里写这么多位运算,看着眼晕还容易改错
有何不可0365 发表于 2026-9-21 10:15 | 显示全部楼层
另外注意一下,SD卡的块大小通常是512字节,这本身就是32的倍数。所以理论上只要你的buffer起始地址对齐,长度也是512的倍数,就不会有问题。出现不对齐大概率是FATFS层为了优化读取速度,传了一个非扇区起始地址的指针。这时候最好让FATFS自己去处理对齐,底层驱动只接收对齐好的地址
漫天星yl 发表于 2026-9-22 08:26 | 显示全部楼层
如果你用的是FreeRTOS,记得任务栈也是可能作为DMA缓冲区的。默认的任务栈对齐可能只有8字节。如果在任务里定义局部数组做SD卡读写,一定要手动对齐。或者干脆定义一个全局的对齐缓冲区,通过消息队列传递指针,这样最安全
t61000 发表于 2026-9-23 09:28 | 显示全部楼层
看看ST的AN4839应用笔记,里面专门讲了Cortex-M7/M33内核的Cache维护。U5虽然是M33,但原理类似。特别是关于DMA缓冲区的最佳实践,官方推荐了几种模式,照着做能避开90%的坑。别自己造轮子了,这种底层驱动还是稳一点好
xuanhuanzi 发表于 2026-9-8 19:20 | 显示全部楼层
绝大多数问题出在 diskio 磁盘驱动缓存逻辑错误。
班杰明 发表于 2026-9-9 11:20 | 显示全部楼层
建议检查FATFS缓冲区分配方式,考虑使用静态分配或堆分配并使用__attribute__((aligned(32)))保证对齐。
WhisperingTrees 发表于 2026-9-14 13:37 | 显示全部楼层
你说的对,确实应该用Invalidate来处理,而且DMA缓冲区地址对齐问题要注意。我之前遇到过类似问题,通过修改DMA缓冲区分配策略,确保地址对齐,解决了缓存失效问题。试试看吧。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部