cancel
Showing results for 
Search instead for 
Did you mean: 

Using ST's FATFS within CubeMX for STM32F4xx - lack of info

childresss
Associate II
Posted on May 22, 2015 at 23:48

I'm reasonably competent in using CubeMX, STM32F4xx with IAR.

I added the SDIO driver for micro-SD cards, via the latest HAL libraries that CubeMX utilizes.

I have this ID'ing and doing read/write at the driver level (HAL calls). It works with only 1/3 of the brands of SD cards; 2/3 either don't ID reliably (init at 400KHz), or they give 100% read errors - code 3 = CRC error on command responses. 

I 'scope checked the SCK and DO/DI signals - waveform quality good. PCB wiring is short/OK (my project's PCB). Vcc OK. I slowed the SCK to 4MHz. No help. But 2 brands of uSD cards work reliably. All are type 2 8GB. SDIO is running with ONE serial channel, not 4.

Now I decided to try ST Micro's adaptation of ChanFS (FAT FS) that's an option from CubeMX. The init calls in main() from CubeMX include   MX_FATFS_Init() and that is no where in the sources. I found a ''thirdparty'' folder in my windows home lib area and in there is the sources to FATFS for ST. But CubeMX did NOT pull any of these into the IAR project.

I read this ST document

UM1721

User manual

Developing Applications on STM32Cube with FatFs

And found it to be  useless, written by someone from another galaxy and does not talk about how to truly get the sources into the (IAR) project and do a build, esp. w.r.t. CubeMX. So ST paid someone for nada.

Anyone out there know how I can (a) troubleshoot why some uSD cards work with the SDIO driver in the latest HAL libraries but most cards don't; (b) how to get FATFS to run on a custom board using CubeMX, STM32F4xx? Maybe a miracle will occur, but FATFS uses the same problematic drivers as I see.

Thanks much.  I have spent way too much time on this!!

(PS: I cannot use the legacy ST peripheral drivers)

11 REPLIES 11
markb
Associate II
Posted on May 23, 2015 at 07:45 Hi Steve, I am using the CubeMX FATFS with 4 bit SDIO and it works OK. One change I had to make was to use DMA, it just didn't work non-DMA. The code change in sd_diskio.c is simple:

/**
* @brief Reads Sector(s)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT SD_read(BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_OK;
if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff, 
(uint64_t)sector * BLOCK_SIZE, 
BLOCK_SIZE, 
count) != MSD_OK)
{
res = RES_ERROR;
}
return res;
}
/**
* @brief Writes Sector(s)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
DRESULT SD_write(const BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_OK;
if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff, 
(uint64_t)sector * BLOCK_SIZE, 
BLOCK_SIZE, count) != MSD_OK)
{
res = RES_ERROR;
}
return res;
}
#endif /* _USE_WRITE == 1 */

And here are the DMA init/deinit routines:

