Electronic – SD card block read/write issue in SPI mode. (STM32) (CMD17, CMD24)

cmicrocontrollersdspistm32cubemx

Good day,

I am using an STM32F103C8T6 and trying to interface an SD card (SanDisk Ultra 16GB MicroSD HC card) with it. I know that the card works fine because I can read, write using a PC and it also works well on an AVR based datalogger I built(both arduino and non arduino). However, I am facing an issue doing the same with my present setup.

Details:

  1. I am using a generic STM32F103C8T6 Minimum System Development Board (at 72MHz). (Identical to the "blue pill".) (similar to: https://www.ebay.com/itm/STM32F103C8T6-ARM-STM32-Minimum-System-Development-Board-Module-For-Arduino-/311156408508)
    STM32F103C8T6 minimum system development board schematic
  2. Catalex micro SD card module. (similar to: https://www.ebay.in/itm/Micro-SD-Card-Module-Reader-Slot-Socket-Shield-Arduino-ARM-MCU-Read-Write-SPI-/302383980892?_trksid=p2059707.m48543.l9013)
    SD module schematic
  3. A custom (DIY) PCB to house the development board and connections to it.
    Header board schematic

In the SD module I have bypassed the buffer, signal wire resistors, removed the LDO regulator, and shorted its input and output. Additionally I have attached a 100uF capacitor across power and ground of the SD module. Basically just using it to connect the microSD cards lines to the controller.

I am using STM32CubeMX to generate the startup and initialization code and using Keil MDK v5. The SPI slave select pin is manually controlled in code.

The card seems to initialize fine(74+ cycles with SS de-asserted, CMD0, CMD1, CMD8, ACMD41, CMD58, CMD16). (Though the init function takes a few run-throughs and is hence placed in a while loop in the main function). Clock speed is maintained at ~140 kHz throughout the initialization function and increased to ~4.5MHz after.

The card even responds with a valid R1 response of 0x00 to read single block command (CMD17). (But needs a noticeable delay for a valid response). However, the data block start byte 0xFE never arrives. It does occasionally catch a 0xFE but any data read is just a series of 0xFF, leading me to believe that its probably just a missed clock or something when it is read.

The card also responds with a valid R1 response of 0x00 to write single block command (CMD24) but no data gets written after the data block start byte and one block of data with CRC is transmitted.

CMD58 responds with R1 response of 0x00 and the OCR readout also is just 0x00 0x00 0x00 0x00.

I am writing down my initialization code, command sending function, block read and block write code:

Initialization:

uint8_t sd_ini(void)
{ 
  uint8_t i,cmd;
  int16_t tmr;
  LD_OFF;
  sdinfo.type = 0;
  uint8_t ocr[4];

  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; //140.625kbps
  HAL_SPI_Init(&hspi2);

  SS_SD_DESELECT();
  for(i=0;i<254;i++) //80 to init SD (74 min)
  {
        SPI_Release();
  }
  SS_SD_SELECT();
  //    while (SD_cmd(CMD0, 0) != 1);
  if(SD_cmd(CMD0, 0) == 1)// Enter Idle state
  {
        SPI_Release();
        if (SD_cmd(CMD8, 0x1AA) == 1) //SD v2
        {
            for (i = 0; i < 4; i++) 
            {
                ocr[i] = SPI_ReceiveByte();
            }
            sprintf(str1,"OCR: 0x%02X 0x%02X 0x%02X 0x%02Xrn",ocr[0],ocr[1],ocr[2],ocr[3]);
            HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
            // Get trailing return value of R7 resp
            if (ocr[2] == 0x01 && ocr[3] == 0xAA) // The card can work at vdd range of 2.7-3.6V
            {
                  for (tmr = 12000; tmr && SD_cmd(ACMD41, 1UL << 30); tmr--); // Wait for leaving idle state (ACMD41 with HCS bit)
                    if (tmr && SD_cmd(CMD58, 0) == 0) 
                    { // Check CCS bit in the OCR
                        for (i = 0; i < 4; i++) 
                        {
                            ocr[i] = SPI_ReceiveByte();
                        }
                        sprintf(str1,"OCR: 0x%02X 0x%02X 0x%02X 0x%02X\r\n",ocr[0],ocr[1],ocr[2],ocr[3]);
                        HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
                        sdinfo.type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; // SDv2 (HC or SC)
                    }

            }
        }
        else //SD v1
        {
            if (SD_cmd(ACMD41, 0) <= 1)
            {
                sdinfo.type = CT_SD1; cmd = ACMD41; // SDv1
            }
            else
            {
                sdinfo.type = CT_MMC; cmd = CMD1; // MMCv3
            }
            for (tmr = 25000; tmr && SD_cmd(cmd, 0); tmr--) ; // Wait for leaving idle state
            if (!tmr || SD_cmd(CMD16, 512) != 0) // Set R/W block length to 512
            {
                sdinfo.type = 0;
            }
        }
  }
  else
  {
    return 1;
  }
    i=SD_cmd(CMD16, 512);
    hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    HAL_SPI_Init(&hspi2);
    return 0;
    //SPI_SendByte(0x35); //test
    //SPI_SendByte(0x53); //test

    //LD_OFF;
    //return sd_raw_init()? 0:1;

}

Sending Commands:

static uint8_t SD_cmd (uint8_t cmd, uint32_t arg)
{
    uint8_t n, res;
    // ACMD<n> is the command sequense of CMD55-CMD<n>
    if (cmd == ACMD41)
    {
        cmd &= 0x7F;
        res = SD_cmd(CMD55, 0);
        if (res > 1) 
        {
            return res;
        }
    }

    // Select the card  
    SS_SD_DESELECT();
    SPI_ReceiveByte();
    SS_SD_SELECT();
    SPI_ReceiveByte();

    // Send a command packet

    SPI_SendByte(cmd); // Start + Command index
    SPI_SendByte((uint8_t)(arg >> 24)); // Argument[31..24]
    SPI_SendByte((uint8_t)(arg >> 16)); // Argument[23..16]
    SPI_SendByte((uint8_t)(arg >> 8)); // Argument[15..8]
    SPI_SendByte((uint8_t)arg); // Argument[7..0]

    n = 0x01; // Dummy CRC + Stop
    if (cmd == CMD0) {n = 0x95;} // Valid CRC for CMD0(0)
    if (cmd == CMD8) {n = 0x87;} // Valid CRC for CMD8(0x1AA)
    SPI_SendByte(n);
    if(cmd==CMD17||cmd==CMD24)
         HAL_Delay(50); //returns 0xFF otherwise

    n = 20; // Wait for a valid response in timeout of 10 attempts
    do 
    {
        res = SPI_ReceiveByte();
        n--;
    } while ((res & 0x80)&&n);
    /*SS_SD_DESELECT();
    SPI_ReceiveByte();
    SS_SD_DESELECT();
    SPI_ReceiveByte();
    SS_SD_SELECT();*/
   return res;
}

Block Read:

uint8_t SD_Read_Block (uint8_t *buff, uint32_t lba)
{
    uint8_t result;
    uint16_t cnt;
    if(!SPI_wait_ready()) 
    {
        return 0;
    }
    result=SD_cmd (CMD17, lba); //CMD17 datasheet pg 50,96
    if (result!=0x00)
    {
        return 5; //exit if result isxt 0x00
    }
    //SPI_Release();
    cnt=0;
    do
    { //Waiting for the beginning of the block
        result=SPI_ReceiveByte();
        cnt++;
    } while ( (result!=0xFE)&&(cnt<0xFFFF) );

    if (cnt>=0xFFFF)
    {
        return 5;
    }   
    for (cnt=0;cnt<512;cnt++)
    {
        buff[cnt]=SPI_ReceiveByte(); //get the bytes of the block from the bus to the buffer
    }
    SPI_Release(); //We omit the checksum
    SPI_Release();
    return 0;
    //return sd_raw_read(lba, buff, 1)?0:1;
}

Block write:

 uint8_t SD_Write_Block (uint8_t *buff, uint32_t lba)
 {
   uint8_t result;
   uint16_t cnt;

   if(!SPI_wait_ready()) 
   {
        return 0;
   }
   result=SD_cmd(CMD24,lba); //CMD24 datasheet page 51 and 97-98
   if (result!=0x00)
   { 
        return 6;
   } //Exit if the result is not 0x00
   SPI_Release();
   SPI_SendByte (0xFE); //Beginning of the buffer
   for (cnt=0;cnt<512;cnt++)
   {
       SPI_SendByte(buff[cnt]); //Send data
   }
   SPI_Release(); //leave crc
   SPI_Release();
   result=SPI_ReceiveByte();
   if ((result&0x05)!=0x05)
   { 
         return 6;
   } //Exit if the result is not 0x05 (Datasheet pg 111)
   cnt=0;
   do 
   { //Waiting for the end of the state BUSY
       result=SPI_ReceiveByte();
       cnt++;
   } while ( (result!=0xFF)&&(cnt<0xFFFF) );
   if (cnt>=0xFFFF)
   { 
       return 6;
   }
   return 0;
   //return sd_raw_write(lba, buff, 1)?0:1;
}

I am at wits end with this issue. Any ideas? Also let me know any additional information required.

Best Answer

Looking to the state machine I designed some time ago for micro-SD card in SPI mode, I see the following sequence:

  • CMD0
  • CMD8
  • CMD55 + ACMD41 in loop until ACMD41 returns success
  • CMD58
  • CMD16

However if CMD55 returns error I go for CMD1 trying to initialize card as MMC-type.

It is hard to say what exactly is happening in your case, but I think CMD1 must not be in this place in your list of commands you used. I recommend you to thoroughly study this excellent webpage by ELM ChaN, which states the following:

Because ACMD41 instead of CMD1 is recommended for SDC, trying ACMD41 first and retry with CMD1 if rejected, is ideal to support both type of the cards.

You are using CMD1 and then ACMD41, which is the wrong sequence, and card goes to some mode or causing this behavior you describe. You also do not mention CMD55 preceding ACMD41, I hope you have it in place.