1
votes

I'm trying to interface a microSD card with a STM32L152-DISCOVERY board (STM32L152RCT6 ARM Cortex-M3), using SPI. The final goal is to use FatFs (http://elm-chan.org/fsw/ff/00index_e.html) to store sensor data on a SDcard that can be read also on a Decktop PC. On Chan's website there is a very helpful flowchart describing the initialization process (http://elm-chan.org/docs/mmc/im/sdinit.png).

Unfortunately, I have a problem in my code that prevents the SDCard to initialize. Specifically, when I send the command ACMD41 (CMD55+CMD41), the SD Card returns always 0x01.

I've modified my code many times, following some posts here on stack overflow, and in particular SDHC microSD card and SPI initialization, but the problem still persists.

Following are the HW and SW setup of my system:

Hardware setup

  • The micro SDcard has interface UHS-I, capacity 16GB
  • The SDcard adapter is connected to the SPI2 port on the discovery board
  • For MISO, MOSI, CLK pins I've enabled the pull-up resistor. According to the mcu datasheet, the internal pull-up resistor should be around 45 Kohm.
  • The power for the microSD Card is provided by an arduino board, using the 5V pin (I know it could sound crazy, but I don't have any other power supply right now, and I've read that the 5V pin of the arduino can deliver up to 400 mA @ 5V)
  • The arduino GND, the discovery GND and the SDcard adapter GND are all connected togheter.

Software setup - SPI initialization

The SPI frequency is initially set between 125 KHz (I've read that must be in the range 100 KHz - 400 KHz).

/* SPI2 init function */
void MX_SPI2_Init(void)
{

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(spiHandle->Instance==SPI2)
  {
    /* SPI2 clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();

    /**SPI2 GPIO Configuration    
    PB13     ------> SPI2_SCK
    PB14     ------> SPI2_MISO
    PB15     ------> SPI2_MOSI 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
}

Software setup - SDCARD initialization

#include <string.h>
#include "ff_gen_drv.h"

#define CS_HIGH() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
#define CS_LOW()  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);

#define DEFAULT_TIMEOUT 10

uint8_t dummy_clocks[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};

/* Definitions for MMC/SDC command */
#define CMD0      (0)     /* GO_IDLE_STATE */
#define CMD1      (1)     /* SEND_OP_COND (MMC) */
#define ACMD41    (41)    /* SEND_OP_COND (SDC) */
#define CMD8      (8)     /* SEND_IF_COND */
#define CMD9      (9)     /* SEND_CSD */
#define CMD10     (10)    /* SEND_CID */
#define CMD12     (12)    /* STOP_TRANSMISSION */
#define ACMD13    (13)    /* SD_STATUS (SDC) */
#define CMD16     (16)    /* SET_BLOCKLEN */
#define CMD17     (17)    /* READ_SINGLE_BLOCK */
#define CMD18     (18)    /* READ_MULTIPLE_BLOCK */
#define CMD23     (23)    /* SET_BLOCK_COUNT (MMC) */
#define ACMD23    (23)    /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24     (24)    /* WRITE_BLOCK */
#define CMD25     (25)    /* WRITE_MULTIPLE_BLOCK */
#define CMD55     (55)    /* APP_CMD */
#define CMD58     (58)    /* READ_OCR */

static volatile DSTATUS Stat = STA_NOINIT;
extern SPI_HandleTypeDef hspi2;

DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);  
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */

Diskio_drvTypeDef  USER_Driver =
{
  USER_initialize,
  USER_status,
  USER_read, 
  #if  _USE_WRITE
  USER_write,
  #endif  /* _USE_WRITE == 1 */  
  #if  _USE_IOCTL == 1
  USER_ioctl,
  #endif /* _USE_IOCTL == 1 */
};

void spi_init(void);
uint8_t send_cmd(BYTE cmd, DWORD arg);

DSTATUS USER_initialize (BYTE pdrv)
{
  /* USER CODE BEGIN INIT */
  Stat = STA_NOINIT;

  enum initialization_state 
  {
    SD_POWER_CYCLE = 0,
    SD_SEND_CMD0,
    SD_WAIT_CMD0_ANSWER,
    SD_SEND_CMD8,
    SD_SEND_CMD55,
    SD_SEND_ACMD41,
    SD_SEND_CMD1,
    SD_SEND_CMD58,
    SD_SEND_CMD16,
    SD_SUCCESS,
    SD_ERROR,
  } init_phase;

  uint8_t response = 0x00;
  DWORD arg = 0;
  init_phase = SD_POWER_CYCLE;

  spi_init();

  while(init_phase < SD_SUCCESS)
  {
    switch(init_phase)
    {
      case SD_POWER_CYCLE:
        // Wait 1 ms
        HAL_Delay(1);
        HAL_SPI_Transmit(&hspi2, dummy_clocks, sizeof(dummy_clocks), 10);
        init_phase = SD_SEND_CMD0;
      break;

      case SD_SEND_CMD0:
        CS_LOW();
        response = send_cmd(CMD0,arg);
        if(response == 0x01)
          init_phase = SD_SEND_CMD8;
        else
          init_phase = SD_ERROR;
      break;

      case SD_SEND_CMD8:
        arg = 0x000001AA;
        response = send_cmd(CMD8,arg);
        if(response == 0x01)
          init_phase = SD_SEND_CMD55;
        else
          init_phase = SD_ERROR;
      break;

      case SD_SEND_CMD55:   
        arg = 0x00000000;
        response = send_cmd(CMD55,arg);
        if(response == 0x01)
          init_phase = SD_SEND_ACMD41;
        else
          init_phase = SD_ERROR;
      break;

      case SD_SEND_ACMD41:
        arg = 0x40000000;
        response = send_cmd(ACMD41,arg);
        if(response == 0x00)
          init_phase = SD_SEND_CMD58;
        else
        {
          // HAL_Delay(1000);
          init_phase = SD_SEND_CMD55;
        }
      break;

      case SD_SEND_CMD58:
        arg = 0x00000000;
        response = send_cmd(CMD58,arg);
      break;

      case SD_ERROR:
        CS_HIGH();
        Stat = STA_NODISK;
      break;

      default:
        // Something went wrong - Try to re-init
        init_phase = SD_POWER_CYCLE;
        spi_init();
      break;

    }
  }

  return Stat;
  /* USER CODE END INIT */
}

...
...

void spi_init(void)
{
  CS_HIGH();
  HAL_Delay(10);
}

uint8_t send_cmd(BYTE cmd, DWORD arg)
{
  // cmd packet is of fixed lenght
  uint8_t cmd_packet[6] = {0};

  // Response
  uint8_t cmd_response = 0xFF;
  // R1 is 1 byte only and it is used for most commands
  uint8_t r1 = 0xFF;
  // Commands R3 and R7 are 5 bytes long, (R1 + trailing 32-bit data)
  uint8_t r3_7[5] = {0};

  // First byte is the command
  // The cmd_packet must start with 01, therefore we add 0x40 to the cmd byte
  cmd_packet[0] = 0x40 | cmd;

  // Four bytes for the argument
  for(uint8_t i = 1; i<=4; i++)
    cmd_packet[i] = (uint8_t)(arg >> (4-i)*8);

  // Add crc: it must be correct for CMD0 and CMD 8 only; for other commands, we use a dummy crc (0x01)
  if(cmd == CMD0)
    cmd_packet[5] = 0x95;
  else if(cmd == CMD8)
    cmd_packet[5] = 0x87;
  else if(cmd == ACMD41)
    cmd_packet[5] = 0x95;
  else
    cmd_packet[5] = 0x01;

  // Send the command
  HAL_SPI_Transmit(&hspi2, cmd_packet, sizeof(cmd_packet), DEFAULT_TIMEOUT);

  // Receive the answer from SDcard

  switch(cmd)
  {
    case CMD0:
      // Try 3 times to get the answer
      for(uint8_t j = 0; j<3; j++)
      {
        HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
        HAL_SPI_Receive(&hspi2,&r1,sizeof(r1),DEFAULT_TIMEOUT);
        if(r1 != 0xFF)
          return r1;
      }
    break;

    case CMD8:
      HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
      HAL_SPI_Receive(&hspi2,r3_7,sizeof(r3_7),DEFAULT_TIMEOUT);
      if( r3_7[3] == 0x01 && r3_7[4] == 0xAA)
        return 0x01;
    break;

    case CMD55:
      HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
      HAL_SPI_Receive(&hspi2,&r1,sizeof(r1),DEFAULT_TIMEOUT);
      if(r1 != 0xFF)
        return r1;
    break;

    case ACMD41:        
      for(int i = 0; i<150; i++)
      {
        HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
        HAL_SPI_Receive(&hspi2,&r1,sizeof(r1),DEFAULT_TIMEOUT);
        if(r1 == 0x00)
          return r1;
        else
          HAL_Delay(10);
      }
      return 0xFF;
    break;

    case CMD58:
      HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
      HAL_SPI_Receive(&hspi2,r3_7,sizeof(r3_7),DEFAULT_TIMEOUT);
      if( r3_7[1] & (1<<7))
        return 0x01;
      else
        return 0x00;
      break;
  }
}
1

1 Answers

1
votes

I have the same problem. I interfaced a SDHC card with a STM32F107VC board.And I succeed in using FatFs. Actually I never find any clues about this problem.I forwent using SD module and attached SD with Dupont Line.Like this(连接)

It worked then...I also interfaced a mircoSD,also succeed.

My code:

SD_SPI_Mode.c

#include <stdio.h>
#include "SD_SPI_Mode.h"
#include "hdware.h"

SD_INFO G_SDCARD_INFO;

SDErrorType SDInit(void)
{
    u16 Response1;
    u16 Buff[6] = {0};
    u16 Retry;

    SPI_SetSpeed(SPI_BaudRatePrescaler_256);

    for (Retry = 0; Retry < 10; Retry++) //至少74个时钟的高电平
    {
        SPI_ReadWriteByte2(DUMMY_BYTE);
    }
    Retry = 0;
    sdEnable();
    for (Retry = 0; Retry < 0xFFF; Retry++)
    {
        Response1 = SDSendCommand(CMD0, 0, 0x95);
        if (Response1 == 0x01)
        {
            Retry = 0;
            break;
        }
    }

    if (Retry == 0xFFF)
    {
        //printf("Reset card into IDLE state failed!\r\n");
        return ERROR_NOT_IN_IDLE;
    }

    Response1 = SDSendCommandHold(CMD8, 0x1AA, 0x87);

    if (Response1 == 0x05)
    {
        G_SDCARD_INFO.CardType = CARDTYPE_SDV1;
        sdDisable();
        //SPI_ReadWriteByte2(DUMMY_BYTE);

        for (Retry = 0; Retry < 0xFFF; Retry++)
        {
            Response1 = SDSendCommand(CMD55, 0, 0);
            if (Response1 != 0x01)
            {
                return ERROR_CMD55;
            }
            Response1 = SDSendCommand(ACMD41, 0, 0);
            if (Response1 == 0x00)
            {
                Retry = 0;
                break;
            }
        }

        if (Retry == 0xFFF)
        {
            for (Retry = 0; Retry < 0xFFF; Retry++)
            {
                Response1 = SDSendCommand(CMD1, 0, 0); /* should be return 0x00 */
                if (Response1 == 0x00)
                {
                    Retry = 0;
                    break;
                }
            }
            if (Retry == 0xFFF)
            {
                return ERROR_CMD1;
            }
            G_SDCARD_INFO.CardType = CARDTYPE_MMC;
            //printf("Card Type: MMC\r\n");
        }
        else
        {
            G_SDCARD_INFO.CardType = CARDTYPE_SDV1;
            //printf("Card Type: SD V1\r\n");
        }

          Response1 = SDSendCommand(CMD16, BLOCKSIZE, 0xFF);

          if (Response1 != 0x00)
          {
              return ERROR_CMD16;
          }
    }
    else if (Response1 == 0x01)
    {
        Buff[0] = SPI_ReadWriteByte2(DUMMY_BYTE); //0x00
        Buff[1] = SPI_ReadWriteByte2(DUMMY_BYTE); //0x00
        Buff[2] = SPI_ReadWriteByte2(DUMMY_BYTE); //0x01
        Buff[3] = SPI_ReadWriteByte2(DUMMY_BYTE); //0xAA

        sdDisable();

        if (Buff[2] == 0x01 && Buff[3] == 0xAA) //检查电压范围
        {
            for (Retry = 0; Retry < 0xFFF; Retry++)
            {
                Response1 = SDSendCommand(CMD55, 0, 0x01);
                sdEnable();
                Response1 = SPI_ReadWriteByte2(DUMMY_BYTE);
                sdDisable();

                Response1 = SDSendCommand(ACMD41, 0x40FF8000, 0xFF);
                if (Response1 == 0x00)
                {
                    Retry = 0;
                    break;
                }
            }
            if (Retry == 0xFFF)
            {
                return ERROR_ACMD41;
            }
                Response1 = SDSendCommandHold(CMD58, 0, 0);
            if (Response1 != 0x00)
            {
                return ERROR_CMD58;
            }

            Buff[0] = SPI_ReadWriteByte2(DUMMY_BYTE);
            Buff[1] = SPI_ReadWriteByte2(DUMMY_BYTE);
            Buff[2] = SPI_ReadWriteByte2(DUMMY_BYTE);
            Buff[3] = SPI_ReadWriteByte2(DUMMY_BYTE);

            sdDisable();

            if (Buff[0] & 0x40) // OCR -> CCS(bit30)  1: SDV2HC  0: SDV2
            {
                G_SDCARD_INFO.CardType = CARDTYPE_SDV2HC;
                //printf("Card Type: SD V2HC\r\n");
            }
            else
            {
                G_SDCARD_INFO.CardType = CARDTYPE_SDV2;
                //printf("Card Type: SD V2\r\n");
            }
        }
    }
    SPI_SetSpeed(SPI_BaudRatePrescaler_2);
    return ERROR_NOP;
}

SDErrorType getCardInfo(SD_INFO *G_SDCARD_INFO)
{
    u8 CSD_Tab[16];
    u8 CID_Tab[16];

    if (SDSendCommand(CMD9, 0, 0x01)) //读CSD
    {
        return ERROR_CMD9;
    }

    sdEnable();
    if (SDReadToBuffer(CSD_Tab, 16, RELEASE))
    {
        return ERROR_CSD_READ;
    }
    sdDisable();

    if (SDSendCommand(CMD10, 0, 0xFF)) //读CID
    {
        return ERROR_CMD10;
    }

    sdEnable();
    if (SDReadToBuffer(CID_Tab, 16, RELEASE))
    {
        return ERROR_CID_READ;
    }
    sdDisable();

    /* Byte 0 */
    G_SDCARD_INFO->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
    G_SDCARD_INFO->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
    G_SDCARD_INFO->CSD.Reserved1 = CSD_Tab[0] & 0x03;
    /* Byte 1 */
    G_SDCARD_INFO->CSD.TAAC = CSD_Tab[1];
    /* Byte 2 */
    G_SDCARD_INFO->CSD.NSAC = CSD_Tab[2];
    /* Byte 3 */
    G_SDCARD_INFO->CSD.MaxBusClkFrec = CSD_Tab[3];
    /* Byte 4 */
    G_SDCARD_INFO->CSD.CardComdClasses = CSD_Tab[4] << 4;
    /* Byte 5 */
    G_SDCARD_INFO->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
    G_SDCARD_INFO->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
    /* Byte 6 */
    G_SDCARD_INFO->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
    G_SDCARD_INFO->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
    G_SDCARD_INFO->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
    G_SDCARD_INFO->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
    G_SDCARD_INFO->CSD.Reserved2 = 0; /* Reserved */
    G_SDCARD_INFO->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
    /* Byte 7 */
    G_SDCARD_INFO->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
    /* Byte 8 */
    G_SDCARD_INFO->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
    G_SDCARD_INFO->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
    G_SDCARD_INFO->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
    /* Byte 9 */
    G_SDCARD_INFO->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
    G_SDCARD_INFO->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
    G_SDCARD_INFO->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
    /* Byte 10 */
    G_SDCARD_INFO->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
    G_SDCARD_INFO->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
    G_SDCARD_INFO->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
    /* Byte 11 */
    G_SDCARD_INFO->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
    G_SDCARD_INFO->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
    /* Byte 12 */
    G_SDCARD_INFO->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
    G_SDCARD_INFO->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
    G_SDCARD_INFO->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
    G_SDCARD_INFO->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
    /* Byte 13 */
    G_SDCARD_INFO->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
    G_SDCARD_INFO->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
    G_SDCARD_INFO->CSD.Reserved3 = 0;
    G_SDCARD_INFO->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
    /* Byte 14 */
    G_SDCARD_INFO->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
    G_SDCARD_INFO->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
    G_SDCARD_INFO->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
    G_SDCARD_INFO->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
    G_SDCARD_INFO->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
    G_SDCARD_INFO->CSD.ECC = (CSD_Tab[14] & 0x03);
    /* Byte 15 */
    G_SDCARD_INFO->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
    G_SDCARD_INFO->CSD.Reserved4 = 1;

    if (G_SDCARD_INFO->CardType == CARDTYPE_SDV2HC)
    {
        /* Byte 7 */
        G_SDCARD_INFO->CSD.DeviceSize = (u16)(CSD_Tab[8]) * 256;
        /* Byte 8 */
        G_SDCARD_INFO->CSD.DeviceSize += CSD_Tab[9];
    }

    G_SDCARD_INFO->Capacity = G_SDCARD_INFO->CSD.DeviceSize * BLOCKSIZE * 1024;
    G_SDCARD_INFO->BlockSize = BLOCKSIZE;

    /* Byte 0 */
    G_SDCARD_INFO->CID.ManufacturerID = CID_Tab[0];
    /* Byte 1 */
    G_SDCARD_INFO->CID.OEM_AppliID = CID_Tab[1] << 8;
    /* Byte 2 */
    G_SDCARD_INFO->CID.OEM_AppliID |= CID_Tab[2];
    /* Byte 3 */
    G_SDCARD_INFO->CID.ProdName1 = CID_Tab[3] << 24;
    /* Byte 4 */
    G_SDCARD_INFO->CID.ProdName1 |= CID_Tab[4] << 16;
    /* Byte 5 */
    G_SDCARD_INFO->CID.ProdName1 |= CID_Tab[5] << 8;
    /* Byte 6 */
    G_SDCARD_INFO->CID.ProdName1 |= CID_Tab[6];
    /* Byte 7 */
    G_SDCARD_INFO->CID.ProdName2 = CID_Tab[7];
    /* Byte 8 */
    G_SDCARD_INFO->CID.ProdRev = CID_Tab[8];
    /* Byte 9 */
    G_SDCARD_INFO->CID.ProdSN = CID_Tab[9] << 24;
    /* Byte 10 */
    G_SDCARD_INFO->CID.ProdSN |= CID_Tab[10] << 16;
    /* Byte 11 */
    G_SDCARD_INFO->CID.ProdSN |= CID_Tab[11] << 8;
    /* Byte 12 */
    G_SDCARD_INFO->CID.ProdSN |= CID_Tab[12];
    /* Byte 13 */
    G_SDCARD_INFO->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
    /* Byte 14 */
    G_SDCARD_INFO->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
    /* Byte 15 */
    G_SDCARD_INFO->CID.ManufactDate |= CID_Tab[14];
    /* Byte 16 */
    G_SDCARD_INFO->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
    G_SDCARD_INFO->CID.Reserved2 = 1;

    return ERROR_NOP;
}

SDErrorType SDReadToBuffer(u8 *buff, u16 len, u8 release)
{
    u16 Response1;
    u16 Retry;

    //Start Block
    for (Retry = 0; Retry < 2000; Retry++)
    {
        Response1 = SPI_ReadWriteByte2(DUMMY_BYTE);
        if (Response1 == 0xFE)
        {
            Retry = 0;
            break;
        }
    }
    //Time out
    if (Retry == 2000)
    {
        return ERROR_TIME_OUT;
    }

    //Start Read
    for (Retry = 0; Retry < len; Retry++)
    {
        *(buff + Retry) = SPI_ReadWriteByte2(DUMMY_BYTE);
    }   

    //CRC
    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);

    if (release)
    {
        sdDisable();        
        SPI_ReadWriteByte2(DUMMY_BYTE);
    }

    return ERROR_NOP;
}

SDErrorType SDReadSingleBlock(u32 sector, u8 *Buffer)
{
    if (G_SDCARD_INFO.CardType != CARDTYPE_SDV2HC)
    {
        sector = sector << 9;
    }
    if (SDSendCommand(CMD17, sector, 0x55))
    {
        return ERROR_CMD17;
    }

    sdEnable();

    if (SDReadToBuffer(Buffer, BLOCKSIZE, RELEASE))
    {
        return ERROR_DATA_READ;
    }
    sdDisable();
    SPI_ReadWriteByte2(DUMMY_BYTE);

    return ERROR_NOP;
}

SDErrorType SDReadMultiBlock(u32 sector, u8 *Buffer, u32 NumOfSector)
{
    u32 i;

    if (G_SDCARD_INFO.CardType != CARDTYPE_SDV2HC)
    {
        sector = sector << 9;
    }

    if (SDSendCommand(CMD18, sector, 0))
    {
        return ERROR_CMD18;
    }

    sdEnable();

    for (i = 0; i < NumOfSector; i++)
    {
        if (SDReadToBuffer(Buffer + i * BLOCKSIZE, BLOCKSIZE, HOLD))
        {
            SDSendCommand(CMD12, 0, 0);
            sdDisable();
            return ERROR_DATA_READ;
        }
    }

    SDSendCommand(CMD12, 0, 0); //停止位

    sdDisable();
    SPI_ReadWriteByte2(DUMMY_BYTE);
    return ERROR_NOP;
}

SDErrorType SDWriteFromBuffer(u8 *buffer, bool isMultiply)
{
    u16 Response1;
    u16 i;
    if (!isMultiply)
    {
        SPI_ReadWriteByte2(0xFE);
    }
    else if (isMultiply)
    {
        SPI_ReadWriteByte2(0xFC);
    }

    for (i = 0; i < BLOCKSIZE; i++)
    {
        SPI_ReadWriteByte2(*buffer++);
    }
    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);

    Response1 = SPI_ReadWriteByte2(DUMMY_BYTE);

    if ((Response1 & 0x1F) != 0x05)
    {
        return ERROR_DATA_WRITE;
    }

    return ERROR_NOP;
}

SDErrorType SDWriteSingleBlock(u32 sector, u8 *buffer)
{
    if (G_SDCARD_INFO.CardType != CARDTYPE_SDV2HC)
    {
        sector = sector << 9;
    }

    if (SDSendCommand(CMD24, sector, 0))
    {
        return ERROR_CMD24;
    }
    sdEnable();

    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);

    SDWriteFromBuffer(buffer, false);

    if(SDBusyWait())
    {
        sdDisable();
        return ERROR_DATA_WRITE;
    }

    sdDisable();
    SPI_ReadWriteByte2(DUMMY_BYTE);
    return ERROR_NOP;
}