void HAL_SD_MspInit(SD_HandleTypeDef* hsd)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hsd->Instance==SDIO)
{
/* USER CODE BEGIN SDIO_MspInit 0 */
/* USER CODE END SDIO_MspInit 0 */
/* Peripheral clock enable */
__SDIO_CLK_ENABLE();
/**SDIO GPIO Configuration 
PC8 ------> SDIO_D0
PC9 ------> SDIO_D1
PC10 ------> SDIO_D2
PC11 ------> SDIO_D3
PC12 ------> SDIO_CK
PD2 ------> SDIO_CMD 
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 
|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* Peripheral DMA init*/
hdma_sdio_rx.Instance = DMA2_Stream3;
hdma_sdio_rx.Init.Channel = DMA_CHANNEL_4;
hdma_sdio_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_sdio_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdio_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdio_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdio_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_sdio_rx.Init.Mode = DMA_PFCTRL;
hdma_sdio_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_sdio_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sdio_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_sdio_rx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_sdio_rx.Init.PeriphBurst = DMA_PBURST_INC4;
HAL_DMA_Init(&hdma_sdio_rx);
__HAL_LINKDMA(hsd,hdmarx,hdma_sdio_rx);
hdma_sdio_tx.Instance = DMA2_Stream6;
hdma_sdio_tx.Init.Channel = DMA_CHANNEL_4;
hdma_sdio_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_sdio_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdio_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdio_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdio_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_sdio_tx.Init.Mode = DMA_PFCTRL;
hdma_sdio_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_sdio_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sdio_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_sdio_tx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_sdio_tx.Init.PeriphBurst = DMA_PBURST_INC4;
HAL_DMA_Init(&hdma_sdio_tx);
__HAL_LINKDMA(hsd,hdmatx,hdma_sdio_tx);
/* System interrupt init*/
HAL_NVIC_SetPriority(SDIO_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SDIO_IRQn);
/* USER CODE BEGIN SDIO_MspInit 1 */
/* USER CODE END SDIO_MspInit 1 */
}
}
void HAL_SD_MspDeInit(SD_HandleTypeDef* hsd)
{
if(hsd->Instance==SDIO)
{
/* USER CODE BEGIN SDIO_MspDeInit 0 */
/* USER CODE END SDIO_MspDeInit 0 */
/* Peripheral clock disable */
__SDIO_CLK_DISABLE();
/**SDIO GPIO Configuration 
PC8 ------> SDIO_D0
PC9 ------> SDIO_D1
PC10 ------> SDIO_D2
PC11 ------> SDIO_D3
PC12 ------> SDIO_CK
PD2 ------> SDIO_CMD 
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 
|GPIO_PIN_12);
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);
/* Peripheral DMA DeInit*/
HAL_DMA_DeInit(hsd->hdmarx);
HAL_DMA_DeInit(hsd->hdmatx);
/* Peripheral interrupt DeInit*/
HAL_NVIC_DisableIRQ(SDIO_IRQn);
/* USER CODE BEGIN SDIO_MspDeInit 1 */
/* USER CODE END SDIO_MspDeInit 1 */
}
}

childresss
Associate II
Posted on May 24, 2015 at 22:26

Thanks very much.

If you have a brief few moments to try one-bit mode (not 4 bits), I'd send you a virtual six-pack or a paypal freebie. I can't use 4 bit mode due to I/O pin count. I also don't use DMA now as I thought I could start simple with polling and use interrupts and/or DMA later if this simple case works.

I'll study your code to see if I can reproduce success with this ST32F415/64 pin chip, running at 64MHz (to reduce power consumption).

1) Since you changed to use DMA, couldn't use tell CubeMX to use DMA and do the configs there instead of changing the .c code?

2) Tried it with various SD card brands? I found that some type 2 cards work, some don't, all 8GB. Some class 1, some 4, some class 10.

3) When you modify a HAL  library .c file, and a new release comes out, or you use CubeMX,  do you have an automated way to re-introduce the customized change?

markb
Associate II
Posted on May 25, 2015 at 08:24

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6qS&d=%2Fa%2F0X0000000bwd%2FhjNM1EqMazEzTXeRE_2gSZRmpbv.0vWZo84x1A.HUlE&asPdf=false
childresss
Associate II
Posted on May 26, 2015 at 02:19

Mark

A thousand thanks. I have it mostly working now on my custom board. The key was when you said ''It just doesn't work without DMA''. I started trying to use DMA but the SDIO DMA driver stuck in while (condition) {} that is in the interrupt handler (!!!!) for the DMA ISR, for RX and again for TX. I've never seen a professionally written ISR have a loop like that. The condition to escape the while loop is a flag being set by the SDIO peripheral. OMG.

So I finally (after way too many hours) realized that this while loop in the ISR assumes that I knew that the other ISR needed to be configured to preemptively interrupt the one with the while loop. Geesh, I'd never do that.

So I changed the interrupt priorities and now it all runs.

Except

