cancel
Showing results for 
Search instead for 
Did you mean: 

STM32Cube + F4-Discovery + FatFS

evan23
Associate II
Posted on May 20, 2014 at 18:44

I'm trying to test out and utilize the STM32Cube configuration / code generation system to help get things on a new project started. The project is using an SD card, to which I want FatFS access internally. Ideally, I also want to get a dual-access system where the SD card is also mounted as a mass storage device over USB. I'll save that for later, though.

To try this out, I started with the STM32F4-Discovery board and the -BB base board with SD Card connector. I started with the base STM32Cube configuration using the new project dialog, then added the SDIO interface (disabling the conflicting interfaces), and FatFS. I left the default settings on there (no DMA or other interrupts). It did not work first time. I used the test function from the FatFS_USBDisk example application from the STM32Cube_FW_F4_V1.1.0 folders, MSC_Application(), just modified to use the FatFS SD Driver's path (SD_Path) from the generated code. I put a single call to this in the USER CODE 3 section in main() with a while(1); after it to catch everything. The default settings for FatFS, though are to use the _DMA functions for transfer, even if you have DMA disabled. And, if they have DMA enabled, they don't function correctly. 1) Here's how I got it working without DMA: I modified the calls in SD_read() within sd_diskio.c from BSP_SD_[Read/Write]Blocks_DMA() toBSP_SD_[Read/Write]Blocks(). Literally, just remove the ''_DMA''. Then, the FatFS system was failing to create files, etc. Tracking this down, I found the heap insufficient by default (0x0200). Raising this to 0x0300 fixed the problem. I would recommend making this higher, though. Both of these problems need to be fixed in the code generation tools: It shouldn't use the _DMA functions when DMA is disabled / not configured. The non-DMA functions are even completely written and available. Also, and the heap size should increase if using FatFS. 2) Now, how to get it working with DMA? First problem I've run into is that the DMA and SDIO interrupts were not configured, so it hung on the while loop in HAL_SD_CheckReadOperation():

while ((tmp1 == 0) && (tmp2 == 0) && (tmp3 == SD_OK) && (timeout > 0))
{
tmp1 = hsd->DmaTransferCplt; 
tmp2 = hsd->SdTransferCplt;
tmp3 = (HAL_SD_ErrorTypedef)hsd->SdTransferErr; 
timeout--;
}

This made me realize that the DMA / SDIO interrupts weren't enabled, nor the DMA configured. So, I enabled them, regenerated the code, and now it hangs on this while loop in the same function:

/* Wait until the Rx transfer is no longer active */
while((__HAL_SD_SDIO_GET_FLAG(hsd, SDIO_FLAG_RXACT)) && (timeout > 0))
{
timeout--; 
}

This is where I'm stuck. In researching further, looking at [DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/SDIO%20and%20SD%20fat%20access%20example&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=8757]Clive's FATFS-DBG example, it looks like it's re-configuring the DMA each transfer, which makes sense - using the same stream for each direction. However, I think this has something to do with the fact that with the code generation, it configures it once in MX_DMA_Init() and then links the DMA handle to two different ''handles''. This would imply that it would be reconfiguring the DMA as appropriate. However, within the HAL_SD_[Read/Write]Blocks_DMA() functions, the references tohsd->hdma[rx/tx] don't seem to ever reconfigure the *.Init structure, nor reinitialize the DMA like Clive's SD_LowLevel_DMA_[Rx/Tx]Config() functions do. I think this needs to be fixed in the STM32Cube generator as well. I will proceed with trying to fix it, but I think that the Cube initiative was to prevent you having to edit the generated code. This was to serve as both a PSA / information about getting it to worth without DMA and also a request for other thoughts about what might be wrong getting it to work with DMA. Comments / thoughts welcome! #stm32-fat-fatfs-sdio-dma
15 REPLIES 15
evan23
Associate II
Posted on May 23, 2014 at 21:00

Ok, so I have the DMA version working now with this new information.