SDErrorType SDWriteMultiBlock(u32 sector, u8 *Buffer, u32 NumOfSector)
{
    u32 n;

    if (G_SDCARD_INFO.CardType != CARDTYPE_SDV2HC)
    {
        sector = sector << 9;
    }

    if (G_SDCARD_INFO.CardType != CARDTYPE_MMC)
    {
        SDSendCommand(ACMD23, NumOfSector, 0x00);
    }

    if (SDSendCommand(CMD25, sector, 0))
    {
        return ERROR_CMD25;
    }
    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);

    sdEnable();
    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);

    SPI_ReadWriteByte2(DUMMY_BYTE);

    for (n = 0; n < NumOfSector; n++)
    {
        SDWriteFromBuffer(Buffer, true);
    }

    if (SPI_ReadWriteByte2(0xFD))
    {
        return ERROR_DATA_WRITE;
    }

    if(SDBusyWait())
    {
        sdDisable();
        return ERROR_DATA_WRITE;
    }

    sdDisable();
    SPI_ReadWriteByte2(DUMMY_BYTE);
    return ERROR_NOP;
}

SDErrorType SDBusyWait(void)
{
    u32 Retry = 0;
    while (SPI_ReadWriteByte2(DUMMY_BYTE) == 0x00)
    {
        /* Timeout return */
        if (Retry++ == 0x40000)
        {
            return ERROR_TIME_OUT;
        }
    }
    return ERROR_NOP;
}

