mirror of
https://github.com/ClusterM/fdskey.git
synced 2025-12-16 19:15:54 +01:00
715 lines
18 KiB
C
715 lines
18 KiB
C
#include "sdcard.h"
|
|
#include "string.h"
|
|
|
|
static uint8_t sd_high_capacity;
|
|
static uint32_t sd_spi_speed;
|
|
|
|
static void SPI_transmit_receive(uint8_t* tx, uint8_t* rx, size_t buff_size)
|
|
{
|
|
HAL_SPI_TransmitReceive(&SD_SPI_PORT, tx, rx, buff_size, SD_TIMEOUT);
|
|
}
|
|
|
|
static void SPI_transmit(uint8_t* tx, size_t buff_size)
|
|
{
|
|
HAL_SPI_Transmit(&SD_SPI_PORT, tx, buff_size, SD_TIMEOUT);
|
|
}
|
|
|
|
static void SD_read_bytes(uint8_t *buff, size_t buff_size)
|
|
{
|
|
// make sure FF is transmitted during receive
|
|
uint8_t tx = 0xFF;
|
|
while (buff_size > 0)
|
|
{
|
|
SPI_transmit_receive(&tx, buff, 1);
|
|
buff++;
|
|
buff_size--;
|
|
}
|
|
}
|
|
|
|
static void SD_select()
|
|
{
|
|
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET);
|
|
delay_us(10); // entry guard time for some SD cards
|
|
}
|
|
|
|
static void SD_unselect()
|
|
{
|
|
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_SET);
|
|
delay_us(10); // exit guard time for some SD cards
|
|
}
|
|
|
|
static void SD_unselect_purge()
|
|
{
|
|
SD_unselect();
|
|
uint8_t tx = 0xFF;
|
|
SPI_transmit(&tx, 1);
|
|
}
|
|
|
|
static SD_RESULT SD_wait_not_busy()
|
|
{
|
|
uint32_t start_time = HAL_GetTick();
|
|
uint8_t busy;
|
|
do
|
|
{
|
|
if (HAL_GetTick() >= start_time + SD_TIMEOUT)
|
|
return SD_RES_BUSY_TIMEOUT;
|
|
SD_unselect();
|
|
SD_select();
|
|
SD_read_bytes(&busy, sizeof(busy));
|
|
} while (busy != 0xFF);
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
static SD_RESULT SD_read_r1(uint8_t *r1)
|
|
{
|
|
/*
|
|
* R1: 0abcdefg
|
|
* |||||||+- 0th bit (g): Card is in idle state
|
|
* ||||||+-- 1th bit (f): Erase sequence cleared
|
|
* |||||+--- 2th bit (e): Illegal command detected
|
|
* ||||+---- 3th bit (d): CRC check error
|
|
* |||+----- 4th bit (c): Error in the sequence of erase commands
|
|
* ||+------ 5th bit (b): Misaligned address used in command
|
|
* |+------- 6th bit (a): Command argument outside allowed range
|
|
* +-------- 7th bit is always zero
|
|
*/
|
|
uint8_t tx = 0xFF;
|
|
int i = 0;
|
|
for (i = 0; i < SD_R1_ANSWER_RETRY_COUNT; i++)
|
|
{
|
|
SPI_transmit_receive(&tx, r1, sizeof(uint8_t));
|
|
if (!(*r1 & (1 << 7)))
|
|
return SD_RES_OK;
|
|
}
|
|
return SD_R1_FAILED;
|
|
}
|
|
|
|
static SD_RESULT SD_read_rx(uint8_t *rx, uint8_t x)
|
|
{
|
|
SD_RESULT r;
|
|
int i;
|
|
uint8_t tx = 0xFF;
|
|
r = SD_read_r1(rx);
|
|
if (r != SD_RES_OK)
|
|
return SD_R1_FAILED;
|
|
rx++;
|
|
for (i = 0; i < x; i++, rx++)
|
|
{
|
|
SPI_transmit_receive(&tx, rx, sizeof(uint8_t));
|
|
}
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
static SD_RESULT SD_read_r3(uint8_t *r3)
|
|
{
|
|
return SD_read_rx(r3, 4);
|
|
}
|
|
|
|
static SD_RESULT SD_read_r7(uint8_t *r7)
|
|
{
|
|
return SD_read_rx(r7, 4);
|
|
}
|
|
|
|
static void SD_send_cmd(uint8_t command, uint32_t arg, uint8_t crc)
|
|
{
|
|
uint8_t cmd[] = { 0x40 | command, (arg >> 24) & 0xFF, (arg >> 16) & 0xFF, (arg >> 8) & 0xFF, arg & 0xFF, (crc << 1) | 1 };
|
|
SD_select();
|
|
SPI_transmit((uint8_t*) cmd, sizeof(cmd));
|
|
}
|
|
|
|
static SD_RESULT SD_wait_data_token()
|
|
{
|
|
uint8_t fb;
|
|
uint8_t tx = 0xFF; // make sure FF is transmitted during receive
|
|
uint32_t start_time = HAL_GetTick();
|
|
while (1)
|
|
{
|
|
SPI_transmit_receive(&tx, &fb, sizeof(fb));
|
|
if (fb == SD_DATA_TOKEN)
|
|
break;
|
|
if (fb != 0xFF)
|
|
return SD_RES_DATA_TOKEN_WRONG;
|
|
if (HAL_GetTick() >= start_time + SD_TIMEOUT)
|
|
return SD_RES_DATA_TOKEN_TIMEOUT;
|
|
}
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
static SD_RESULT SD_init_app_op_cond(uint32_t arg)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
uint32_t start_time = HAL_GetTick();
|
|
while (1)
|
|
{
|
|
// CMD55 (APP_CMD) before any ACMD command
|
|
SD_send_cmd(55, 0, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
SD_unselect_purge();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD55_R1_FAILED;
|
|
// ACMD41 - send operating condition
|
|
SD_send_cmd(41, arg, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
SD_unselect_purge();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_ACMD41_R1_FAILED;
|
|
if (r1 == 0x00)
|
|
return SD_RES_OK; // initialization finished
|
|
if (HAL_GetTick() >= start_time + SD_ACMD41_TIMEOUT)
|
|
return SD_RES_DATA_TOKEN_TIMEOUT;
|
|
HAL_Delay(1);
|
|
}
|
|
return SD_RES_ACMD41_TIMEOUT;
|
|
}
|
|
|
|
SD_RESULT SD_init()
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
uint8_t r3[5];
|
|
uint8_t r7[5];
|
|
int i;
|
|
|
|
SD_select();
|
|
SD_unselect();
|
|
HAL_Delay(1);
|
|
|
|
// 80 clock pulses to enter SPI mode
|
|
uint8_t high = 0xFF;
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
SPI_transmit(&high, sizeof(high));
|
|
}
|
|
|
|
// CMD0 - reset card
|
|
for (i = 0; ; i++)
|
|
{
|
|
SD_send_cmd(0, 0x00000000, 0x4A);
|
|
r = SD_read_r1(&r1);
|
|
SD_unselect_purge();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD0_R1_FAILED;
|
|
if (r1 == SD_R1_IDLE)
|
|
break;
|
|
if (i == SD_CMD0_RETRY_COUNT - 1)
|
|
return SD_RES_CMD0_COUNT_FAILED;
|
|
}
|
|
|
|
/*
|
|
* CMD8 - send interface condition
|
|
*
|
|
* 0th byte: R1 response
|
|
*
|
|
* 1st byte: Command version
|
|
* rrrrmnop (7th bit to 0th bit)
|
|
* ||||||||
|
|
* |||||+++-- 3th to 0th (m-p): Command version. It corresponds to the command version in the argument of the CMD8 command.
|
|
* ++++------ Reserved
|
|
*
|
|
* 2nd byte: Reserved and always 0
|
|
*
|
|
* 3nd byte: Reserved and always 0
|
|
*
|
|
* 4rd byte: Voltage acceptance
|
|
* rrrrrvwx (7th bit to 0th bit)
|
|
* ||||||||
|
|
* |||||+++-- 3th to 0th bits (x-u): Voltage acceptance (VHS). It echoes back the VHS field in the argument of the CMD8 command.
|
|
* ||||| 0b0001: The card supports 2.7-3.6V range.
|
|
* ||||| 0b0010: The card supports low voltage range 1.65-1.95V.
|
|
* ||||| 0b0100 and 0b1000: Reserved for future use.
|
|
* +++++----- Reserved
|
|
*
|
|
* 5th byte: Check pattern echo
|
|
* yzABCDEF (7th bit to 0th bit)
|
|
* ||||||||
|
|
* ++++++++-- 7th to 0th bits (F-y): Echo of check pattern. This field is an echo-back of the check pattern set in the argument of the CMD8 command.
|
|
*/
|
|
SD_send_cmd(8, 0x000001AA, 0x43); // request 2.7-3.63v
|
|
r = SD_read_r7(r7);
|
|
SD_unselect_purge();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD8_R7_FAILED;
|
|
if (r7[0] & SD_R1_ILLEGAL_COMMAND)
|
|
{
|
|
// command not supported - old SD card version
|
|
sd_high_capacity = 0;
|
|
r = SD_init_app_op_cond(0);
|
|
if (r != SD_RES_OK)
|
|
return r;
|
|
} else if (r7[0] == SD_R1_IDLE)
|
|
{
|
|
// new version
|
|
// check that voltage accepted and echo byte
|
|
if (((r7[3] & 0x01) != 1) || (r7[4] != 0xAA))
|
|
return SD_RES_CMD8_VOLTAGE_FAILED;
|
|
r = SD_init_app_op_cond(0x40000000);
|
|
if (r != SD_RES_OK)
|
|
return r;
|
|
/*
|
|
* CMD58 - Read OCR
|
|
*
|
|
* 0th byte: R1 response
|
|
*
|
|
* 1st byte: OCR register [31:24]
|
|
* IJKrrrrP (7th bit to 0th bit)
|
|
* ||||||||
|
|
* |||||||+-- 0th bit (P): Switching to 1.8V Accepted (S18A).
|
|
* |||++++--- Reserved.
|
|
* ||+------- 5th (K) bits: UHS-II Card Status.
|
|
* |+-------- 6th (J) bits: Card Capacity Status (CCS).
|
|
* +--------- 7th (I) bits: Card power up status bit (busy).
|
|
*
|
|
*
|
|
* 2nd byte: OCR register [23:16]
|
|
* QRSTUVWX (7th bit to 0th bit)
|
|
* ||||||||
|
|
* |||||||+-- 0th bit (X): Voltage window 2.8-2.9V.
|
|
* ||||||+--- 1th bit (W): Voltage window 2.9-3.0V.
|
|
* |||||+---- 2th bit (V): Voltage window 3.0-3.1V.
|
|
* ||||+----- 3th bit (U): Voltage window 3.1-3.2V.
|
|
* |||+------ 4th bit (T): Voltage window 3.2-3.3V.
|
|
* ||+------- 5th bit (S): Voltage window 3.3-3.4V.
|
|
* |+-------- 6th bit (R): Voltage window 3.4-3.5V.
|
|
* +--------- 7th bit (Q): Voltage window 3.5-3.6V.
|
|
*
|
|
* 3rd byte: OCR register [15:8]
|
|
* Yrrrrrrr (7th bit to 0th bit)
|
|
* ||||||||
|
|
* |+++++++-- Reserved
|
|
* +--------- 7th bit (Y): Voltage window 2.7-2.8V.
|
|
*
|
|
* 4th byte: OCR register [7:0]
|
|
* grrrrrrr (7th bit to 0th bit)
|
|
* ||||||||
|
|
* |+++++++-- Reserved
|
|
* +--------- 7th bit (g): Reserved for Low Voltage Range.
|
|
*/
|
|
SD_send_cmd(58, 0x00000000, 0xFF);
|
|
r = SD_read_r3(r3);
|
|
SD_unselect_purge();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD58_R3_FAILED;
|
|
if (r3[0] != 0)
|
|
return SD_RES_CMD58_GEN_FAILED;
|
|
// another voltage check
|
|
if (!(r3[2] & (0b00110000))) // 3.2-3.3V or 3.3-3.4V
|
|
return SD_RES_CMD58_VOLTAGE_FAILED;
|
|
sd_high_capacity = (r3[1] & 0x40) >> 6;
|
|
} else {
|
|
return SD_RES_CMD8_GEN_FAILED;
|
|
}
|
|
|
|
// set block length
|
|
SD_send_cmd(16, SD_BLOCK_LENGTH, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
SD_unselect_purge();
|
|
if (r != SD_RES_CMD16_R1_FAILED)
|
|
return r;
|
|
if (r1 != 0x00)
|
|
return SD_RES_CMD16_R1_NOT_NULL;
|
|
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
// multiple init tries
|
|
SD_RESULT SD_init_tries()
|
|
{
|
|
SD_RESULT r;
|
|
int i;
|
|
|
|
for (i = 0; i < SD_INIT_TRIES; i++)
|
|
{
|
|
r = SD_init();
|
|
if (r == SD_RES_OK)
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// reduce SPI speed until SD card init is ok
|
|
SD_RESULT SD_init_try_speed()
|
|
{
|
|
SD_RESULT r;
|
|
|
|
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
|
|
sd_spi_speed = SPI_BAUDRATEPRESCALER_2;
|
|
HAL_SPI_Init(&SD_SPI_PORT);
|
|
r = SD_init_tries();
|
|
if (r == SD_RES_OK)
|
|
return r;
|
|
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
|
|
sd_spi_speed = SPI_BAUDRATEPRESCALER_4;
|
|
HAL_SPI_Init(&SD_SPI_PORT);
|
|
r = SD_init_tries();
|
|
if (r == SD_RES_OK)
|
|
return r;
|
|
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
|
|
sd_spi_speed = SPI_BAUDRATEPRESCALER_8;
|
|
HAL_SPI_Init(&SD_SPI_PORT);
|
|
r = SD_init_tries();
|
|
if (r == SD_RES_OK)
|
|
return r;
|
|
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
|
|
sd_spi_speed = SPI_BAUDRATEPRESCALER_16;
|
|
HAL_SPI_Init(&SD_SPI_PORT);
|
|
return SD_init_tries();
|
|
}
|
|
|
|
uint32_t SD_get_spi_speed()
|
|
{
|
|
return sd_spi_speed;
|
|
}
|
|
|
|
SD_RESULT SD_read_single_block(uint32_t blockNum, uint8_t *buff)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t crc[2];
|
|
uint8_t r1;
|
|
|
|
if (!sd_high_capacity)
|
|
blockNum *= SD_BLOCK_LENGTH;
|
|
|
|
// CMD17 - send block
|
|
SD_send_cmd(17, blockNum, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD17_R1_FAILED;
|
|
if (r1 != 0x00)
|
|
return SD_RES_CMD17_R1_NOT_NULL;
|
|
r = SD_wait_data_token();
|
|
if (r != SD_RES_OK)
|
|
return r;
|
|
SD_read_bytes(buff, SD_BLOCK_LENGTH);
|
|
SD_read_bytes(crc, 2);
|
|
|
|
SD_unselect_purge();
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
SD_RESULT SD_write_single_block(uint32_t blockNum, const uint8_t *buff)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
|
|
SD_select();
|
|
|
|
if (!sd_high_capacity)
|
|
blockNum *= SD_BLOCK_LENGTH;
|
|
|
|
// CMD24 - write block
|
|
SD_send_cmd(24, blockNum, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD24_R1_FAILED;
|
|
if (r1 != 0x00)
|
|
return SD_RES_CMD24_R1_NOT_NULL;
|
|
|
|
// send dummy bytes for NWR timing
|
|
uint8_t dummy = 0xFF;
|
|
SPI_transmit(&dummy, sizeof(dummy));
|
|
SPI_transmit(&dummy, sizeof(dummy));
|
|
|
|
// start token
|
|
uint8_t dataToken = SD_DATA_TOKEN;
|
|
uint8_t crc[2] = { 0xFF, 0xFF };
|
|
SPI_transmit(&dataToken, sizeof(dataToken));
|
|
SPI_transmit((uint8_t*)buff, SD_BLOCK_LENGTH);
|
|
SPI_transmit(crc, sizeof(crc));
|
|
|
|
/*
|
|
dataResp:
|
|
xxx0abc1
|
|
010 - Data accepted
|
|
101 - Data rejected due to CRC error
|
|
110 - Data rejected due to write error
|
|
*/
|
|
uint8_t dataResp;
|
|
SD_read_bytes(&dataResp, sizeof(dataResp));
|
|
if ((dataResp & 0x1F) != 0x05)
|
|
return SD_RES_CMD24_DATA_REJECTED;
|
|
|
|
r = SD_wait_not_busy();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD24_BUSY_TIMEOUT;
|
|
|
|
SD_unselect_purge();
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
SD_RESULT SD_read_begin(uint32_t blockNum)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
|
|
SD_select();
|
|
|
|
if (!sd_high_capacity)
|
|
blockNum *= SD_BLOCK_LENGTH;
|
|
|
|
/* CMD18 (READ_MULTIPLE_BLOCK) command */
|
|
SD_send_cmd(18, blockNum, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD18_R1_FAILED;
|
|
if (r1 != 0x00)
|
|
return SD_RES_CMD18_R1_NOT_NULL;
|
|
|
|
SD_unselect_purge();
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
SD_RESULT SD_read_data(uint8_t *buff)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t crc[2];
|
|
|
|
SD_select();
|
|
|
|
r = SD_wait_data_token();
|
|
if (r != SD_RES_OK)
|
|
return r;
|
|
SD_read_bytes(buff, SD_BLOCK_LENGTH);
|
|
SD_read_bytes(crc, 2);
|
|
|
|
SD_unselect_purge();
|
|
return HAL_OK;
|
|
}
|
|
|
|
SD_RESULT SD_read_end()
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
|
|
SD_select();
|
|
|
|
// CMD12 - stop transmission
|
|
SD_send_cmd(12, 0x00000000, 0xFF);
|
|
|
|
/*
|
|
The received byte immediataly following CMD12 is a stuff byte, it should be
|
|
discarded before receive the response of the CMD12
|
|
*/
|
|
uint8_t stuffByte;
|
|
SD_read_bytes(&stuffByte, sizeof(stuffByte));
|
|
|
|
r = SD_read_r1(&r1);
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD12_R1_FAILED;
|
|
|
|
SD_unselect_purge();
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
SD_RESULT SD_write_begin(uint32_t blockNum)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
|
|
SD_select();
|
|
|
|
if (!sd_high_capacity)
|
|
blockNum *= SD_BLOCK_LENGTH;
|
|
|
|
// CMD25 - write multiple blocks
|
|
SD_send_cmd(25, blockNum, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD25_R1_FAILED;
|
|
if (r1 != 0x00)
|
|
return SD_RES_CMD25_R1_NOT_NULL;
|
|
|
|
SD_unselect_purge();
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
SD_RESULT SD_write_data(const uint8_t *buff)
|
|
{
|
|
SD_RESULT r;
|
|
|
|
SD_select();
|
|
|
|
uint8_t dataToken = SD_SEND_MULTIPLE_DATA_TOKEN;
|
|
uint8_t crc[2] = { 0xFF, 0xFF };
|
|
SPI_transmit(&dataToken, sizeof(dataToken));
|
|
SPI_transmit((uint8_t*)buff, SD_BLOCK_LENGTH);
|
|
SPI_transmit(crc, sizeof(crc));
|
|
|
|
/*
|
|
dataResp:
|
|
xxx0abc1
|
|
010 - Data accepted
|
|
101 - Data rejected due to CRC error
|
|
110 - Data rejected due to write error
|
|
*/
|
|
uint8_t dataResp;
|
|
SD_read_bytes(&dataResp, sizeof(dataResp));
|
|
if ((dataResp & 0x1F) != 0x05)
|
|
return SD_RES_WRITE_MULTI_DATA_REJECTED;
|
|
|
|
r = SD_wait_not_busy();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_WRITE_MULTI_BUSY_TIMEOUT;
|
|
|
|
SD_unselect_purge();
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
SD_RESULT SD_write_end()
|
|
{
|
|
SD_RESULT r;
|
|
|
|
SD_select();
|
|
|
|
uint8_t stopTran = SD_STOP_DATA_TOKEN; // stop transaction token for CMD25
|
|
SPI_transmit(&stopTran, sizeof(stopTran));
|
|
|
|
// skip one byte before readyng "busy"
|
|
// this is required by the spec and is necessary for some real SD-cards!
|
|
uint8_t skipByte;
|
|
SD_read_bytes(&skipByte, sizeof(skipByte));
|
|
|
|
r = SD_wait_not_busy();
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_WRITE_MULTI_END_NOT_BUSY_TIMEOUT;
|
|
|
|
SD_unselect_purge();
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
|
|
SD_RESULT SD_read_CSD(SD_CSD* csd)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
uint8_t csd_data[16];
|
|
uint8_t crc[2];
|
|
|
|
// CMD9 - read CSD register and SD card capacity
|
|
SD_send_cmd(9, 0x00000000, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD9_R1_FAILED;
|
|
if (r1 != 0x00)
|
|
return SD_RES_CMD9_R1_NOT_NULL;
|
|
r = SD_wait_data_token();
|
|
if (r != SD_RES_OK)
|
|
return r;
|
|
SD_read_bytes(csd_data, sizeof(csd_data));
|
|
SD_read_bytes(crc, sizeof(crc));
|
|
|
|
SD_unselect_purge();
|
|
|
|
csd->CSDStruct = (csd_data[0] & 0xC0) >> 6;
|
|
csd->Reserved1 = csd_data[0] & 0x3F;
|
|
csd->TAAC = csd_data[1];
|
|
csd->NSAC = csd_data[2];
|
|
csd->MaxBusClkFrec = csd_data[3];
|
|
csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4);
|
|
csd->RdBlockLen = csd_data[5] & 0x0F;
|
|
csd->PartBlockRead = (csd_data[6] & 0x80) >> 7;
|
|
csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6;
|
|
csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5;
|
|
csd->DSRImpl = (csd_data[6] & 0x10) >> 4;
|
|
switch (csd->CSDStruct) {
|
|
case 0:
|
|
// CSD version 1
|
|
csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2);
|
|
csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) |
|
|
((csd_data[8] & 0xC0) >> 6);
|
|
csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3;
|
|
csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07);
|
|
csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5;
|
|
csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2;
|
|
csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) |
|
|
((csd_data[10] & 0x80) >> 7);
|
|
break;
|
|
case 1:
|
|
// CSD version 2
|
|
csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) |
|
|
((csd_data[7] & 0xC0) >> 6);
|
|
csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) |
|
|
csd_data[9];
|
|
csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8);
|
|
break;
|
|
default:
|
|
return SD_RES_INVALID_CSD_VERSION;
|
|
}
|
|
csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6;
|
|
csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7);
|
|
csd->WrProtectGrSize = (csd_data[11] & 0x7F);
|
|
csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7;
|
|
csd->Reserved2 = (csd_data[12] & 0x60) >> 5;
|
|
csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2;
|
|
csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6);
|
|
csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5;
|
|
csd->Reserved3 = (csd_data[13] & 0x1F);
|
|
csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7;
|
|
csd->CopyFlag = (csd_data[14] & 0x40) >> 6;
|
|
csd->PermWrProtect = (csd_data[14] & 0x20) >> 5;
|
|
csd->TempWrProtect = (csd_data[14] & 0x10) >> 4;
|
|
csd->FileFormat = (csd_data[14] & 0x0C) >> 2;
|
|
csd->Reserved4 = (csd_data[14] & 0x03);
|
|
csd->crc = (csd_data[15] & 0xFE) >> 1;
|
|
csd->Reserved5 = (csd_data[15] & 0x01);
|
|
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
SD_RESULT SD_read_CID(SD_CID* cid)
|
|
{
|
|
SD_RESULT r;
|
|
uint8_t r1;
|
|
uint8_t cid_data[16];
|
|
uint8_t crc[2];
|
|
|
|
// CMD9 - read CSD register and SD card capacity
|
|
SD_send_cmd(10, 0x00000000, 0xFF);
|
|
r = SD_read_r1(&r1);
|
|
if (r != SD_RES_OK)
|
|
return SD_RES_CMD10_R1_FAILED;
|
|
if (r1 != 0x00)
|
|
return SD_RES_CMD10_R1_NOT_NULL;
|
|
r = SD_wait_data_token();
|
|
if (r != SD_RES_OK)
|
|
return r;
|
|
SD_read_bytes(cid_data, sizeof(cid_data));
|
|
SD_read_bytes(crc, sizeof(crc));
|
|
|
|
SD_unselect_purge();
|
|
|
|
cid->ManufacturerID = cid_data[0];
|
|
memcpy(cid->OEM_AppliID, cid_data + 1, 2);
|
|
cid->OEM_AppliID[2] = 0;
|
|
memcpy(cid->ProdName, cid_data + 3, 5);
|
|
cid->ProdName[5] = 0;
|
|
cid->ProdRev = cid_data[8];
|
|
cid->ProdSN = cid_data[9] << 24;
|
|
cid->ProdSN |= cid_data[10] << 16;
|
|
cid->ProdSN |= cid_data[11] << 8;
|
|
cid->ProdSN |= cid_data[12];
|
|
cid->Reserved1 = (cid_data[13] & 0xF0) >> 4;
|
|
cid->ManufactYear = (cid_data[13] & 0x0F) << 4;
|
|
cid->ManufactYear |= (cid_data[14] & 0xF0) >> 4;
|
|
cid->ManufactMonth = (cid_data[14] & 0x0F);
|
|
cid->CID_CRC = (cid_data[15] & 0xFE) >> 1;
|
|
cid->Reserved2 = 1;
|
|
|
|
return SD_RES_OK;
|
|
}
|
|
|
|
uint64_t SD_read_capacity()
|
|
{
|
|
SD_CSD csd;
|
|
SD_RESULT r;
|
|
r = SD_read_CSD(&csd);
|
|
if (r == SD_RES_OK)
|
|
{
|
|
if (sd_high_capacity)
|
|
return (uint64_t)(csd.version.v2.DeviceSize + 1) * SD_BLOCK_LENGTH * 1024;
|
|
else
|
|
return (uint64_t)(csd.version.v1.DeviceSize + 1) * (1UL << (csd.version.v1.DeviceSizeMul + 2)) * (1UL << csd.RdBlockLen);
|
|
}
|
|
return 0;
|
|
}
|