I changed the DMA initialization code in HAL_SD_MspInit() to be pretty much everything in the working example above.

  • I put the static definitions of DMA handles in at the top. 
  • I left the GPIO / SD initialization alone.
  • I put everything from ''

    /*##-3- Configure the DMA streams ##########################################*/''

     to

    ''

    HAL_DMA_Init(&dmaTxHandle);

    ''

     into this function under the nominal '''' section, before the peripheral NVIC configuration.

  • I

     changed the interrupt priority of the peripheral to match.

Then, I placed the DMA NVIC interrupt configuration parts of the above example within the MX_DMA_Init() function in main.c.

I placed the required #defines at the top of the _msp.c file so that the function would compile.

Finally, I had to modify the interrupts:

  • Add the DMA2_Stream6_IRQHandler() (copy the Stream3 one)
  • Change the reference of &hdma_sdio to hsd.hdmarx in Stream3
  • Change the reference of &hdma_sdio to hsd.hdmatx in Stream6

Once this compiled, everything seemed to work just fine.

So, what does this mean for suggestions for CubeMX to implement / fix?

  1. Allow enabling TX / RX DMA configurations for each peripheral. If I could have done this through CubeMX, I would have easily been able to generate the code without a problem. Since most of the peripherals include a DMA handler reference for TX and RX, this is something that should be implemented.

    Currently, I could only configure one assigned to SDIO.
  2. When doing TX / RX configurations, the IRQ handlers need to make sure they use the correct DMA handle, rather than configuring to hdma_sdio, it should use the individual TX / RX references. Maybe this means that the DMA handles should be created globally and referenced into the peripheral handles like they are, but have only that one global DMA handle tied to that DMA stream's interrupt.

joe
Associate III
Posted on June 06, 2014 at 19:13

Hi elaske

,

thank you so much for posting your findings. After many days of trying to get SD working with F4 I discovered your post and have started following your recommendations. I now have SDIO+FatFs working without DMA mode which is a huge step forward from not working at all. 

I can now build on the working version and try to get it working with DMA, again following your recommendations. They are invaluable.

It's a real shame that CubeMX isn't tested more before release or at least updated to fix issues reported by users more regularly. It will eventually be a great tool but currently is frustratingly buggy and may be causing users to waste more time tracking down bugs instead of saving them the time it's meant to by configuring the peripherals reliably.

joe
Associate III
Posted on June 09, 2014 at 16:36

Hi elaske,

I'm happy enough that SDIO+FatFs is working without DMA (thanks again for that) but I've been trying to get the DMA version working out of stubbornness.

I have an issue you mentioned above where the SDIO_IRQHandler is not firing, unless I pause on HAL_SD_CheckReadOperation() and then continue. Were you able to resolve this issue?

joe
Associate III
Posted on June 10, 2014 at 13:03 Hi, I implemented a workaround for by above issue ie IRQ not firing. There are two locations where the code would hang waiting for interrupt to fire, inside SD_DMA_RxCplt andSD_DMA_TxCplt.

static void SD_DMA_TxCplt(DMA_HandleTypeDef *hdma)
{
SD_HandleTypeDef *hsd = (SD_HandleTypeDef*)((DMA_HandleTypeDef*)hdma)->Parent;
/* DMA transfer is complete */
hsd->DmaTransferCplt = 1;
while( 0 == NVIC_GetPendingIRQ(SDIO_IRQn) )
{
} 
HAL_NVIC_ClearPendingIRQ(SDIO_IRQn);
HAL_SD_IRQHandler(hsd);
/* Wait until SD transfer is complete */
while(hsd->SdTransferCplt == 0)
{
}
/* Transfer complete user callback */
HAL_SD_DMA_TxCpltCallback(hsd->hdmatx); 
}

I added: while( 0 == NVIC_GetPendingIRQ(SDIO_IRQn) ) { } HAL_NVIC_ClearPendingIRQ(SDIO_IRQn); HAL_SD_IRQHandler(hsd); just before thewhile(hsd->SdTransferCplt == 0) so the functions get called even if the interrupt handler doesn't get called automatically ( I did the same for the SD_DMA_RxCplt function ). The code now works and reading/writing using FatFs is working fine. I'm still not entirely happy that the interrupt is not firing as expected and will probably stick with the non-DMA code for now.
joe
Associate III
Posted on June 10, 2014 at 17:36 Hi, just for anyone else having this issue, in my case I had the NVIC priorities messed up. SDIO_IRQn needs to have a higher priority than either DMA2_Stream3_IRQn orDMA2_Stream6_IRQn as it needs to fire and be handled while inside these DMA handlers. The above code from elaske is correct but I had different settings. I now have:

void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* Sets the priority grouping field */
/* NVIC configuration for DMA transfer complete interrupt */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
HAL_NVIC_SetPriority(SD_DMAx_Rx_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(SD_DMAx_Rx_IRQn);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* NVIC configuration for DMA transfer complete interrupt */
HAL_NVIC_SetPriority(SD_DMAx_Tx_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(SD_DMAx_Tx_IRQn);
}

for the DMA channels and for the SDIO inside HAL_SD_MspInit function:

/* Peripheral interrupt init*/
/* Sets the priority grouping field */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
HAL_NVIC_SetPriority(SDIO_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SDIO_IRQn);

All systems go now with DMA...Thanks again elaske for your invaluable information and detailed explanation.
stm32cube-t
Senior III
Posted on September 24, 2014 at 16:32

Hello,

This implementation will be available in STM32CubeMX 4.4 release.

Best Regards