u16 SDSendCommand(u8 cmd, u32 arg, u8 crc)
{
    u16 Response1;
    u16 Retry;

    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);

    sdEnable();

    SPI_ReadWriteByte2(cmd | 0x40);
    SPI_ReadWriteByte2(arg >> 24);
    SPI_ReadWriteByte2(arg >> 16);
    SPI_ReadWriteByte2(arg >> 8);
    SPI_ReadWriteByte2(arg);
    SPI_ReadWriteByte2(crc | 1);

    //Busy
    for (Retry = 0; Retry < 200; Retry++)
    {
        Response1 = SPI_ReadWriteByte2(DUMMY_BYTE);
        if (Response1 != 0xFF)
        {
            break;
        }
    }
    sdDisable();
    SPI_ReadWriteByte2(DUMMY_BYTE);

    return Response1;
}

u16 SDSendCommandHold(u8 cmd, u32 arg, u8 crc)
{
    u16 Response1;
    u16 Retry;

    SPI_ReadWriteByte2(DUMMY_BYTE);
    SPI_ReadWriteByte2(DUMMY_BYTE);
    sdEnable();

    SPI_ReadWriteByte2(cmd | 0x40);
    SPI_ReadWriteByte2(arg >> 24);
    SPI_ReadWriteByte2(arg >> 16);
    SPI_ReadWriteByte2(arg >> 8);
    SPI_ReadWriteByte2(arg);
    SPI_ReadWriteByte2(crc);

    //Busy
    for (Retry = 0; Retry < 200; Retry++)
    {
        Response1 = SPI_ReadWriteByte2(DUMMY_BYTE);
        if (Response1 != 0xFF)
        {
            break;
        }
    }

    return Response1;
}

