2015-05-22 02:48 PM
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)2015-05-22 10:45 PM
/**
* @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 */
}
}
2015-05-24 01:26 PM
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?2015-05-24 11:24 PM
2015-05-25 05:19 PM
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. Steve2015-05-26 12:32 AM
2015-05-29 10:06 PM
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_DMA2. 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.Thanks2015-05-29 11:56 PM
2015-05-31 08:38 PM
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.Thanks2015-06-01 01:08 AM