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
Posted on May 21, 2014 at 09:12

You should have posted this in the sub-forum dedicated to software tools

https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Java/AllItems.aspx

JW
evan23
Associate II
Posted on May 21, 2014 at 15:36

You are probably correct; most of the solutions that I've found related to this had been found on this sub-forum, so that's where I started. 

Not sure if I should re-post it there, or not?

evan23
Associate II
Posted on May 22, 2014 at 00:29

I figured out by further comparison that the working examples I've seen all had their DMA configured as Peripheral Flow Control. Changing this in the *.ioc file and regenerating, it now hangs on

/* Wait until SD transfer is complete */
while(hsd->SdTransferCplt == 0)
{
}

within SD_DMA_RxCplt().
evan23
Associate II
Posted on May 22, 2014 at 01:16

So, the only place where this would get set in any generated code is in HAL_SD_IRQHandler(), which means that even though the interrupt is enabled, it is never firing (this goes for the parent SDIO_IRQHandler() as well).

Still trying to figure out why this interrupt isn't firing. Stepping through the code, all of the interrupt registers seem to get set appropriately...

evan23
Associate II
Posted on May 22, 2014 at 01:39

So, apparently pausing at HAL_SD_CheckReadOperation() and just clicking run again results in the HAL_SD_IRQHandler() getting called at least.

Once this successfully completes, though, it still results with f_open() returning 0xD, or FR_NO_FILESYSTEM. This is, of course, not the case. I can load one of the other examples (or the non-DMA version) and access it just fine.

Posted on May 23, 2014 at 11:22

Hi,

It's better to compare your generated code with one of the provided applications working with SD in DMA mode. Otherwise, a new diskio driver will be available within the next release of STM32Cube_FW_F4 package, that interfaces the SD card in polling mode. Thus you will not face such issue found due a missed DMA generation code.

With regards.

evan23
Associate II
Posted on May 23, 2014 at 17:04

Well, if the non-DMA code is fixed, then that's good. Still, it should be working with DMA - the preferable way to move large amounts of data.

There are no given demonstrations with DMA transfer working with the SD card. The only given examples by ST use the SD card interface in other ways. I am developing another part of the application and actually found out a difference in how they initialize the DMA for UART TX/RX transfers and how they do it for the SDIO interface. I think the UART interface, since it's a given example, should be how the one for the SDIO interface is made. Here's how the SDIO interface DMA is generated:

/* Peripheral DMA init*/
hdma_sdio.Instance = DMA2_Stream3;
hdma_sdio.Init.Channel = DMA_CHANNEL_4;
hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_sdio.Init.Mode = DMA_PFCTRL;
hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;
hdma_sdio.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sdio.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_sdio.Init.MemBurst = DMA_MBURST_INC4;
hdma_sdio.Init.PeriphBurst = DMA_PBURST_SINGLE;
HAL_DMA_Init(&hdma_sdio);
/* Several peripheral DMA handle pointers point to the same DMA handle.
Be aware that there is only one stream to perform all the requested DMAs. */
__HAL_LINKDMA(hsd,hdmarx,hdma_sdio);
__HAL_LINKDMA(hsd,hdmatx,hdma_sdio);

Compare this to how the UART one is generated:

/*##-3- Configure the DMA streams ##########################################*/
/* Configure the DMA handler for Transmission process */
hdma_tx.Instance = USARTx_TX_DMA_STREAM;
hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL;
hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_tx.Init.Mode = DMA_NORMAL;
hdma_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;
HAL_DMA_Init(&hdma_tx); 
/* Associate the initialized DMA handle to the the UART handle */
__HAL_LINKDMA(huart, hdmatx, hdma_tx);
/* Configure the DMA handler for Transmission process */
hdma_rx.Instance = USARTx_RX_DMA_STREAM;
hdma_rx.Init.Channel = USARTx_RX_DMA_CHANNEL;
hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_rx.Init.Mode = DMA_NORMAL;
hdma_rx.Init.Priority = DMA_PRIORITY_HIGH;
hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; 
hdma_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_rx.Init.MemBurst = DMA_MBURST_INC4;
hdma_rx.Init.PeriphBurst = DMA_PBURST_INC4; 
HAL_DMA_Init(&hdma_rx);
/* Associate the initialized DMA handle to the the UART handle */
__HAL_LINKDMA(huart, hdmarx, hdma_rx);

The difference is that this seems to use two different DMA streams:

/* Definition for USARTx's DMA */
#define USARTx_TX_DMA_CHANNEL DMA_CHANNEL_4
#define USARTx_TX_DMA_STREAM DMA1_Stream6
#define USARTx_RX_DMA_CHANNEL DMA_CHANNEL_4
#define USARTx_RX_DMA_STREAM DMA1_Stream5

The generated SDIO code used DMA_CHANNEL_4 and DMA2_Stream3. In RM0090, it doesn't have different Rx/Tx DMA streams, however it does have SDIO noted as on Stream 3 and 6. In the working example that I have (from before the HAL days), it uses one or the other, but reconfigures it as setup before each transfer (i.e. within the SD_[Read/Write]MultiBlocks[/FIXED]() functions).
evan23
Associate II
Posted on May 23, 2014 at 17:10

More accurately, the differences are that they use two different DMA handles, each corresponding to a different physical DMA stream; whereas the SDIO version uses a single stream configured then linked to both the TX and RX handle pointers.