SD_SPI_Mode.h

#ifndef _SD_SPI_MODE_H_
#define _SD_SPI_MODE_H_
/* Includes ------------------------------------------------------------------*/
#include "DIDO.h"

/* Private define ------------------------------------------------------------*/
#define CARDTYPE_MMC 0x00
#define CARDTYPE_SDV1 0x01
#define CARDTYPE_SDV2 0x02
#define CARDTYPE_SDV2HC 0x04

#define DUMMY_BYTE 0xFF
#define BLOCKSIZE 512

/* SD/MMC command list - SPI mode */
#define CMD0 0    /* Reset */
#define CMD1 1    /* Send Operator Condition - SEND_OP_COND */
#define CMD8 8    /* Send Interface Condition - SEND_IF_COND    */
#define CMD9 9    /* Read CSD */
#define CMD10 10  /* Read CID */
#define CMD12 12  /* Stop data transmit */
#define CMD16 16  /* Set block size, should return 0x00 */
#define CMD17 17  /* Read single block */
#define CMD18 18  /* Read multi block */
#define ACMD23 23 /* Prepare erase N-blokcs before multi block write */
#define CMD24 24  /* Write single block */
#define CMD25 25  /* Write multi block */
#define ACMD41 41 /* should return 0x00 */
#define CMD55 55  /* should return 0x01 */
#define CMD58 58  /* Read OCR */
#define CMD59 59  /* CRC disable/enbale, should return 0x00 */

