flash_sdcard: add write data function and improve other functions
This commit is contained in:
parent
60c337125e
commit
c3b35be66c
@ -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<n_ac && token==0xff; i++) { // wait for N_AC before reading data block (see section 7.5.2.1)
|
||||
token = flash_sdcard_spi_read(); // get control token (see section 7.3.3)
|
||||
}
|
||||
if (0==(token&0xf0)) { // data error token received (see section 7.3.3.3)
|
||||
if (0==(token&0x0f)) { // unknown error
|
||||
token = 0xff;
|
||||
}
|
||||
} else if (0xfe==token) { // 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<size/2; i++) {
|
||||
uint16_t word = flash_sdcard_spi_read(); // get word
|
||||
data[i*2+0] = (word>>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<size/2; i++) {
|
||||
uint16_t word = (data[i*2+0]<<8)+data[i*2+1]; // prepare SPI frame
|
||||
spi_send(SPI(FLASH_SDCARD_SPI), word); // senf data frame
|
||||
}
|
||||
spi_set_next_tx_from_crc(SPI(FLASH_SDCARD_SPI)); // send CRC
|
||||
// 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_set_next_tx_from_buffer(SPI(FLASH_SDCARD_SPI)); // don't send CRC
|
||||
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
|
||||
|
||||
uint8_t token = 0xff;
|
||||
while (0x01!=(token&0x11)) {
|
||||
token = flash_sdcard_spi_read(); // get data response token (see section 7.3.3.1)
|
||||
}
|
||||
while (0==flash_sdcard_spi_read()); // wait N_EC while the card is busy programming the data
|
||||
|
||||
return token;
|
||||
}
|
||||
/** 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)
|
||||
static uint16_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)
|
||||
@ -185,39 +283,11 @@ static uint8_t flash_sdcard_status(uint8_t* status)
|
||||
if (0==(r2&0x8000)) { // got the first byte
|
||||
r2 += flash_sdcard_spi_read(); // read second byte (see 7.3.2.3)
|
||||
}
|
||||
|
||||
// get data block
|
||||
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
|
||||
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<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<size/2; i++) {
|
||||
uint16_t word = flash_sdcard_spi_read(); // get word
|
||||
data[i*2+0] = (word>>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
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user