flash_sdcard: add function to retrieve erase block size

This commit is contained in:
King Kévin 2017-07-02 12:11:39 +02:00
parent 22d4dc31f8
commit f681026ec2
2 changed files with 99 additions and 7 deletions

View File

@ -19,7 +19,7 @@
* @note peripherals used: SPI @ref flash_sdcard_spi
* @warning all calls are blocking
* @implements SD Specifications, Part 1, Physical Layer, Simplified Specification, Version 6.00, 10 April 10 2017
* @todo use SPI unidirectional mode and DMA
* @todo use SPI unidirectional mode, use DMA, force/wait going to idle state when initializing
*/
/* standard libraries */
@ -54,6 +54,8 @@ static uint32_t n_ac = 8;
static bool sdsc = false;
/** size of card in bytes */
static uint64_t sdcard_size = 0;
/** size of an erase block bytes */
static uint32_t erase_size = 0;
/** table for CRC-7 calculation for the command messages (see section 4.5)
* @note faster than calculating the CRC and doesn't cost a lot of space
@ -130,7 +132,7 @@ static void flash_sdcard_send_command(uint8_t index, uint32_t argument)
* @param[in] argument command argument
* @param[out] response response data to read (if no error occurred)
* @param[in] size size of response to read
* @return response token R1 of 0xff if error occurred or card is not present
* @return response token R1 or 0xff if error occurred or card is not present
*/
static uint8_t flash_sdcard_command_response(uint8_t index, uint32_t argument, uint8_t* response, size_t size)
{
@ -158,12 +160,81 @@ static uint8_t flash_sdcard_command_response(uint8_t index, uint32_t argument, u
return r1;
}
/** get card status
* @param[out] status SD status (512 bits)
* @return response token R2 or 0xffff if error occurred or card is not present
*/
static uint8_t flash_sdcard_status(uint8_t* status)
{
// send CMD55 (APP_CMD) to issue following application command (see table 7-4)
uint8_t r1 = flash_sdcard_command_response(55, 0, NULL, 0); // (see table 7-3)
if ((r1&0xfe)) { // error occurred, not in idle state
return false;
}
// send ACMD13 command
gpio_clear(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set CS low to select slave and start SPI mode (see section 7.2)
flash_sdcard_spi_wait(); // wait for N_CS (min. 0, but it works better with 8 clock cycles) before writing command (see section 7.5.2.1)
flash_sdcard_send_command(13, 0); // send ACMD13 (SD_STATUS) (see table 7-4)
// get response token R2
uint16_t r2 = 0xffff; // response token R2 (see section 7.3.2.3)
for (uint8_t i=0; i<8 && r2&0x8000; i++) { // wait for N_CR (1 to 8 8 clock cycles) before reading response (see section 7.5.1.1)
r2 = (flash_sdcard_spi_read()<<8); // get first byte of response (see section 7.3.2.1)
}
if (0==(r2&0x8000)) { // got the first byte
r2 += flash_sdcard_spi_read(); // read second byte (see 7.3.2.3)
}
if (0==r2) { // no error
uint8_t sbt = 0; // to save the start block token (see section 7.3.3.2)
for (uint32_t i=0; i<n_ac && sbt!=0xfe; i++) { // wait for N_AC before reading data block (see section 7.5.2.1)
sbt = flash_sdcard_spi_read(); // get start block token (see section 7.3.3.2)
}
if (0xfe==sbt) { // start block token received (see section 7.3.3.2)
// switch to 16-bits SPI data frame so we can use use built-in CRC-16
while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission
while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling
spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change format
spi_set_dff_16bit(SPI(FLASH_SDCARD_SPI)); // set SPI frame to 16 bits
SPI_CRC_PR(FLASH_SDCARD_SPI) = 0x1021; // set CRC-16-CCITT polynomial for data blocks (x^16+x^12+x^5+1) (see section 7.2.3)
spi_enable_crc(SPI(FLASH_SDCARD_SPI)); // enable and clear CRC
spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back
// get block data (ideally use DMA, but switching makes it more complex and this part doesn't take too much time)
for (size_t i=0; i<64/2; i++) { // read 512 bits
uint16_t word = flash_sdcard_spi_read(); // get word
status[i*2+0] = (word>>8); // save byte
status[i*2+1] = (word>>0); // save byte
}
flash_sdcard_spi_read(); // read CRC (the CRC after the data block should clear the computed CRC)
if (SPI_CRC_RXR(FLASH_SDCARD_SPI)) { // CRC is wrong
r2 |= (1<<11); // set communication CRC error
}
// switch back to 8-bit SPI frames
while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission
while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling
spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change format
spi_disable_crc(SPI(FLASH_SDCARD_SPI)); // disable CRC since we don't use it anymore (and this allows us to clear the CRC next time we use it)
spi_set_dff_8bit(SPI(FLASH_SDCARD_SPI)); // set SPI frame to 8 bits
spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back
} else { // start block token not received
r2 |= (1<<11); // set communication CRC error
}
}
// end communication
while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy (= transmission completed)
// wait for N_EC (min. 0) before closing communication (see section 7.5.1.1)
gpio_set(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set CS high to unselect card
// wait for N_DS (min. 0) before allowing any further communication (see section 7.5.1.1)
return r2;
}
/** transmit command token, receive response token and data block
* @param[in] index command index
* @param[in] argument command argument
* @param[out] data data block to read (if no error occurred)
* @param[in] size size of response to read (a multiple of 2)
* @return response token R1 of 0xff if error occurred or card is not present
* @return response token R1 or 0xff if error occurred or card is not present
*/
static uint8_t flash_sdcard_data_read(uint8_t index, uint32_t argument, uint8_t* data, size_t size)
{
@ -180,7 +251,7 @@ static uint8_t flash_sdcard_data_read(uint8_t index, uint32_t argument, uint8_t*
uint8_t r1 = 0xff; // response token R1 (see section 7.3.2.1)
for (uint8_t i=0; i<8 && r1&0x80; i++) { // wait for N_CR (1 to 8 8 clock cycles) before reading response (see section 7.5.1.1)
r1 = flash_sdcard_spi_read(); // get response (see section 7.3.2.1)
}
}
if (0x00==r1 && 0!=size && NULL!=data) { // we have to read a response
uint8_t sbt = 0; // to save the start block token (see section 7.3.3.2)
for (uint32_t i=0; i<n_ac && sbt!=0xfe; i++) { // wait for N_AC before reading data block (see section 7.5.2.1)
@ -231,6 +302,7 @@ bool flash_sdcard_setup(void)
initialized = false;
n_ac = 8;
sdcard_size = 0;
erase_size = 0;
// check if card is present
if (!flash_sdcard_card_detect()) {
@ -336,7 +408,7 @@ bool flash_sdcard_setup(void)
} else { // value is fixed to 100 ms
n_ac=100E-3*16E6/8;
}
// calculate size
// calculate card size
if (sdsc) { // see section 5.3.2
uint16_t c_size = (((uint16_t)csd[6]&0x03)<<10)+((uint16_t)csd[7]<<2)+(csd[8]>>6);
uint8_t c_size_mutl = ((csd[9]&0x03)<<1)+((csd[10]&0x80)>>7);
@ -346,6 +418,17 @@ bool flash_sdcard_setup(void)
uint32_t c_size = ((uint32_t)(csd[7]&0x3f)<<16)+((uint16_t)csd[8]<<8)+csd[9];
sdcard_size = (c_size+1)*(512<<10);
}
// calculate erase size
if (sdsc) { // see section 5.3.2
erase_size = (((csd[10]&0x3f)<<1)+((csd[11]&0x80)>>7)+1)<<(((csd[12]&0x03)<<2)+(csd[13]>>6));
} else {
uint8_t status[64] = {0}; // SD status (see section 4.10.2)
uint16_t r2 = flash_sdcard_status(status); // get status (see table 7-4)
if (r2) { // error occurred
return false;
}
erase_size = (8192UL<<(status[10]>>4)); // calculate erase size (see table 4-44, section 4.10.2.4)
}
// ensure block length is 512 bytes for SDSC (should be per default) to we match SDHC/SDXC block size
if (sdsc) {
@ -393,3 +476,8 @@ uint64_t flash_sdcard_size(void)
{
return sdcard_size;
}
uint32_t flash_sdcard_erase_size(void)
{
return erase_size;
}

View File

@ -25,7 +25,11 @@
* @return if card has been initialized correctly
*/
bool flash_sdcard_setup(void);
/** return size of SD card flash memory
* @return size of SD card flash memory in bytes
/** get size of SD card flash memory
* @return size of SD card flash memory (in bytes)
*/
uint64_t flash_sdcard_size(void);
/** get size of a erase block
* @return size of a erase block (in bytes)
*/
uint32_t flash_sdcard_erase_size(void);