#define sdEnable() GPIO_ResetBits(SD_CS_PORT, SD_CS_PIN)
#define sdDisable() GPIO_SetBits(SD_CS_PORT, SD_CS_PIN)
#define MSD0_card_power_on() ;
#define isCardInsert() 0

enum _CD_HOLD
{
  HOLD = 0,
  RELEASE = 1,
};

typedef enum    
{
    ERROR_NOP = 0,
    ERROR_NOT_IN_IDLE,
    ERROR_TIME_OUT,
    ERROR_CSD_READ,
    ERROR_CID_READ,
    ERROR_DATA_READ,
    ERROR_DATA_WRITE,
    ERROR_ACMD41,
    ERROR_CMD1,
    ERROR_CMD9,
    ERROR_CMD10,
    ERROR_CMD16,
    ERROR_CMD17,
    ERROR_CMD18,
    ERROR_CMD24,
    ERROR_CMD25,
    ERROR_CMD55,
    ERROR_CMD58,
    ERROR_CMD59,
} SDErrorType;

typedef struct /* Card Specific Data */
{
  vu8 CSDStruct;           /* CSD structure */
  vu8 SysSpecVersion;      /* System specification version */
  vu8 Reserved1;           /* Reserved */
  vu8 TAAC;                /* Data read access-time 1 */
  vu8 NSAC;                /* Data read access-time 2 in CLK cycles */
  vu8 MaxBusClkFrec;       /* Max. bus clock frequency */
  vu16 CardComdClasses;    /* Card command classes */
  vu8 RdBlockLen;          /* Max. read data block length */
  vu8 PartBlockRead;       /* Partial blocks for read allowed */
  vu8 WrBlockMisalign;     /* Write block misalignment */
  vu8 RdBlockMisalign;     /* Read block misalignment */
  vu8 DSRImpl;             /* DSR implemented */
  vu8 Reserved2;           /* Reserved */
  vu32 DeviceSize;         /* Device Size */
  vu8 MaxRdCurrentVDDMin;  /* Max. read current @ VDD min */
  vu8 MaxRdCurrentVDDMax;  /* Max. read current @ VDD max */
  vu8 MaxWrCurrentVDDMin;  /* Max. write current @ VDD min */
  vu8 MaxWrCurrentVDDMax;  /* Max. write current @ VDD max */
  vu8 DeviceSizeMul;       /* Device size multiplier */
  vu8 EraseGrSize;         /* Erase group size */
  vu8 EraseGrMul;          /* Erase group size multiplier */
  vu8 WrProtectGrSize;     /* Write protect group size */
  vu8 WrProtectGrEnable;   /* Write protect group enable */
  vu8 ManDeflECC;          /* Manufacturer default ECC */
  vu8 WrSpeedFact;         /* Write speed factor */
  vu8 MaxWrBlockLen;       /* Max. write data block length */
  vu8 WriteBlockPaPartial; /* Partial blocks for write allowed */
  vu8 Reserved3;           /* Reserded */
  vu8 ContentProtectAppli; /* Content protection application */
  vu8 FileFormatGrouop;    /* File format group */
  vu8 CopyFlag;            /* Copy flag (OTP) */
  vu8 PermWrProtect;       /* Permanent write protection */
  vu8 TempWrProtect;       /* Temporary write protection */
  vu8 FileFormat;          /* File Format */
  vu8 ECC;                 /* ECC code */
  vu8 CSD_CRC;             /* CSD CRC */
  vu8 Reserved4;           /* always 1*/
} MSD_CSD;