Of course, in using a UART versus an SD card, you're doing bidirectional transfers all the time, whereas the SD card is only one or the other. Hence, I understand why this works just fine in Clive's working example - reconfiguring the same DMA stream - however, I'm not sure why it's not working here.

As you've seen in some of the other debug posts, it seems to be hanging on the fact that the SDIO peripheral is not triggering an interrupt like it should. Granted, this could of course be because of a misconfiguration of the DMA transfer, which in effect makes the DMA transfer ''complete'' but the SD card transfer not actually complete...

evan23
Associate II
Posted on May 23, 2014 at 18:23

Ah, I've found an example in the Cube firmware that uses FatFS and SDIO:STM32Cube_FW_F4_V1.1.0\Projects\STM324x9I_EVAL\Applications\FatFs\FatFs_uSD

However, I can't test it, since I don't have the board. I'll have to assume it works. Here's the code for its SD configuration:

/**
* @brief Initializes the SD MSP.
* @param None
* @retval None
*/
static void SD_MspInit(void)
{
static DMA_HandleTypeDef dmaRxHandle;
static DMA_HandleTypeDef dmaTxHandle;
GPIO_InitTypeDef GPIO_Init_Structure;
SD_HandleTypeDef *hsd = &uSdHandle;
/* Enable SDIO clock */
__SDIO_CLK_ENABLE();
/* Enable DMA2 clocks */
__DMAx_TxRx_CLK_ENABLE();
/* Enable GPIOs clock */
__GPIOC_CLK_ENABLE();
__GPIOD_CLK_ENABLE();
/* Common GPIO configuration */
GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
GPIO_Init_Structure.Pull = GPIO_PULLUP;
GPIO_Init_Structure.Speed = GPIO_SPEED_HIGH;
GPIO_Init_Structure.Alternate = GPIO_AF12_SDIO;
/* GPIOC configuration */
GPIO_Init_Structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
HAL_GPIO_Init(GPIOC, &GPIO_Init_Structure);
/* GPIOD configuration */
GPIO_Init_Structure.Pin = GPIO_PIN_2;
HAL_GPIO_Init(GPIOD, &GPIO_Init_Structure);
/* NVIC configuration for SDIO interrupts */
HAL_NVIC_SetPriority(SDIO_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SDIO_IRQn);
/* Configure DMA Rx parameters */
dmaRxHandle.Init.Channel = SD_DMAx_Rx_CHANNEL;
dmaRxHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;
dmaRxHandle.Init.PeriphInc = DMA_PINC_DISABLE;
dmaRxHandle.Init.MemInc = DMA_MINC_ENABLE;
dmaRxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
dmaRxHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
dmaRxHandle.Init.Mode = DMA_PFCTRL;
dmaRxHandle.Init.Priority = DMA_PRIORITY_VERY_HIGH;
dmaRxHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
dmaRxHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
dmaRxHandle.Init.MemBurst = DMA_MBURST_INC4;
dmaRxHandle.Init.PeriphBurst = DMA_PBURST_INC4;
dmaRxHandle.Instance = SD_DMAx_Rx_STREAM;
/* Associate the DMA handle */
__HAL_LINKDMA(hsd, hdmarx, dmaRxHandle);
/* Deinitialize the stream for new transfer */
HAL_DMA_DeInit(&dmaRxHandle);
/* Configure the DMA stream */
HAL_DMA_Init(&dmaRxHandle);
/* Configure DMA Tx parameters */
dmaTxHandle.Init.Channel = SD_DMAx_Tx_CHANNEL;
dmaTxHandle.Init.Direction = DMA_MEMORY_TO_PERIPH;
dmaTxHandle.Init.PeriphInc = DMA_PINC_DISABLE;
dmaTxHandle.Init.MemInc = DMA_MINC_ENABLE;
dmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
dmaTxHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
dmaTxHandle.Init.Mode = DMA_PFCTRL;
dmaTxHandle.Init.Priority = DMA_PRIORITY_VERY_HIGH;
dmaTxHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
dmaTxHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
dmaTxHandle.Init.MemBurst = DMA_MBURST_INC4;
dmaTxHandle.Init.PeriphBurst = DMA_PBURST_INC4;
dmaTxHandle.Instance = SD_DMAx_Tx_STREAM;
/* Associate the DMA handle */
__HAL_LINKDMA(hsd, hdmatx, dmaTxHandle);
/* Deinitialize the stream for new transfer */
HAL_DMA_DeInit(&dmaTxHandle);
/* Configure the DMA stream */
HAL_DMA_Init(&dmaTxHandle);
/* NVIC configuration for DMA transfer complete interrupt */
HAL_NVIC_SetPriority(SD_DMAx_Rx_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(SD_DMAx_Rx_IRQn);
/* NVIC configuration for DMA transfer complete interrupt */
HAL_NVIC_SetPriority(SD_DMAx_Tx_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(SD_DMAx_Tx_IRQn);
}

This is quite a bit different than the code that is generated from Cube. Just like the UART code, it uses two different streams:

#define SD_DMAx_Tx_CHANNEL DMA_CHANNEL_4
#define SD_DMAx_Rx_CHANNEL DMA_CHANNEL_4
#define SD_DMAx_Tx_STREAM DMA2_Stream6
#define SD_DMAx_Rx_STREAM DMA2_Stream3

I guess I'll just have to make sure to implement it like this, and I'll report back. One would expect this to work, though, if the example is actually working.