/**
* @brief Following commands are SD Card Specific commands.
* SDIO_APP_CMD should be sent before sending these commands.
*/
#define SDIO_SEND_IF_COND ((uint32_t)0x00000008)
/**
* @brief Initializes the SD Card and put it into StandBy State (Ready for
* data transfer).
* @param None
* @retval None
*/
void SD_LowLevel_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/**
* @brief Initializes the SD Card and put it into StandBy State (Ready for data
* transfer).
* @param None
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_Init(void)
{
SD_Error errorstatus = SD_OK;
if (errorstatus == SD_OK)
{
errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
}
/* Set Device Transfer Mode to DMA */
if (errorstatus == SD_OK)
{
errorstatus = SD_SetDeviceMode(SD_DMA_MODE);
}
return(errorstatus);
}
/**
* @brief Gets the cuurent sd card data transfer status.
* @param None
* @retval SDTransferState: Data Transfer state.
* This value can be:
* - SD_TRANSFER_OK: No data transfer is acting
* - SD_TRANSFER_BUSY: Data transfer is acting
*/
SDTransferState SD_GetStatus(void)
{
SDCardState cardstate = SD_CARD_TRANSFER;
/**
* @brief Detect if SD card is correctly plugged in the memory slot.
* @param None
* @retval Return if SD is detected or not
*/
uint8_t SD_Detect(void)
{
__IO uint8_t status = SD_PRESENT;
/*!< Check GPIO to detect SD */
if (GPIO_ReadInputDataBit(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != Bit_RESET)
{
status = SD_NOT_PRESENT;
}
return status;
}
/*!< If errorstatus is Command TimeOut, it is a MMC card */
/*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)
or SD card 1.x */
if (errorstatus == SD_OK)
{
/*!< SD CARD */
/*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
{
/*!< Set Power State to OFF */
SDIO_SetPowerState(SDIO_PowerState_OFF);
return(errorstatus);
}
/**
* @brief Intialises all cards or single card as the case may be Card(s) come
* into standby state.
* @param None
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_InitializeCards(void)
{
SD_Error errorstatus = SD_OK;
uint16_t rca = 0x01;
if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
{
errorstatus = SD_REQUEST_NOT_APPLICABLE;
return(errorstatus);
}
/**
* @brief Enables wide bus opeartion for the requeseted card if supported by
* card.
* @param WideMode: Specifies the SD card wide bus mode.
* This parameter can be one of the following values:
* @arg SDIO_BusWide_8b: 8-bit data transfer (Only for MMC)
* @arg SDIO_BusWide_4b: 4-bit data transfer
* @arg SDIO_BusWide_1b: 1-bit data transfer
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_EnableWideBusOperation(uint32_t WideMode)
{
SD_Error errorstatus = SD_OK;
/*!< MMC Card doesn't support this feature */
if (SDIO_MULTIMEDIA_CARD == CardType)
{
errorstatus = SD_UNSUPPORTED_FEATURE;
return(errorstatus);
}
else if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
{
if (SDIO_BusWide_8b == WideMode)
{
errorstatus = SD_UNSUPPORTED_FEATURE;
return(errorstatus);
}
else if (SDIO_BusWide_4b == WideMode)
{
errorstatus = SDEnWideBus(ENABLE);
/**
* @brief Sets device mode whether to operate in Polling, Interrupt or DMA mode.
* @param Mode: Specifies the Data Transfer mode.
* This parameter can be one of the following values:
* @arg SD_DMA_MODE: Data transfer using DMA.
* @arg SD_INTERRUPT_MODE: Data transfer using interrupts.
* @arg SD_POLLING_MODE: Data transfer using flags.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_SetDeviceMode(uint32_t Mode)
{
SD_Error errorstatus = SD_OK;
/**
* @brief Allows to read one block from a specified address in a card.
* @param readbuff: pointer to the buffer that will contain the received data
* @param ReadAddr: Address from where data are to be read.
* @param BlockSize: the SD card Data block size.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize)
{
SD_Error errorstatus = SD_OK;
uint32_t count = 0, *tempbuff = (uint32_t *)readbuff;
uint8_t power = 0;
if (NULL == readbuff)
{
errorstatus = SD_INVALID_PARAMETER;
return(errorstatus);
}
if (errorstatus != SD_OK)
{
return(errorstatus);
}
/*!< In case of single block transfer, no need of stop transfer at all.*/
if (DeviceMode == SD_POLLING_MODE)
{
/*!< Polling mode */
while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
{
if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)
{
for (count = 0; count < 8; count++)
{
*(tempbuff + count) = SDIO_ReadData();
}
tempbuff += 8;
}
}
/**
* @brief Allows to read blocks from a specified address in a card.
* @param readbuff: pointer to the buffer that will contain the received data.
* @param ReadAddr: Address from where data are to be read.
* @param BlockSize: the SD card Data block size.
* @param NumberOfBlocks: number of blocks to be read.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_ReadMultiBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{
SD_Error errorstatus = SD_OK;
uint32_t count = 0, *tempbuff = (uint32_t *)readbuff;
uint8_t power = 0;
if (NULL == readbuff)
{
errorstatus = SD_INVALID_PARAMETER;
return(errorstatus);
}
if (NumberOfBlocks > 1)
{
/*!< Common to all modes */
if (NumberOfBlocks * BlockSize > SD_MAX_DATA_LENGTH)
{
errorstatus = SD_INVALID_PARAMETER;
return(errorstatus);
}
/**
* @brief Allows to write one block starting from a specified address in a card.
* @param writebuff: pointer to the buffer that contain the data to be transferred.
* @param WriteAddr: Address from where data are to be read.
* @param BlockSize: the SD card Data block size.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize)
{
SD_Error errorstatus = SD_OK;
uint8_t power = 0, cardstate = 0;
uint32_t timeout = 0, bytestransferred = 0;
uint32_t cardstatus = 0, count = 0, restwords = 0;
uint32_t *tempbuff = (uint32_t *)writebuff;
if (writebuff == NULL)
{
errorstatus = SD_INVALID_PARAMETER;
return(errorstatus);
}
/*!< Set the block size, both on controller and card */
if ((BlockSize > 0) && (BlockSize <= 2048) && ((BlockSize & (BlockSize - 1)) == 0))
{
power = convert_from_bytes_to_power_of_two(BlockSize);
/*!< In case of single data block transfer no need of stop command at all */
if (DeviceMode == SD_POLLING_MODE)
{
while (!(SDIO->STA & (SDIO_FLAG_DBCKEND | SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_STBITERR)))
{
if (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) != RESET)
{
if ((TotalNumberOfBytes - bytestransferred) < 32)
{
restwords = ((TotalNumberOfBytes - bytestransferred) % 4 == 0) ? ((TotalNumberOfBytes - bytestransferred) / 4) : (( TotalNumberOfBytes - bytestransferred) / 4 + 1);
/**
* @brief Allows to write blocks starting from a specified address in a card.
* @param WriteAddr: Address from where data are to be read.
* @param writebuff: pointer to the buffer that contain the data to be transferred.
* @param BlockSize: the SD card Data block size.
* @param NumberOfBlocks: number of blocks to be written.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{
SD_Error errorstatus = SD_OK;
uint8_t power = 0, cardstate = 0;
uint32_t bytestransferred = 0;
uint32_t restwords = 0;
uint32_t *tempbuff = (uint32_t *)writebuff;
__IO uint32_t count = 0;
if (writebuff == NULL)
{
errorstatus = SD_INVALID_PARAMETER;
return(errorstatus);
}
/*!< Set the block size, both on controller and card */
if ((BlockSize > 0) && (BlockSize <= 2048) && ((BlockSize & (BlockSize - 1)) == 0))
{
power = convert_from_bytes_to_power_of_two(BlockSize);
/*!< Wait till card is ready for data Added */
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_SEND_STATUS);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
if (NumberOfBlocks > 1)
{
/*!< Common to all modes */
if (NumberOfBlocks * BlockSize > SD_MAX_DATA_LENGTH)
{
errorstatus = SD_INVALID_PARAMETER;
return(errorstatus);
}
/*!< Add some delay before checking the Card Status */
for(count = 0; count < 0xFFFF; count++)
{
}
/*!< Wait till the card is in programming state */
errorstatus = IsCardProgramming(&cardstate);
/**
* @brief Gets the cuurent data transfer state.
* @param None
* @retval SDTransferState: Data Transfer state.
* This value can be:
* - SD_TRANSFER_OK: No data transfer is acting
* - SD_TRANSFER_BUSY: Data transfer is acting
*/
SDTransferState SD_GetTransferState(void)
{
if (SDIO->STA & (SDIO_FLAG_TXACT | SDIO_FLAG_RXACT))
{
return(SD_TRANSFER_BUSY);
}
else
{
return(SD_TRANSFER_OK);
}
}
/**
* @brief Returns the current card's status.
* @param pcardstatus: pointer to the buffer that will contain the SD card
* status (Card Status register).
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_SendStatus(uint32_t *pcardstatus)
{
SD_Error errorstatus = SD_OK;
if (pcardstatus == NULL)
{
errorstatus = SD_INVALID_PARAMETER;
return(errorstatus);
}
if (errorstatus != SD_OK)
{
return(errorstatus);
}
*pcardstatus = SDIO_GetResponse(SDIO_RESP1);
return(errorstatus);
}
/**
* @brief Returns the current SD card's status.
* @param psdstatus: pointer to the buffer that will contain the SD card status
* (SD Status register).
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_SendSDStatus(uint32_t *psdstatus)
{
SD_Error errorstatus = SD_OK;
uint32_t count = 0;
if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
{
errorstatus = SD_LOCK_UNLOCK_FAILED;
return(errorstatus);
}
/*!< Set block size for card if it is not equal to current block size for card. */
SDIO_CmdInitStructure.SDIO_Argument = 64;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)) && (timeout > 0))
{
timeout--;
status = SDIO->STA;
}
if ((timeout == 0) || (status & SDIO_FLAG_CTIMEOUT))
{
/*!< Card is not V2.0 complient or card does not support the set voltage range */
errorstatus = SD_CMD_RSP_TIMEOUT;
SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
return(errorstatus);
}
if (status & SDIO_FLAG_CMDREND)
{
/*!< Card is SD V2.0 compliant */
errorstatus = SD_OK;
SDIO_ClearFlag(SDIO_FLAG_CMDREND);
return(errorstatus);
}
return(errorstatus);
}
/**
* @brief Checks for error conditions for R1 response.
* @param cmd: The sent command index.
* @retval SD_Error: SD Card Error code.
*/
static SD_Error CmdResp1Error(uint8_t cmd)
{
SD_Error errorstatus = SD_OK;
uint32_t status;
uint32_t response_r1;
status = SDIO->STA;
while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
{
status = SDIO->STA;
}
if (response_r1 & SD_R6_GENERAL_UNKNOWN_ERROR)
{
return(SD_GENERAL_UNKNOWN_ERROR);
}
if (response_r1 & SD_R6_ILLEGAL_CMD)
{
return(SD_ILLEGAL_CMD);
}
if (response_r1 & SD_R6_COM_CRC_FAILED)
{
return(SD_COM_CRC_FAILED);
}
return(errorstatus);
}
/**
* @brief Enables or disables the SDIO wide bus mode.
* @param NewState: new state of the SDIO wide bus mode.
* This parameter can be: ENABLE or DISABLE.
* @retval SD_Error: SD Card Error code.
*/
static SD_Error SDEnWideBus(FunctionalState NewState)
{
SD_Error errorstatus = SD_OK;
uint32_t scr[2] = {0, 0};
if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
{
errorstatus = SD_LOCK_UNLOCK_FAILED;
return(errorstatus);
}
/*!< Get SCR Register */
errorstatus = FindSCR(RCA, scr);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
/*!< If wide bus operation to be enabled */
if (NewState == ENABLE)
{
/*!< If requested card supports wide bus operation */
if ((scr[1] & SD_WIDE_BUS_SUPPORT) != SD_ALLZERO)
{
/*!< Send CMD55 APP_CMD with argument as card's RCA.*/
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
/*!< Send ACMD6 APP_CMD with argument as 2 for wide bus mode */
SDIO_CmdInitStructure.SDIO_Argument = 0x2;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_SD_SET_BUSWIDTH;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
/**
* @brief Converts the number of bytes in power of two and returns the power.
* @param NumberOfBytes: number of bytes.
* @retval None
*/
static uint8_t convert_from_bytes_to_power_of_two(uint16_t NumberOfBytes)
{
uint8_t count = 0;