867 lines
25 KiB
C
867 lines
25 KiB
C
/*
|
|
* Copyright (c) 2017-2020, Texas Instruments Incorporated
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* ======== SDSPI.c ========
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
|
|
#include <driverlib.h>
|
|
#include <device.h>
|
|
|
|
#include <sdspi/sdspi.h>
|
|
|
|
void GPIO_init();
|
|
void SPI_init();
|
|
|
|
/* Definitions for MMC/SDC command */
|
|
#define CMD0 (0x40+0) /* GO_IDLE_STATE */
|
|
#define CMD1 (0x40+1) /* SEND_OP_COND */
|
|
#define CMD8 (0x40+8) /* SEND_IF_COND */
|
|
#define CMD9 (0x40+9) /* SEND_CSD */
|
|
#define CMD10 (0x40+10) /* SEND_CID */
|
|
#define CMD12 (0x40+12) /* STOP_TRANSMISSION */
|
|
#define CMD16 (0x40+16) /* SET_BLOCKLEN */
|
|
#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */
|
|
#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */
|
|
#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */
|
|
#define CMD24 (0x40+24) /* WRITE_BLOCK */
|
|
#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */
|
|
#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */
|
|
#define CMD55 (0x40+55) /* APP_CMD */
|
|
#define CMD58 (0x40+58) /* READ_OCR */
|
|
#define START_BLOCK_TOKEN (0xFE)
|
|
#define START_MULTIBLOCK_TOKEN (0xFC)
|
|
#define STOP_MULTIBLOCK_TOKEN (0xFD)
|
|
|
|
#define SD_SECTOR_SIZE (512)
|
|
|
|
#define DRIVE_NOT_MOUNTED ((uint16_t) ~0)
|
|
|
|
int16_t SDSPI_control(SDSPI_Handle handle, uint16_t cmd,
|
|
void *arg);
|
|
|
|
static inline void assertCS(uint16_t spiCsGpioIndex);
|
|
static inline void deassertCS(uint16_t spiCsGpioIndex);
|
|
static bool recvDataBlock(uint32_t spi_handle, void *buf, uint32_t count);
|
|
static uint8_t sendCmd(uint32_t spi_handle, uint8_t cmd, uint32_t arg);
|
|
static int16_t spiTransfer(uint32_t spi_handle, void *rxBuf,
|
|
void *txBuf, size_t count);
|
|
static bool waitUntilReady(uint32_t spi_handle);
|
|
static bool transmitDataBlock(uint32_t spi_handle, void *buf, uint32_t count,
|
|
uint8_t token);
|
|
|
|
void
|
|
SPI_pollingFIFOTransactionWithNullSupport(uint32_t base, uint16_t charLength,
|
|
uint16_t *pTxBuffer, uint16_t *pRxBuffer,
|
|
uint16_t numOfWords, uint16_t txDelay);
|
|
|
|
/*
|
|
* ======== SDSPI_close ========
|
|
*/
|
|
void SDSPI_close(SDSPI_Handle handle)
|
|
{
|
|
if (handle->spiHandle) {
|
|
SPI_disableModule(handle->spiHandle);
|
|
handle->spiHandle = NULL;
|
|
}
|
|
|
|
|
|
handle->cardType = SD_NOCARD;
|
|
handle->isOpen = false;
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_control ========
|
|
*/
|
|
int16_t SDSPI_control(SDSPI_Handle handle, uint16_t cmd, void *arg)
|
|
{
|
|
return (SD_STATUS_UNDEFINEDCMD);
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_getNumSectors ========
|
|
*/
|
|
uint32_t SDSPI_getNumSectors(SDSPI_Handle handle)
|
|
{
|
|
uint8_t n;
|
|
uint8_t csd[16];
|
|
uint32_t csize;
|
|
uint32_t sectors = 0;
|
|
|
|
assertCS(handle->spiCsGpioIndex);
|
|
|
|
/* Get number of sectors on the disk (uint32_t) */
|
|
if ((sendCmd(handle->spiHandle, CMD9, 0) == 0) &&
|
|
recvDataBlock(handle->spiHandle, csd, 16)) {
|
|
/* SDC ver 2.00 */
|
|
if ((csd[0] >> 6) == 1) {
|
|
csize = csd[9] + (csd[8] << 8) + 1;
|
|
sectors = (uint32_t)csize << 10;
|
|
}
|
|
/* MMC or SDC ver 1.XX */
|
|
else {
|
|
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) +
|
|
((csd[9] & 3) << 1) + 2;
|
|
|
|
csize = (csd[8] >> 6) + ((uint16_t) csd[7] << 2) +
|
|
((uint16_t) (csd[6] & 3) << 10) + 1;
|
|
sectors = (uint32_t)csize << (n - 9);
|
|
}
|
|
}
|
|
|
|
deassertCS(handle->spiCsGpioIndex);
|
|
|
|
|
|
return (sectors);
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_getSectorSize ========
|
|
*/
|
|
uint32_t SDSPI_getSectorSize(SDSPI_Handle handle)
|
|
{
|
|
return (SD_SECTOR_SIZE);
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_init ========
|
|
*/
|
|
void SDSPI_init(SDSPI_Handle handle)
|
|
{
|
|
GPIO_init();
|
|
SPI_init();
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_initialize ========
|
|
*/
|
|
int16_t SDSPI_initialize(SDSPI_Handle handle)
|
|
{
|
|
SD_CardType cardType = SD_NOCARD;
|
|
uint8_t i;
|
|
uint8_t ocr[4];
|
|
uint8_t txDummy[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
int16_t status;
|
|
uint32_t timeout;
|
|
|
|
|
|
/*
|
|
* The CS line should not be asserted when attempting to put the
|
|
* SD card into SPI mode.
|
|
*/
|
|
deassertCS(handle->spiCsGpioIndex);
|
|
|
|
/*
|
|
* To put the SD card in SPI mode we must keep the TX line high while
|
|
* toggling the clock line several times. To do this we transmit 0xFF
|
|
* 10 times. Do not assert CS during this time
|
|
*/
|
|
status = spiTransfer(handle->spiHandle, NULL, &txDummy, 10);
|
|
if (status != SD_STATUS_SUCCESS) {
|
|
return (status);
|
|
}
|
|
|
|
/* Now select the SD Card's chip select to send CMD0 command */
|
|
assertCS(handle->spiCsGpioIndex);
|
|
|
|
/*
|
|
* Send CMD0 to put the SD card in idle mode. Depending on the previous
|
|
* state of the SD card, this may take up to a couple hundred milliseconds.
|
|
* Rather than delay between attempts, we try up to 255 attempts.
|
|
* Failure is returned if the card does not respond will a valid byte
|
|
* within 255 attempts. When the card will respond with 0x1 when its
|
|
* in idle mode.
|
|
*/
|
|
for (i = 255, status = 0xFF; i > 0 && status != 0x1; i--) {
|
|
status = sendCmd(handle->spiHandle, CMD0, 0);
|
|
}
|
|
|
|
/* If the card never transitioned into idle mode */
|
|
if (status != 0x1) {
|
|
deassertCS(handle->spiCsGpioIndex);
|
|
return (SD_STATUS_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Proceed with initialization since the SD Card is in the idle state
|
|
* Determine what SD Card version we are dealing with
|
|
* Depending on which SD Card version, we need to send different SD
|
|
* commands to the SD Card, which will have different response fields.
|
|
*/
|
|
if (sendCmd(handle->spiHandle, CMD8, 0x1AA) == 1) {
|
|
/* SD Version 2.0 or higher */
|
|
status = spiTransfer(handle->spiHandle, &ocr, &txDummy, 4);
|
|
if (status == SD_STATUS_SUCCESS) {
|
|
/*
|
|
* Ensure that the card's voltage range is valid
|
|
* The card can work at VDD range of 2.7-3.6V
|
|
*/
|
|
if ((ocr[2] == 0x01) && (ocr[3] == 0xAA)) {
|
|
/*
|
|
* Wait for data packet in timeout of 300000 attempts - status used to
|
|
* indicate if a timeout occurred before operation
|
|
* completed.
|
|
*/
|
|
status = SD_STATUS_ERROR;
|
|
timeout = 300000;
|
|
do {
|
|
/* ACMD41 with HCS bit */
|
|
if ((sendCmd(handle->spiHandle, CMD55, 0) <= 1) &&
|
|
(sendCmd(handle->spiHandle, CMD41, 1UL << 30) == 0)) {
|
|
status = SD_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
timeout--;
|
|
} while (timeout);
|
|
|
|
/*
|
|
* Check CCS bit to determine which type of capacity we are
|
|
* dealing with
|
|
*/
|
|
if ((status == SD_STATUS_SUCCESS) &&
|
|
sendCmd(handle->spiHandle, CMD58, 0) == 0) {
|
|
status = spiTransfer(handle->spiHandle, &ocr, &txDummy, 4);
|
|
if (status == SD_STATUS_SUCCESS) {
|
|
cardType = (ocr[0] & 0x40) ? SD_SDHC : SD_SDSC;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* SDC Version 1 or MMC */
|
|
/*
|
|
* The card version is not SDC V2+ so check if we are dealing with a
|
|
* SDC or MMC card
|
|
*/
|
|
if ((sendCmd(handle->spiHandle, CMD55, 0) <= 1) &&
|
|
(sendCmd(handle->spiHandle, CMD41, 0) <= 1)) {
|
|
cardType = SD_SDSC;
|
|
}
|
|
else {
|
|
cardType = SD_MMC;
|
|
}
|
|
|
|
/*
|
|
* Wait for data packet in timeout of 1s - status used to
|
|
* indicate if a timeout occurred before operation
|
|
* completed.
|
|
*/
|
|
status = SD_STATUS_ERROR;
|
|
timeout = 300000;
|
|
do {
|
|
if (cardType == SD_SDSC) {
|
|
/* ACMD41 */
|
|
if ((sendCmd(handle->spiHandle, CMD55, 0) <= 1) &&
|
|
(sendCmd(handle->spiHandle, CMD41, 0) == 0)) {
|
|
status = SD_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
/* CMD1 */
|
|
if (sendCmd(handle->spiHandle, CMD1, 0) == 0) {
|
|
status = SD_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
timeout--;
|
|
} while (timeout);
|
|
|
|
/* Select R/W block length */
|
|
if ((status == SD_STATUS_ERROR) ||
|
|
(sendCmd(handle->spiHandle, CMD16, SD_SECTOR_SIZE) != 0)) {
|
|
cardType = SD_NOCARD;
|
|
}
|
|
}
|
|
|
|
deassertCS(handle->spiCsGpioIndex);
|
|
|
|
handle->cardType = cardType;
|
|
|
|
/* Check to see if a card type was determined */
|
|
if (cardType == SD_NOCARD) {
|
|
status = SD_STATUS_ERROR;
|
|
}
|
|
else {
|
|
/* Reconfigure the SPI to operate @ 2.5 MHz */
|
|
|
|
// spiHandle initialization
|
|
SPI_disableModule(handle->spiHandle);
|
|
SPI_setConfig(handle->spiHandle, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1,
|
|
SPI_MODE_CONTROLLER, 2500000, 8);
|
|
SPI_enableFIFO(handle->spiHandle);
|
|
SPI_disableLoopback(handle->spiHandle);
|
|
SPI_setEmulationMode(handle->spiHandle, SPI_EMULATION_STOP_AFTER_TRANSMIT);
|
|
SPI_enableModule(handle->spiHandle);
|
|
|
|
}
|
|
|
|
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_open ========
|
|
*/
|
|
SDSPI_Handle SDSPI_open(SDSPI_Handle handle)
|
|
{
|
|
if (handle->isOpen) {
|
|
return (NULL);
|
|
}
|
|
handle->isOpen = true;
|
|
|
|
/* Configure the SPI CS pin as output set high */
|
|
GPIO_setDirectionMode(handle->spiCsGpioIndex, GPIO_DIR_MODE_OUT);
|
|
GPIO_setPadConfig(handle->spiCsGpioIndex, GPIO_PIN_TYPE_STD);
|
|
//
|
|
// GPIO_setMasterCore(handle->spiCsGpioIndex, GPIO_CORE_CPU1);
|
|
//
|
|
GPIO_setQualificationMode(handle->spiCsGpioIndex, GPIO_QUAL_SYNC);
|
|
GPIO_writePin(handle->spiCsGpioIndex, 1);
|
|
|
|
/*
|
|
* SPI is initially set to 400 kHz to perform SD initialization. This is
|
|
* is done to ensure compatibility with older SD cards. Once the card has
|
|
* been initialized (in SPI mode) the SPI peripheral will be closed &
|
|
* reopened at 2.5 MHz.
|
|
*/
|
|
|
|
//spiHandle initialization
|
|
SPI_disableModule(handle->spiHandle);
|
|
SPI_setConfig(handle->spiHandle, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1,
|
|
SPI_MODE_CONTROLLER, 400000, 8);
|
|
SPI_enableFIFO(handle->spiHandle);
|
|
SPI_disableLoopback(handle->spiHandle);
|
|
SPI_setEmulationMode(handle->spiHandle, SPI_EMULATION_STOP_AFTER_TRANSMIT);
|
|
SPI_enableModule(handle->spiHandle);
|
|
|
|
|
|
/* Ensure the CS line is de-asserted. */
|
|
deassertCS(handle->spiCsGpioIndex);
|
|
|
|
return (handle);
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_read ========
|
|
*/
|
|
int16_t SDSPI_read(SDSPI_Handle handle, void *buf, int32_t sector,
|
|
uint32_t sectorCount)
|
|
{
|
|
uint8_t ffByte = 0xFF;
|
|
int16_t status = SD_STATUS_ERROR;
|
|
uint8_t sendCmdResult = 0;
|
|
bool recvStatus = false;
|
|
|
|
if (sectorCount == 0) {
|
|
return (SD_STATUS_ERROR);
|
|
}
|
|
|
|
|
|
/*
|
|
* On a SDSC card, the sector address is a byte address on the SD Card
|
|
* On a SDHC card, the sector addressing is via sector blocks
|
|
*/
|
|
if (handle->cardType != SD_SDHC) {
|
|
/* Convert to byte address */
|
|
sector *= SD_SECTOR_SIZE;
|
|
}
|
|
|
|
assertCS(handle->spiCsGpioIndex);
|
|
|
|
/* Single block read */
|
|
if (sectorCount == 1) {
|
|
sendCmdResult = sendCmd(handle->spiHandle, CMD17, sector);
|
|
recvStatus = recvDataBlock(handle->spiHandle, buf, SD_SECTOR_SIZE);
|
|
if ((sendCmdResult == 0) &&
|
|
recvStatus) {
|
|
status = SD_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ESTOP0;
|
|
}
|
|
}
|
|
/* Multiple block read */
|
|
else {
|
|
if (sendCmd(handle->spiHandle, CMD18, sector) == 0) {
|
|
do {
|
|
if (!recvDataBlock(handle->spiHandle, buf, SD_SECTOR_SIZE)) {
|
|
break;
|
|
}
|
|
buf = (void *) (((uint32_t) buf) + SD_SECTOR_SIZE);
|
|
} while (--sectorCount);
|
|
|
|
/*
|
|
* STOP_TRANSMISSION - order is important; always want to send
|
|
* stop signal
|
|
*/
|
|
if (sendCmd(handle->spiHandle, CMD12, 0) == 0 && sectorCount == 0) {
|
|
status = SD_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ESTOP0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ESTOP0;
|
|
}
|
|
}
|
|
|
|
deassertCS(handle->spiCsGpioIndex);
|
|
|
|
/* Send a 0xFF with CS high to try to put SD card into low power mode */
|
|
spiTransfer(handle->spiHandle, NULL, &ffByte, 1);
|
|
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* ======== SDSPI_write ========
|
|
*/
|
|
int16_t SDSPI_write(SDSPI_Handle handle, const void *buf,
|
|
int32_t sector, uint32_t sectorCount)
|
|
{
|
|
int16_t status = SD_STATUS_SUCCESS;
|
|
|
|
if (sectorCount == 0) {
|
|
return (SD_STATUS_ERROR);
|
|
}
|
|
|
|
|
|
/*
|
|
* On a SDSC card, the sector address is a byte address on the SD Card
|
|
* On a SDHC card, the sector addressing is via sector blocks
|
|
*/
|
|
if (handle->cardType != SD_SDHC) {
|
|
/* Convert to byte address if needed */
|
|
sector *= SD_SECTOR_SIZE;
|
|
}
|
|
|
|
assertCS(handle->spiCsGpioIndex);
|
|
|
|
/* Single block write */
|
|
if (sectorCount == 1) {
|
|
if ((sendCmd(handle->spiHandle, CMD24, sector) == 0) &&
|
|
transmitDataBlock(handle->spiHandle, (void *) buf, SD_SECTOR_SIZE,
|
|
START_BLOCK_TOKEN)) {
|
|
sectorCount = 0;
|
|
}
|
|
}
|
|
/* Multiple block write */
|
|
else {
|
|
if ((handle->cardType == SD_SDSC) || (handle->cardType == SD_SDHC)) {
|
|
if (sendCmd(handle->spiHandle, CMD55, 0) != 0) {
|
|
status = SD_STATUS_ERROR;
|
|
}
|
|
|
|
/* ACMD23 */
|
|
if ((status == SD_STATUS_SUCCESS) &&
|
|
(sendCmd(handle->spiHandle, CMD23, sectorCount) != 0)) {
|
|
status = SD_STATUS_ERROR;
|
|
}
|
|
}
|
|
|
|
/* WRITE_MULTIPLE_BLOCK command */
|
|
if ((status == SD_STATUS_SUCCESS) &&
|
|
(sendCmd(handle->spiHandle, CMD25, sector) == 0)) {
|
|
do {
|
|
if (!transmitDataBlock(handle->spiHandle, (void *) buf,
|
|
SD_SECTOR_SIZE, START_MULTIBLOCK_TOKEN)) {
|
|
break;
|
|
}
|
|
buf = (void *) (((uint32_t) buf) + SD_SECTOR_SIZE);
|
|
} while (--sectorCount);
|
|
|
|
/* STOP_TRAN token */
|
|
if (!transmitDataBlock(handle->spiHandle, NULL, 0,
|
|
STOP_MULTIBLOCK_TOKEN)) {
|
|
sectorCount = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wait for SD card to finish storing the data it received. This may help
|
|
* the card go into low power mode.
|
|
*/
|
|
waitUntilReady(handle->spiHandle);
|
|
|
|
deassertCS(handle->spiCsGpioIndex);
|
|
|
|
|
|
return ((sectorCount) ? SD_STATUS_ERROR : SD_STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* ======== assertCS ========
|
|
*/
|
|
static inline void assertCS(uint16_t spiCsGpioIndex)
|
|
{
|
|
GPIO_writePin(spiCsGpioIndex, 0);
|
|
}
|
|
|
|
/*
|
|
* ======== deassertCS ========
|
|
*/
|
|
static inline void deassertCS(uint16_t spiCsGpioIndex)
|
|
{
|
|
GPIO_writePin(spiCsGpioIndex, 1);
|
|
}
|
|
|
|
/*
|
|
* ======== recvDataBlock ========
|
|
* Function to receive a block of data from the SDCard
|
|
*/
|
|
static bool recvDataBlock(uint32_t spi_handle, void *buf, uint32_t count)
|
|
{
|
|
uint8_t rxBuf[2];
|
|
uint8_t txBuf[2] = {0xFF, 0xFF};
|
|
int16_t status;
|
|
uint32_t timeout;
|
|
|
|
/*
|
|
* Wait for SD card to be ready up to 300000 attempts. SD card is ready when the
|
|
* START_BLOCK_TOKEN is received.
|
|
*/
|
|
timeout = 300000;
|
|
do {
|
|
status = spiTransfer(spi_handle, &rxBuf, &txBuf, 1);
|
|
timeout--;
|
|
} while ((status == SD_STATUS_SUCCESS) && (rxBuf[0] == 0xFF) &&
|
|
timeout);
|
|
|
|
if (rxBuf[0] != START_BLOCK_TOKEN) {
|
|
/* Return error if valid data token was not received */
|
|
return (false);
|
|
}
|
|
|
|
/* Receive the data block into buffer */
|
|
if (spiTransfer(spi_handle, buf, NULL, count) != SD_STATUS_SUCCESS) {
|
|
return (false);
|
|
}
|
|
|
|
/* Read the 16 bit CRC, but discard it */
|
|
if (spiTransfer(spi_handle, &rxBuf, &txBuf, 2) != SD_STATUS_SUCCESS) {
|
|
return (false);
|
|
}
|
|
|
|
/* Return with success */
|
|
return (true);
|
|
}
|
|
|
|
/*
|
|
* ======== sendCmd ========
|
|
* Function to send a command to the SD card. Command responses from
|
|
* SD card are returned. (0xFF) is returned on failures.
|
|
*/
|
|
static uint8_t sendCmd(uint32_t spi_handle, uint8_t cmd, uint32_t arg)
|
|
{
|
|
uint8_t i;
|
|
uint8_t rxBuf;
|
|
uint8_t txBuf[6];
|
|
int16_t status;
|
|
|
|
if ((cmd != CMD0) && !waitUntilReady(spi_handle)) {
|
|
return (0xFF);
|
|
}
|
|
|
|
/* Setup SPI transaction */
|
|
txBuf[0] = cmd; /* Command */
|
|
txBuf[1] = (uint8_t)(arg >> 24); /* Argument[31..24] */
|
|
txBuf[2] = (uint8_t)(arg >> 16); /* Argument[23..16] */
|
|
txBuf[3] = (uint8_t)(arg >> 8); /* Argument[15..8] */
|
|
txBuf[4] = (uint8_t) arg; /* Argument[7..0] */
|
|
|
|
if (cmd == CMD0) {
|
|
/* CRC for CMD0(0) */
|
|
txBuf[5] = 0x95;
|
|
}
|
|
else if (cmd == CMD8) {
|
|
/* CRC for CMD8(0x1AA) */
|
|
txBuf[5] = 0x87;
|
|
}
|
|
else {
|
|
/* Default CRC should be at least 0x01 */
|
|
txBuf[5] = 0x01;
|
|
}
|
|
|
|
if (spiTransfer(spi_handle, NULL, &txBuf, 6) != SD_STATUS_SUCCESS) {
|
|
return (0xFF);
|
|
}
|
|
|
|
/* Prepare to receive SD card response (send 0xFF) */
|
|
txBuf[0] = 0xFF;
|
|
|
|
/*
|
|
* CMD 12 has R1b response which transfers an additional
|
|
* "busy" byte
|
|
*/
|
|
if ((cmd == CMD12) &&
|
|
(spiTransfer(spi_handle, &rxBuf, &txBuf, 1) != SD_STATUS_SUCCESS)) {
|
|
return (0xFF);
|
|
}
|
|
|
|
/* Wait for a valid response; 10 attempts */
|
|
i = 10;
|
|
do {
|
|
status = spiTransfer(spi_handle, &rxBuf, &txBuf, 1);
|
|
} while ((status == SD_STATUS_SUCCESS) && (rxBuf & 0x80) && (--i));
|
|
|
|
/* Return with the response value */
|
|
return (rxBuf);
|
|
}
|
|
|
|
/*
|
|
* ======== spiTransfer ========
|
|
* Returns SD_STATUS_SUCCESS when transfer is completed;
|
|
* SD_STATUS_ERROR otherwise.
|
|
*/
|
|
static int16_t spiTransfer(uint32_t spi_handle, void *rxBuf,
|
|
void *txBuf, size_t count) {
|
|
|
|
SPI_pollingFIFOTransactionWithNullSupport(spi_handle, 8,
|
|
txBuf, rxBuf,
|
|
count, 0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
void
|
|
SPI_pollingFIFOTransactionWithNullSupport(uint32_t base, uint16_t charLength,
|
|
uint16_t *pTxBuffer, uint16_t *pRxBuffer,
|
|
uint16_t numOfWords, uint16_t txDelay)
|
|
{
|
|
ASSERT((charLength >= 1U) && (charLength <= 16U));
|
|
SPI_setcharLength(base, charLength);
|
|
|
|
//
|
|
// Reset the TX / RX FIFO buffers to default state
|
|
//
|
|
SPI_disableFIFO(base); // Disable FIFO register
|
|
SPI_enableFIFO(base); // Enable FIFO register
|
|
|
|
//
|
|
// Configure the FIFO Transmit Delay
|
|
//
|
|
SPI_setTxFifoTransmitDelay(base, txDelay);
|
|
|
|
//
|
|
// Determine the number of 16-level words from number of words to be
|
|
// transmitted / received
|
|
//
|
|
uint16_t numOfSixteenWords = numOfWords / SPI_FIFO_TXFULL;
|
|
|
|
//
|
|
// Determine the number of remaining words from number of words to be
|
|
// transmitted / received
|
|
//
|
|
uint16_t remainingWords = numOfWords % SPI_FIFO_TXFULL;
|
|
|
|
uint16_t count = 0;
|
|
uint16_t i = 0;
|
|
uint16_t txBuffer_pos = 0;
|
|
uint16_t rxBuffer_pos = 0;
|
|
|
|
//
|
|
// Number of transactions is based on numOfSixteenWords
|
|
// Each transaction will transmit and receive 16 words.
|
|
//
|
|
while(count < numOfSixteenWords)
|
|
{
|
|
//
|
|
// Fill-up the SPI Transmit FIFO buffers
|
|
//
|
|
for(i = 1; i <= SPI_FIFO_TXFULL; i++)
|
|
{
|
|
if (pTxBuffer == NULL)
|
|
{
|
|
SPI_writeDataBlockingFIFO(base, 0xFFFF << (16U - charLength));
|
|
}
|
|
else
|
|
{
|
|
SPI_writeDataBlockingFIFO(base, pTxBuffer[txBuffer_pos++] <<
|
|
(16U - charLength));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait till SPI Receive FIFO buffer is full
|
|
//
|
|
while(SPI_getRxFIFOStatus(base) < SPI_FIFO_RXFULL);
|
|
|
|
//
|
|
// Read the SPI Receive FIFO buffers
|
|
//
|
|
for(i = 1U; i <= SPI_FIFO_RXFULL; i++)
|
|
{
|
|
if(pRxBuffer == NULL)
|
|
{
|
|
SPI_readDataBlockingFIFO(base);
|
|
}
|
|
else
|
|
{
|
|
pRxBuffer[rxBuffer_pos++] = SPI_readDataBlockingFIFO(base);
|
|
}
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
//
|
|
// Number of transactions is based on remainingWords
|
|
//
|
|
for(i = 0U; i < remainingWords; i++)
|
|
{
|
|
if (pTxBuffer == NULL)
|
|
{
|
|
SPI_writeDataBlockingFIFO(base, 0xFFFF << (16U - charLength));
|
|
}
|
|
else
|
|
{
|
|
SPI_writeDataBlockingFIFO(base, pTxBuffer[txBuffer_pos++] <<
|
|
(16U - charLength));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait till SPI Receive FIFO buffer remaining words
|
|
//
|
|
while(SPI_getRxFIFOStatus(base) < remainingWords);
|
|
|
|
//
|
|
// Read the SPI Receive FIFO buffers
|
|
//
|
|
for(i = 0; i < remainingWords; i++)
|
|
{
|
|
if(pRxBuffer == NULL)
|
|
{
|
|
SPI_readDataBlockingFIFO(base);
|
|
}
|
|
else
|
|
{
|
|
pRxBuffer[rxBuffer_pos++] = SPI_readDataBlockingFIFO(base);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Disable SPI FIFO
|
|
//
|
|
SPI_disableFIFO(base);
|
|
}
|
|
|
|
/*
|
|
* ======== transmitDataBlock ========
|
|
* Function to transmit a block of data to the SD card. A valid command
|
|
* token must be sent to the SD card prior to sending the data block.
|
|
* The available tokens are:
|
|
* START_BLOCK_TOKEN
|
|
* START_MULTIBLOCK_TOKEN
|
|
* STOP_MULTIBLOCK_TOKEN
|
|
*/
|
|
static bool transmitDataBlock(uint32_t spi_handle, void *buf, uint32_t count,
|
|
uint8_t token)
|
|
{
|
|
uint8_t rxBuf;
|
|
uint8_t txBuf[2] = {0xFF, 0xFF};
|
|
|
|
if (!waitUntilReady(spi_handle)) {
|
|
return (false);
|
|
}
|
|
|
|
/* transmit data token */
|
|
txBuf[0] = token;
|
|
if (spiTransfer(spi_handle, NULL, &txBuf, 1) != SD_STATUS_SUCCESS) {
|
|
return (false);
|
|
}
|
|
|
|
/* Send data only when token != STOP_MULTIBLOCK_TOKEN */
|
|
if (token != STOP_MULTIBLOCK_TOKEN) {
|
|
/* Write data to the SD card */
|
|
if (spiTransfer(spi_handle, NULL, buf, count) != SD_STATUS_SUCCESS) {
|
|
return (false);
|
|
}
|
|
|
|
/* Receive the 16 bit CRC, but discard it */
|
|
txBuf[0] = (0xFF);
|
|
if (spiTransfer(spi_handle, NULL, &txBuf, 2) != SD_STATUS_SUCCESS) {
|
|
return (false);
|
|
}
|
|
|
|
/* Receive data response token from SD card */
|
|
if (spiTransfer(spi_handle, &rxBuf, &txBuf, 1) != SD_STATUS_SUCCESS) {
|
|
return (false);
|
|
}
|
|
|
|
/* Check data response; return error if data was rejected */
|
|
if ((rxBuf & 0x1F) != 0x05) {
|
|
return (false);
|
|
}
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
/*
|
|
* ======== waitUntilReady ========
|
|
* Function to check if the SD card is busy.
|
|
*
|
|
* Returns true if SD card is ready; false indicates the SD card is still busy
|
|
* & a timeout occurred.
|
|
*/
|
|
static bool waitUntilReady(uint32_t spi_handle)
|
|
{
|
|
uint8_t rxDummy;
|
|
uint8_t txDummy = 0xFF;
|
|
int16_t status;
|
|
uint32_t timeout;
|
|
|
|
/* Wait up to 1s for data packet */
|
|
timeout = 300000;
|
|
do {
|
|
status = spiTransfer(spi_handle, &rxDummy, &txDummy, 1);
|
|
timeout--;
|
|
} while ((status == SD_STATUS_SUCCESS) && (rxDummy != 0xFF) &&
|
|
timeout);
|
|
|
|
return (rxDummy == 0xFF);
|
|
}
|