wch-code/ch32v307_mp3_dac/User/bsp/bsp_spi_sd.c
2024-05-05 22:27:52 +08:00

810 lines
24 KiB
C

/* Includes ------------------------------------------------------------------*/
#include "bsp_spi_sd.h"
#include "debug.h"
/* Private types -------------------------------------------------------------*/
typedef enum card_type_e {
CARD_TYPE_MMC = 0x00,
CARD_TYPE_SDV1 = 0x01,
CARD_TYPE_SDV2 = 0x02,
CARD_TYPE_SDV2HC = 0x04
} card_type_t;
/* Private define ------------------------------------------------------------*/
#define DUMMY_BYTE 0xFF
#define BLOCK_SIZE 512
#define CMD0 0 /* Reset */
#define CMD1 1 /* Send Operator Condition - SEND_OP_COND */
#define CMD8 8 /* Send Interface Condition - SEND_IF_COND */
#define CMD9 9 /* Read CSD */
#define CMD10 10 /* Read CID */
#define CMD12 12 /* Stop data transmit */
#define CMD16 16 /* Set block size, should return 0x00 */
#define CMD17 17 /* Read single block */
#define CMD18 18 /* Read multi block */
#define ACMD23 23 /* Prepare erase N-blocks before multi block write */
#define CMD24 24 /* Write single block */
#define CMD25 25 /* Write multi block */
#define ACMD41 41 /* should return 0x00 */
#define CMD55 55 /* should return 0x01 */
#define CMD58 58 /* Read OCR */
#define CMD59 59 /* CRC disable/enable, should return 0x00 */
#define CMD_WAIT_RESP_TIMEOUT (100000U)
#define WAIT_IDLE_TIMEOUT (20000U)
#define SPI_PRESCALER_LOW (SPI_BaudRatePrescaler_256)
#define SPI_PRESCALER_HIGH (SPI_BaudRatePrescaler_4)
/* Private variables ---------------------------------------------------------*/
static card_type_t _card_type;
/* Private functions ---------------------------------------------------------*/
#define SPISD_LOG(...) LOG_E(__VA_ARGS__)
static void _spi_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
/*!< SD_SPI_CS_GPIO, SD_SPI_MOSI_GPIO, SD_SPI_MISO_GPIO, SD_SPI_DETECT_GPIO
and SD_SPI_SCK_GPIO Periph clock enable */
RCC_APB2PeriphClockCmd(SD_CS_GPIO_CLK | SD_SPI_MOSI_GPIO_CLK | SD_SPI_MISO_GPIO_CLK |
SD_SPI_SCK_GPIO_CLK | SD_DETECT_GPIO_CLK, ENABLE);
/*!< SD_SPI Periph clock enable */
SD_SPI_CLK_ENABLE();
/*!< DMA1 Periph clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/*!< Configure SD_SPI pins: SCK */
GPIO_InitStructure.GPIO_Pin = SD_SPI_SCK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(SD_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
/*!< Configure SD_SPI pins: MOSI */
GPIO_InitStructure.GPIO_Pin = SD_SPI_MOSI_PIN;
GPIO_Init(SD_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
/*!< Configure SD_SPI pins: MISO */
GPIO_InitStructure.GPIO_Pin = SD_SPI_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SD_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
/*!< Configure SD_SPI_CS_PIN pin: SD Card CS pin */
GPIO_InitStructure.GPIO_Pin = SD_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SD_CS_GPIO_PORT, &GPIO_InitStructure);
/*!< Configure SD_SPI_DETECT_PIN pin: SD Card detect pin */
GPIO_InitStructure.GPIO_Pin = SD_DETECT_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStructure);
/*!< SD_SPI Config */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_PRESCALER_LOW;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SD_SPI, &SPI_InitStructure);
SPI_Cmd(SD_SPI, ENABLE); /*!< SD_SPI enable */
}
static void _set_spi_prescaler(uint32_t prescaler)
{
SD_SPI->CTLR1 &= 0xFFC7;
SD_SPI->CTLR1 |= prescaler;
}
static void _select(void)
{
GPIO_ResetBits(SD_CS_GPIO_PORT, SD_CS_PIN);
}
static void _release(void)
{
GPIO_SetBits(SD_CS_GPIO_PORT, SD_CS_PIN);
}
static bool _is_present(void)
{
return GPIO_ReadInputData(SD_DETECT_GPIO_PORT) & SD_DETECT_PIN ? false : true;
}
static __always_inline uint8_t _wr_rd_byte(uint8_t byte)
{
while((SD_SPI->STATR & SPI_I2S_FLAG_TXE) == RESET);
SD_SPI->DATAR = byte;
while((SD_SPI->STATR & SPI_I2S_FLAG_RXNE) == RESET);
return SD_SPI->DATAR;
}
static void _write(uint8_t const *buffer, uint32_t size)
{
if (size < BLOCK_SIZE) { //For small transactions, use polling
for (uint32_t i = 0; i < size; i ++) {
_wr_rd_byte(buffer[i]);
}
return;
}
uint8_t spi_rx_dummy_byte;
/* Wait for last transaction to be done */
while((SD_SPI->STATR & SPI_I2S_FLAG_TXE) == 0);
/* Configure Rx channel first */
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize = size;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SD_SPI->DATAR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&spi_rx_dummy_byte;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //Repeatedly Rx dummy bytes to claer SPI_I2S_FLAG_RXNE flag
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure); //SPI2_RX
/* Tx dummy bytes to generate clock */
DMA_InitStructure.DMA_BufferSize = size;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SD_SPI->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_Priority = DMA_Priority_Low; //Tx priority should be lower than Rx to avoid FIFO overrun
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure); //SPI2_TX
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
while (DMA_GetITStatus(DMA1_IT_TC4) != SET) { //Poll for Rx done, use interrupt instead when using RTOS
// __WFI();
}
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
DMA_DeInit(DMA1_Channel4);
DMA_DeInit(DMA1_Channel5);
}
static void _read(uint8_t *buffer, uint32_t size)
{
uint8_t spi_tx_dummy_byte = DUMMY_BYTE;
/* Wait for last transaction to be done */
while((SD_SPI->STATR & SPI_I2S_FLAG_TXE) == 0);
/* Configure Rx channel first */
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize = size;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SD_SPI->DATAR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure); //SPI2_RX
/* Tx dummy bytes to generate clock */
DMA_InitStructure.DMA_BufferSize = size;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SD_SPI->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&spi_tx_dummy_byte;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_Priority = DMA_Priority_Low; //Tx priority should be lower than Rx to avoid FIFO overrun
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //Repeatedly Tx dummy byte for clock generation
DMA_Init(DMA1_Channel5, &DMA_InitStructure); //SPI2_TX
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
while (DMA_GetITStatus(DMA1_IT_TC4) != SET) { //Poll for Rx done, use interrupt instead when using RTOS
// __WFI();
}
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
DMA_DeInit(DMA1_Channel4);
DMA_DeInit(DMA1_Channel5);
}
static uint8_t _send_command(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t response = 0xFF;
/* Dummy byte and chip enable */
_wr_rd_byte(DUMMY_BYTE);
_select();
uint8_t packet[] = {cmd | 0x40, arg >> 24, arg >> 16, arg >> 8, arg, crc};
_write(packet, sizeof(packet));
/* Wait response, quit till timeout */
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
response = _wr_rd_byte(DUMMY_BYTE);
if (response != 0xFF) {
break;
}
}
/* Chip disable and dummy byte */
_release();
_wr_rd_byte(DUMMY_BYTE);
return response;
}
static uint8_t _send_command_recv_response(uint8_t cmd, uint32_t arg, uint8_t crc, uint8_t* data, size_t size)
{
/* Dummy byte and chip enable */
_wr_rd_byte(DUMMY_BYTE);
_select();
uint8_t packet[] = {cmd | 0x40, arg >> 24, arg >> 16, arg >> 8, arg, crc};
_write(packet, sizeof(packet));
uint8_t response = DUMMY_BYTE;
/* Wait response, quit till timeout */
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
response = _wr_rd_byte(DUMMY_BYTE);
if (response != DUMMY_BYTE) {
_read(data, size);
break;
}
}
/* Chip disable and dummy byte */
_release();
_wr_rd_byte(DUMMY_BYTE);
return response;
}
static uint8_t _send_command_hold(uint8_t cmd, uint32_t arg, uint8_t crc)
{
/* Dummy byte and chip enable */
_wr_rd_byte(DUMMY_BYTE);
_select();
uint8_t packet[] = {cmd | 0x40, arg >> 24, arg >> 16, arg >> 8, arg, crc};
_write(packet, sizeof(packet));
/* Wait response, quit till timeout */
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
uint8_t response = _wr_rd_byte(DUMMY_BYTE);
if (response != 0xFF) {
return response;
}
}
return 0xFF;
}
static spisd_result_t _read_buffer(uint8_t *buff, uint32_t len)
{
uint8_t response = 0;
/* Wait start-token 0xFE */
for (uint32_t i = 0; i < CMD_WAIT_RESP_TIMEOUT; i++) {
response = _wr_rd_byte(DUMMY_BYTE);
if (response == 0xFE) {
break;
}
}
if (response != 0xFE) {
return SPISD_RESULT_ERROR;
}
_read(buff, len);
/* 2bytes dummy CRC */
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
return SPISD_RESULT_OK;
}
spisd_result_t spisd_init(void)
{
_spi_init();
if ( !_is_present() ) {
SPISD_LOG("There is no card detected!");
return SPISD_RESULT_NO_CARD;
}
_release();
_set_spi_prescaler(SPI_PRESCALER_LOW);
/* Start send 74 clocks at least */
for (uint32_t i = 0; i < 20; i++) {
_wr_rd_byte(DUMMY_BYTE);
}
uint32_t timeout = WAIT_IDLE_TIMEOUT;
uint8_t response = 0;
do {
response = _send_command(CMD0, 0, 0x95);
timeout--;
} while ((response != SPISD_R1_IDLE_FLAG) && timeout > 0 );
if (!timeout) {
SPISD_LOG("Reset card into IDLE state failed!");
return SPISD_RESULT_TIMEOUT;
}
uint8_t buff[4];
response = _send_command_recv_response(CMD8, 0x1AA, 0x87, buff, sizeof(buff));
if (response == SPISD_R1_IDLE_FLAG) {
/* Check voltage range be 2.7-3.6V */
if (buff[2] == 0x01 && buff[3] == 0xAA) {
for (uint32_t i = 0; i < 0xFFF; i++) {
response = _send_command(CMD55, 0, 0); /* should be return 0x01 */
if (response != 0x01) {
SPISD_LOG("Send CMD55 should return 0x01, response=0x%02x", response);
return SPISD_RESULT_TIMEOUT;
}
response = _send_command(ACMD41, 0x40000000, 0); /* should be return 0x00 */
if (response == 0x00) {
break;
}
}
if (response != 0x00) {
SPISD_LOG("Send ACMD41 should return 0x00, response=0x%02x", response);
return SPISD_RESULT_TIMEOUT;
}
/* Read OCR by CMD58 */
response = _send_command_recv_response(CMD58, 0, 0, buff, sizeof(buff));
if (response != 0x00) {
SPISD_LOG("Send CMD58 should return 0x00, response=0x%02x", response);
return SPISD_RESULT_TIMEOUT;
}
/* OCR -> CCS(bit30) 1: SDV2HC 0: SDV2 */
_card_type = (buff[0] & 0x40) ? CARD_TYPE_SDV2HC : CARD_TYPE_SDV2;
_set_spi_prescaler(SPI_PRESCALER_HIGH);
}
#if USE_MMC_CARD == 1
} else if (response & SPISD_R1_ILLEGAL_CMD_FLAG) {
_card_type = CARD_TYPE_SDV1;
/* End of CMD8, chip disable and dummy byte */
_release();
_wr_rd_byte(DUMMY_BYTE);
/* SD1.0/MMC start initialize */
/* Send CMD55+ACMD41, No-response is a MMC card, otherwise is a SD1.0 card */
for (uint32_t i = 0; i < 0xFFF; i++) {
response = _send_command(CMD55, 0, 0);
if (response != 0x01) {
SPISD_LOG("Send CMD55 should return 0x01, response=0x%02x", response);
return SPISD_RESULT_TIMEOUT;
}
response = _send_command(ACMD41, 0, 0);
if (response == 0x00) {
break;
}
}
/* MMC card initialize start */
if (response != 0x00) {
for (uint32_t i = 0; i < 0xFFF; i++) {
response = _send_command(CMD1, 0, 0);
if (response == 0x00) {
break;
}
}
/* Timeout return */
if (response != 0x00) {
SPISD_LOG("Send CMD1 should return 0x00, response=0x%02x", response);
return SPISD_RESULT_TIMEOUT;
}
_card_type = CARD_TYPE_MMC;
SPISD_LOG("Card Type : MMC");
} else {
SPISD_LOG("Card Type : SD V1");
}
_set_spi_prescaler(true);
/* CRC disable */
response = _send_command(CMD59, 0, 0x01);
if (response != 0x00) {
SPISD_LOG("Send CMD59 should return 0x00, response=0x%02x", response);
return SPISD_RESULT_TIMEOUT;
}
/* Set the block size */
response = _send_command(CMD16, BLOCK_SIZE, 0xFF);
if (response != 0x00) {
SPISD_LOG("Send CMD16 should return 0x00, response=0x%02x", response);
return SPISD_RESULT_TIMEOUT;
}
#endif //USE_MMC_CARD
} else {
return SPISD_RESULT_ERROR;
}
return SPISD_RESULT_OK;
}
int spisd_get_card_info(spisd_info_t *cardinfo)
{
_wr_rd_byte(DUMMY_BYTE);
/* Send CMD9, Read CSD */
uint8_t response = _send_command(CMD9, 0, 0xFF);
if (response != 0x00) {
return response;
}
uint8_t temp[16];
_select();
spisd_result_t ret = _read_buffer(temp, sizeof(temp));
/* chip disable and dummy byte */
_release();
_wr_rd_byte(DUMMY_BYTE);
if (ret != SPISD_RESULT_OK) {
return 1;
}
/* Byte 0 */
cardinfo->csd.CSDStruct = (temp[0] & 0xC0) >> 6;
cardinfo->csd.SysSpecVersion = (temp[0] & 0x3C) >> 2;
cardinfo->csd.Reserved1 = temp[0] & 0x03;
/* Byte 1 */
cardinfo->csd.TAAC = temp[1] ;
/* Byte 2 */
cardinfo->csd.NSAC = temp[2];
/* Byte 3 */
cardinfo->csd.MaxBusClkFrec = temp[3];
/* Byte 4 */
cardinfo->csd.CardComdClasses = temp[4] << 4;
/* Byte 5 */
cardinfo->csd.CardComdClasses |= (temp[5] & 0xF0) >> 4;
cardinfo->csd.RdBlockLen = temp[5] & 0x0F;
/* Byte 6 */
cardinfo->csd.PartBlockRead = (temp[6] & 0x80) >> 7;
cardinfo->csd.WrBlockMisalign = (temp[6] & 0x40) >> 6;
cardinfo->csd.RdBlockMisalign = (temp[6] & 0x20) >> 5;
cardinfo->csd.DSRImpl = (temp[6] & 0x10) >> 4;
cardinfo->csd.Reserved2 = 0; /* Reserved */
cardinfo->csd.DeviceSize = (temp[6] & 0x03) << 10;
/* Byte 7 */
cardinfo->csd.DeviceSize |= (temp[7]) << 2;
/* Byte 8 */
cardinfo->csd.DeviceSize |= (temp[8] & 0xC0) >> 6;
cardinfo->csd.MaxRdCurrentVDDMin = (temp[8] & 0x38) >> 3;
cardinfo->csd.MaxRdCurrentVDDMax = (temp[8] & 0x07);
/* Byte 9 */
cardinfo->csd.MaxWrCurrentVDDMin = (temp[9] & 0xE0) >> 5;
cardinfo->csd.MaxWrCurrentVDDMax = (temp[9] & 0x1C) >> 2;
cardinfo->csd.DeviceSizeMul = (temp[9] & 0x03) << 1;
/* Byte 10 */
cardinfo->csd.DeviceSizeMul |= (temp[10] & 0x80) >> 7;
cardinfo->csd.EraseGrSize = (temp[10] & 0x7C) >> 2;
cardinfo->csd.EraseGrMul = (temp[10] & 0x03) << 3;
/* Byte 11 */
cardinfo->csd.EraseGrMul |= (temp[11] & 0xE0) >> 5;
cardinfo->csd.WrProtectGrSize = (temp[11] & 0x1F);
/* Byte 12 */
cardinfo->csd.WrProtectGrEnable = (temp[12] & 0x80) >> 7;
cardinfo->csd.ManDeflECC = (temp[12] & 0x60) >> 5;
cardinfo->csd.WrSpeedFact = (temp[12] & 0x1C) >> 2;
cardinfo->csd.MaxWrBlockLen = (temp[12] & 0x03) << 2;
/* Byte 13 */
cardinfo->csd.MaxWrBlockLen |= (temp[13] & 0xc0) >> 6;
cardinfo->csd.WriteBlockPaPartial = (temp[13] & 0x20) >> 5;
cardinfo->csd.Reserved3 = 0;
cardinfo->csd.ContentProtectAppli = (temp[13] & 0x01);
/* Byte 14 */
cardinfo->csd.FileFormatGrouop = (temp[14] & 0x80) >> 7;
cardinfo->csd.CopyFlag = (temp[14] & 0x40) >> 6;
cardinfo->csd.PermWrProtect = (temp[14] & 0x20) >> 5;
cardinfo->csd.TempWrProtect = (temp[14] & 0x10) >> 4;
cardinfo->csd.FileFormat = (temp[14] & 0x0C) >> 2;
cardinfo->csd.ECC = (temp[14] & 0x03);
/* Byte 15 */
cardinfo->csd.CSD_CRC = (temp[15] & 0xFE) >> 1;
cardinfo->csd.Reserved4 = 1;
if (cardinfo->card_type == CARD_TYPE_SDV2HC) {
/* Byte 7 */
cardinfo->csd.DeviceSize = (uint16_t)(temp[8]) * 256;
/* Byte 8 */
cardinfo->csd.DeviceSize += temp[9] ;
}
cardinfo->capacity = cardinfo->csd.DeviceSize * BLOCK_SIZE * 1024;
cardinfo->block_size = BLOCK_SIZE;
/* Send CMD10, Read CID */
response = _send_command(CMD10, 0, 0xFF);
if (response != 0x00) {
return response;
}
_select();
ret = _read_buffer(temp, sizeof(temp));
/* chip disable and dummy byte */
_release();
_wr_rd_byte(DUMMY_BYTE);
if (ret != SPISD_RESULT_OK) {
return 2;
}
/* Byte 0 */
cardinfo->cid.ManufacturerID = temp[0];
/* Byte 1 */
cardinfo->cid.OEM_AppliID = temp[1] << 8;
/* Byte 2 */
cardinfo->cid.OEM_AppliID |= temp[2];
/* Byte 3 */
cardinfo->cid.ProdName1 = temp[3] << 24;
/* Byte 4 */
cardinfo->cid.ProdName1 |= temp[4] << 16;
/* Byte 5 */
cardinfo->cid.ProdName1 |= temp[5] << 8;
/* Byte 6 */
cardinfo->cid.ProdName1 |= temp[6];
/* Byte 7 */
cardinfo->cid.ProdName2 = temp[7];
/* Byte 8 */
cardinfo->cid.ProdRev = temp[8];
/* Byte 9 */
cardinfo->cid.ProdSN = temp[9] << 24;
/* Byte 10 */
cardinfo->cid.ProdSN |= temp[10] << 16;
/* Byte 11 */
cardinfo->cid.ProdSN |= temp[11] << 8;
/* Byte 12 */
cardinfo->cid.ProdSN |= temp[12];
/* Byte 13 */
cardinfo->cid.Reserved1 |= (temp[13] & 0xF0) >> 4;
/* Byte 14 */
cardinfo->cid.ManufactDate = (temp[13] & 0x0F) << 8;
/* Byte 15 */
cardinfo->cid.ManufactDate |= temp[14];
/* Byte 16 */
cardinfo->cid.CID_CRC = (temp[15] & 0xFE) >> 1;
cardinfo->cid.Reserved2 = 1;
return 0;
}
spisd_result_t spisd_read_block(uint32_t sector, uint8_t *buffer)
{
if (_card_type != CARD_TYPE_SDV2HC) {
sector = sector << 9;
}
spisd_result_t ret = SPISD_RESULT_ERROR;
if (_send_command_hold(CMD17, sector, 0) == 0x00) {
ret = _read_buffer(buffer, BLOCK_SIZE);
}
_release();
_wr_rd_byte(DUMMY_BYTE);
return ret;
}
spisd_result_t spisd_write_block(uint32_t sector, const uint8_t *buffer)
{
spisd_result_t ret = SPISD_RESULT_ERROR;
if (_card_type != CARD_TYPE_SDV2HC) {
sector = sector << 9;
}
if (_send_command(CMD24, sector, 0) != 0x00) {
return ret;
}
_select();
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
/* Start data write token: 0xFE */
_wr_rd_byte(0xFE);
_write(buffer, BLOCK_SIZE);
/* 2Bytes dummy CRC */
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
/* MSD card accept the data */
uint8_t response = _wr_rd_byte(DUMMY_BYTE);
if ((response & 0x1F) == 0x05) {
/* Wait all the data program finished */
for (uint32_t i = 0; i < 0x40000; i++) {
if ( _wr_rd_byte(DUMMY_BYTE) != 0x00) {
ret = SPISD_RESULT_OK;
break;
}
}
}
_release();
_wr_rd_byte(DUMMY_BYTE);
return ret;
}
spisd_result_t spisd_read_multi_block(uint32_t sector, uint8_t *buffer, uint32_t num_sectors)
{
/* if ver = SD2.0 HC, sector need <<9 */
if (_card_type != CARD_TYPE_SDV2HC) {
sector = sector << 9;
}
if (_send_command(CMD18, sector, 0) != 0x00) {
return SPISD_RESULT_ERROR;
}
_wr_rd_byte(DUMMY_BYTE);
_select();
for (uint32_t i = 0; i < num_sectors; i++) {
spisd_result_t ret = _read_buffer(&buffer[i * BLOCK_SIZE], BLOCK_SIZE);
if (ret != SPISD_RESULT_OK) {
/* Send stop data transmit command - CMD12 */
_send_command(CMD12, 0, 0);
return SPISD_RESULT_ERROR;
}
}
_release();
_wr_rd_byte(DUMMY_BYTE);
/* Send stop data transmit command - CMD12 */
_send_command(CMD12, 0, 0);
return SPISD_RESULT_OK;
}
spisd_result_t spisd_write_multi_block(uint32_t sector, uint8_t const *buffer, uint32_t num_sectors)
{
/* if ver = SD2.0 HC, sector need <<9 */
if (_card_type != CARD_TYPE_SDV2HC) {
sector = sector << 9;
}
/* Send command ACMD23 before multi write if is not a MMC card */
if (_card_type != CARD_TYPE_MMC) {
_send_command(CMD55, 0, 0x00);
_send_command(ACMD23, num_sectors, 0x00);
}
if (_send_command(CMD25, sector, 0) != 0x00) {
return SPISD_RESULT_ERROR;
}
_select();
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
for (uint32_t i = 0; i < num_sectors; i++) {
/* Start multi block write token: 0xFC */
_wr_rd_byte(0xFC);
_write(&buffer[i * BLOCK_SIZE], BLOCK_SIZE);
/* 2Bytes dummy CRC */
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
/* MSD card accept the data */
if ((_wr_rd_byte(DUMMY_BYTE) & 0x1F) != 0x05) {
_release();
_wr_rd_byte(DUMMY_BYTE);
return SPISD_RESULT_ERROR;
}
/* Wait all the data program finished */
uint32_t timeout = 0;
while (_wr_rd_byte(DUMMY_BYTE) != 0xFF) {
/* Timeout return */
if (timeout++ == 0x40000) {
_release();
_wr_rd_byte(DUMMY_BYTE);
return SPISD_RESULT_ERROR;
}
}
}
/* Send end of transmit token: 0xFD */
if (_wr_rd_byte(0xFD) != 0x00) {
_wr_rd_byte(DUMMY_BYTE);
_wr_rd_byte(DUMMY_BYTE);
/* Wait all the data program finished */
for (uint32_t i = 0; i < 0x40000; i++) {
if (_wr_rd_byte(DUMMY_BYTE) == 0xFF) {
_release();
_wr_rd_byte(DUMMY_BYTE);
for (uint32_t i = 0; i < 0x40000; i++) {
if (_wr_rd_byte(DUMMY_BYTE) == 0xFF) {
return SPISD_RESULT_OK;
}
}
}
}
}
_release();
_wr_rd_byte(DUMMY_BYTE);
return SPISD_RESULT_ERROR;
}