mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
1597 lines
47 KiB
C
1597 lines
47 KiB
C
/*------------------------------------------------------------------------*/
|
|
/* STM32F100: MMCv3/SDv1/SDv2 (SPI mode) control module */
|
|
/*------------------------------------------------------------------------*/
|
|
/*
|
|
/ Copyright (C) 2018, ChaN, all right reserved.
|
|
/
|
|
/ * This software is a free software and there is NO WARRANTY.
|
|
/ * No restriction on use. You can use, modify and redistribute it for
|
|
/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
|
|
/ * Redistributions of source code must retain the above copyright notice.
|
|
/
|
|
/-------------------------------------------------------------------------*/
|
|
|
|
/*--------------------------------------------------------------------------
|
|
Module Private Functions
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#include "ost_hal.h"
|
|
#include "debug.h"
|
|
#include "sdcard.h"
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
/* STM32_Private_Types */
|
|
|
|
/** @defgroup STM32_Private_Defines
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Dummy byte
|
|
*/
|
|
#define SD_DUMMY_BYTE 0xFF
|
|
|
|
/**
|
|
* @brief Number of 8-bit cycles for RUMP UP phase
|
|
*/
|
|
#define SD_NUM_TRIES_RUMPUP ((uint32_t)2500)
|
|
|
|
/**
|
|
* @brief Maximum number of tries to send a command
|
|
*/
|
|
#define SD_NUM_TRIES ((uint16_t)300)
|
|
|
|
/**
|
|
* @brief Maximum number of tries until ACMD41/CMD1 initializes SD card
|
|
* It means a time until "In Idle State" flag is set during initialization
|
|
* For information:
|
|
* ~ 11000 for Kingston 4Gb
|
|
* ~ 10000 for SanDisk 1Gb
|
|
* ~ 6000 for Samsung 8Gb
|
|
*/
|
|
#define SD_NUM_TRIES_INIT ((uint16_t)20000)
|
|
|
|
/**
|
|
* @brief Maximum number of tries to receive data transmission token
|
|
* It means a time before data transmission starts
|
|
* For information:
|
|
* ~ 300 for SanDisk 1Gb
|
|
* ~ 600 for Kingston 4Gb
|
|
* ~ 900 for SP 4Gb
|
|
* ~ 500 for Samsung 8Gb
|
|
* ~ 300 for Lexar 4Gb
|
|
*/
|
|
#define SD_NUM_TRIES_READ ((uint16_t)2000)
|
|
|
|
/**
|
|
* @brief Maximum number of tries until SD card writes data
|
|
* It means a time while BUSY flag is set, i.e. card writes the data received
|
|
* For information:
|
|
* ~ 6100 for Kingston 4Gb
|
|
* ~ 4600 for Lexar 4Gb
|
|
* ~ 80000 for SP 4Gb (9000)
|
|
* ~ 10000 for SanDisk 1Gb
|
|
* ~119000 for Samsung 8Gb
|
|
*/
|
|
#define SD_NUM_TRIES_WRITE ((uint32_t)1000000)
|
|
|
|
/**
|
|
* @brief Maximum number of tries until SD card erases data
|
|
* It means a time while BUSY flag is set, i.e. card erases sectors
|
|
* For information:
|
|
* ~ 6100 for Kingston 4Gb
|
|
* ~ 5200 for Lexar 4Gb
|
|
* ~ 10300 for SP 4Gb
|
|
* ~ N/A for SanDisk 1Gb
|
|
* ~120000 for Samsung 8Gb
|
|
*/
|
|
#define SD_NUM_TRIES_ERASE ((uint32_t)1000000)
|
|
|
|
/**
|
|
* @brief Start Data tokens:
|
|
* Tokens (necessary because at nop/idle (and CS active) only 0xff is
|
|
* on the data/command line)
|
|
*/
|
|
#define SD_DATA_BLOCK_READ_START 0xFE /*!< Data token start byte, Start Single/Multiple Block Read */
|
|
#define SD_DATA_SINGLE_BLOCK_WRITE_START 0xFE /*!< Data token start byte, Start Single Block Write */
|
|
#define SD_DATA_MULTIPLE_BLOCK_WRITE_START 0xFC /*!< Data token start byte, Start Multiple Block Write */
|
|
#define SD_DATA_MULTIPLE_BLOCK_WRITE_STOP 0xFD /*!< Data token stop byte, Stop Multiple Block Write */
|
|
|
|
static SDCardType cardType;
|
|
|
|
/**
|
|
* @brief Detect if SD card is correctly plugged in the memory slot.
|
|
* @param None
|
|
* @retval Return if SD is detected or not
|
|
*/
|
|
uint8_t SD_Detect(void)
|
|
{ /* check GPIO to detect SD */
|
|
if (ost_hal_sdcard_get_presence())
|
|
{
|
|
return SD_PRESENT;
|
|
}
|
|
return SD_NOT_PRESENT;
|
|
}
|
|
|
|
/**
|
|
* @brief Hold SPI bus for SD card
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static void SD_Bus_Hold(void)
|
|
{ /* Select SD Card: set SD chip select pin low */
|
|
ost_hal_sdcard_cs_low();
|
|
}
|
|
|
|
static uint8_t SD_SpiWriteByte(uint8_t byte)
|
|
{
|
|
ost_hal_sdcard_spi_exchange(&byte, &byte, 1);
|
|
return byte;
|
|
}
|
|
|
|
/**
|
|
* @brief Release SPI bus used by SD card
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static void SD_Bus_Release(void)
|
|
{ /* Deselect SD Card: set SD chip select pin high */
|
|
ost_hal_sdcard_cs_high();
|
|
SD_SpiWriteByte(0xFF); /* send dummy byte: 8 Clock pulses of delay */
|
|
}
|
|
|
|
/**
|
|
* @brief Send a command to SD card and receive R1 response
|
|
* @param Cmd: Command to send to SD card
|
|
* @param Arg: Command argument
|
|
* @param Crc: CRC
|
|
* @retval R1 response byte
|
|
*/
|
|
static SD_Error SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
|
|
{
|
|
uint8_t res;
|
|
uint16_t i = SD_NUM_TRIES;
|
|
/* send a command */
|
|
SD_SpiWriteByte((cmd & 0x3F) | 0x40); /*!< byte 1 */
|
|
SD_SpiWriteByte((uint8_t)(arg >> 24)); /*!< byte 2 */
|
|
SD_SpiWriteByte((uint8_t)(arg >> 16)); /*!< byte 3 */
|
|
SD_SpiWriteByte((uint8_t)(arg >> 8)); /*!< byte 4 */
|
|
SD_SpiWriteByte((uint8_t)arg); /*!< byte 5 */
|
|
SD_SpiWriteByte(crc | 0x01); /*!< byte 6: CRC */
|
|
/* a byte received immediately after CMD12 should be discarded... */
|
|
if (cmd == SD_CMD_STOP_TRANSMISSION)
|
|
SD_SpiWriteByte(0xFF);
|
|
/* SD Card responds within Ncr (response time),
|
|
which is 0-8 bytes for SDSC cards, 1-8 bytes for MMC cards */
|
|
do
|
|
{
|
|
res = SD_SpiWriteByte(0xFF);
|
|
/* R1 response always starts with 7th bit set to 0 */
|
|
} while ((res & SD_CHECK_BIT) != 0x00 && i-- > 0);
|
|
return (SD_Error)res;
|
|
}
|
|
|
|
/**
|
|
* @brief Some commands take longer time and respond with R1b response,
|
|
* so we have to wait until 0xFF recieved (MISO is set to HIGH)
|
|
* @retval Nonzero if required state wasn't recieved
|
|
*/
|
|
static SD_Error SD_WaitReady(void)
|
|
{
|
|
uint16_t i = SD_NUM_TRIES;
|
|
while (i-- > 0)
|
|
{
|
|
if (SD_SpiWriteByte(0xFF) == 0xFF)
|
|
{
|
|
// debug_printf( " [[ WAIT delay %d ]] ", SD_NUM_TRIES - i );
|
|
return SD_RESPONSE_NO_ERROR;
|
|
}
|
|
}
|
|
// debug_printf( " [[ WAIT delay was not enough ]] " );
|
|
return SD_RESPONSE_FAILURE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get 4 bytes of R3 or R7 response
|
|
* @param pres: Pointer to uint32_t variable for result
|
|
* @retval None
|
|
*/
|
|
static void SD_GetResponse4b(uint8_t *pres)
|
|
{
|
|
pres[3] = SD_SpiWriteByte(0xFF);
|
|
pres[2] = SD_SpiWriteByte(0xFF);
|
|
pres[1] = SD_SpiWriteByte(0xFF);
|
|
pres[0] = SD_SpiWriteByte(0xFF);
|
|
}
|
|
|
|
/**
|
|
* @brief Set SD Card sector size to SD_CMD_SET_BLOCKLEN (512 bytes)
|
|
* @param New sector size
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
static SD_Error SD_FixSectorSize(uint16_t ssize)
|
|
{
|
|
return SD_SendCmd(SD_CMD_SET_BLOCKLEN, (uint32_t)ssize, 0xFF);
|
|
}
|
|
|
|
/**
|
|
* @brief Wait until data transmission token is received
|
|
* @retval Data transmission token or 0xFF if timeout occured
|
|
*/
|
|
static uint8_t SD_WaitBytesRead(void)
|
|
{
|
|
uint16_t i = SD_NUM_TRIES_READ;
|
|
uint8_t b;
|
|
do
|
|
{
|
|
b = SD_SpiWriteByte(0xFF);
|
|
} while (b == 0xFF && i-- > 0);
|
|
|
|
// if (b != 0xFF)
|
|
// debug_printf(" [[ READ delay %d ]] ", SD_NUM_TRIES_READ - i);
|
|
// else
|
|
// debug_printf(" [[ READ delay was not enough ]] ");
|
|
|
|
return b;
|
|
}
|
|
|
|
/**
|
|
* @brief Writing data into flash takes even longer time and it responds with R1b response,
|
|
* so we have to wait until 0xFF recieved (MISO is set to HIGH)
|
|
* @retval Nonzero if required state wasn't recieved
|
|
*/
|
|
static SD_Error SD_WaitBytesWritten(void)
|
|
{
|
|
uint32_t i = SD_NUM_TRIES_WRITE;
|
|
while (i-- > 0)
|
|
{
|
|
if (SD_SpiWriteByte(0xFF) == 0xFF)
|
|
{
|
|
// debug_printf(" [[ WRITE delay %lu ]] ", SD_NUM_TRIES_WRITE - i);
|
|
return SD_RESPONSE_NO_ERROR;
|
|
}
|
|
}
|
|
// debug_printf(" [[ WRITE delay was not enough ]] ");
|
|
return SD_RESPONSE_FAILURE;
|
|
}
|
|
|
|
/**
|
|
* @brief Erasing data into flash takes some time and it responds with R1b response,
|
|
* so we have to wait until 0xFF recieved (MISO is set to HIGH)
|
|
* @retval Nonzero if required state wasn't recieved
|
|
*/
|
|
static SD_Error SD_WaitBytesErased(void)
|
|
{
|
|
uint32_t i = SD_NUM_TRIES_ERASE;
|
|
while (i-- > 0)
|
|
{
|
|
if (SD_SpiWriteByte(0xFF) == 0xFF)
|
|
{
|
|
// debug_printf(" [[ ERASE delay %lu ]] ", SD_NUM_TRIES_ERASE - i);
|
|
return SD_RESPONSE_NO_ERROR;
|
|
}
|
|
}
|
|
// debug_printf(" [[ ERASE delay was not enough ]] ");
|
|
return SD_RESPONSE_FAILURE;
|
|
}
|
|
|
|
/**
|
|
* @brief Recieve data from SD Card
|
|
* @param data: Pre-allocated data buffer
|
|
* @param len: Number of bytes to receive
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
static SD_Error SD_ReceiveData(uint8_t *data, uint16_t len)
|
|
{
|
|
uint16_t i = 0;
|
|
uint8_t b;
|
|
|
|
/* some cards need time before transmitting the data... */
|
|
b = SD_WaitBytesRead();
|
|
|
|
if (b != 0xFF)
|
|
{ /* most cards send transmission start token, don't fail if it's not the case... */
|
|
data[i] = b;
|
|
if (data[i] == SD_DATA_BLOCK_READ_START) /* 0xFE */
|
|
data[i] = SD_SpiWriteByte(0xFF); /* just get the next byte... */
|
|
|
|
/* receive the rest of data... */
|
|
// for (i = 1; i < len; ++i)
|
|
// data[i] = SD_SpiWriteByte(0xFF);
|
|
ost_hal_sdcard_spi_read(&data[1], (len - 1));
|
|
|
|
/* get CRC bytes (not really needed by us, but required by SD) */
|
|
SD_SpiWriteByte(0xFF);
|
|
SD_SpiWriteByte(0xFF);
|
|
|
|
return SD_RESPONSE_NO_ERROR;
|
|
}
|
|
return SD_RESPONSE_FAILURE;
|
|
}
|
|
|
|
/**
|
|
* @brief Put SD in Idle state.
|
|
* @param None
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
static SD_Error SD_GoIdleState(void)
|
|
{
|
|
uint32_t res = 0;
|
|
uint16_t i;
|
|
uint8_t state;
|
|
|
|
/* --- put SD card in SPI mode */
|
|
SD_Bus_Hold();
|
|
|
|
i = SD_NUM_TRIES; /* reset try count... */
|
|
do
|
|
{ /* loop until In Idle State Response (in R1 format) confirmation */
|
|
state = SD_SendCmd(SD_CMD_GO_IDLE_STATE, 0x00000000, 0x95); /* valid CRC is mandatory here */
|
|
} while (state != SD_IN_IDLE_STATE && i-- > 0);
|
|
/* still no Idle State Response => return response failure */
|
|
if (state != SD_IN_IDLE_STATE)
|
|
return SD_RESPONSE_FAILURE;
|
|
|
|
/* --- SD card is now in idle state and in SPI mode, activate it and get its type */
|
|
cardType = SD_Card_SDSC_v2;
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* --- try to send CMD8 to offer voltage 2.7-3.6V with check pattern 0xAA */
|
|
i = SD_NUM_TRIES; /* reset try count... */
|
|
do
|
|
{
|
|
state = SD_SendCmd(SD_CMD_SEND_IF_COND, 0x000001AA, 0x87); /* valid CRC is mandatory here */
|
|
if ((state & SD_ILLEGAL_COMMAND) != 0)
|
|
{ /* SD card doesn't accept CMD8 => it's SDSC or MMC card... */
|
|
cardType = SD_Card_SDSC_v1;
|
|
break;
|
|
}
|
|
else /* SD card accepts CMD8 => it's SDHC or SDXC card... */
|
|
{ /* get R7 response and verify pattern for sanity check... */
|
|
SD_GetResponse4b((uint8_t *)&res);
|
|
if ((res & 0x0000FFFF) == 0x000001AA)
|
|
break; /* check pattern is OK, card accepted offered voltage... */
|
|
/* else specification recommends to retry CMD8 again */
|
|
}
|
|
} while (i-- > 0);
|
|
if (i == 0)
|
|
return SD_RESPONSE_FAILURE; /* error occurred... */
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* --- activate card initialization sequence... */
|
|
/* CMD55(0) -> ACMD41(0) -> ... */
|
|
i = SD_NUM_TRIES_INIT; /* reset try count... */
|
|
do
|
|
{
|
|
state = SD_SendCmd(SD_CMD_SEND_APP, 0x00000000, 0x65);
|
|
if (state != SD_IN_IDLE_STATE)
|
|
{ /* error occurred => last chance is to try it as a legacy MMC card */
|
|
cardType = SD_Card_MMC;
|
|
break;
|
|
}
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
if (cardType == SD_Card_SDSC_v1) /* HCS bit (0 here) is ignored by SDSC card */
|
|
state = SD_SendCmd(SD_CMD_ACTIVATE_INIT, 0x00000000, 0xFF);
|
|
else
|
|
state = SD_SendCmd(SD_CMD_ACTIVATE_INIT, 0x40000000, 0x77);
|
|
/* loop while SD_IN_IDLE_STATE bit is set, meaning card is still performing initialization */
|
|
} while ((state & SD_IN_IDLE_STATE) != 0x00 && i-- > 0);
|
|
/* it might be legacy MMC card... */
|
|
if (cardType == SD_Card_SDSC_v1 &&
|
|
(state & SD_IN_IDLE_STATE) != 0x00)
|
|
cardType = SD_Card_MMC;
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
if (cardType == SD_Card_MMC) /* legacy MMC card is initialized with CMD1... */
|
|
{ /* -> CMD1(0) -> ... */
|
|
i = SD_NUM_TRIES_INIT; /* reset try count... */
|
|
do
|
|
{
|
|
state = SD_SendCmd(SD_CMD_SEND_OP_COND, 0x00000000, 0xFF);
|
|
} while ((state & SD_IN_IDLE_STATE) != 0x00 && i-- > 0);
|
|
if (i == 0)
|
|
return SD_RESPONSE_FAILURE; /* error occurred... */
|
|
}
|
|
else if (cardType == SD_Card_SDSC_v2) /* recent cards support byte-addressing, check it... */
|
|
{ /* -> CMD58(0)... */
|
|
if (i == 0) /* first check if timeout occured during its initialization... */
|
|
return SD_RESPONSE_FAILURE; /* error occurred... */
|
|
/* request OCR register (send CMD58)... */
|
|
state = SD_SendCmd(SD_CMD_READ_OCR, 0x00000000, 0xFF);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
{ /* get OCR register (R3 response) and check its CCS (bit 30) */
|
|
SD_GetResponse4b((uint8_t *)&res);
|
|
cardType = (res & 0x40000000) ? SD_Card_SDHC : SD_Card_SDSC_v2;
|
|
}
|
|
}
|
|
/* else cardType == SD_Card_SDSC_v1 */
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* print out detected SD card type... */
|
|
switch (cardType)
|
|
{
|
|
case SD_Card_SDSC_v1:
|
|
debug_printf("SDSC v1 (byte address)");
|
|
break;
|
|
case SD_Card_SDSC_v2:
|
|
debug_printf("SDSC v2 (byte address)");
|
|
break;
|
|
case SD_Card_SDHC:
|
|
debug_printf("SDHC (512-bytes sector address)");
|
|
break;
|
|
case SD_Card_MMC:
|
|
debug_printf("MMC (byte address)");
|
|
break;
|
|
default:
|
|
debug_printf("UNKNOWN");
|
|
break;
|
|
}
|
|
|
|
debug_printf(" card initialized successfully\r\n");
|
|
|
|
return SD_RESPONSE_NO_ERROR;
|
|
}
|
|
|
|
SD_Error sdcard_init()
|
|
{
|
|
uint32_t i = 0;
|
|
SD_Error state;
|
|
|
|
/* step 0:
|
|
* Check if SD card is present... */
|
|
if (SD_Detect() == SD_NOT_PRESENT)
|
|
{
|
|
return SD_RESPONSE_FAILURE;
|
|
}
|
|
|
|
debug_printf("\r\n [TEST] SD Card detected\r\n");
|
|
|
|
/* step 2:
|
|
* Card is now powered up (i.e. 1ms at least elapsed at 0.5V),
|
|
* Supply rump up time (set MOSI HIGH) to let voltage reach stable 2.2V at least.
|
|
* According to the specs it must be 74 SPI clock cycles minimum at 100-400Khz
|
|
* At 25Mhz it'll be 250 times more cycles => send 2500 times 0xFF byte.
|
|
* Chip Select pin should be set HIGH too. */
|
|
|
|
/* set SD chip select pin high */
|
|
ost_hal_sdcard_cs_high();
|
|
|
|
/* send dummy byte 0xFF (rise MOSI high for 2500*8 SPI bus clock cycles) */
|
|
while (i++ < SD_NUM_TRIES_RUMPUP)
|
|
{
|
|
SD_SpiWriteByte(SD_DUMMY_BYTE);
|
|
}
|
|
|
|
/* step 3:
|
|
* Put SD in SPI mode & perform soft reset */
|
|
state = SD_GoIdleState();
|
|
|
|
/* step 4:
|
|
* Force sector size to SD_BLOCK_SIZE (i.e. 512 bytes) */
|
|
if (state == SD_RESPONSE_NO_ERROR && cardType != SD_Card_SDHC)
|
|
{
|
|
state = SD_FixSectorSize((uint16_t)SD_BLOCK_SIZE);
|
|
}
|
|
|
|
/* step 5:
|
|
* Release SPI bus for other devices */
|
|
SD_Bus_Release();
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Reads a sector of SD_BLOCK_SIZE bytes from the SD card
|
|
* @param pBuffer: pointer to the buffer that receives the data read from SD.
|
|
* @param readAddr: SD's internal address to read from (sector number)
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
SD_Error sdcard_sector_read(uint32_t readAddr, uint8_t *pBuffer)
|
|
{
|
|
SD_Error state;
|
|
|
|
// debug_printf("--> reading sector %lu ...", readAddr);
|
|
|
|
/* non High Capacity cards use byte-oriented addresses */
|
|
if (cardType != SD_Card_SDHC)
|
|
readAddr <<= 9;
|
|
|
|
SD_Bus_Hold(); /* hold SPI bus... */
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block */
|
|
state = SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK, readAddr, 0xFF);
|
|
/* receive data if command acknowledged... */
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
state = SD_ReceiveData(pBuffer, SD_BLOCK_SIZE);
|
|
|
|
SD_Bus_Release(); /* release SPI bus... */
|
|
|
|
// if (state == SD_RESPONSE_NO_ERROR)
|
|
// debug_printf("OK\r\n");
|
|
// else
|
|
// debug_printf("KO(%d)\r\n", state);
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Reads multiple sectors of SD_BLOCK_SIZE bytes from the SD card
|
|
* @param pBuffer: pointer to the buffer that receives the data read from SD.
|
|
* @param readAddr: SD's internal address to read from.
|
|
* @param nbSectors: number of blocks to be read.
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
SD_Error sdcard_sectors_read(uint32_t readAddr, uint8_t *pBuffer, uint32_t nbSectors)
|
|
{
|
|
SD_Error state;
|
|
|
|
// debug_printf("--> reading %lu sectors from %lu ...", nbSectors, readAddr);
|
|
|
|
/* non High Capacity cards use byte-oriented addresses */
|
|
if (cardType != SD_Card_SDHC)
|
|
readAddr <<= 9;
|
|
|
|
SD_Bus_Hold(); /* hold SPI bus... */
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* send CMD18 (SD_CMD_READ_MULT_BLOCK) to read multiple blocks */
|
|
state = SD_SendCmd(SD_CMD_READ_MULT_BLOCK, readAddr, 0xFF);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
{ /* receive data... */
|
|
while (nbSectors-- > 0)
|
|
{
|
|
state = SD_ReceiveData(pBuffer, SD_BLOCK_SIZE);
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
break;
|
|
pBuffer += SD_BLOCK_SIZE;
|
|
}
|
|
/* transmission is open-ended (no block count was set) =>
|
|
* send CMD12 (SD_CMD_STOP_TRANSMISSION) to stop it... */
|
|
state = SD_SendCmd(SD_CMD_STOP_TRANSMISSION, 0x00000000, 0xFF);
|
|
}
|
|
|
|
SD_Bus_Release(); /* release SPI bus... */
|
|
|
|
// if (state == SD_RESPONSE_NO_ERROR)
|
|
// debug_printf("OK\r\n");
|
|
// else
|
|
// debug_printf("KO(%d)\r\n", state);
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Writes a sector of SD_BLOCK_SIZE bytes on the SD card
|
|
* @param pBuffer: pointer to the buffer with the data to be written on SD.
|
|
* @param writeAddr: address to write on.
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
SD_Error sdcard_sector_write(uint32_t writeAddr, const uint8_t *pBuffer)
|
|
{
|
|
SD_Error state;
|
|
SD_DataResponse res;
|
|
uint16_t BlockSize = SD_BLOCK_SIZE;
|
|
|
|
debug_printf("--> writing sector %lu ...", writeAddr);
|
|
|
|
/* non High Capacity cards use byte-oriented addresses */
|
|
if (cardType != SD_Card_SDHC)
|
|
writeAddr <<= 9;
|
|
|
|
SD_Bus_Hold(); /* hold SPI bus... */
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write single block */
|
|
state = SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK, writeAddr, 0xFF);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
{ /* wait at least 8 clock cycles (send >=1 0xFF bytes) before transmission starts */
|
|
SD_SpiWriteByte(SD_DUMMY_BYTE);
|
|
SD_SpiWriteByte(SD_DUMMY_BYTE);
|
|
SD_SpiWriteByte(SD_DUMMY_BYTE);
|
|
/* send data token to signify the start of data transmission... */
|
|
SD_SpiWriteByte(SD_DATA_SINGLE_BLOCK_WRITE_START); /* 0xFE */
|
|
/* send data... */
|
|
while (BlockSize-- > 0)
|
|
SD_SpiWriteByte(*pBuffer++);
|
|
/* put 2 CRC bytes (not really needed by us, but required by SD) */
|
|
SD_SpiWriteByte(SD_DUMMY_BYTE);
|
|
SD_SpiWriteByte(SD_DUMMY_BYTE);
|
|
/* check data response... */
|
|
res = (SD_DataResponse)(SD_SpiWriteByte(0xFF) & SD_RESPONSE_MASK); /* mask unused bits */
|
|
if ((res & SD_RESPONSE_ACCEPTED) != 0)
|
|
{ /* card is now processing data and goes to BUSY mode, wait until it finishes... */
|
|
state = SD_WaitBytesWritten(); /* make sure card is ready before we go further... */
|
|
}
|
|
else
|
|
state = SD_RESPONSE_FAILURE;
|
|
}
|
|
|
|
SD_Bus_Release(); /* release SPI bus... */
|
|
|
|
// if (state == SD_RESPONSE_NO_ERROR)
|
|
// debug_printf("OK\r\n");
|
|
// else
|
|
// debug_printf("KO(%d)\r\n", state);
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Writes multiple sectors of SD_BLOCK_SIZE bytes on the SD card
|
|
* @param pBuffer: pointer to the buffer with the data to be written on the SD.
|
|
* @param writeAddr: address to write on.
|
|
* @param nbSectors: number of blocks to be written.
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
SD_Error sdcard_sectors_write(uint32_t writeAddr, const uint8_t *pBuffer, uint32_t nbSectors)
|
|
{
|
|
SD_Error state;
|
|
SD_DataResponse res;
|
|
uint16_t BlockSize = SD_BLOCK_SIZE;
|
|
|
|
debug_printf("--> writing %lu sectors at %lu ...", nbSectors, writeAddr);
|
|
|
|
/* non High Capacity cards use byte-oriented addresses */
|
|
if (cardType != SD_Card_SDHC)
|
|
writeAddr <<= 9;
|
|
|
|
SD_Bus_Hold(); /* hold SPI bus... */
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* it is recommended to specify in advance the number of blocks being written
|
|
* to let SD card erase needed number of blocks, write operation should take less time then */
|
|
if (cardType != SD_Card_MMC)
|
|
{ /* notify card about the total number of blocks to be sent (send CMD23)... */
|
|
state = SD_SendCmd(SD_CMD_SET_BLOCK_COUNT, (uint32_t)nbSectors, 0xFF);
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
{
|
|
SD_Bus_Release(); /* release SPI bus... */
|
|
return state;
|
|
}
|
|
}
|
|
|
|
/* request writing data starting from the given address (send CMD25)... */
|
|
state = SD_SendCmd(SD_CMD_WRITE_MULT_BLOCK, writeAddr, 0xFF);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
{
|
|
/* send some dummy bytes before transmission starts... */
|
|
SD_SpiWriteByte(0xFF);
|
|
SD_SpiWriteByte(0xFF);
|
|
SD_SpiWriteByte(0xFF);
|
|
/* transfer data... */
|
|
while (nbSectors-- > 0 && state != SD_RESPONSE_FAILURE)
|
|
{ /* --- {{ send data packet */
|
|
SD_SpiWriteByte(SD_DATA_MULTIPLE_BLOCK_WRITE_START); /* 0xFC */
|
|
/* send data... */
|
|
while (BlockSize-- > 0)
|
|
SD_SpiWriteByte(*pBuffer++);
|
|
/* put CRC bytes (not really needed by us, but required by SD) */
|
|
SD_SpiWriteByte(0xFF);
|
|
SD_SpiWriteByte(0xFF);
|
|
/* --- }} */
|
|
/* check data response... */
|
|
res = (SD_DataResponse)(SD_SpiWriteByte(0xFF) & SD_RESPONSE_MASK); /* mask unused bits */
|
|
if ((res & SD_RESPONSE_ACCEPTED) != 0)
|
|
{ /* card is now processing data and goes to BUSY mode, wait until it finishes... */
|
|
state = SD_WaitBytesWritten(); /* make sure card is ready before we go further... */
|
|
}
|
|
else
|
|
state = SD_RESPONSE_FAILURE;
|
|
}
|
|
/* notify SD card that we finished sending data to write on it */
|
|
SD_SpiWriteByte(SD_DATA_MULTIPLE_BLOCK_WRITE_STOP); /* 0xFD */
|
|
SD_SpiWriteByte(0xFF); /* read and discard 1 byte from card */
|
|
/* card is now processing data and goes to BUSY mode, wait until it finishes... */
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
}
|
|
|
|
SD_Bus_Release(); /* release SPI bus... */
|
|
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
debug_printf("OK\n");
|
|
else
|
|
debug_printf("KO(%d)\n", state);
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Erase specified range of sectors on SD card
|
|
* @param eraseAddrFrom: Starting sector number
|
|
* @param eraseAddrTo: End sector number
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
SD_Error sdcard_sectors_erase(uint32_t eraseAddrFrom, uint32_t eraseAddrTo)
|
|
{
|
|
SD_Error state;
|
|
|
|
if (cardType == SD_Card_MMC)
|
|
{
|
|
debug_printf("--> erasing sectors is not supported for MMC cards\n");
|
|
return SD_ILLEGAL_COMMAND;
|
|
}
|
|
|
|
// debug_printf("--> erasing sectors from %lu to %lu ...", eraseAddrFrom, eraseAddrTo);
|
|
|
|
/* non High Capacity cards use byte-oriented addresses */
|
|
if (cardType != SD_Card_SDHC)
|
|
{
|
|
eraseAddrFrom <<= 9;
|
|
eraseAddrTo <<= 9;
|
|
}
|
|
|
|
SD_Bus_Hold(); /* hold SPI bus... */
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
|
|
/* send starting block address (CMD32)... */
|
|
state = SD_SendCmd(SD_CMD_ERASE_BLOCK_START, (uint32_t)eraseAddrFrom, 0xFF);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
{ /* send end block address (CMD33)... */
|
|
state = SD_SendCmd(SD_CMD_ERASE_BLOCK_END, (uint32_t)eraseAddrTo, 0xFF);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
{ /* erase all selected blocks (CMD38)... */
|
|
state = SD_SendCmd(SD_CMD_ERASE, 0x00000000, 0xFF);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
{ /* wait until sectors get erased... */
|
|
state = SD_WaitBytesErased(); /* make sure card is ready before we go further... */
|
|
}
|
|
}
|
|
}
|
|
|
|
SD_Bus_Release(); /* release SPI bus... */
|
|
|
|
// if (state == SD_RESPONSE_NO_ERROR)
|
|
// debug_printf("OK\r\n");
|
|
// else
|
|
// debug_printf("KO(%d)\r\n", state);
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the CSD card register.
|
|
* Reading the contents of the CSD register in SPI mode is a simple
|
|
* read-block transaction.
|
|
* @param SD_csd: pointer on an SCD register structure
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
static SD_Error SD_GetCSDRegister(SD_CSD *SD_csd)
|
|
{
|
|
SD_Error state;
|
|
uint8_t CSD_Tab[16];
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
return SD_RESPONSE_FAILURE;
|
|
|
|
/* request CSD register (send CMD9)... */
|
|
state = SD_SendCmd(SD_CMD_SEND_CSD, 0x00000000, 0xFF);
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
return SD_RESPONSE_FAILURE;
|
|
state = SD_ReceiveData(CSD_Tab, 16); /* receive CSD register data */
|
|
|
|
SD_csd->CSDStruct = (CSD_Tab[0] & 0xC0) >> 6; /* Byte 0 */
|
|
SD_csd->SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
|
|
SD_csd->Reserved1 = CSD_Tab[0] & 0x03;
|
|
SD_csd->TAAC = CSD_Tab[1]; /* Byte 1 */
|
|
SD_csd->NSAC = CSD_Tab[2]; /* Byte 2 */
|
|
SD_csd->MaxBusClkFrec = CSD_Tab[3]; /* Byte 3 */
|
|
SD_csd->CardComdClasses = CSD_Tab[4] << 4; /* Byte 4 */
|
|
SD_csd->CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4; /* Byte 5 */
|
|
SD_csd->RdBlockLen = CSD_Tab[5] & 0x0F;
|
|
SD_csd->PartBlockRead = (CSD_Tab[6] & 0x80) >> 7; /* Byte 6 */
|
|
SD_csd->WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
|
|
SD_csd->RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
|
|
SD_csd->DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
|
|
SD_csd->Reserved2 = (CSD_Tab[6] & 0x0C) >> 2;
|
|
if (SD_csd->CSDStruct == 0)
|
|
{ /* v1 */
|
|
SD_csd->DeviceSize = (CSD_Tab[6] & 0x03) << 10; /* DeviceSize has 12 bits here */
|
|
SD_csd->DeviceSize |= CSD_Tab[7] << 2; /* Byte 7 */
|
|
SD_csd->DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6; /* Byte 8 */
|
|
SD_csd->MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
|
|
SD_csd->MaxRdCurrentVDDMax = CSD_Tab[8] & 0x07;
|
|
SD_csd->MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5; /* Byte 9 */
|
|
SD_csd->MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
|
|
SD_csd->DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
|
|
SD_csd->DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7; /* Byte 10 */
|
|
}
|
|
else
|
|
{ /* v2 */
|
|
SD_csd->Reserved5 = (CSD_Tab[6] & 0x03) << 2;
|
|
SD_csd->Reserved5 |= (CSD_Tab[7] & 0xC0) >> 6; /* Byte 7 */
|
|
SD_csd->DeviceSize = (CSD_Tab[7] & 0x3F) << 16; /* DeviceSize has 22 bits here */
|
|
SD_csd->DeviceSize |= CSD_Tab[8] << 8; /* Byte 8 */
|
|
SD_csd->DeviceSize |= CSD_Tab[9]; /* Byte 9 */
|
|
SD_csd->Reserved6 = (CSD_Tab[10] & 0x80) >> 7; /* Byte 10 */
|
|
}
|
|
SD_csd->EraseBlockEnable = (CSD_Tab[10] & 0x40) >> 6;
|
|
SD_csd->EraseSectorSize = (CSD_Tab[10] & 0x3F) << 1;
|
|
SD_csd->EraseSectorSize |= (CSD_Tab[11] & 0x80) >> 7; /* Byte 11 */
|
|
SD_csd->WrProtectGrSize = CSD_Tab[11] & 0x7F;
|
|
SD_csd->WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7; /* Byte 12 */
|
|
SD_csd->ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
|
|
SD_csd->WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
|
|
SD_csd->MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
|
|
SD_csd->MaxWrBlockLen |= (CSD_Tab[13] & 0xC0) >> 6; /* Byte 13 */
|
|
SD_csd->WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
|
|
SD_csd->Reserved3 = CSD_Tab[13] & 0x1E;
|
|
SD_csd->ContentProtectAppli = CSD_Tab[13] & 0x01;
|
|
SD_csd->FileFormatGroup = (CSD_Tab[14] & 0x80) >> 7; /* Byte 14 */
|
|
SD_csd->CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
|
|
SD_csd->PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
|
|
SD_csd->TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
|
|
SD_csd->FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
|
|
SD_csd->ECC = CSD_Tab[14] & 0x03;
|
|
SD_csd->CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1; /* Byte 15 */
|
|
SD_csd->Reserved4 = CSD_Tab[15] & 0x01;
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the CID card register.
|
|
* Reading the contents of the CID register in SPI mode is a simple
|
|
* read-block transaction.
|
|
* @param SD_cid: pointer on an CID register structure
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
static SD_Error SD_GetCIDRegister(SD_CID *SD_cid)
|
|
{
|
|
SD_Error state;
|
|
uint8_t CID_Tab[16];
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
return SD_RESPONSE_FAILURE;
|
|
|
|
/* request CID register (send CMD10)... */
|
|
state = SD_SendCmd(SD_CMD_SEND_CID, 0x00000000, 0xFF);
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
return SD_RESPONSE_FAILURE;
|
|
state = SD_ReceiveData(CID_Tab, 16); /* receive CID register data */
|
|
|
|
SD_cid->ManufacturerID = CID_Tab[0]; /* Byte 0 */
|
|
SD_cid->OEM_AppliID = CID_Tab[1] << 8; /* Byte 1 */
|
|
SD_cid->OEM_AppliID |= CID_Tab[2]; /* Byte 2 */
|
|
SD_cid->ProdName1 = CID_Tab[3] << 24; /* Byte 3 */
|
|
SD_cid->ProdName1 |= CID_Tab[4] << 16; /* Byte 4 */
|
|
SD_cid->ProdName1 |= CID_Tab[5] << 8; /* Byte 5 */
|
|
SD_cid->ProdName1 |= CID_Tab[6]; /* Byte 6 */
|
|
SD_cid->ProdName2 = CID_Tab[7]; /* Byte 7 */
|
|
SD_cid->ProdRev = CID_Tab[8]; /* Byte 8 */
|
|
SD_cid->ProdSN = CID_Tab[9] << 24; /* Byte 9 */
|
|
SD_cid->ProdSN |= CID_Tab[10] << 16; /* Byte 10 */
|
|
SD_cid->ProdSN |= CID_Tab[11] << 8; /* Byte 11 */
|
|
SD_cid->ProdSN |= CID_Tab[12]; /* Byte 12 */
|
|
SD_cid->Reserved1 |= (CID_Tab[13] & 0xF0) >> 4; /* Byte 13 */
|
|
SD_cid->ManufactDate = (CID_Tab[13] & 0x0F) << 8;
|
|
SD_cid->ManufactDate |= CID_Tab[14]; /* Byte 14 */
|
|
SD_cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; /* Byte 15 */
|
|
SD_cid->Reserved2 = 1;
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the SCR card register.
|
|
* Reading the contents of the SCR register in SPI mode is a simple
|
|
* read-block transaction.
|
|
* @param SD_scr: pointer on an SCR register structure
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
static SD_Error SD_GetSCRRegister(SD_SCR *SD_scr)
|
|
{
|
|
SD_Error state;
|
|
uint8_t SCR_Tab[8];
|
|
|
|
if (cardType == SD_Card_MMC)
|
|
{
|
|
debug_printf("SCR Register is not available for MMC cards\n");
|
|
return SD_ILLEGAL_COMMAND;
|
|
}
|
|
|
|
state = SD_WaitReady(); /* make sure card is ready before we go further... */
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
return SD_RESPONSE_FAILURE;
|
|
|
|
/* request SCR register (send ACMD51)... */
|
|
state = SD_SendCmd(SD_CMD_SEND_APP, 0x00000000, 0x65);
|
|
if (state == SD_RESPONSE_NO_ERROR)
|
|
state = SD_SendCmd(SD_CMD_SEND_SCR, 0x00000000, 0xFF);
|
|
if (state != SD_RESPONSE_NO_ERROR)
|
|
return SD_RESPONSE_FAILURE;
|
|
state = SD_ReceiveData(SCR_Tab, 8); /* receive SCR register data */
|
|
|
|
SD_scr->SCR_Version = (SCR_Tab[0] & 0xF0) >> 4; /* Byte 0 */
|
|
SD_scr->SpecVersion = SCR_Tab[0] & 0x0F;
|
|
SD_scr->StateAfterErase = (SCR_Tab[1] & 0x80) >> 7; /* Byte 1 */
|
|
SD_scr->Security = (SCR_Tab[1] & 0x70) >> 4;
|
|
SD_scr->BusWidth = SCR_Tab[1] & 0x0F;
|
|
SD_scr->SpecVersion3 = (SCR_Tab[2] & 0x80) >> 7; /* Byte 2 */
|
|
SD_scr->ExSecurity = (SCR_Tab[2] & 0x78) >> 3;
|
|
SD_scr->Reserved1 = (SCR_Tab[2] & 0x07) << 6;
|
|
SD_scr->Reserved1 |= (SCR_Tab[3] & 0xFC) >> 2; /* Byte 3 */
|
|
SD_scr->CmdSupport1 = (SCR_Tab[3] & 0x02) >> 1;
|
|
SD_scr->CmdSupport2 = SCR_Tab[3] & 0x01;
|
|
SD_scr->Reserved2 = SCR_Tab[4] << 24; /* Byte 4 */
|
|
SD_scr->Reserved2 |= SCR_Tab[5] << 16; /* Byte 5 */
|
|
SD_scr->Reserved2 |= SCR_Tab[6] << 8; /* Byte 6 */
|
|
SD_scr->Reserved2 |= SCR_Tab[7]; /* Byte 7 */
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns information about specific card.
|
|
* @param cardinfo: pointer to a SD_CardInfo structure that contains all SD
|
|
* card information.
|
|
* @retval The SD Response:
|
|
* - SD_RESPONSE_FAILURE: Sequence failed
|
|
* - SD_RESPONSE_NO_ERROR: Sequence succeed
|
|
*/
|
|
SD_Error sdcard_get_card_info(SD_CardInfo *cardinfo)
|
|
{
|
|
SD_Error status;
|
|
|
|
SD_Bus_Hold(); /* hold SPI bus... */
|
|
|
|
status = SD_GetCSDRegister(&(cardinfo->SD_csd));
|
|
if (status == SD_RESPONSE_NO_ERROR)
|
|
status = SD_GetCIDRegister(&(cardinfo->SD_cid));
|
|
if (status == SD_RESPONSE_NO_ERROR && cardType != SD_Card_MMC)
|
|
status = SD_GetSCRRegister(&(cardinfo->SD_scr));
|
|
|
|
SD_Bus_Release(); /* release SPI bus... */
|
|
|
|
if (status == SD_RESPONSE_NO_ERROR)
|
|
{ /* to avoid overflow, card capacity is calculated in Kbytes */
|
|
if (cardinfo->SD_csd.CSDStruct == 0)
|
|
{ // v1:
|
|
cardinfo->CardCapacity = cardinfo->SD_csd.DeviceSize + 1;
|
|
cardinfo->CardCapacity *= (1 << (cardinfo->SD_csd.DeviceSizeMul + 2));
|
|
cardinfo->CardBlockSize = 1 << cardinfo->SD_csd.RdBlockLen;
|
|
if (cardinfo->SD_csd.RdBlockLen > 10)
|
|
cardinfo->CardCapacity *= (1 << (cardinfo->SD_csd.RdBlockLen - 10));
|
|
else
|
|
cardinfo->CardCapacity /= (1 << (10 - cardinfo->SD_csd.RdBlockLen));
|
|
}
|
|
else
|
|
{ // v2:
|
|
cardinfo->CardCapacity = cardinfo->SD_csd.DeviceSize + 1;
|
|
cardinfo->CardBlockSize = 1 << cardinfo->SD_csd.RdBlockLen;
|
|
cardinfo->CardCapacity *= cardinfo->CardBlockSize;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief Prints out human-readable information about SD Card
|
|
* @param Previously retrieved card info structure
|
|
* @retval None
|
|
*/
|
|
void sdcard_dump_card_info(const SD_CardInfo *cardinfo)
|
|
{
|
|
uint8_t is_OSRv1 = (cardinfo->SD_csd.CSDStruct == 0);
|
|
debug_printf("\nDumping SD Card information:\n\n GLOBAL INFO\nSD Card type : ");
|
|
/* some cards report wrong CSDStruct field in CSR register => use detected card type instead... */
|
|
if (is_OSRv1 != 0)
|
|
debug_printf("SDSC (v1 or v2)\n");
|
|
else
|
|
debug_printf("SDHC or SDXC\n");
|
|
debug_printf("Card Capacity : %lu Kbytes\n", cardinfo->CardCapacity);
|
|
debug_printf("Card Block Size : %lu bytes\n", cardinfo->CardBlockSize);
|
|
|
|
debug_printf("\n Card identification register (CID)\n");
|
|
debug_printf("Manufacturer ID : %d\n", cardinfo->SD_cid.ManufacturerID);
|
|
debug_printf("OEM / Application ID : %c%c\n",
|
|
((char *)&(cardinfo->SD_cid.OEM_AppliID))[1],
|
|
((char *)&(cardinfo->SD_cid.OEM_AppliID))[0]);
|
|
debug_printf("Product Name : %c%c%c%c%c\n",
|
|
((char *)&(cardinfo->SD_cid.ProdName1))[3],
|
|
((char *)&(cardinfo->SD_cid.ProdName1))[2],
|
|
((char *)&(cardinfo->SD_cid.ProdName1))[1],
|
|
((char *)&(cardinfo->SD_cid.ProdName1))[0],
|
|
(char)(cardinfo->SD_cid.ProdName2));
|
|
debug_printf("Product Revision : %d.%d\n",
|
|
(cardinfo->SD_cid.ProdRev & 0xF0) >> 4,
|
|
(cardinfo->SD_cid.ProdRev & 0x0F));
|
|
debug_printf("Product Serial Number : %lu\n",
|
|
cardinfo->SD_cid.ProdSN);
|
|
debug_printf("Manufacturing Date (YYYY-MM) : %d-%d\n",
|
|
2000 + ((cardinfo->SD_cid.ManufactDate & 0x0FF0) >> 4),
|
|
cardinfo->SD_cid.ManufactDate & 0x000F);
|
|
debug_printf("CID CRC : %d\n", cardinfo->SD_cid.CID_CRC & 0x7F);
|
|
|
|
debug_printf("\n Card-specific data register (CSD)\n");
|
|
if (is_OSRv1 != 0)
|
|
{
|
|
debug_printf("Data read access-time : ");
|
|
switch ((cardinfo->SD_csd.TAAC & 0x78) >> 3)
|
|
{
|
|
case 0x0:
|
|
debug_printf("0.0");
|
|
break;
|
|
case 0x1:
|
|
debug_printf("1.0");
|
|
break;
|
|
case 0x2:
|
|
debug_printf("1.2");
|
|
break;
|
|
case 0x3:
|
|
debug_printf("1.3");
|
|
break;
|
|
case 0x4:
|
|
debug_printf("1.5");
|
|
break;
|
|
case 0x5:
|
|
debug_printf("2.0");
|
|
break;
|
|
case 0x6:
|
|
debug_printf("2.5");
|
|
break;
|
|
case 0x7:
|
|
debug_printf("3.0");
|
|
break;
|
|
case 0x8:
|
|
debug_printf("3.5");
|
|
break;
|
|
case 0x9:
|
|
debug_printf("4.0");
|
|
break;
|
|
case 0xA:
|
|
debug_printf("4.5");
|
|
break;
|
|
case 0xB:
|
|
debug_printf("5.0");
|
|
break;
|
|
case 0xC:
|
|
debug_printf("5.5");
|
|
break;
|
|
case 0xD:
|
|
debug_printf("6.0");
|
|
break;
|
|
case 0xE:
|
|
debug_printf("7.0");
|
|
break;
|
|
case 0xF:
|
|
debug_printf("8.0");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug_printf(" x 1");
|
|
switch (cardinfo->SD_csd.TAAC & 0x07)
|
|
{
|
|
case 0:
|
|
debug_printf("n");
|
|
break;
|
|
case 1:
|
|
debug_printf("0n");
|
|
break;
|
|
case 2:
|
|
debug_printf("00n");
|
|
break;
|
|
case 3:
|
|
debug_printf("u");
|
|
break;
|
|
case 4:
|
|
debug_printf("0u");
|
|
break;
|
|
case 5:
|
|
debug_printf("00u");
|
|
break;
|
|
case 6:
|
|
debug_printf("m");
|
|
break;
|
|
case 7:
|
|
debug_printf("0m");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug_printf("s\nData read access-time in CLK cycles : %d\n", cardinfo->SD_csd.NSAC);
|
|
}
|
|
debug_printf("Max. bus clock frequency : %x", cardinfo->SD_csd.MaxBusClkFrec);
|
|
switch (cardinfo->SD_csd.MaxBusClkFrec)
|
|
{
|
|
case 0x32:
|
|
debug_printf(" (25Mhz)\n");
|
|
break;
|
|
case 0x5A:
|
|
debug_printf(" (50Mhz)\n");
|
|
break;
|
|
case 0x0B:
|
|
debug_printf(" (100Mhz)\n");
|
|
break;
|
|
case 0x2B:
|
|
debug_printf(" (200Mhz)\n");
|
|
break;
|
|
default:
|
|
debug_printf("\n");
|
|
break;
|
|
}
|
|
debug_printf("\nCard command classes :");
|
|
if (cardinfo->SD_csd.CardComdClasses & (0x001))
|
|
debug_printf(" 0(basic)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 1))
|
|
debug_printf(" 1");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 2))
|
|
debug_printf(" 2(read)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 3))
|
|
debug_printf(" 3");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 4))
|
|
debug_printf(" 4(write)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 5))
|
|
debug_printf(" 5(erase)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 6))
|
|
debug_printf(" 6(protect)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 7))
|
|
debug_printf(" 7(lock)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 8))
|
|
debug_printf(" 8(app)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 9))
|
|
debug_printf(" 9(i/o)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 10))
|
|
debug_printf(" 10(switch)");
|
|
if (cardinfo->SD_csd.CardComdClasses & (1 << 11))
|
|
debug_printf(" 11");
|
|
debug_printf("\n");
|
|
|
|
if (is_OSRv1 != 0)
|
|
{
|
|
debug_printf("Max. read data block length : %d ( %d bytes )\n",
|
|
cardinfo->SD_csd.RdBlockLen,
|
|
1 << (cardinfo->SD_csd.RdBlockLen));
|
|
debug_printf("Partial blocks for read allowed : %d\n", cardinfo->SD_csd.PartBlockRead);
|
|
debug_printf("Write block misalignment : %d\n", cardinfo->SD_csd.WrBlockMisalign);
|
|
debug_printf("Read block misalignment : %d\n", cardinfo->SD_csd.RdBlockMisalign);
|
|
}
|
|
else
|
|
{
|
|
debug_printf("Max. read data block length : always 512 bytes\n");
|
|
debug_printf("Partial blocks for read are not allowed\n");
|
|
debug_printf("Read/Write block misalignment is not allowed\n");
|
|
}
|
|
debug_printf("DSR implemented : %d\n", cardinfo->SD_csd.DSRImpl);
|
|
debug_printf("Device Size (4112 <= and <= 65375): %lu\n", cardinfo->SD_csd.DeviceSize);
|
|
|
|
if (is_OSRv1 != 0)
|
|
{
|
|
debug_printf("Max. read current at VDD min : ");
|
|
switch (cardinfo->SD_csd.MaxRdCurrentVDDMin)
|
|
{
|
|
case 0:
|
|
debug_printf("0.5");
|
|
break;
|
|
case 1:
|
|
debug_printf("1");
|
|
break;
|
|
case 2:
|
|
debug_printf("5");
|
|
break;
|
|
case 3:
|
|
debug_printf("10");
|
|
break;
|
|
case 4:
|
|
debug_printf("25");
|
|
break;
|
|
case 5:
|
|
debug_printf("35");
|
|
break;
|
|
case 6:
|
|
debug_printf("60");
|
|
break;
|
|
case 7:
|
|
debug_printf("100");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug_printf("mA\nMax. read current at VDD max : ");
|
|
switch (cardinfo->SD_csd.MaxRdCurrentVDDMax)
|
|
{
|
|
case 0:
|
|
debug_printf("0.5");
|
|
break;
|
|
case 1:
|
|
debug_printf("1");
|
|
break;
|
|
case 2:
|
|
debug_printf("5");
|
|
break;
|
|
case 3:
|
|
debug_printf("10");
|
|
break;
|
|
case 4:
|
|
debug_printf("25");
|
|
break;
|
|
case 5:
|
|
debug_printf("35");
|
|
break;
|
|
case 6:
|
|
debug_printf("60");
|
|
break;
|
|
case 7:
|
|
debug_printf("100");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug_printf("mA\nMax. write current at VDD min : ");
|
|
switch (cardinfo->SD_csd.MaxWrCurrentVDDMin)
|
|
{
|
|
case 0:
|
|
debug_printf("1");
|
|
break;
|
|
case 1:
|
|
debug_printf("5");
|
|
break;
|
|
case 2:
|
|
debug_printf("10");
|
|
break;
|
|
case 3:
|
|
debug_printf("25");
|
|
break;
|
|
case 4:
|
|
debug_printf("35");
|
|
break;
|
|
case 5:
|
|
debug_printf("45");
|
|
break;
|
|
case 6:
|
|
debug_printf("80");
|
|
break;
|
|
case 7:
|
|
debug_printf("200");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug_printf("mA\nMax. write current at VDD max : ");
|
|
switch (cardinfo->SD_csd.MaxWrCurrentVDDMax)
|
|
{
|
|
case 0:
|
|
debug_printf("1");
|
|
break;
|
|
case 1:
|
|
debug_printf("5");
|
|
break;
|
|
case 2:
|
|
debug_printf("10");
|
|
break;
|
|
case 3:
|
|
debug_printf("25");
|
|
break;
|
|
case 4:
|
|
debug_printf("35");
|
|
break;
|
|
case 5:
|
|
debug_printf("45");
|
|
break;
|
|
case 6:
|
|
debug_printf("80");
|
|
break;
|
|
case 7:
|
|
debug_printf("200");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug_printf("mA\nDevice size multiplier : %d\n", cardinfo->SD_csd.DeviceSizeMul);
|
|
if (cardinfo->SD_csd.EraseBlockEnable == 0)
|
|
debug_printf("Erase size : 1 or more units of %d bytes each\n", cardinfo->SD_csd.EraseSectorSize);
|
|
else
|
|
debug_printf("Erase size : 1 or more blocks of 512 bytes each\n");
|
|
|
|
debug_printf("Write protect group size : %d\n", cardinfo->SD_csd.WrProtectGrSize);
|
|
debug_printf("Write protect group enable : %d\n", cardinfo->SD_csd.WrProtectGrEnable);
|
|
debug_printf("Write speed factor (Twrite/Tread) : %d\n", 1 << (cardinfo->SD_csd.WrSpeedFact & 0x3F));
|
|
debug_printf("Max. write data block length : %d\n", 1 << (cardinfo->SD_csd.MaxWrBlockLen & 0xF));
|
|
debug_printf("Partial blocks for write allowed : %d\n", cardinfo->SD_csd.WriteBlockPaPartial);
|
|
debug_printf("File format group : %d\n", cardinfo->SD_csd.FileFormatGroup);
|
|
}
|
|
else
|
|
{
|
|
debug_printf("Erase size : 1 or more blocks of 512 bytes each\n");
|
|
debug_printf("Write protect group disabled\n");
|
|
debug_printf("Write timeout : 250ms\n");
|
|
debug_printf("Max. write data block length : 512 bytes\n");
|
|
debug_printf("Partial blocks for write are not allowed\n");
|
|
}
|
|
debug_printf("Copy flag (OTP) : %d\n", cardinfo->SD_csd.CopyFlag);
|
|
debug_printf("Permanent write protection : %d\n", cardinfo->SD_csd.PermWrProtect);
|
|
debug_printf("Temporary write protection : %d\n", cardinfo->SD_csd.TempWrProtect);
|
|
|
|
if (is_OSRv1 != 0)
|
|
{
|
|
debug_printf("File Format : ");
|
|
switch (cardinfo->SD_csd.FileFormat)
|
|
{
|
|
case 0:
|
|
debug_printf("HDD-like file system with partition table\n");
|
|
break;
|
|
case 1:
|
|
debug_printf("DOS FAT (FDD-like) with boot sector only (no partition table)\n");
|
|
break;
|
|
case 2:
|
|
debug_printf("Universal File Format\n");
|
|
break;
|
|
case 3:
|
|
debug_printf("Others/Unknown\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
debug_printf("CSD CRC : %d\n", cardinfo->SD_csd.CSD_CRC);
|
|
|
|
if (cardType != SD_Card_MMC)
|
|
{
|
|
debug_printf("\n SD Card configuration register (SCR)\n");
|
|
debug_printf("SCR structure version : %d\n", cardinfo->SD_scr.SCR_Version);
|
|
debug_printf("Physical layer specification version number : ");
|
|
switch (cardinfo->SD_scr.SpecVersion)
|
|
{
|
|
case 0:
|
|
debug_printf("Version 1.0 and 1.01");
|
|
break;
|
|
case 1:
|
|
debug_printf("Version 1.10");
|
|
break;
|
|
case 2:
|
|
debug_printf("Version %s", (cardinfo->SD_scr.SpecVersion3 == 0) ? "2.00" : "3.0x");
|
|
break;
|
|
default:
|
|
debug_printf("reserved");
|
|
break;
|
|
}
|
|
debug_printf("\nState of bits after sector erase : 0x%s\n", cardinfo->SD_scr.StateAfterErase ? "FF" : "00");
|
|
debug_printf("CPRM security version : ");
|
|
switch (cardinfo->SD_scr.Security)
|
|
{
|
|
case 0:
|
|
debug_printf("no security");
|
|
break;
|
|
case 1:
|
|
debug_printf("not used");
|
|
break;
|
|
case 2:
|
|
debug_printf("SDSC security ver 1.01");
|
|
break;
|
|
case 3:
|
|
debug_printf("SDHC security ver 2.00");
|
|
break;
|
|
case 4:
|
|
debug_printf("SDXC security ver 3.xx");
|
|
break;
|
|
default:
|
|
debug_printf("reserved");
|
|
break;
|
|
}
|
|
debug_printf("\nSupported data bus width :");
|
|
if (cardinfo->SD_scr.BusWidth & 0x01)
|
|
debug_printf(" 1 bit");
|
|
if (cardinfo->SD_scr.BusWidth & 0x04)
|
|
debug_printf(" 4 bit");
|
|
debug_printf("\nExtended security is%s supported\n", (cardinfo->SD_scr.ExSecurity == 0) ? " not" : "");
|
|
debug_printf("Support of CMD23 (set block count) : %c\n", cardinfo->SD_scr.CmdSupport1 ? 'Y' : 'N');
|
|
debug_printf("Support of CMD20 (speed class control) : %c\n", cardinfo->SD_scr.CmdSupport2 ? 'Y' : 'N');
|
|
}
|
|
debug_printf("\nDONE\n");
|
|
}
|
|
|
|
/**
|
|
* @brief Prints out SD card status in human-readable form
|
|
* @param Previously retrieved SD card status structure
|
|
* @retval None
|
|
*/
|
|
void sdcard_dump_status(const SD_Status *SD_status)
|
|
{
|
|
debug_printf("\nDumping SD Card status information:\n\n");
|
|
if (cardType != SD_Card_MMC)
|
|
{
|
|
debug_printf("Bus width : ");
|
|
switch (SD_status->BusWidth)
|
|
{
|
|
case 0x00:
|
|
debug_printf("1 bit");
|
|
break;
|
|
case 0x02:
|
|
debug_printf("4 bits");
|
|
break;
|
|
default:
|
|
debug_printf("reserved");
|
|
break;
|
|
}
|
|
debug_printf("\nSD card is%s in secured mode\n", SD_status->InSecuredMode ? "" : " not");
|
|
debug_printf("Card Type : ");
|
|
switch (SD_status->CardType)
|
|
{
|
|
case 0x0000:
|
|
debug_printf("Regular SD card");
|
|
break;
|
|
case 0x0001:
|
|
debug_printf("SD ROM card");
|
|
break;
|
|
case 0x0002:
|
|
debug_printf("OTP card");
|
|
break;
|
|
default:
|
|
debug_printf("other card");
|
|
break;
|
|
}
|
|
debug_printf("\nSize of protected area : %lu\n", SD_status->SizeProtectedArea);
|
|
debug_printf("Speed class : ");
|
|
switch (SD_status->SpeedClass)
|
|
{
|
|
case 0x00:
|
|
debug_printf("Class 0");
|
|
break;
|
|
case 0x01:
|
|
debug_printf("Class 2");
|
|
break;
|
|
case 0x02:
|
|
debug_printf("Class 4");
|
|
break;
|
|
case 0x03:
|
|
debug_printf("Class 6");
|
|
break;
|
|
case 0x04:
|
|
debug_printf("Class 10");
|
|
break;
|
|
default:
|
|
debug_printf("Reserved");
|
|
break;
|
|
}
|
|
debug_printf("\nPerformance move : ");
|
|
switch (SD_status->PerformanceMove)
|
|
{
|
|
case 0x00:
|
|
debug_printf("Sequential write");
|
|
break;
|
|
case 0xFF:
|
|
debug_printf("Infinity");
|
|
break;
|
|
default:
|
|
debug_printf("%d Mb/sec", SD_status->PerformanceMove);
|
|
break;
|
|
}
|
|
debug_printf("\nAllocation Unit size : ");
|
|
switch (SD_status->AU_Size)
|
|
{
|
|
case 0x00:
|
|
debug_printf("not defined");
|
|
break;
|
|
case 0x01:
|
|
debug_printf("16 Kb");
|
|
break;
|
|
case 0x02:
|
|
debug_printf("32 Kb");
|
|
break;
|
|
case 0x03:
|
|
debug_printf("64 Kb");
|
|
break;
|
|
case 0x04:
|
|
debug_printf("128 Kb");
|
|
break;
|
|
case 0x05:
|
|
debug_printf("256 Kb");
|
|
break;
|
|
case 0x06:
|
|
debug_printf("512 Kb");
|
|
break;
|
|
case 0x07:
|
|
debug_printf("1 Mb");
|
|
break;
|
|
case 0x08:
|
|
debug_printf("2 Mb");
|
|
break;
|
|
case 0x09:
|
|
debug_printf("4 Mb");
|
|
break;
|
|
case 0x0A:
|
|
debug_printf("8 Mb");
|
|
break;
|
|
case 0x0B:
|
|
debug_printf("12 Mb");
|
|
break;
|
|
case 0x0C:
|
|
debug_printf("16 Mb");
|
|
break;
|
|
case 0x0D:
|
|
debug_printf("24 Mb");
|
|
break;
|
|
case 0x0E:
|
|
debug_printf("32 Mb");
|
|
break;
|
|
case 0x0F:
|
|
debug_printf("64 Mb");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug_printf("\nErase Size : %d AU blocks\n", SD_status->EraseSize);
|
|
debug_printf("Erase Timeout : %d seconds\n", SD_status->EraseTimeout);
|
|
debug_printf("Erase Offset : %d seconds\n", SD_status->EraseOffset);
|
|
debug_printf("Speed Grade for UHS mode : %s\n",
|
|
(SD_status->UHS_SpeedGrade == 0) ? "< 10 Mb/sec" : "> 10 Mb/sec");
|
|
debug_printf("Allocation Unit size for UHS mode : ");
|
|
switch (SD_status->UHS_AU_Size)
|
|
{
|
|
case 0x00:
|
|
debug_printf("not defined");
|
|
break;
|
|
case 0x07:
|
|
debug_printf("1 Mb");
|
|
break;
|
|
case 0x08:
|
|
debug_printf("2 Mb");
|
|
break;
|
|
case 0x09:
|
|
debug_printf("4 Mb");
|
|
break;
|
|
case 0x0A:
|
|
debug_printf("8 Mb");
|
|
break;
|
|
case 0x0B:
|
|
debug_printf("12 Mb");
|
|
break;
|
|
case 0x0C:
|
|
debug_printf("16 Mb");
|
|
break;
|
|
case 0x0D:
|
|
debug_printf("24 Mb");
|
|
break;
|
|
case 0x0E:
|
|
debug_printf("32 Mb");
|
|
break;
|
|
case 0x0F:
|
|
debug_printf("64 Mb");
|
|
break;
|
|
default:
|
|
debug_printf("not used");
|
|
break;
|
|
}
|
|
}
|
|
debug_printf("\n\nDONE\n");
|
|
}
|