cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 SDMMC1 + FreeRTOS + FatFs: f_open() blocks forever (DMA RTOS diskio)

geriTricky
Associate II

I’m working on an STM32H7R7 project using SDMMC1 + FreeRTOS (CMSIS-RTOS v2) + FatFs, all generated with STM32CubeMX.

I’m facing an issue where f_mount(opt=0) works, but the system blocks inside f_open() and never returns, so the return value cannot be checked.

A key aspect of the problem is that the behavior depends on how the device is started:

  • After flashing the firmware with a debugger connected and running immediately, the system behaves differently.

  • After a cold power-up (power removed and re-applied, no debugger connected), the same binary consistently freezes when reaching f_open().

In both cases:

  • The code is identical

  • The clock configuration and FreeRTOS setup are unchanged

  • No additional initialization is performed when the debugger is attached

This suggests a timing-, initialization-, or peripheral-state-related issue that manifests only on a true power-on reset and not after a debugger-initiated reset.

Additional observations:

  • f_mount(&SDFatFs, "0:", 1) initially caused a block, but changing to f_mount(..., opt = 0) resolved the mount stage.

  • The freeze now occurs only when f_open() is called.

  • GPIO instrumentation shows that the SDMMC1 interrupt fires during f_open(), but the system never exits the function.

Because the failure happens only after a cold start and not after flashing/debug reset, it appears that some SDMMC, DMA, cache, or RTOS interaction behaves differently between power-on reset and debug reset, even though the application code is the same.

Does anybody have any solution?
Note: I used the newest cubemx (6.16.1)

4 REPLIES 4
AScha.3
Super User

Setting  f_mount(..., opt = 0) is NO solution, because it does not mount here/now !

Set f_mount(&SDFatFs, "0:", 1) , = mount NOW ; this has to work, otherwise it shows : no working access to sd-card.

So first check/modify your hardware and settings , because most problems arise from there.

Try:  

- set all pins for sdmmc to medium speed , pullup on.

- set using no DMA

- custom board? check all lines cpu->card short and about equal lenght

- set 1 bit mode (at least at first, because more forgiving ...)

- set clock for sdmmc (and internal divider) to something < 50MHz , maybe try 10M until its working.

If you feel a post has answered your question, please click "Accept as Solution".
DavidHorton5339
Associate II

This sounds very much like what I've spent the last 2 days debugging. 

The root cause was a cache coherency issue with the SDFatFs structure, which contains a buffer used for data reads/writes, and accessed by DMA. This was not cache-aligned, so adjacent parameters in memory, could be damaged by cache invalidation operations on the misaligned SDFatFs.win buffer. In my case it caused a recently created (but unrelated) xQueue handle to become null, so that operations on it hit an assert and stalled the code. So, my code stalled on the first FatFs operation, just as yours did. Because it is an alignment/caching problem, it comes and goes with the whims of linker allocation and timing.

To resolve it, you need to make sure that the cache maintenance code, and some 'scratch buffer' code that uses a 32-byte aligned intermediate buffer for DMA operations, are both enabled. You will need to uncomment the following  settings in sd_diskio.c (curiously, the settings are not in CubeMX with everything else).

#define ENABLE_SD_DMA_CACHE_MAINTENANCE  1
#define ENABLE_SCRATCH_BUFFER

The scratch buffer code in sd_diskio.c is bugged:

In SD_read, the alignment check:

#if defined(ENABLE_SCRATCH_BUFFER)
if (!((uint32_t)buff & 0x3))
{
#endif

should have the 0x3 changed to 0x1f, as cache alignment requires 32-byte alignment, not 4-byte.

There's another case of this in SD_write, so change that too.

Furthermore, there's a bracketing problem in SD_write with the code under ENABLE_SCRATCH_BUFFER conditional compilation, which is supposed to be invoked when the buffer is misaligned as an else condition for if (!((uint32_t)buff & 0x1f), but is instead an else condition for a DMA success check. This means that the scratch buffer code does not run at all when a buffer misalignment is detected.

To fix it, add an closing brace to this code (lines 488-491)

    #if defined(ENABLE_SCRATCH_BUFFER)
+   }
    else {
    /* Slow path, fetch each sector a part and memcpy to destination buffer */ 

and delete the closing brace from this code (lines 556-561):

    if ((i == count) && (ret == MSD_OK ))
    res = RES_OK;
-   }

   }

   #endif

 

Hope this helps, if you still have the problem.

geriTricky
Associate II

Hi All,

For me what it helped to add these lines:

  SCB_InvalidateDCache();
  SCB_InvalidateICache();
The rest of them were good.
Thanks for the help:):):)
DavidHorton5339
Associate II

Glad you got it working!

Do have a read of https://community.st.com/t5/stm32-mcus-touchgfx-and-gui/different-cache-behavior-between-stm32h7-and-stm32f7/m-p/151165 and beware global DCache invalidation as it can cause corruptions in other memory areas that have writes on their way from cache to memory.

BTW I found a missing SCB_CleanDCache_by_Addr() call prior to the DMA operation in the unaligned code path of SD_write. The aligned code path was OK.

      memcpy((void *)scratch, buff, BLOCKSIZE);
      SCB_CleanDCache_by_Addr((void *)scratch, BLOCKSIZE);
      buff += BLOCKSIZE;

For me, this caused corruptions in filenames and contents.