poly-1-fw/sd-spi/sd_spi.c

1465 lines
30 KiB
C

/******************************************************************************/
/**
@file sd_spi.cpp
@author Wade Penson
@date June, 2015
@brief SD SPI library implementation.
@copyright Copyright 2015 Wade Penson
modified in 2022 by alexis
@license Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
*/
/******************************************************************************/
#include "sd_spi_platform_dependencies.h"
#include "sd_spi.h"
static uint8_t sd_spi_dirty_write = 0;
/* An sd_spi_card_t structure for internal state. */
static sd_spi_card_t card;
/**
@brief Clears the buffer and sets the values to 0.
@param[in] card An sd_spi_card_t which has been initialized.
@return An error code as defined by one of the SD_ERR_* definitions.
*/
static void
sd_spi_clear_buffer(
void
);
/**
@brief Performs the direct write to the card.
@param block_address The address of the block on the card.
@param[in] data An array of data / an address to the data in
memory.
@param number_of_bytes The size of the data in bytes.
@param byte_offset The byte offset of where to start writing in the
block.
@return An error code as defined by one of the SD_ERR_* definitions.
*/
static int8_t
sd_spi_write_out_data(
uint32_t block_address,
void *data,
uint16_t number_of_bytes,
uint16_t byte_offset
);
/**
@brief Performs the direct read from the card.
@param block_address The address of the block on the card.
@param[out] data_buffer A location in memory to write the data to.
@param number_of_bytes The number of bytes to read.
@param byte_offset The byte offset of where to start reading in the
block.
@return An error code as defined by one of the SD_ERR_* definitions.
*/
static int8_t
sd_spi_read_in_data(
uint32_t block_address,
void *data_buffer,
uint16_t number_of_bytes,
uint16_t byte_offset
);
/**
@brief Sends a command to the card.
@param command The command.
@param argument A four byte argument that depends on the command.
@return The R1 response from the card.
*/
static uint8_t
sd_spi_send_byte_command(
uint8_t command,
uint32_t argument
);
/**
@brief Sends an application command to the card.
@param command The command.
@param argument A four byte argument that depends on the command.
@return The R1 response from the card.
*/
static uint8_t
spi_send_byte_app_command(
uint8_t command,
uint32_t argument
);
/**
@brief Waits for card to complete specific operations.
@param max_time_to_wait The number of ms when to timeout.
@return A 1 on timeout and 0 otherwise.
*/
static int8_t
sd_spi_wait_if_busy(
uint32_t max_time_to_wait
);
/**
@brief Get the first flag in the R1 register from the card that indicates an
error.
@param r1_response The byte from the R1 register.
@return An error code as defined by one of the SD_ERR_* definitions.
*/
static int8_t
sd_spi_r1_error(
uint8_t r1_response
);
/**
@brief Get the first flag in the R2 register from the card that indicates an
error.
@param r2_response The two bytes from the R2 register.
@return An error code as defined by one of the SD_ERR_* definitions.
*/
static int8_t
sd_spi_r2_error(
uint16_t r2_response
);
/**
@brief Asserts the chip select pin for the card.
*/
static void
sd_spi_select_card(
void
);
/**
@brief De-asserts the chip select pin for the card.
*/
static void
sd_spi_unselect_card(
void
);
int8_t
sd_spi_init(
uint8_t chip_select_pin
)
{
//sd_spi_dirty_write = 0;
card.spi_speed = 0;
card.card_type = SD_CARD_TYPE_UNKNOWN;
card.chip_select_pin = chip_select_pin;
card.is_chip_select_high = 1;
card.is_read_write_continuous = 0;
card.continuous_block_address = 0;
#if defined(SD_SPI_BUFFER)
card.buffered_block_address = 0;
card.is_buffer_current = 0;
card.is_buffer_written = 1;
#endif
sd_spi_pin_mode(chip_select_pin, OUTPUT);
sd_spi_digital_write(chip_select_pin, HIGH);
sd_spi_begin();
sd_spi_begin_transaction(250000);
/* Send at least 74 clock pulses to enter the native operating mode
(80 in this case). */
uint16_t i;
for (i = 0; i < 10; i++)
{
sd_spi_send_byte(0xFF);
}
sd_spi_end_transaction();
/* Record start time to check for initialization timeout. */
uint16_t init_start_time = sd_spi_millis();
/* Send CMD0 to put card in SPI mode. The card will respond with 0x01. */
while (sd_spi_send_byte_command(SD_CMD_GO_IDLE_STATE, 0) != SD_IN_IDLE_STATE)
{
if (((uint16_t) sd_spi_millis() - init_start_time) > SD_INIT_TIMEOUT)
{
sd_spi_unselect_card();
return SD_ERR_INIT_TIMEOUT;
}
}
/* Check SD card type. First send CMD8 with the argument for the voltage
range of 2.7 - 3.6 and a test pattern AA. If the card doesn't support
this command, then it is of type SD1 or MMC */
if ((sd_spi_send_byte_command(SD_CMD_SEND_IF_COND, 0x1AA) & SD_ILLEGAL_COMMAND)
== 0)
{
/* Discard first 2 bytes of R7. */
sd_spi_receive_byte();
sd_spi_receive_byte();
/* Check if voltage range is accepted. */
if ((sd_spi_receive_byte() & 0x01) == 0)
{
sd_spi_unselect_card();
return SD_ERR_OUTSIDE_VOLTAGE_RANGE;
}
/* Check if the test pattern is the same. */
if (sd_spi_receive_byte() != 0xAA)
{
sd_spi_unselect_card();
return SD_ERR_SEND_IF_COND_WRONG_TEST_PATTERN;
}
card.card_type = SD_CARD_TYPE_SD2;
}
init_start_time = sd_spi_millis();
/* Initialize card. */
if (card.card_type == SD_CARD_TYPE_SD2)
{
while (spi_send_byte_app_command(SD_ACMD_SEND_OP_COND, 0x40000000) != 0)
{
if (((uint16_t) sd_spi_millis() - init_start_time) > SD_INIT_TIMEOUT)
{
sd_spi_unselect_card();
return SD_ERR_INIT_TIMEOUT;
}
}
/* Check to see if card is of type SDHC (or SDXC) by reading OCR. */
if (sd_spi_send_byte_command(SD_CMD_READ_OCR, 0))
{
sd_spi_unselect_card();
return SD_ERR_OCR_REGISTER;
}
if ((sd_spi_receive_byte() & 0x40) != 0)
{
card.card_type = SD_CARD_TYPE_SDHC;
}
/* Discard rest of OCR. */
sd_spi_receive_byte();
sd_spi_receive_byte();
sd_spi_receive_byte();
}
else
{
/* Send AMCD41 to try initializing card and if card doesn't support this
command, it is most likely of type MMC or an earlier version of SD. */
while (spi_send_byte_app_command(SD_ACMD_SEND_OP_COND, 0) != 0)
{
if (((uint16_t) sd_spi_millis() - init_start_time) > 500)
{
/* If sending CMD1 times out, the card is of unknown type. */
while (sd_spi_send_byte_command(SD_CMD_SEND_OP_COND, 0) !=
SD_IN_IDLE_STATE)
{
if (((uint16_t) sd_spi_millis() - init_start_time) >
SD_INIT_TIMEOUT + 500)
{
sd_spi_unselect_card();
return SD_ERR_UNKNOWN_CARD_TYPE;
}
}
card.card_type = SD_CARD_TYPE_MMC;
break;
}
}
if (card.card_type != SD_CARD_TYPE_MMC)
{
card.card_type = SD_CARD_TYPE_SD1;
}
}
/* Set block size to 512 bytes. */
if (sd_spi_send_byte_command(SD_CMD_SET_BLOCKLEN, 512))
{
sd_spi_unselect_card();
return SD_ERR_SETTING_BLOCK_LENGTH;
}
for (i = 0; i < 512; i++)
{
card.sd_spi_buffer[i] = 0;
}
card.spi_speed = 1;
sd_spi_unselect_card();
return SD_ERR_OK;
}
int8_t
sd_spi_write(
uint32_t block_address,
void *data,
uint16_t number_of_bytes,
uint16_t byte_offset
)
{
/* Check to make sure that data is within page bounds. */
if (number_of_bytes + byte_offset > 512)
{
return SD_ERR_WRITE_OUTSIDE_OF_BLOCK;
}
#if defined(SD_SPI_BUFFER)
/* Write a whole block out if it is 512 bytes, otherwise read block into
buffer for partial writing. */
if (number_of_bytes == 512 && !card.is_read_write_continuous)
{
int8_t response = sd_spi_write_block(block_address, data);
sd_spi_unselect_card();
return response;
}
else if (!card.is_read_write_continuous && (card.buffered_block_address !=
block_address || !card.is_buffer_current))
{
int8_t response;
if ((response = sd_spi_flush()))
{
return response;
}
if (sd_spi_dirty_write)
{
card.buffered_block_address = block_address;
}
else
{
if ((response = sd_spi_read_in_data(block_address, card.sd_spi_buffer,
512, 0)))
{
return response;
}
}
}
memcpy(card.sd_spi_buffer + byte_offset, data, number_of_bytes);
card.is_buffer_written = 0;
return SD_ERR_OK;
#else
int8_t response = sd_spi_write_out_data(block_address, data,
number_of_bytes, byte_offset);
sd_spi_unselect_card();
return response;
#endif
}
int8_t
sd_spi_write_block(
uint32_t block_address,
void *data
)
{
int8_t response;
#if defined(SD_SPI_BUFFER)
if ((response = sd_spi_flush()))
{
return response;
}
#endif
response = sd_spi_write_out_data(block_address, data, 512, 0);
#if defined(SD_SPI_BUFFER)
if (!card.is_read_write_continuous ||
block_address == card.continuous_block_address) // TODO: Does this logic make sense?
{
memcpy(card.sd_spi_buffer, data, 512);
}
card.is_buffer_written = 1;
card.buffered_block_address = block_address;
#endif
sd_spi_unselect_card();
return response;
}
int8_t
sd_spi_flush(
void
)
{
#if defined(SD_SPI_BUFFER)
if (card.is_buffer_written)
{
return SD_ERR_OK;
}
int8_t response;
if ((response = sd_spi_write_out_data(card.buffered_block_address,
card.sd_spi_buffer, 512, 0)))
{
return response;
}
if (!card.is_read_write_continuous)
{
card.is_buffer_current = 1;
}
card.is_buffer_written = 1;
sd_spi_unselect_card();
#endif
return SD_ERR_OK;
}
int8_t
sd_spi_write_continuous_start(
uint32_t start_block_address,
uint32_t num_blocks_pre_erase
)
{
#if defined(SD_SPI_BUFFER)
int8_t response;
if ((response = sd_spi_flush()))
{
return response;
}
#endif
/* Keep track of block address for error checking and buffering. */
card.continuous_block_address = start_block_address;
/* SD cards 2GB or less address by bytes so multiply by 512 to address
by blocks. */
if (card.card_type != SD_CARD_TYPE_SDHC)
{
start_block_address <<= 9;
}
/* Optionally pre-erase blocks for faster writing. */
if (num_blocks_pre_erase != 0)
{
if (spi_send_byte_app_command(SD_ACMD_SET_WR_BLK_ERASE_COUNT,
num_blocks_pre_erase))
{
sd_spi_unselect_card();
return SD_ERR_WRITE_PRE_ERASE;
}
}
/* Start multiple block write. */
if (sd_spi_send_byte_command(SD_CMD_WRITE_MULTIPLE_BLOCK, start_block_address))
{
sd_spi_unselect_card();
return SD_ERR_WRITE_FAILURE;
}
card.is_read_write_continuous = 1;
#if defined(SD_SPI_BUFFER)
sd_spi_clear_buffer();
card.buffered_block_address = card.continuous_block_address;
#endif
sd_spi_unselect_card();
return SD_ERR_OK;
}
int8_t
sd_spi_write_continuous(
void *data,
uint16_t number_of_bytes,
uint16_t byte_offset
)
{
return sd_spi_write(card.continuous_block_address, data, number_of_bytes,
byte_offset);
}
int8_t
sd_spi_write_continuous_next(
void
)
{
#if defined(SD_SPI_BUFFER)
int8_t response = sd_spi_flush();
sd_spi_clear_buffer();
return response;
#else
return SD_ERR_OK;
#endif
}
int8_t
sd_spi_write_continuous_stop(
void
)
{
#if defined(SD_SPI_BUFFER)
/* Flush buffer if it hasn't been written. */
int8_t response;
if ((response = sd_spi_flush()))
{
return response;
}
#endif
sd_spi_select_card();
/* Wait for card to complete the write. */
if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT))
{
sd_spi_unselect_card();
return SD_ERR_WRITE_TIMEOUT;
}
/* Token is sent to signal card to stop multiple block writing. */
sd_spi_send_byte(SD_TOKEN_MULTIPLE_WRITE_STOP_TRANSFER);
card.is_read_write_continuous = 0;
/* Wait for card to complete the write. */
if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT))
{
sd_spi_unselect_card();
return SD_ERR_WRITE_TIMEOUT;
}
return sd_spi_card_status();
}
int8_t
sd_spi_read(
uint32_t block_address,
void *data_buffer,
uint16_t number_of_bytes,
uint16_t byte_offset
)
{
#if defined(SD_SPI_BUFFER)
int8_t response;
if ((response = sd_spi_flush()))
{
return response;
}
/* Check to make sure that data is within page bounds. */
if (number_of_bytes + byte_offset > 512)
{
return SD_ERR_READ_OUTSIDE_OF_BLOCK;
}
if (card.buffered_block_address != block_address ||
!card.is_buffer_current)
{
/* Read block into buffer. */
if ((response = sd_spi_read_in_data(block_address, card.sd_spi_buffer,
512, 0)))
{
return response;
}
}
memcpy(data_buffer, card.sd_spi_buffer + byte_offset, number_of_bytes);
return SD_ERR_OK;
#else
return sd_spi_read_in_data(block_address, data_buffer, number_of_bytes,
byte_offset);
#endif
}
int8_t
sd_spi_read_continuous_start(
uint32_t start_block_address
)
{
#if defined(SD_SPI_BUFFER)
int8_t response;
if ((response = sd_spi_flush()))
{
return response;
}
#endif
card.continuous_block_address = start_block_address;
/* SD cards 2GB or less address by bytes so multiply by 512 to address
by blocks. */
if (card.card_type != SD_CARD_TYPE_SDHC)
{
start_block_address <<= 9;
}
/* Start multiple block reading. */
if (sd_spi_send_byte_command(SD_CMD_READ_MULTIPLE_BLOCK, start_block_address))
{
sd_spi_unselect_card();
return SD_ERR_READ_FAILURE;
}
card.is_read_write_continuous = 1;
#if defined(SD_SPI_BUFFER)
if ((response = sd_spi_read_in_data(card.continuous_block_address,
card.sd_spi_buffer, 512, 0)))
{
sd_spi_unselect_card();
return response;
}
card.continuous_block_address--;
card.buffered_block_address = card.continuous_block_address;
#endif
sd_spi_unselect_card();
return SD_ERR_OK;
}
int8_t
sd_spi_read_continuous(
void *data_buffer,
uint16_t number_of_bytes,
uint16_t byte_offset
)
{
#if defined(SD_SPI_BUFFER)
return sd_spi_read(card.continuous_block_address, data_buffer,
number_of_bytes, byte_offset);
#else
return sd_spi_read_in_data(card.continuous_block_address, data_buffer,
number_of_bytes, byte_offset);
#endif
}
int8_t
sd_spi_read_continuous_next(
void
)
{
#if defined(SD_SPI_BUFFER)
return sd_spi_read_in_data(card.continuous_block_address,
card.sd_spi_buffer, 512, 0);
#else
return SD_ERR_OK;
#endif
}
int8_t
sd_spi_read_continuous_stop(
void
)
{
sd_spi_select_card();
uint16_t timeout_start = sd_spi_millis();
/* Since the stop command is not sent during a read, it must be sent when a
read token is received. */
while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK)
{
if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT)
{
return sd_spi_card_status();
}
}
timeout_start = sd_spi_millis();
/* Send command to stop continuous reading. */
if ((sd_spi_send_byte_command(SD_CMD_STOP_TRANSMISSION, 0) & 0x08) != 0)
{
while ((sd_spi_receive_byte() & 0x08) != 0)
{
if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT)
{
sd_spi_unselect_card();
return SD_ERR_READ_FAILURE;
}
}
}
card.is_read_write_continuous = 0;
sd_spi_unselect_card();
return SD_ERR_OK;
}
int8_t
sd_spi_erase_all(
void
)
{
return sd_spi_erase_blocks(0, sd_spi_card_size());
}
int8_t
sd_spi_erase_blocks(
uint32_t start_block_address,
uint32_t end_block_address
)
{
#if defined(SD_SPI_BUFFER)
int8_t response;
if ((response = sd_spi_flush()))
{
return response;
}
if (card.buffered_block_address > start_block_address &&
card.buffered_block_address < end_block_address)
{
sd_spi_clear_buffer();
card.is_buffer_written = 1;
card.is_buffer_current = 1;
}
#endif
/* SD cards 2GB or less address by bytes so multiply by 512 to address
by blocks. */
if (card.card_type != SD_CARD_TYPE_SDHC)
{
start_block_address <<= 9;
end_block_address <<= 9;
}
/* The start and end address of the blocks to be erased must be sent to the
SD and then the erase command is called. */
if (sd_spi_send_byte_command(SD_CMD_ERASE_WR_BLK_START, start_block_address) ||
sd_spi_send_byte_command(SD_CMD_ERASE_WR_BLK_END, end_block_address) ||
sd_spi_send_byte_command(SD_CMD_ERASE, 0))
{
sd_spi_unselect_card();
return SD_ERR_ERASE_FAILURE;
}
if (sd_spi_wait_if_busy(SD_ERASE_TIMEOUT))
{
sd_spi_unselect_card();
return SD_ERR_ERASE_TIMEOUT;
}
sd_spi_unselect_card();
return SD_ERR_OK;
}
uint32_t
sd_spi_card_size(
void
)
{
if (sd_spi_send_byte_command(SD_CMD_SEND_CSD, 0))
{
sd_spi_unselect_card();
return SD_ERR_READ_REGISTER;
}
uint16_t timeout_start = sd_spi_millis();
while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK)
{
if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT)
{
return sd_spi_card_status();
}
}
uint8_t c_size_high;
uint8_t c_size_mid;
uint8_t c_size_low;
uint32_t number_of_blocks;
uint8_t b = sd_spi_receive_byte();
uint8_t csd_structure = b >> 6;
uint8_t current_byte;
for (current_byte = 0; current_byte < 5; current_byte++)
{
sd_spi_receive_byte();
}
b = sd_spi_receive_byte();
uint8_t max_read_bl_len = b & 0x0F;
if (csd_structure == 0)
{
c_size_high = (b << 2) & 0x0C;
b = sd_spi_receive_byte();
c_size_high |= b >> 6;
c_size_low = b << 2;
b = sd_spi_receive_byte();
c_size_low |= b >> 6;
sd_spi_receive_byte();
uint8_t c_size_mult;
c_size_mult = (b << 1) & 0x06;
b = sd_spi_receive_byte();
c_size_mult |= b >> 7;
number_of_blocks = (uint32_t) ((c_size_high << 8 | c_size_low) + 1) *
(1 << (c_size_mult + 2));
number_of_blocks *= (uint32_t) (1 << max_read_bl_len) / 512;
}
else
{
b = sd_spi_receive_byte();
c_size_high = b;
b = sd_spi_receive_byte();
c_size_mid = b;
b = sd_spi_receive_byte();
c_size_low = b;
number_of_blocks = (uint32_t) ((uint32_t) c_size_high << 16 | c_size_mid << 8 |
c_size_low) + 1;
number_of_blocks <<= 10;
}
for (current_byte = 11; current_byte < 16; current_byte++)
{
sd_spi_receive_byte();
}
/* Discard CRC. */
sd_spi_receive_byte();
sd_spi_receive_byte();
sd_spi_unselect_card();
return number_of_blocks;
}
int8_t
sd_spi_read_cid_register(
sd_spi_cid_t *cid
)
{
if (sd_spi_send_byte_command(SD_CMD_SEND_CID, 0))
{
sd_spi_unselect_card();
return SD_ERR_READ_REGISTER;
}
uint16_t timeout_start = sd_spi_millis();
while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK)
{
if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT)
{
return sd_spi_card_status();
}
}
/* See SD Specifications for more information. */
cid->mid = sd_spi_receive_byte();
uint8_t i;
for (i = 0; i < 2; i++)
{
cid->oid[i] = sd_spi_receive_byte();
}
for (i = 0; i < 5; i++)
{
cid->pnm[i] = sd_spi_receive_byte();
}
i = sd_spi_receive_byte();
cid->prv_n = i >> 4;
cid->prv_m = i;
cid->psn_high = sd_spi_receive_byte();
cid->psn_mid_high = sd_spi_receive_byte();
cid->psn_mid_low = sd_spi_receive_byte();
cid->psn_low = sd_spi_receive_byte();
i = sd_spi_receive_byte();
cid->mdt_year = (i << 4) & 0xF0;
i = sd_spi_receive_byte();
cid->mdt_year |= i >> 4;
cid->mdt_month = i;
cid->crc = sd_spi_receive_byte() >> 1;
/* Discard CRC */
sd_spi_receive_byte();
sd_spi_receive_byte();
sd_spi_unselect_card();
return SD_ERR_OK;
}
int8_t
sd_spi_read_csd_register(
sd_spi_csd_t *csd
)
{
if (sd_spi_send_byte_command(SD_CMD_SEND_CSD, 0))
{
sd_spi_unselect_card();
return SD_ERR_READ_REGISTER;
}
uint16_t timeout_start = sd_spi_millis();
while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK)
{
if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT)
{
return sd_spi_card_status();
}
}
/* See SD Specification for more information. */
uint8_t b = sd_spi_receive_byte();
csd->csd_structure = b >> 6;
b = sd_spi_receive_byte();
csd->taac = b;
b = sd_spi_receive_byte();
csd->nsac = b;
b = sd_spi_receive_byte();
csd->tran_speed = b;
b = sd_spi_receive_byte();
csd->ccc_high = b >> 4;
csd->ccc_low |= b << 4;
b = sd_spi_receive_byte();
csd->ccc_low |= b >> 4;
csd->max_read_bl_len = b;
b = sd_spi_receive_byte();
csd->read_bl_partial = b >> 7;
csd->write_bl_misalign = b >> 6;
csd->read_bl_misalign = b >> 5;
csd->dsr_imp = b >> 4;
if (csd->csd_structure == 0)
{
csd->cvsi.v1.c_size_high = (b << 2) & 0x0C;
b = sd_spi_receive_byte();
csd->cvsi.v1.c_size_high |= b >> 6;
csd->cvsi.v1.c_size_low = b << 2;
b = sd_spi_receive_byte();
csd->cvsi.v1.c_size_low |= b >> 6;
csd->cvsi.v1.vdd_r_curr_min = b >> 3;
csd->cvsi.v1.vdd_r_curr_max = b;
b = sd_spi_receive_byte();
csd->cvsi.v1.vdd_w_curr_min = b >> 5;
csd->cvsi.v1.vdd_w_curr_max = b >> 2;
csd->cvsi.v1.c_size_mult = (b << 1) & 0x06;
b = sd_spi_receive_byte();
csd->cvsi.v1.c_size_mult |= b >> 7;
}
else
{
b = sd_spi_receive_byte();
csd->cvsi.v2.c_size_high = b;
b = sd_spi_receive_byte();
csd->cvsi.v2.c_size_mid = b;
b = sd_spi_receive_byte();
csd->cvsi.v2.c_size_low = b;
b = sd_spi_receive_byte();
}
csd->erase_bl_en = b >> 6;
csd->erase_sector_size = (b << 1) & 0x7E;
b = sd_spi_receive_byte();
csd->erase_sector_size |= b >> 7;
csd->wp_grp_size = b << 1;
b = sd_spi_receive_byte();
csd->wp_grp_enable = b >> 7;
csd->r2w_factor = b >> 2;
csd->write_bl_len = (b << 2) & 0x0C;
b = sd_spi_receive_byte();
csd->write_bl_len |= b >> 6;
csd->write_bl_partial = b >> 5;
b = sd_spi_receive_byte();
csd->file_format_grp = b >> 7;
csd->copy = b >> 6;
csd->perm_write_protect = b >> 5;
csd->tmp_write_protect = b >> 4;
csd->file_format = b >> 2;
b = sd_spi_receive_byte();
csd->crc = b >> 1;
/* Discard CRC. */
sd_spi_receive_byte();
sd_spi_receive_byte();
sd_spi_unselect_card();
return SD_ERR_OK;
}
int8_t
sd_spi_card_status(
void
)
{
int8_t response = sd_spi_r2_error((sd_spi_send_byte_command(SD_CMD_SEND_STATUS, 0)
<< 8) | sd_spi_receive_byte());
sd_spi_unselect_card();
return response;
}
uint8_t
sd_spi_card_type(
void
)
{
return card.card_type;
}
uint32_t
sd_spi_current_buffered_block(
void
)
{
return card.buffered_block_address;
}
static void
sd_spi_clear_buffer(
void
)
{
#if defined(SD_SPI_BUFFER)
uint16_t i;
for (i = 0; i < 512; i++) {
card.sd_spi_buffer[i] = 0;
}
card.is_buffer_written = 0;
card.is_buffer_current = 0;
#endif
}
static int8_t
sd_spi_write_out_data(
uint32_t block_address,
void *data,
uint16_t number_of_bytes,
uint16_t byte_offset
)
{
sd_spi_select_card();
if (card.is_read_write_continuous)
{
/* Wait for card to complete the previous write. */
if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT))
{
sd_spi_unselect_card();
return SD_ERR_WRITE_TIMEOUT;
}
/* Send token for multiple block write. */
sd_spi_send_byte(SD_TOKEN_MULTIPLE_WRITE_START_BLOCK);
}
else
{
/* SD cards 2GB or less address by bytes so multiply by 512 to address
by blocks. */
if (card.card_type != SD_CARD_TYPE_SDHC)
{
block_address <<= 9;
}
/* Send the command to start writing a single block. */
if (sd_spi_send_byte_command(SD_CMD_SET_WRITE_BLOCK, block_address))
{
sd_spi_unselect_card();
return SD_ERR_WRITE_FAILURE;
}
/* Send token for single block write. */
sd_spi_send_byte(SD_TOKEN_START_BLOCK);
}
uint16_t i;
/* Pad data with 0. */
for (i = 0; i < byte_offset; i++)
{
sd_spi_send_byte(0);
}
/* Write block. */
for (i = byte_offset; i < byte_offset + number_of_bytes; i++)
{
sd_spi_send_byte(((uint8_t *) data)[i - byte_offset]);
}
/* Pad data with 0. */
for (i = byte_offset + number_of_bytes; i < 512; i++)
{
sd_spi_send_byte(0);
}
/* Send dummy CRC. */
sd_spi_send_byte(0xFF);
sd_spi_send_byte(0xFF);
/* Check if write was successful. */
switch (sd_spi_receive_byte() & 0x0F)
{
case SD_TOKEN_DATA_REJECTED_CRC:
return SD_ERR_WRITE_DATA_CRC_REJECTED;
case SD_TOKEN_DATA_REJECTED_WRITE_ERR:
return SD_ERR_WRITE_DATA_REJECTED;
case SD_TOKEN_DATA_ACCEPTED:
break;
default:
return SD_ERR_WRITE_FAILURE;
}
if (card.is_read_write_continuous)
{
card.continuous_block_address++;
#if defined(SD_SPI_BUFFER)
card.buffered_block_address = card.continuous_block_address;
#endif
}
else {
/* Wait for card to complete the write. */
if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT))
{
sd_spi_unselect_card();
return SD_ERR_WRITE_TIMEOUT;
}
return sd_spi_card_status();
}
return SD_ERR_OK;
}
static int8_t
sd_spi_read_in_data(
uint32_t block_address,
void *data_buffer,
uint16_t number_of_bytes,
uint16_t byte_offset
)
{
sd_spi_select_card();
if (!card.is_read_write_continuous)
{
/* SD cards 2GB or less address by bytes so multiply by 512 to address
by blocks. */
if (card.card_type != SD_CARD_TYPE_SDHC)
{
block_address <<= 9;
}
/* Start single block reading. */
if (sd_spi_send_byte_command(SD_CMD_READ_SINGLE_BLOCK, block_address))
{
sd_spi_unselect_card();
return SD_ERR_READ_FAILURE;
}
}
uint16_t timeout_start = sd_spi_millis();
/* Must wait for read token from card before reading. */
while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK)
{
if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT)
{
return sd_spi_card_status();
}
}
uint16_t i;
#if defined(SD_SPI_BUFFER) /* Read block into sd_spi_buffer if it is defined. */
/* Read in the bytes to the buffer. */
for (i = 0; i < 512; i++)
{
card.sd_spi_buffer[i] = sd_spi_receive_byte();
}
/* Throw out CRC. */
sd_spi_receive_byte();
sd_spi_receive_byte();
if (card.is_read_write_continuous)
{
card.continuous_block_address++;
card.buffered_block_address = card.continuous_block_address;
}
else
{
if (card.card_type != SD_CARD_TYPE_SDHC)
{
block_address >>= 9;
}
card.buffered_block_address = block_address;
}
card.is_buffer_current = 1;
#else
/* Throw out the bytes until the offset is reached. */
for (i = 0; i < byte_offset; i++)
{
sd_spi_receive_byte();
}
/* Read in the bytes to the buffer. */
for (i = 0; i < number_of_bytes; i++)
{
((uint8_t *) data_buffer)[i] = sd_spi_receive_byte();
}
/* Throw out any remaining bytes in the page plus CRC. */
for (i = 0; number_of_bytes + byte_offset + i < 514; i++)
{
sd_spi_receive_byte();
}
#endif
return SD_ERR_OK;
}
static uint8_t
sd_spi_send_byte_command(
uint8_t command,
uint32_t argument
)
{
sd_spi_select_card();
sd_spi_receive_byte();
/* Send command with transmission bit. */
sd_spi_send_byte(0x40 | command);
/* Send argument. */
sd_spi_send_byte(argument >> 24);
sd_spi_send_byte(argument >> 16);
sd_spi_send_byte(argument >> 8);
sd_spi_send_byte(argument >> 0);
/* Send CRC. */
switch(command)
{
/* CRC for CMD0 with arg 0. */
case SD_CMD_GO_IDLE_STATE:
sd_spi_send_byte(0x95);
break;
/* CRC for CMD8 with arg 0X1AA. */
case SD_CMD_SEND_IF_COND:
sd_spi_send_byte(0x87);
break;
default:
sd_spi_send_byte(0xFF);
break;
}
/* Wait for response. Can take up to 64 clock cycles. */
uint8_t i;
uint8_t response;
for (i = 0; i < 8; i++)
{
response = sd_spi_receive_byte();
if (response != 0xFF)
{
break;
}
}
return response;
}
static uint8_t
spi_send_byte_app_command(
uint8_t command,
uint32_t argument
)
{
sd_spi_send_byte_command(SD_CMD_APP, 0);
return sd_spi_send_byte_command(command, argument);
}
static int8_t
sd_spi_wait_if_busy(
uint32_t max_time_to_wait
)
{
uint32_t timeout_start = sd_spi_millis();
while (sd_spi_receive_byte() != 0xFF)
{
if (((uint32_t) sd_spi_millis() - timeout_start) > max_time_to_wait)
{
return 1;
}
}
return 0;
}
static int8_t
sd_spi_r1_error(
uint8_t r1_response
)
{
if (r1_response == 0x00)
{
return SD_ERR_OK;
}
else if (r1_response & SD_IN_IDLE_STATE)
{
return SD_ERR_NOT_INITIALIZED;
}
else if (r1_response & SD_ERASE_RESET)
{
return SD_ERR_ERASE_RESET;
}
else if (r1_response & SD_ILLEGAL_COMMAND)
{
return SD_ERR_ILLEGAL_COMMAND;
}
else if (r1_response & SD_COMMUNICATION_CRC_ERR)
{
return SD_ERR_COMMUNICATION_CRC;
}
else if (r1_response & SD_ERASE_SEQUENCE_ERR)
{
return SD_ERR_ERASE_SEQUENCE;
}
else if (r1_response & SD_ADDRESS_ERR)
{
return SD_ERR_ILLEGAL_ADDRESS;
}
else if (r1_response & SD_PARAMETER_ERR)
{
return SD_ERR_ILLEGAL_PARAMETER;
}
return SD_ERR_OK;
}
static int8_t
sd_spi_r2_error(
uint16_t r2_response
)
{
if (sd_spi_r1_error(r2_response >> 8))
{
return sd_spi_r1_error(r2_response >> 8);
}
else
{
if (r2_response == 0x00)
{
return SD_ERR_OK;
}
else if (r2_response & SD_CARD_IS_LOCKED)
{
return SD_ERR_CARD_IS_LOCKED;
}
else if (r2_response & SD_WP_ERASE_SKIP)
{
return SD_ERR_WRITE_PROTECTION_ERASE_SKIP;
}
else if (r2_response & SD_GENERAL_ERR)
{
return SD_ERR_GENERAL;
}
else if (r2_response & SD_CC_ERR)
{
return SD_ERR_CARD_CONTROLLER;
}
else if (r2_response & SD_CARD_ECC_FAILURE)
{
return SD_ERR_CARD_ECC_FAILURE;
}
else if (r2_response & SD_WP_VIOLATION)
{
return SD_ERR_WRITE_PROTECTION_VIOLATION;
}
else if (r2_response & SD_ERASE_PARAM)
{
return SD_ERR_ERASE_PARAMETER;
}
else if (r2_response & SD_OUT_OF_RANGE)
{
return SD_ERR_ARGUMENT_OUT_OF_RANGE;
}
}
return SD_ERR_OK;
}
static void
sd_spi_select_card(
void
)
{
sd_spi_digital_write(card.chip_select_pin, LOW);
if (card.is_chip_select_high)
{
card.is_chip_select_high = 0;
if (card.spi_speed == 0)
{
sd_spi_begin_transaction(250000);
}
else
{
sd_spi_begin_transaction(25000000);
}
}
}
static void
sd_spi_unselect_card(
void
)
{
sd_spi_receive_byte();
/* Host has to wait 8 clock cycles after a command. */
sd_spi_digital_write(card.chip_select_pin, HIGH);
if (!card.is_chip_select_high)
{
card.is_chip_select_high = 1;
sd_spi_end_transaction();
}
}