AnsweredAssumed Answered

Bug in HAL SD driver code

Question asked by Dave Jones on Apr 6, 2018

I spent the day chasing a bug that I had discovered a year ago, but forgot about.

 

As detailed here: SDIO demo code doesn't work with uSD ver. 1?  , there is a flaw in STM32L4xx_hal_sd.c when using an old v1.x SD card with the SDMMC port. It works fine with SDHC cards, which are v2.x. But with older, smaller v1.x SD cards it fails initialization. And so can't read the card.

 

I chased this last year when I was doing my tests on an STM Eval board. Now I have my own hardware finished and just ran into the same issue with a project created using STM32CubeMX for the STM32L452. The code in stm32l4xx_hal_sd.c is different than it was a year ago, but still has the same issue.

 

I don't know if the problem originates in hardware inside the processor (the SDMMC port itself) or something with the firmware, but what happens is that after sending the CMD8 to the card, if that call fails then it's a v1.x card. At that point if you send a CMD55 followed by a ACMD41, the CMD55 creates an error. So the code gives up and returns an error, which ends the attempt to initialize the card.

 

BUT, if you send the CMD55 twice, the second time it works. And the ACMD41 then works after that. And the card initializes just fine. So I fixed it by modifying that file, in the SD_PowerON() function by adding an additional call sending CMD55, but ignoring the result. Then the original call sends CMD55 again, and does pay attention to the result.

 

Original code:

 /* CMD8: SEND_IF_COND: Command available only on V2.0 cards */
errorstate = SDMMC_CmdOperCond(hsd->Instance);
if(errorstate != HAL_SD_ERROR_NONE)
{
   hsd->SdCard.CardVersion = CARD_V1_X;

#if !defined(STM32L4R5xx) && !defined(STM32L4R7xx) && !defined(STM32L4R9xx) && !defined(STM32L4S5xx) && !defined(STM32L4S7xx) && !defined(STM32L4S9xx)
   /* Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
   while(validvoltage == 0)
   {
     if(count++ == SDMMC_MAX_VOLT_TRIAL)
     {
       return HAL_SD_ERROR_INVALID_VOLTRANGE;
     }

     /* SEND CMD55 APP_CMD with RCA as 0 */
     errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0);
     if(errorstate != HAL_SD_ERROR_NONE)
     {
       return HAL_SD_ERROR_UNSUPPORTED_FEATURE;
     }

     /* Send CMD41 */
     errorstate = SDMMC_CmdAppOperCommand(hsd->Instance, SDMMC_STD_CAPACITY);
.
.
.
etc...

 

Modified code:

 /* CMD8: SEND_IF_COND: Command available only on V2.0 cards */
errorstate = SDMMC_CmdOperCond(hsd->Instance);
if(errorstate != HAL_SD_ERROR_NONE)
{
   hsd->SdCard.CardVersion = CARD_V1_X;
#if !defined(STM32L4R5xx) && !defined(STM32L4R7xx) && !defined(STM32L4R9xx) && !defined(STM32L4S5xx) && !defined(STM32L4S7xx) && !defined(STM32L4S9xx)

   /* SEND CMD55 APP_CMD with RCA as 0 */
   errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0);

   /* Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
   while(validvoltage == 0)
   {
     if(count++ == SDMMC_MAX_VOLT_TRIAL)
     {
       return HAL_SD_ERROR_INVALID_VOLTRANGE;
     }

     /* SEND CMD55 APP_CMD with RCA as 0 */
     errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0);
     if(errorstate != HAL_SD_ERROR_NONE)
     {
       return HAL_SD_ERROR_UNSUPPORTED_FEATURE;
     }

     /* Send CMD41 */
     errorstate = SDMMC_CmdAppOperCommand(hsd->Instance, SDMMC_STD_CAPACITY);
.
.
.
etc...

Basically just duplicating these two lines and placing them above the loop that sends CMD55 / ACMD41 to determine the voltage of the card

/* SEND CMD55 APP_CMD with RCA as 0 */
errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0);

 

The big problem is that this is not meant to be user modified code. So if I regenerate files in STM32CubeMX, I lose this modification, and have to remember that it was there and add it back.

Outcomes