From c3b35be66c00de27c2e7b2375a2f88d87df34867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Mon, 3 Jul 2017 15:00:21 +0200 Subject: [PATCH] flash_sdcard: add write data function and improve other functions --- lib/flash_sdcard.c | 268 +++++++++++++++++++++++++++++++++------------ lib/flash_sdcard.h | 12 ++ 2 files changed, 210 insertions(+), 70 deletions(-) diff --git a/lib/flash_sdcard.c b/lib/flash_sdcard.c index 7ab8c60..b1c394d 100644 --- a/lib/flash_sdcard.c +++ b/lib/flash_sdcard.c @@ -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, use DMA, force/wait going to idle state when initializing + * @todo use SPI unidirectional mode, use DMA, force/wait going to idle state when initializing, filter out reserved values, check sector against size */ /* standard libraries */ @@ -93,8 +93,8 @@ static void flash_sdcard_spi_wait(void) static uint16_t flash_sdcard_spi_read(void) { spi_send(SPI(FLASH_SDCARD_SPI), 0xffff); // send not command token (i.e. starting with 1) - while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until Tx buffer is empty before clearing the (previous) RXNE flag (void)SPI_DR(SPI(FLASH_SDCARD_SPI)); // clear RXNE flag (by reading previously received data (not done by spi_read or spi_xref) + while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until Tx buffer is empty before clearing the (previous) RXNE flag while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_RXNE)); // wait for next data to be available return SPI_DR(SPI(FLASH_SDCARD_SPI)); // return received adat } @@ -160,11 +160,109 @@ static uint8_t flash_sdcard_command_response(uint8_t index, uint32_t argument, u return r1; } +/** read a data block + * @param[out] data data block to read (if no error occurred) + * @param[in] size size of response to read (a multiple of 2) + * @return 0 if succeeded, else control token (0xff for other errors) + */ +static uint8_t flash_sdcard_read_block(uint8_t* data, size_t size) +{ + if (size%2 || 0==size || NULL==data) { // can't (and shouldn't) read odd number of bytes + return 0xff; + } + + uint8_t token = 0xff; // to save the control block token (see section 7.3.3) + for (uint32_t i=0; i>8); // save byte + data[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 + token = 0xff; + } else { // no error occurred + token = 0; + } + // 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 + token = 0xff; + } + + return token; +} + +/** write a data block + * @param[in] data data block to write + * @param[in] size size of response to read (a multiple of 2) + * @return data response token (0xff for other errors) + */ +static uint8_t flash_sdcard_write_block(uint8_t* data, size_t size) +{ + if (size%2 || 0==size || NULL==data) { // can't (and shouldn't) read odd number of bytes + return 0xff; + } + + spi_send(SPI(FLASH_SDCARD_SPI), 0xfe); // send start block token (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 + // send 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>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 + if (flash_sdcard_read_block(status, 64)) { // read 512 bits data block containing SD status + r2 |= (1<<11); // set communication error } } @@ -233,12 +303,12 @@ static uint8_t flash_sdcard_status(uint8_t* status) * @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) + * @param[in] size size of data to read (a multiple of 2) * @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) { - if (size%2) { // can't (and shouldn't) read odd number of bytes + if (size%2 || 0==size || NULL==data) { // can't (and shouldn't) read odd number of bytes return 0xff; } @@ -252,39 +322,11 @@ static uint8_t flash_sdcard_data_read(uint8_t index, uint32_t argument, uint8_t* 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>8); // save byte - data[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 - r1 |= (1<<3); // 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 - r1 |= (1<<3); // set communication CRC error + + // get data block + if (0x00==r1) { // we can read a data block + if (flash_sdcard_read_block(data, size)) { // read data block + r1 |= (1<<3); // set communication error } } @@ -296,6 +338,46 @@ static uint8_t flash_sdcard_data_read(uint8_t index, uint32_t argument, uint8_t* return r1; } +/** transmit command token, receive response token and write data block + * @param[in] index command index + * @param[in] argument command argument + * @param[out] data data block to write + * @param[in] size size of data to write (a multiple of 2) + * @return data response token, or 0xff if error occurred or card is not present + * @note at the end of a write operation the SD status should be check to ensure no error occurred during programming + */ +static uint8_t flash_sdcard_data_write(uint8_t index, uint32_t argument, uint8_t* data, size_t size) +{ + if (size%2 || 0==size || NULL==data) { // can't (and shouldn't) write odd number of bytes + return 0xff; + } + + // send command token + 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(index, argument); // send command token + + // get response token R1 + 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) + } + + // write data block + uint8_t drt = 0xff; // data response token (see section 7.3.3.1) + if (0x00==r1) { // we have to write the data block + drt = flash_sdcard_write_block(data, size); // write data block + } + + // 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 drt; +} + bool flash_sdcard_setup(void) { // reset values @@ -481,3 +563,49 @@ uint32_t flash_sdcard_erase_size(void) { return erase_size; } + +bool flash_sdcard_read_data(uint32_t block, uint8_t* data) +{ + if (NULL==data) { + return false; + } + if (sdsc) { // the address for standard capacity cards must be provided in bytes + if (block>UINT32_MAX/512) { // check for integer overflow + return false; + } else { + block *= 512; // calculate byte address from block address + } + } + return (0==flash_sdcard_data_read(17, block, data, 512)); // read single data block using CMD17 (READ_SINGLE_BLOCK) (see table 7-3) +} + +bool flash_sdcard_write_data(uint32_t block, uint8_t* data) +{ + if (NULL==data) { + return false; + } + + if (sdsc) { // the address for standard capacity cards must be provided in bytes + if (block>UINT32_MAX/512) { // check for integer overflow + return false; + } else { + block *= 512; // calculate byte address from block address + } + } + + uint8_t drt = flash_sdcard_data_write(24, block, data, 512); // write single data block using CMD24 (WRITE_SINGLE_BLOCK) (see table 7-3) + if (0x05!=(drt&0x1f)) { // write block failed + return false; + } + + // get status to check if programming succeeded + uint8_t r2[1] = {0}; // to store response token R2 (see section 7.3.2.3) + uint8_t r1 = flash_sdcard_command_response(13, 0, r2, sizeof(r2)); // get SD status using CMD13 (SEND_STATUS) (see table 7-3) + if (0x00!=r1) { // error occurred + return false; + } else if (r2[0]) { // programming error + return false; + } + + return true; // programming succeeded +} diff --git a/lib/flash_sdcard.h b/lib/flash_sdcard.h index 8a07f4c..7b3c718 100644 --- a/lib/flash_sdcard.h +++ b/lib/flash_sdcard.h @@ -33,3 +33,15 @@ uint64_t flash_sdcard_size(void); * @return size of a erase block (in bytes) */ uint32_t flash_sdcard_erase_size(void); +/** read data on flash of SD card + * @param[in] block address of data to read (in block in 512 bytes unit) + * @param[out] data data block to read (with a size of 512 bytes) + * @return if read succeeded + */ +bool flash_sdcard_read_data(uint32_t block, uint8_t* data); +/** write data on flash of SD card + * @param[in] block address of data to write (in block in 512 bytes unit) + * @param[in] data data block to write (with a size of 512 bytes) + * @return if write succeeded + */ +bool flash_sdcard_write_data(uint32_t block, uint8_t* data);