Did I read that the SDIO DMA cannot use multi-block read/write? I think so. I had to code my own for() loops for multiple block read/write else things were flaky. But that killed the speed. For this single channel SDIO I'm using, at 24Mbps for the clock, the read speed is under 100KB/s. I have to get multi-block working.

Thanks again. I wish ST would document this stuff. Too much time and $$$ wasted and I may not be the first or last.

So when DMA originally hung, I tried to use polling with a much slower clock speed. That led to all sorts of misleading error codes. And oddly, polling did work on 1/3 of the uSD cards.

I owe you a beer or two, and dinner, if  you're in So. CA sometime.

Steve

markb
Associate II
Posted on May 26, 2015 at 09:32

Hi Steve,

Yes, the ISR code is weird but, then again, most of the cube code is pretty dodgy so I guess it's in keeping with the rest of it!

I think that multi-block read/write does work (not 100% sure, need to check).

Cheers,

Mark

rwmao
Senior
Posted on May 30, 2015 at 07:06

Hi Mark,

You have a very detailed instruction, very helpful.

I am struggling the micro-sd reading/writing using the code generated by cubemx. 

You mentioned you got it working by the revision below.

Can you confirm the change?

1. in sd_diskio.c, you changed the sd_read and sd_write routine. The change is you literally add _DMA to the name of the subroutine, such as BSP_SD_WriteBlocks to BSP_SD_WriteBlocks_DMA

2. For DMA setting in cubemx. You just set in the setting to use DMA2.

The code is actually generated by the cubemx without revision.

Are these all what you did? And microsd reading/write is working?

By the way I checked your ioc, which is very helpful. You use the usb as VPC. Is that necessary for microsd?

And for the GPIO of SD, you use internal pull up instead of external resistor to pull up?

Appreciate your further instructions.

Thanks

markb
Associate II
Posted on May 30, 2015 at 08:56

Hello Mao,

Answers to your questions + other random thoughts...

1 - Yes, I do that but I also fix the calculation of the byte offset because in the original code they convert to 64 bits AFTER doing the multiplication which will just extend the 32 bit result. So I now have:

(uint64_t)sector * BLOCK_SIZE

rather than:

(uint64_t)(sector * BLOCK_SIZE)

2 - Yes, the cube code is used without revision (amazing it works, really, given the quality of the cube code). After mounting the SD card (f_mount()), I go on to do this:

    HAL_SD_ErrorTypedef hse;

    hse = HAL_SD_WideBusOperation_Config(&hsd, SDIO_BUS_WIDE_4B);

    if(hse != SD_OK) {

      printf(''Error: failed to set SD card to 4 bit transfers (%d)\r\n'', hse);

The hardware I have tested this most on uses a full size SD card (Panasonic, industrial) and it appears to work OK for reads/writes. I have just started testing some other hardware which does use a micro SD card and that also appears to work OK but, so far, I have only read from the card.

I am using the USB as a simple means of getting debugging output. I connect stdin/stdout to the VCP so I can just run a terminal emulator on my PC and printf(), getchar() etc all work as normal except that I have to output \r\n instead of \n because it's all working in raw mode. I prefer doing that to adding some mode whereby the \r gets added automatically when a \n goes by.

Yes, I use internal pull up instead of external resistor for the SD GPIO.

One other thing I do is have a P-channel MOSFET to switch the power to the SD card under MCU control. I like that because it means I can bring up the power to the SD card in a controlled manner when I want rather than it powering up with everything else which can lead to the SD card not being accessible sometimes after powering up the board.

Cheers,

Mark

rwmao
Senior
Posted on June 01, 2015 at 05:38

Mark,

Do you use STM32F4 discovery board? If you do, do you mind send me your example project to access the uSD?

I still didn't get through it.

Thanks

markb
Associate II
Posted on June 01, 2015 at 10:08

No, I am using my own hardware design. The SD part of it consists of:

STM32F405 + uSD socket + P channel MOSFET to switch +3V3 to the SD card + a few decoupling caps. That's it.

I am using the 4 bit SD interface.