/*******************************************************************************
* Function Name  : _send_command_hold
* Description    : None
* Input          : - cmd:
*				   - arg:
*                  - crc:
* Output         : None
* Return         : R1 value, response from card
* Attention		 : None
*******************************************************************************/
int _send_command_hold(uint8_t cmd, uint32_t arg, uint8_t crc)
{
    uint8_t r1;
    uint8_t retry;

    /* Dummy byte and chip enable */
    _spi_read_write(DUMMY_BYTE);
    _card_enable();

    /* Command, argument and crc */
    _spi_read_write(cmd | 0x40);
    _spi_read_write(arg >> 24);
    _spi_read_write(arg >> 16);
    _spi_read_write(arg >> 8);
    _spi_read_write(arg);
    _spi_read_write(crc);

    /* Wait response, quit till timeout */
    for(retry = 0; retry < 200; retry++)
    {
        r1 = _spi_read_write(DUMMY_BYTE);
        if(r1 != 0xFF)
        {
            break;
        }
    }

    return r1;
}
/*******************************************************************************
* Function Name  : MSD_SendCmd
* Description    : Send 5 bytes command to the MSD card.
* Input          : - Cmd: the user expected command to send to MSD card
*                  - Arg: the command argument
*                  - Crc: the CRC
* Output         : None
* Return         : None
*******************************************************************************/
void MSD_SendCmd(u8 Cmd, u32 Arg, u8 Crc)
{
    u32 i = 0x00;
    u8 Frame[6];

    /* Construct byte1 */
    Frame[0] = (Cmd | 0x40);
    /* Construct byte2 */
    Frame[1] = (u8)(Arg >> 24);
    /* Construct byte3 */
    Frame[2] = (u8)(Arg >> 16);
    /* Construct byte4 */
    Frame[3] = (u8)(Arg >> 8);
    /* Construct byte5 */
    Frame[4] = (u8)(Arg);
    /* Construct CRC: byte6 */
    Frame[5] = (Crc);

    /* Send the Cmd bytes */
    for (i = 0; i < 6; i++)
    {
        //MSD_WriteByte(Frame[i]);
        _spi_read_write(Frame[i]);
    }
}
/*******************************************************************************
* Function Name  : _read_buffer
* Description    : None
* Input          : - *buff:
*				   - len:
*				   - release:
* Output         : None
* Return         : 0:NO_ERR; TRUE: Error
* Attention		 : None
*******************************************************************************/
int _read_buffer(uint8_t *buff, uint16_t len, uint8_t release)
{
    uint8_t r1;
    uint16_t retry;

    /* Card enable, Prepare to read	*/
    _card_enable();

    /* Wait start-token 0xFE */
    for(retry = 0; retry < 2000; retry++)
    {
        r1 = _spi_read_write(DUMMY_BYTE);
        if(r1 == 0xFE)
        {
            retry = 0;
            break;
        }
    }

    /* Timeout return	*/
    if(retry == 2000)
    {
        _card_disable();
        return 1;
    }

    /* Start reading */
    for(retry = 0; retry < len; retry++)
    {
        *(buff + retry) = _spi_read_write(DUMMY_BYTE);
    }

    /* 2bytes dummy CRC */
    _spi_read_write(DUMMY_BYTE);
    _spi_read_write(DUMMY_BYTE);

    /* chip disable and dummy byte */
    if(release)
    {
        _card_disable();
        _spi_read_write(DUMMY_BYTE);
    }

    return 0;
}
/*******************************************************************************
* Function Name  : MSD_GetDataResponse
* Description    : Get MSD card data response.
* Input          : None
* Output         : None
* Return         : The MSD status: Read data response xxx0<status>1
*                   - status 010: Data accecpted
*                   - status 101: Data rejected due to a crc error
*                   - status 110: Data rejected due to a Write error.
*                   - status 111: Data rejected due to other error.
*******************************************************************************/
u8 MSD_GetDataResponse(void)
{
    u32 i = 0;
    u8 response, rvalue;

    while (i <= 64)
    {
        /* Read resonse */
        //response = MSD_ReadByte();
        response = _spi_read_write(DUMMY_BYTE);
        /* Mask unused bits */
        response &= 0x1F;

        switch (response)
        {
        case MSD_DATA_OK:
        {
            rvalue = MSD_DATA_OK;
            break;
        }

        case MSD_DATA_CRC_ERROR:
            return MSD_DATA_CRC_ERROR;

        case MSD_DATA_WRITE_ERROR:
            return MSD_DATA_WRITE_ERROR;

        default:
        {
            rvalue = MSD_DATA_OTHER_ERROR;
            break;
        }
        }
        /* Exit loop in case of data ok */
        if (rvalue == MSD_DATA_OK)
            break;
        /* Increment loop counter */
        i++;
    }
    /* Wait null data */
    while (_spi_read_write(DUMMY_BYTE) == 0);
    /* Return response */
    return response;
}
/*******************************************************************************
* Function Name  : MSD_GetResponse
* Description    : Returns the MSD response.
* Input          : None
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_GetResponse(u8 Response)
{
    u32 Count = 0xFFF;

    /* Check if response is got or a timeout is happen */
    while ((_spi_read_write(DUMMY_BYTE) != Response) && Count)
    {
        Count--;
    }

    if (Count == 0)
    {
        /* After time out */
        return MSD_RESPONSE_FAILURE;
    }
    else
    {
        /* Right response got */
        return MSD_RESPONSE_NO_ERROR;
    }
}
/*******************************************************************************
* Function Name  : MSD_ReadMultiBlock
* Description    : None
* Input          : - sector:
*				   - buffer:
*                  - NbrOfSector:
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int MSD_ReadMultiBlock(uint32_t sector, uint8_t *buffer, uint32_t NbrOfSector)
{
  uint8_t r1;
  uint32_t i;

  /* if ver = SD2.0 HC, sector need <<9 */
  if(CardInfo.CardType != CARDTYPE_SDV2HC)
  {
	 sector = sector<<9;
  }

  /* Send CMD18 : Read multi block command */
  r1 = _send_command(CMD18, sector, 0);
  if(r1 != 0x00)
  {
     return 1;
  }

  /* Start read	*/
  for(i=0; i<NbrOfSector; i++)
  {
     if(_read_buffer(buffer+i*MSD_BLOCKSIZE, MSD_BLOCKSIZE, HOLD))
     {
		 /* Send stop data transmit command - CMD12	*/
		 _send_command(CMD12, 0, 0);
		 /* chip disable and dummy byte */
		 ;
		 return 2;
     }
  }

  /* Send stop data transmit command - CMD12 */
  _send_command(CMD12, 0, 0);

  /* chip disable and dummy byte */
  _CARD_DISABLE();
  _spi_read_write(DUMMY_BYTE);

  return 0;
}
/*******************************************************************************
* Function Name  : MSD_ReadSingleBlock
* Description    : None
* Input          : - sector:
*				   - buffer:
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int MSD_ReadSingleBlock_DMA(uint32_t sector, uint8_t *buffer)
{
    DMA_InitTypeDef  DMA_InitStructure;
    u8 rvalue = MSD_RESPONSE_FAILURE;
    uint8_t r1;
    uint16_t retry;
    char DuumyClock = DUMMY_BYTE;

    if(CardInfo.CardType != CARDTYPE_SDV2HC)
    {
        sector = sector << 9;
    }
    /*initial dma channel 2*/
    DMA_DeInit(DMA1_Channel4);
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 200;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);


    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&DuumyClock;  //512字节的dummy
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
    //    DMA_ClearFlag(DMA_FLAG_TC2);
    /* Send CMD17 : Read single block command */
    r1 = _send_command(CMD17, sector, 0);

    if(r1 != 0x00)
    {
        return 1;
    }

    /* Card enable, Prepare to read	*/
    _card_enable();

    /* Wait start-token 0xFE */
    for(retry = 0; retry < 2000; retry++)
    {
        r1 = _spi_read_write(DUMMY_BYTE);
        if(r1 == 0xFE)
        {
            retry = 0;
            break;
        }
    }

    /* Timeout return	*/
    if(retry == 2000)
    {
        _card_disable();
        return 1;
    }

    DMA_Cmd(DMA1_Channel5, ENABLE);
    DMA_Cmd(DMA1_Channel4, ENABLE);

    while(!DMA_GetFlagStatus(DMA1_FLAG_TC5));

    while(!DMA_GetFlagStatus(DMA1_FLAG_TC4));

    DMA_ClearFlag(DMA1_FLAG_TC4);

    /* 2bytes dummy CRC */
    _spi_read_write(DUMMY_BYTE);
    _spi_read_write(DUMMY_BYTE);

    /* chip disable and dummy byte */
    _card_disable();
    _spi_read_write(DUMMY_BYTE);

    /* Set response value to success */
    rvalue = MSD_RESPONSE_NO_ERROR;
    DMA_Cmd(DMA1_Channel4, DISABLE);
    DMA_Cmd(DMA1_Channel5, DISABLE);




    /* Returns the reponse */
    return rvalue;
}
/*******************************************************************************
* Function Name  : MSD_Init
* Description    : SD Card initializtion
* Input          : None
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int MSD_Init(void)
{
    uint8_t r1;
    uint8_t buff[6] = {0};
    uint16_t retry;
    uint16_t retry_time = 5;
    //	uint32 lu32SDStatus = 0;
    //	uint8 err;


    gu8SDStatus = 0;

    /* Check , if no card insert */
    //if( _card_insert() )
    //{
    /* FATFS error flag */
    //return -1;
    //}

    /* Power on and delay some times */
    for(retry = 0; retry < 0x100; retry++)
    {
        _card_power_on();
    }

    /* Satrt send 74 clocks at least */
    for(retry = 0; retry < 10; retry++)
    {
        _spi_read_write(DUMMY_BYTE);
    }

    /* Start send CMD0 till return 0x01 means in IDLE state */
    /*begin:yangfei modified 2013-09-28 for SD卡重试次数太多,等待时间太长,SD卡正常的时候只需1次就能成功*/