typedef struct /*Card Identification Data*/
{
  vu8 ManufacturerID; /* ManufacturerID */
  vu16 OEM_AppliID;   /* OEM/Application ID */
  vu32 ProdName1;     /* Product Name part1 */
  vu8 ProdName2;      /* Product Name part2*/
  vu8 ProdRev;        /* Product Revision */
  vu32 ProdSN;        /* Product Serial Number */
  vu8 Reserved1;      /* Reserved1 */
  vu16 ManufactDate;  /* Manufacturing Date */
  vu8 CID_CRC;        /* CID CRC */
  vu8 Reserved2;      /* always 1 */
} MSD_CID;

typedef struct
{
  MSD_CSD CSD;
  MSD_CID CID;
  u32 Capacity;  /* Card Capacity */
  u32 BlockSize; /* Card Block Size */
  u16 RCA;
  u8 CardType;
  u32 SpaceTotal; /* Total space size in file system */
  u32 SpaceFree;  /* Free space size in file system */
} SD_INFO;

extern SD_INFO G_SDCARD_INFO;

SDErrorType SDInit(void);
SDErrorType getCardInfo(SD_INFO *G_SDCARD_INFO);
SDErrorType SDReadSingleBlock(u32 sector, u8 *buffer);
SDErrorType SDReadMultiBlock(u32 sector, u8 *buffer, u32 NumOfSector);
SDErrorType SDWriteSingleBlock(u32 sector, u8 *buffer);
SDErrorType SDWriteMultiBlock(u32 sector, u8 *buffer, u32 NumOfSector);

