FatFS disk_read() / disk_write() DMA STM32

anonymous wrote on Thursday, May 24, 2012:

I am using a STM32 Cortex-M3 and just recently got FatFS up and running with a SD card in SPI mode.  I would like to modify the disk_read() and disk_write() functions to use DMA transfers.  I understand how to use the DMA peripherals to transfer the data, and how to enable the DMA interrupts so they are triggered when the transfer is complete.

How should I ensure that the disk_read() / disk_write() functions don’t try to trigger another DMA transfer when one is already taking place?

I have read the user manual and searched the forums and it seems like what I need to do is to use a binary semaphore (vSemaphoreCreateBinary()).  Then in the beginning of the disk_read()/disk_write() functions, I would call xSemaphoreTake().  Once this returns, I can safely start a DMA transfer.  Then, in the DMA interrupt, if the transfer is complete, I would call xSemaphoreGiveFromISR().

I was thinking that I would implement the DMA Interrupt directly in the diskio.c file (where disk_read()/disk_write() ) are implemented, and make the semaphore have file scope so that only disk_read()/disk_write() and the interrupt can touch the semaphore.

Does this seem reasonable?  Is there a better/different approach?

The disk_read()/disk_write() functions are called from higher level FatFS functions such as f_read()/f_write(), which themselves would be called from various tasks in my project.  Are there any requirements on the implementation of disk_read()/disk_write() to make sure this all works properly?  I think they currently both access a variable that is at file scope (disk_status) in diskio.c, will this cause any issues?

Thank you,

nathanwiebe wrote on Thursday, May 24, 2012:

Using a non-mutex semaphore does leave you open to priority inversion.  For example, if a low priority thread grabs the semaphore and is about to start the DMA transfer, a high priority thread could wake up and need the file system, meanwhile a long-running medium priority thread could block the low priority thread, halting the high-priority thread for an indefinite amount of time.  Unfortunately (and I could be wrong), I don’t think mutex’s can be released from an ISR in FreeRTOS.

What you could do is use a mutex to wrap all in-thread access up to and including the DMA transfer initialization, and release it once the DMA transfer has started and the interrupt is now imminent.  You would then also use a binary semaphore that you would take once inside the mutex, and it can be released from the ISR, thus waking up threads waiting on the DMA.  This way the low-priority thread would priority inherit from any thread needing the file system until the DMA transfer is started and the sequence has been passed off to the interrupt.

I’m not familiar with FatFS, but if it isn’t designed to be thread-safe, then you will want to scour the code for globals and statics, as well as use of non-synced resources like malloc/free and sometimes other compiler-specific clib stuff.  Also keep in mind that you might want to do more than just sync the shared resources.  You may want to also have a file-specific lock list/table to ensure that a given file isn’t opened more than once for write access, read+write, etc.

anonymous wrote on Thursday, May 24, 2012:

Thanks for the reply.  I am pretty new to all this stuff but I think I understand what you mean about the priority inversion issue.  While I don’t anticipate having more than one task need to use the FatFS calls, it would be nice to know how to do this properly so that if/when I do have that need, I can safely do it.

If I understand the FreeRTOS documentation correctly, mutex’s in FreeRTOS are just special kind of semaphore (created with xSemaphoreCreateMutex()), which have the same actual type as binary semaphore (xSemaphoreHandle).  According to the documentation for xSemaphoreGiveFromISR(), all semaphore types except for recursive semaphores can be ‘given’ from ISR’s with this function.

Does this imply that the situation you described which uses a mutex and a binary semaphore could effectively be simplified to just a mutex and acheive the same result?

rtel wrote on Thursday, May 24, 2012:

There is an example FatFS driver here:


It uses interrupt driven input and output.  Task accessing the SPI port performs an operation then blocks on a semaphore to know when the operation is complete.  The interrupt service routine simply gives the semaphore to indicate that it is safe for the next operation to commence.  You are describing a system that works in exactly the same way, except the DMA is used to control the IO so you would use the DMA interrupt rather than the SPI interrupt.