#if 0
    for(retry = 0; retry < 0xFFF; retry++)
#else
    for(retry = 0; retry < retry_time; retry++)
#endif
    {
        r1 = _send_command(CMD0, 0, 0x95);
        if(r1 == 0x01)
        {
            retry = 0;
            break;
        }
    }
    /* Timeout return */
#if 0
    if(retry == 0xFFF)
#else
    if(retry == retry_time)
#endif
    {
        gu8SDStatus = 1;
        return 1;
    }
    /*end:yangfei modified 2013-09-28 for SD卡重试次数太多,等待时间太长,SD卡正常的时候只需1次就能成功*/

    /* Get the card type, version */
    r1 = _send_command_hold(CMD8, 0x1AA, 0x87);
    /* r1=0x05 -> V1.0 */
    if(r1 == 0x05)
    {
        CardInfo.CardType = CARDTYPE_SDV1;

        /* End of CMD8, chip disable and dummy byte */
        _card_disable();
        _spi_read_write(DUMMY_BYTE);

        /* SD1.0/MMC start initialize */
        /* Send CMD55+ACMD41, No-response is a MMC card, otherwise is a SD1.0 card */
        for(retry = 0; retry < 0xFFF; retry++)
        {
            r1 = _send_command(CMD55, 0, 0);			/* should be return 0x01 */
            if(r1 != 0x01)
            {
                return r1;
            }

            r1 = _send_command(ACMD41, 0, 0);			/* should be return 0x00 */
            if(r1 == 0x00)
            {
                retry = 0;
                break;
            }
        }

        /* MMC card initialize start */
        if(retry == 0xFFF)
        {
            for(retry = 0; retry < 0xFFF; retry++)
            {
                r1 = _send_command(CMD1, 0, 0);		/* should be return 0x00 */
                if(r1 == 0x00)
                {
                    retry = 0;
                    break;
                }
            }

            /* Timeout return */
            if(retry == 0xFFF)
            {
                gu8SDStatus = 2;
                return 2;
            }

            CardInfo.CardType = CARDTYPE_MMC;
        }

        /* Set spi speed high */
        MSD_SPIHighSpeed(1);

        /* CRC disable */
        r1 = _send_command(CMD59, 0, 0x01);
        if(r1 != 0x00)
        {
            return r1;		/* response error, return r1 */
        }

        /* Set the block size */
        r1 = _send_command(CMD16, MSD_BLOCKSIZE, 0xFF);
        if(r1 != 0x00)
        {
            return r1;		/* response error, return r1 */
        }
    }

    /* r1=0x01 -> V2.x, read OCR register, check version */
    else if(r1 == 0x01)
    {
        /* 4Bytes returned after CMD8 sent	*/
        buff[0] = _spi_read_write(DUMMY_BYTE);				/* should be 0x00 */
        buff[1] = _spi_read_write(DUMMY_BYTE);				/* should be 0x00 */
        buff[2] = _spi_read_write(DUMMY_BYTE);				/* should be 0x01 */
        buff[3] = _spi_read_write(DUMMY_BYTE);				/* should be 0xAA */

        /* End of CMD8, chip disable and dummy byte */
        _card_disable();
        _spi_read_write(DUMMY_BYTE);

        /* Check voltage range be 2.7-3.6V	*/
        if(buff[2] == 0x01 && buff[3] == 0xAA)
        {
            for(retry = 0; retry < 0xFFF; retry++)
            {
                r1 = _send_command(CMD55, 0, 0);			/* should be return 0x01 */
                if(r1 != 0x01)
                {
                    return r1;
                }

                r1 = _send_command(ACMD41, 0x40000000, 0);	/* should be return 0x00 */
                if(r1 == 0x00)
                {
                    retry = 0;
                    break;
                }
            }

            /* Timeout return */
            if(retry == 0xFFF)
            {
                gu8SDStatus = 3;
                return 3;
            }

            /* Read OCR by CMD58 */
            r1 = _send_command_hold(CMD58, 0, 0);
            if(r1 != 0x00)
            {
                return r1;		/* response error, return r1 */
            }

            buff[0] = _spi_read_write(DUMMY_BYTE);
            buff[1] = _spi_read_write(DUMMY_BYTE);
            buff[2] = _spi_read_write(DUMMY_BYTE);
            buff[3] = _spi_read_write(DUMMY_BYTE);

            /* End of CMD58, chip disable and dummy byte */
            _card_disable();
            _spi_read_write(DUMMY_BYTE);

            /* OCR -> CCS(bit30)  1: SDV2HC	 0: SDV2 */
            if(buff[0] & 0x40)
            {
                CardInfo.CardType = CARDTYPE_SDV2HC;
            }
            else
            {
                CardInfo.CardType = CARDTYPE_SDV2;
            }

            /* Set spi speed high */
            MSD_SPIHighSpeed(1);
        }
    }



    return 0;
}
/*******************************************************************************
* Function Name  : MSD_WriteMultipleBlock
* Description    : None
* Input          : - sector:
*				   - buffer:
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int MSD_WriteMultipleBlock(uint32_t sector, uc8 *buffer, uint8_t count)
{
    uint8_t r1;
    uint16_t i;
    uint32_t retry;

    /* if ver = SD2.0 HC, sector need <<9 */
    if(CardInfo.CardType != CARDTYPE_SDV2HC)
    {
        sector = sector << 9;
    }
    if(CardInfo.CardType != CARDTYPE_MMC)
    {
        _send_command(ACMD23, count, 0);
    }
    /* Send CMD25 : Write multiple block command */
    r1 = _send_command(CMD25, sector, 0);

    if(r1 != 0x00)
    {
        return 1;
    }

    /* Card enable, Prepare to write */
    _card_enable();
    _spi_read_write(DUMMY_BYTE);
    _spi_read_write(DUMMY_BYTE);
    _spi_read_write(DUMMY_BYTE);
    /* Start data write token: 0xFE */
    //_spi_read_write(0xFE);

    do
    {
        _spi_read_write(0xFC);
        for(i = 0; i < MSD_BLOCKSIZE; i++)
        {
            _spi_read_write(*buffer++);
        }
        /* 2Bytes dummy CRC */
        _spi_read_write(DUMMY_BYTE);
        _spi_read_write(DUMMY_BYTE);

        /* MSD card accept the data */
        r1 = _spi_read_write(DUMMY_BYTE);
        if((r1 & 0x1F) != 0x05)
        {
            _card_disable();
            return 2;
        }

        /* Wait all the data programm finished */
        retry = 0;
        while(_spi_read_write(DUMMY_BYTE) == 0x00)
        {
            /* Timeout return */
            if(retry++ == 0x40000)
            {
                _card_disable();
                return 3;
            }
        }

    }
    while (--count);

    r1 = _spi_read_write(0xFD);
    if(r1 == 0x00)
    {
        return 4;
    }

    /* Wait all the data programm finished */
    retry = 0;
    while(_spi_read_write(DUMMY_BYTE) == 0x00)
    {
        /* Timeout return */
        if(retry++ == 0x40000)
        {
            _card_disable();
            return 5;
        }
    }
    /* chip disable and dummy byte */
    _card_disable();
    _spi_read_write(DUMMY_BYTE);

    return count;
}
Esempio n. 10
0
/************************************************************************************
* Function Name  : MSD_WriteMultipleBlock_DMA(uint32_t sector, uc8 *buffer,u16 NumByteToWrite)
* Description    : None
* Input          : - sector:
*				   - buffer:
* Output         : None
* Return         : None
* Attention		 : None
************************************************************************************/
int MSD_WriteMultipleBlock_DMA(uint32_t sector, uc8 *buffer, u8 NbrOfSector, u16 NumByteToWrite)
{
    uint8 r1;
    //  uint16_t i=0;
    uint32_t retry;
    u8 value = 0;
    //  INT8U err;
    u8 rvalue = MSD_RESPONSE_FAILURE;
    DMA_InitTypeDef DMA_InitStructure;                                    //定义DMA初始化结构体

    /*begin:yangfei added 2012.11.29*/
    //    debug("MSD_WriteMultipleBlock_DMA\r\n");
    _card_enable();
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = NumByteToWrite;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    /* Enable DMA1 Channel5 Transfer Complete interrupt */
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
    /*end:yangfei added 2012.11.29*/
    //SPI_DMA_Send_Init(buffer,MSD_BLOCKSIZE);
    /* if ver = SD2.0 HC, sector need <<9 */
    if(CardInfo.CardType != CARDTYPE_SDV2HC)
    {
        sector = sector << 9;
    }
    if(CardInfo.CardType != CARDTYPE_MMC)
    {
        _send_command(ACMD23, NbrOfSector, 0);
    }
    /* Send CMD25 : Write multiple block command */
    MSD_SendCmd(CMD25, sector, 0xff);
    //_spi_read_write(DUMMY_BYTE);
    //_spi_read_write(DUMMY_BYTE);
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
        _spi_read_write(DUMMY_BYTE);
        /* Start data write token: 0xFE */
        _spi_read_write(0xFC);

        /*begin:yangfei added 2012.11.28*/
        DMA_Cmd(DMA1_Channel5, ENABLE);

#ifdef DMA1_IRQ
        OSFlagPend(Sem_SD_DMA, (OS_FLAGS)1, OS_FLAG_WAIT_SET_ALL, 0, &err); //请求信号量集的第0位置1
        DMA_ClearFlag(DMA1_FLAG_TC5);
#else

        while(!DMA_GetFlagStatus(DMA1_FLAG_TC5))  ;
        DMA_ClearFlag(DMA1_FLAG_TC5);
#endif
        /* 2Bytes dummy CRC */
        _spi_read_write(DUMMY_BYTE);
        _spi_read_write(DUMMY_BYTE);

        value = MSD_GetDataResponse();

        if (value == MSD_DATA_OK)
        {
            rvalue = MSD_RESPONSE_NO_ERROR;
        }
        //	   debug("value=%x\r\n",value);
    }
    /* Send end of transmit token: 0xFD */
    r1 = _spi_read_write(0xFD);
    if(r1 == 0x00)
    {
        return 4;
    }
    /*begin:yangfei added 2012.12.07 for wait programm finished else write error*/
    /* Wait all the data programm finished */
    retry = 0;
    while(_spi_read_write(DUMMY_BYTE) == 0x00)
    {
        /* Timeout return */
        if(retry++ == 0x40000)
        {
            _card_disable();
            return 3;
        }
    }
    /* chip disable and dummy byte */
    _card_disable();
    _spi_read_write(DUMMY_BYTE);

    /*yangfei added*/
    DMA_Cmd(DMA1_Channel5, DISABLE);

    return rvalue;
}
/*******************************************************************************
* Function Name  : MSD_WriteMultiBlock
* Description    : None
* Input          : - sector:
*				   - buffer:
*                  - NbrOfSector:
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int MSD_WriteMultiBlock(uint32_t sector, uc8 *buffer, uint32_t NbrOfSector)
{
  uint8_t r1;
  uint16_t i;
  uint32_t n;
  uint32_t retry;

  /* if ver = SD2.0 HC, sector need <<9 */
  if(CardInfo.CardType != CARDTYPE_SDV2HC)
  {
	  sector = sector<<9;
  }

  /* Send command ACMD23 berfore multi write if is not a MMC card */
  if(CardInfo.CardType != CARDTYPE_MMC)
  {
	  _send_command(ACMD23, NbrOfSector, 0x00);
  }

  /* Send CMD25 : Write nulti block command	*/
  r1 = _send_command(CMD25, sector, 0);

  if(r1 != 0x00)
  {
	  return 1;
  }

  /* Card enable, Prepare to write */
  _CARD_ENABLE();
  _spi_read_write(DUMMY_BYTE);
  _spi_read_write(DUMMY_BYTE);
  _spi_read_write(DUMMY_BYTE);

  for(n=0; n<NbrOfSector; n++)
  {
	 /* Start multi block write token: 0xFC */
	 _spi_read_write(0xFC);

	 for(i=0; i<MSD_BLOCKSIZE; i++)
	 {
		_spi_read_write(*buffer++);
	 }

	 /* 2Bytes dummy CRC */
	 _spi_read_write(DUMMY_BYTE);
	 _spi_read_write(DUMMY_BYTE);

	 /* MSD card accept the data */
	 r1 = _spi_read_write(DUMMY_BYTE);
	 if((r1&0x1F) != 0x05)
	 {
	    _CARD_DISABLE();
	    return 2;
	 }

	 /* Wait all the data programm finished	*/
	 retry = 0;
	 while(_spi_read_write(DUMMY_BYTE) != 0xFF)
	 {
		/* Timeout return */
		if(retry++ == 0x40000)
		{
		   _CARD_DISABLE();
		   return 3;
		}
	 }
  }

  /* Send end of transmit token: 0xFD */
  r1 = _spi_read_write(0xFD);
  if(r1 == 0x00)
  {
	 return 4;
  }

  /* Wait all the data programm finished */
  retry = 0;
  while(_spi_read_write(DUMMY_BYTE) != 0xFF)
  {
	 /* Timeout return */
	 if(retry++ == 0x40000)
	 {
	     _CARD_DISABLE();
	     return 5;
	 }
  }

  /* chip disable and dummy byte */
  _CARD_DISABLE();
  _spi_read_write(DUMMY_BYTE);

  return 0;
}
/*******************************************************************************
* Function Name  : MSD_WriteSingleBlock
* Description    : None
* Input          : - sector:
*				   - buffer:
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int MSD_WriteSingleBlock(uint32_t sector, uc8 *buffer)
{
  uint8_t r1;
  uint16_t i;
  uint32_t retry;

  /* if ver = SD2.0 HC, sector need <<9 */
  if(CardInfo.CardType != CARDTYPE_SDV2HC)
  {
	 sector = sector<<9;
  }

  /* Send CMD24 : Write single block command */
  r1 = _send_command(CMD24, sector, 0);

  if(r1 != 0x00)
  {
	 return 1;
  }

  /* Card enable, Prepare to write */
  _CARD_ENABLE();
  _spi_read_write(DUMMY_BYTE);
  _spi_read_write(DUMMY_BYTE);
  _spi_read_write(DUMMY_BYTE);
  /* Start data write token: 0xFE */
  _spi_read_write(0xFE);

  /* Start single block write the data buffer */
  for(i=0; i<MSD_BLOCKSIZE; i++)
  {
    _spi_read_write(*buffer++);
  }

  /* 2Bytes dummy CRC */
  _spi_read_write(DUMMY_BYTE);
  _spi_read_write(DUMMY_BYTE);

  /* MSD card accept the data */
  r1 = _spi_read_write(DUMMY_BYTE);
  if((r1&0x1F) != 0x05)
  {
    _CARD_DISABLE();
    return 2;
  }

  /* Wait all the data programm finished */
  retry = 0;
  while(_spi_read_write(DUMMY_BYTE) == 0x00)
  {
	 /* Timeout return */
	 if(retry++ == 0x40000)
	 {
	    _CARD_DISABLE();
	    return 3;
	 }
  }

  /* chip disable and dummy byte */
  _CARD_DISABLE();
  _spi_read_write(DUMMY_BYTE);

  return 0;
}
/*******************************************************************************
* Function Name  : MSD_Init
* Description    : SD Card initializtion
* Input          : None
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int MSD_Init(void)
{
	uint8_t r1;
	uint8_t buff[6] = {0};
	uint16_t retry;

	/* Check , if no card insert */
	if(_NO_CARD_INSERT())
	{
		//printf("There is no card detected! \r\n");
			return -1;
	}

	/* Power on and delay some times */
	for(retry=0; retry<0x100; retry++)
	{
		_CARD_POWER_ON();
	}

	/* Satrt send 74 clocks at least */
	for(retry=0; retry<10; retry++)
	{
		_spi_read_write(DUMMY_BYTE);
	}

	/* Start send CMD0 till return 0x01 means in IDLE state */
	for(retry=0; retry<0xFFF; retry++)
	{
		r1 = _send_command(CMD0, 0, 0x95);
		if(r1 == 0x01)
		{
			retry = 0;
			break;
		}
	}
	/* Timeout return */
	if(retry == 0xFFF)
	{
		//printf("Reset card into IDLE state failed!\r\n");
		return 1;
	}

	/* Get the card type, version */
	r1 = _send_command_hold(CMD8, 0x1AA, 0x87);
	/* r1=0x05 -> V1.0 */
	if(r1 == 0x05)
	{
		CardInfo.CardType = CARDTYPE_SDV1;

		/* End of CMD8, chip disable and dummy byte */
		_CARD_DISABLE();
		_spi_read_write(DUMMY_BYTE);

		/* SD1.0/MMC start initialize */
		/* Send CMD55+ACMD41, No-response is a MMC card, otherwise is a SD1.0 card */
		for(retry=0; retry<0xFFF; retry++)
		{
			r1 = _send_command(CMD55, 0, 0);			/* should be return 0x01 */
			if(r1 != 0x01)
			{
				//printf("Send CMD55 should return 0x01, response=0x%02x\r\n", r1);
				return r1;
			}

			r1 = _send_command(ACMD41, 0, 0);			/* should be return 0x00 */
			if(r1 == 0x00)
			{
				retry = 0;
				break;
			}
		}

		/* MMC card initialize start */
		if(retry == 0xFFF)
		{
			for(retry=0; retry<0xFFF; retry++)
			{
				r1 = _send_command(CMD1, 0, 0);		/* should be return 0x00 */
				if(r1 == 0x00)
				{
					retry = 0;
					break;
				}
			}

			/* Timeout return */
			if(retry == 0xFFF)
			{
				//printf("Send CMD1 should return 0x00, response=0x%02x\r\n", r1);
				return 2;
			}

			CardInfo.CardType = CARDTYPE_MMC;
			//printf("Card Type: MMC\r\n");
		}
		/* SD1.0 card detected, print information */
		else
		{
			//printf("Card Type: SD V1\r\n");
		}

		/* Set spi speed high */
		MSD_SPIHighSpeed(1);

		/* CRC disable */
		r1 = _send_command(CMD59, 0, 0x01);
		if(r1 != 0x00)
		{
			//printf("Send CMD59 should return 0x00, response=0x%02x\r\n", r1);
			return r1;		/* response error, return r1 */
		}

		/* Set the block size */
		r1 = _send_command(CMD16, MSD_BLOCKSIZE, 0xFF);
		if(r1 != 0x00)
		{
			//printf("Send CMD16 should return 0x00, response=0x%02x\r\n", r1);
			return r1;		/* response error, return r1 */
		}
	}
	/* r1=0x01 -> V2.x, read OCR register, check version */
	else if(r1 == 0x01)
	{
		/* 4Bytes returned after CMD8 sent	*/
		buff[0] = _spi_read_write(DUMMY_BYTE);				/* should be 0x00 */
		buff[1] = _spi_read_write(DUMMY_BYTE);				/* should be 0x00 */
		buff[2] = _spi_read_write(DUMMY_BYTE);				/* should be 0x01 */
		buff[3] = _spi_read_write(DUMMY_BYTE);				/* should be 0xAA */

		/* End of CMD8, chip disable and dummy byte */
		_CARD_DISABLE();
		_spi_read_write(DUMMY_BYTE);

		/* Check voltage range be 2.7-3.6V	*/
		if(buff[2]==0x01 && buff[3]==0xAA)
		{
			for(retry=0; retry<0xFFF; retry++)
			{
				r1 = _send_command(CMD55, 0, 0);			/* should be return 0x01 */
				if(r1!=0x01)
				{
					//printf("Send CMD55 should return 0x01, response=0x%02x\r\n", r1);
					return r1;
				}

				r1 = _send_command(ACMD41, 0x40000000, 0);	/* should be return 0x00 */
				if(r1 == 0x00)
				{
					retry = 0;
					break;
				}
			}

			/* Timeout return */
			if(retry == 0xFFF)
			{
				//printf("Send ACMD41 should return 0x00, response=0x%02x\r\n", r1);
				return 3;
			}

			/* Read OCR by CMD58 */
			r1 = _send_command_hold(CMD58, 0, 0);
			if(r1!=0x00)
			{
				//printf("Send CMD58 should return 0x00, response=0x%02x\r\n", r1);
				return r1;		/* response error, return r1 */
			}

			buff[0] = _spi_read_write(DUMMY_BYTE);
			buff[1] = _spi_read_write(DUMMY_BYTE);
			buff[2] = _spi_read_write(DUMMY_BYTE);
			buff[3] = _spi_read_write(DUMMY_BYTE);

			/* End of CMD58, chip disable and dummy byte */
			_CARD_DISABLE();
			_spi_read_write(DUMMY_BYTE);

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

			/* Set spi speed high */
			MSD_SPIHighSpeed(1);
		}
	}
	return 0;
}