static SDErrorType SDWriteFromBuffer(u8 *buffer,bool isMultiply);
static u16 SDSendCommand(u8 cmd, u32 arg, u8 crc);
static u16 SDSendCommandHold(u8 cmd, u32 arg, u8 crc);
static SDErrorType SDReadToBuffer(u8 *buff, u16 len, u8 release);
static SDErrorType SDBusyWait(void);
#endif

Additionly,my file operation code:

FileOperation.c

#include "stdlib.h"
#include <stdbool.h>
#include "string.h"
#include "FileOperation.h"


static FATFS Fs;          //逻辑磁盘工作区.
static FIL File;          //文件
static FILINFO FileInfo; //文件信息
static DIR Dir;       //目录
static u8 SDBuf;          //SD缓存

FRESULT SDMount(u8 device)
{
    return f_mount(device, &Fs);
}

FRESULT SDCreateDir(const u8 *pname)
{
    return f_mkdir((const TCHAR *)pname);
}

FRESULT SDDirOpen(const u8 *path)
{
    return f_opendir(&Dir, (const TCHAR *)path);
}

FRESULT SDDirRead(void)
{
    FRESULT res;
    char *FileName;
    FileInfo.lfsize = _MAX_LFN * 2 + 1;
    FileInfo.lfname = malloc(FileInfo.lfsize);
    res = f_readdir(&Dir, &FileInfo); //读取一个文件的信息
    if (res != FR_OK || FileInfo.fname[0] == 0)
    {
        free((void *)FileInfo.lfname);
        return res; //读完了.
    }
    FileName = *(FileInfo.lfname) ? FileInfo.lfname : FileInfo.fname;
    free((void *)FileInfo.lfname);
    return FR_OK;
}

FRESULT SDFileRead(u8 *buf, u16 len)
{
    u16 i;
    FRESULT res;
    UINT ByteCount;
    for (i = 0; i < len / 512; i++)
    {
        res = f_read(&File, buf, 512, &ByteCount);
        if (res)
        {
            break;
        }
    }
    if (len % 512)
    {
        res = f_read(&File, buf, len % 512, &ByteCount);
    }
    return res;
}

TCHAR *SDReadString(u16 size)
{
    TCHAR *rbuf;
    rbuf = f_gets((TCHAR *)&SDBuf, size, &File);
    if (*rbuf == 0)
        return NULL; //没有数据读到
    else
    {
        return rbuf;
    }
}

FRESULT SDFileWrite(const u8 *data, u16 len)
{
    UINT ByteCount;
    return f_write(&File, data, len, &ByteCount);
}

FRESULT SDScanFiles(const u8 *path, char **FileName)
{
    FRESULT res;
    u16 FileNum = 0;
    FileInfo.lfsize = _MAX_LFN * 2 + 1;
    FileInfo.lfname = malloc(FileInfo.lfsize);

    res = f_opendir(&Dir, (const TCHAR *)path); //打开一个目录
    if (res == FR_OK)
    {
        while (1)
        {
            res = f_readdir(&Dir, &FileInfo); //读取目录下的一个文件
            if (res != FR_OK || FileInfo.fname[0] == 0)
                break; //错误了/到末尾了,退出
            FileName[FileNum] = *FileInfo.lfname ? FileInfo.lfname :     FileInfo.fname;
            FileNum++;
        }
    }
    free(FileInfo.lfname);
    return res;
}

FRESULT SDShowFree(const u8 *drv, u32 *FreeSector, u32 *TotalSector)
{
    FATFS *fs1;
    FRESULT res;
    u32 FreeClust = 0;
    res = f_getfree((const TCHAR *)drv, (DWORD *)&FreeClust, &fs1);
    if (res == FR_OK)
    {
        *TotalSector = (fs1->n_fatent - 2) * fs1->csize; //总扇区数
        *FreeSector = FreeClust * fs1->csize;            //空闲扇区数
        (*TotalSector) >>= 11;
        (*FreeSector) >>= 11;
    }
    return res;
}

FRESULT SDFormat(u8 device, u8 mode, u16 au)
{
    return f_mkfs(device, mode, au); //格式化,drv:盘符;mode:模式;au:簇大小
}

FRESULT SDRemoveFileOrDir(const u8 *pname)
{
    return f_unlink((const TCHAR *)pname);
}

FRESULT SDRename(const u8 *oldname, const u8 *newname)
{
    return f_rename((const TCHAR *)oldname, (const TCHAR *)newname);
}

FRESULT SDFileOpen(const u8 *path, u8 mode)
{
    return f_open(&File, (const TCHAR *)path, mode);
}

FRESULT SDFileClose(void)
{
    return f_close(&File);
}

u32 SDFileSize(void)
{
    return f_size(&File);
}

FRESULT SDArgumentRead(const u8 *path, ArgumentInfo *argument, UINT *RecordNum)
{
    FRESULT res;
    u8 *PointToBuff;
    UINT ByteCount;

    res = SDFileOpen(path, FA_READ);
    if (res != FR_OK)
        return res;

    PointToBuff = malloc(File.fsize);

    res = f_read(&File, PointToBuff, File.fsize, &ByteCount);
    if (res != FR_OK)
    {
        return res;
    }

    SDDataSolve(PointToBuff, argument, ByteCount, RecordNum);

    free(PointToBuff);

    return SDFileClose();
}

FRESULT SDFileAppend(const u8 *path, const u8 *filename, const u8 *buff)
{
    FRESULT res;
    u16 len = strlen((const char*)buff);
    u8 *FullPath;

    res = SDCreateDir(path);
    FullPath = malloc(strlen((const char*)path) + strlen((const char*)filename) + 2);

    SDComplatePath(path, filename, FullPath);

    res = SDFileOpen(FullPath, FA_OPEN_EXISTING | FA_WRITE);

    if (res == FR_NO_FILE)
        res = SDFileOpen(FullPath, FA_CREATE_ALWAYS | FA_WRITE);

    if (res != FR_OK)
        return res;

    free(FullPath);

    SDLseek(File.fsize);
    res = SDFileWrite(buff, len);

    if (res != FR_OK)
        return res;

    return SDFileClose();
}

float stringToFloat(u8 *InputString)
{
    return atof((const char*)(InputString));
}   

int stringToInt(u8 *InputString)
{
    return atoi((const char*)(InputString));
}

FRESULT SDLseek(u32 offset)
{
    return f_lseek(&File, offset);
}

void SDDataSolve(u8 *buff, ArgumentInfo *argument, UINT ByteCount, UINT *RecordNum)
{
    u8 col = 0;
    UINT fileRow = 0, CharNum = 0;
    u8 *CorChar = buff;
    bool isValueRegion = false;

    while (CharNum != ByteCount)
    {
        if (*CorChar == '=')
        {
            isValueRegion = true;
            argument[fileRow].key[col] = '\0';
            col = 0;
        }
        else if (*CorChar == '\r' && *(CorChar + 1) == '\n')
        {
            CorChar++;
            argument[fileRow].value[col] = '\0';
            isValueRegion = false;
            col = 0;
            fileRow++;
        }
        else
        {
            if (isValueRegion)
            {
                argument[fileRow].value[col] = *CorChar;
            }
            else
            {
                argument[fileRow].key[col] = *CorChar;
            }
            col++;
        }
        CorChar++;
        CharNum++;
    }
    *RecordNum = CharNum;
}

void SDComplatePath(const u8 *Path,const u8 *FileName, u8 *FullPath)
{
    u8 TempPath = '/';

    strcpy((char*)FullPath, (const char*)Path);
    strcat((char*)FullPath, (const char*)&TempPath);
    strcat((char*)FullPath, (const char*)FileName);

}

Just for test.May be helpful...

Sorry for my poor English,It’s not my mother tongue.