562c089e17
Get the status of the Toshiba FlashAir, namely its IP address. The IP address is currently reported through an M46, but this is subject to change and the IP address shall be shown on the display.
812 lines
25 KiB
C++
812 lines
25 KiB
C++
/* Arduino Sd2Card Library
|
|
* Copyright (C) 2009 by William Greiman
|
|
*
|
|
* This file is part of the Arduino Sd2Card Library
|
|
*
|
|
* This Library is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This Library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with the Arduino Sd2Card Library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "Marlin.h"
|
|
|
|
#ifdef SDSUPPORT
|
|
#include "Sd2Card.h"
|
|
//------------------------------------------------------------------------------
|
|
#ifndef SOFTWARE_SPI
|
|
// functions for hardware SPI
|
|
//------------------------------------------------------------------------------
|
|
// make sure SPCR rate is in expected bits
|
|
#if (SPR0 != 0 || SPR1 != 1)
|
|
#error unexpected SPCR bits
|
|
#endif
|
|
/**
|
|
* Initialize hardware SPI
|
|
* Set SCK rate to F_CPU/pow(2, 1 + spiRate) for spiRate [0,6]
|
|
*/
|
|
static void spiInit(uint8_t spiRate) {
|
|
// See avr processor documentation
|
|
SPCR = (1 << SPE) | (1 << MSTR) | (spiRate >> 1);
|
|
SPSR = spiRate & 1 || spiRate == 6 ? 0 : 1 << SPI2X;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** SPI receive a byte */
|
|
static uint8_t spiRec() {
|
|
SPDR = 0XFF;
|
|
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
|
|
return SPDR;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** SPI read data - only one call so force inline */
|
|
static inline __attribute__((always_inline))
|
|
void spiRead(uint8_t* buf, uint16_t nbyte) {
|
|
if (nbyte-- == 0) return;
|
|
SPDR = 0XFF;
|
|
for (uint16_t i = 0; i < nbyte; i++) {
|
|
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
|
|
buf[i] = SPDR;
|
|
SPDR = 0XFF;
|
|
}
|
|
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
|
|
buf[nbyte] = SPDR;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** SPI send a byte */
|
|
static void spiSend(uint8_t b) {
|
|
SPDR = b;
|
|
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** SPI send block - only one call so force inline */
|
|
static inline __attribute__((always_inline))
|
|
void spiSendBlock(uint8_t token, const uint8_t* buf) {
|
|
SPDR = token;
|
|
for (uint16_t i = 0; i < 512; i += 2) {
|
|
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
|
|
SPDR = buf[i];
|
|
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
|
|
SPDR = buf[i + 1];
|
|
}
|
|
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
#else // SOFTWARE_SPI
|
|
//------------------------------------------------------------------------------
|
|
/** nop to tune soft SPI timing */
|
|
#define nop asm volatile ("nop\n\t")
|
|
//------------------------------------------------------------------------------
|
|
/** Soft SPI receive byte */
|
|
static uint8_t spiRec() {
|
|
uint8_t data = 0;
|
|
// no interrupts during byte receive - about 8 us
|
|
cli();
|
|
// output pin high - like sending 0XFF
|
|
fastDigitalWrite(SPI_MOSI_PIN, HIGH);
|
|
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
fastDigitalWrite(SPI_SCK_PIN, HIGH);
|
|
|
|
// adjust so SCK is nice
|
|
nop;
|
|
nop;
|
|
|
|
data <<= 1;
|
|
|
|
if (fastDigitalRead(SPI_MISO_PIN)) data |= 1;
|
|
|
|
fastDigitalWrite(SPI_SCK_PIN, LOW);
|
|
}
|
|
// enable interrupts
|
|
sei();
|
|
return data;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Soft SPI read data */
|
|
static void spiRead(uint8_t* buf, uint16_t nbyte) {
|
|
for (uint16_t i = 0; i < nbyte; i++) {
|
|
buf[i] = spiRec();
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Soft SPI send byte */
|
|
static void spiSend(uint8_t data) {
|
|
// no interrupts during byte send - about 8 us
|
|
cli();
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
fastDigitalWrite(SPI_SCK_PIN, LOW);
|
|
|
|
fastDigitalWrite(SPI_MOSI_PIN, data & 0X80);
|
|
|
|
data <<= 1;
|
|
|
|
fastDigitalWrite(SPI_SCK_PIN, HIGH);
|
|
}
|
|
// hold SCK high for a few ns
|
|
nop;
|
|
nop;
|
|
nop;
|
|
nop;
|
|
|
|
fastDigitalWrite(SPI_SCK_PIN, LOW);
|
|
// enable interrupts
|
|
sei();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Soft SPI send block */
|
|
void spiSendBlock(uint8_t token, const uint8_t* buf) {
|
|
spiSend(token);
|
|
for (uint16_t i = 0; i < 512; i++) {
|
|
spiSend(buf[i]);
|
|
}
|
|
}
|
|
#endif // SOFTWARE_SPI
|
|
//------------------------------------------------------------------------------
|
|
// send command and return error code. Return zero for OK
|
|
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
|
|
// select card
|
|
chipSelectLow();
|
|
|
|
// wait up to 300 ms if busy
|
|
waitNotBusy(300);
|
|
|
|
// send command
|
|
spiSend(cmd | 0x40);
|
|
|
|
// send argument
|
|
for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s);
|
|
|
|
// send CRC
|
|
uint8_t crc = 0XFF;
|
|
if (cmd == CMD0) crc = 0X95; // correct crc for CMD0 with arg 0
|
|
if (cmd == CMD8) crc = 0X87; // correct crc for CMD8 with arg 0X1AA
|
|
spiSend(crc);
|
|
|
|
// skip stuff byte for stop read
|
|
if (cmd == CMD12) spiRec();
|
|
|
|
// wait for response
|
|
for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++) { /* Intentionally left empty */ }
|
|
return status_;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
* Determine the size of an SD flash memory card.
|
|
*
|
|
* \return The number of 512 byte data blocks in the card
|
|
* or zero if an error occurs.
|
|
*/
|
|
uint32_t Sd2Card::cardSize() {
|
|
csd_t csd;
|
|
if (!readCSD(&csd)) return 0;
|
|
if (csd.v1.csd_ver == 0) {
|
|
uint8_t read_bl_len = csd.v1.read_bl_len;
|
|
uint16_t c_size = (csd.v1.c_size_high << 10)
|
|
| (csd.v1.c_size_mid << 2) | csd.v1.c_size_low;
|
|
uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1)
|
|
| csd.v1.c_size_mult_low;
|
|
return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7);
|
|
} else if (csd.v2.csd_ver == 1) {
|
|
uint32_t c_size = ((uint32_t)csd.v2.c_size_high << 16)
|
|
| (csd.v2.c_size_mid << 8) | csd.v2.c_size_low;
|
|
return (c_size + 1) << 10;
|
|
} else {
|
|
error(SD_CARD_ERROR_BAD_CSD);
|
|
return 0;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void Sd2Card::chipSelectHigh() {
|
|
digitalWrite(chipSelectPin_, HIGH);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void Sd2Card::chipSelectLow() {
|
|
#ifndef SOFTWARE_SPI
|
|
spiInit(spiRate_);
|
|
#endif // SOFTWARE_SPI
|
|
digitalWrite(chipSelectPin_, LOW);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Erase a range of blocks.
|
|
*
|
|
* \param[in] firstBlock The address of the first block in the range.
|
|
* \param[in] lastBlock The address of the last block in the range.
|
|
*
|
|
* \note This function requests the SD card to do a flash erase for a
|
|
* range of blocks. The data on the card after an erase operation is
|
|
* either 0 or 1, depends on the card vendor. The card must support
|
|
* single block erase.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
|
|
csd_t csd;
|
|
if (!readCSD(&csd)) goto fail;
|
|
// check for single block erase
|
|
if (!csd.v1.erase_blk_en) {
|
|
// erase size mask
|
|
uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
|
|
if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) {
|
|
// error card can't erase specified area
|
|
error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
|
|
goto fail;
|
|
}
|
|
}
|
|
if (type_ != SD_CARD_TYPE_SDHC) {
|
|
firstBlock <<= 9;
|
|
lastBlock <<= 9;
|
|
}
|
|
if (cardCommand(CMD32, firstBlock)
|
|
|| cardCommand(CMD33, lastBlock)
|
|
|| cardCommand(CMD38, 0)) {
|
|
error(SD_CARD_ERROR_ERASE);
|
|
goto fail;
|
|
}
|
|
if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
|
|
error(SD_CARD_ERROR_ERASE_TIMEOUT);
|
|
goto fail;
|
|
}
|
|
chipSelectHigh();
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Determine if card supports single block erase.
|
|
*
|
|
* \return The value one, true, is returned if single block erase is supported.
|
|
* The value zero, false, is returned if single block erase is not supported.
|
|
*/
|
|
bool Sd2Card::eraseSingleBlockEnable() {
|
|
csd_t csd;
|
|
return readCSD(&csd) ? csd.v1.erase_blk_en : false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
* Initialize an SD flash memory card.
|
|
*
|
|
* \param[in] sckRateID SPI clock rate selector. See setSckRate().
|
|
* \param[in] chipSelectPin SD chip select pin number.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure. The reason for failure
|
|
* can be determined by calling errorCode() and errorData().
|
|
*/
|
|
bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
|
|
errorCode_ = type_ = 0;
|
|
chipSelectPin_ = chipSelectPin;
|
|
// 16-bit init start time allows over a minute
|
|
uint16_t t0 = (uint16_t)millis();
|
|
uint32_t arg;
|
|
|
|
// set pin modes
|
|
pinMode(chipSelectPin_, OUTPUT);
|
|
chipSelectHigh();
|
|
pinMode(SPI_MISO_PIN, INPUT);
|
|
pinMode(SPI_MOSI_PIN, OUTPUT);
|
|
pinMode(SPI_SCK_PIN, OUTPUT);
|
|
|
|
#ifndef SOFTWARE_SPI
|
|
// SS must be in output mode even it is not chip select
|
|
pinMode(SS_PIN, OUTPUT);
|
|
// set SS high - may be chip select for another SPI device
|
|
#if SET_SPI_SS_HIGH
|
|
digitalWrite(SS_PIN, HIGH);
|
|
#endif // SET_SPI_SS_HIGH
|
|
// set SCK rate for initialization commands
|
|
spiRate_ = SPI_SD_INIT_RATE;
|
|
spiInit(spiRate_);
|
|
#endif // SOFTWARE_SPI
|
|
|
|
// must supply min of 74 clock cycles with CS high.
|
|
for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
|
|
|
|
// command to go idle in SPI mode
|
|
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
|
|
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
|
|
error(SD_CARD_ERROR_CMD0);
|
|
goto fail;
|
|
}
|
|
}
|
|
// check SD version
|
|
if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
|
|
type(SD_CARD_TYPE_SD1);
|
|
} else {
|
|
// only need last byte of r7 response
|
|
for (uint8_t i = 0; i < 4; i++) status_ = spiRec();
|
|
if (status_ != 0XAA) {
|
|
error(SD_CARD_ERROR_CMD8);
|
|
goto fail;
|
|
}
|
|
type(SD_CARD_TYPE_SD2);
|
|
}
|
|
// initialize card and send host supports SDHC if SD2
|
|
arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0;
|
|
|
|
while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
|
|
// check for timeout
|
|
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
|
|
error(SD_CARD_ERROR_ACMD41);
|
|
goto fail;
|
|
}
|
|
}
|
|
// if SD2 read OCR register to check for SDHC card
|
|
if (type() == SD_CARD_TYPE_SD2) {
|
|
if (cardCommand(CMD58, 0)) {
|
|
error(SD_CARD_ERROR_CMD58);
|
|
goto fail;
|
|
}
|
|
if ((spiRec() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC);
|
|
// discard rest of ocr - contains allowed voltage range
|
|
for (uint8_t i = 0; i < 3; i++) spiRec();
|
|
}
|
|
chipSelectHigh();
|
|
|
|
#ifndef SOFTWARE_SPI
|
|
return setSckRate(sckRateID);
|
|
#else // SOFTWARE_SPI
|
|
return true;
|
|
#endif // SOFTWARE_SPI
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
* Read a 512 byte block from an SD card.
|
|
*
|
|
* \param[in] blockNumber Logical block to be read.
|
|
* \param[out] dst Pointer to the location that will receive the data.
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
|
|
#ifdef SD_CHECK_AND_RETRY
|
|
uint8_t retryCnt = 3;
|
|
// use address if not SDHC card
|
|
if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
|
retry2:
|
|
retryCnt --;
|
|
if (cardCommand(CMD17, blockNumber)) {
|
|
error(SD_CARD_ERROR_CMD17);
|
|
if (retryCnt > 0) goto retry;
|
|
goto fail;
|
|
}
|
|
if (!readData(dst, 512))
|
|
{
|
|
if (retryCnt > 0) goto retry;
|
|
goto fail;
|
|
}
|
|
return true;
|
|
retry:
|
|
chipSelectHigh();
|
|
cardCommand(CMD12, 0);//Try sending a stop command, but ignore the result.
|
|
errorCode_ = 0;
|
|
goto retry2;
|
|
#else
|
|
// use address if not SDHC card
|
|
if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
|
if (cardCommand(CMD17, blockNumber)) {
|
|
error(SD_CARD_ERROR_CMD17);
|
|
goto fail;
|
|
}
|
|
return readData(dst, 512);
|
|
#endif
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Read one data block in a multiple block read sequence
|
|
*
|
|
* \param[in] dst Pointer to the location for the data to be read.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::readData(uint8_t *dst) {
|
|
chipSelectLow();
|
|
return readData(dst, 512);
|
|
}
|
|
|
|
#ifdef SD_CHECK_AND_RETRY
|
|
static const uint16_t crctab[] PROGMEM = {
|
|
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
|
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
|
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
|
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
|
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
|
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
|
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
|
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
|
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
|
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
|
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
|
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
|
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
|
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
|
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
|
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
|
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
|
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
|
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
|
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
|
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
|
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
|
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
|
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
|
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
|
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
|
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
|
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
|
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
|
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
|
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
|
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
|
};
|
|
static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
|
|
uint16_t crc = 0;
|
|
for (size_t i = 0; i < n; i++) {
|
|
crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0XFF]) ^ (crc << 8);
|
|
}
|
|
return crc;
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
|
|
// wait for start block token
|
|
uint16_t t0 = millis();
|
|
while ((status_ = spiRec()) == 0XFF) {
|
|
if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) {
|
|
error(SD_CARD_ERROR_READ_TIMEOUT);
|
|
goto fail;
|
|
}
|
|
}
|
|
if (status_ != DATA_START_BLOCK) {
|
|
error(SD_CARD_ERROR_READ);
|
|
goto fail;
|
|
}
|
|
// transfer data
|
|
spiRead(dst, count);
|
|
|
|
#ifdef SD_CHECK_AND_RETRY
|
|
{
|
|
uint16_t calcCrc = CRC_CCITT(dst, count);
|
|
uint16_t recvCrc = spiRec() << 8;
|
|
recvCrc |= spiRec();
|
|
if (calcCrc != recvCrc)
|
|
{
|
|
error(SD_CARD_ERROR_CRC);
|
|
goto fail;
|
|
}
|
|
}
|
|
#else
|
|
// discard CRC
|
|
spiRec();
|
|
spiRec();
|
|
#endif
|
|
chipSelectHigh();
|
|
// Toshiba FlashAir Patch. Purge pending status byte.
|
|
spiSend(0XFF);
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
// Toshiba FlashAir Patch. Purge pending status byte.
|
|
spiSend(0XFF);
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** read CID or CSR register */
|
|
bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
|
|
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
|
|
if (cardCommand(cmd, 0)) {
|
|
error(SD_CARD_ERROR_READ_REG);
|
|
goto fail;
|
|
}
|
|
return readData(dst, 16);
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Start a read multiple blocks sequence.
|
|
*
|
|
* \param[in] blockNumber Address of first block in sequence.
|
|
*
|
|
* \note This function is used with readData() and readStop() for optimized
|
|
* multiple block reads. SPI chipSelect must be low for the entire sequence.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::readStart(uint32_t blockNumber) {
|
|
if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
|
if (cardCommand(CMD18, blockNumber)) {
|
|
error(SD_CARD_ERROR_CMD18);
|
|
goto fail;
|
|
}
|
|
chipSelectHigh();
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** End a read multiple blocks sequence.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::readStop() {
|
|
chipSelectLow();
|
|
if (cardCommand(CMD12, 0)) {
|
|
error(SD_CARD_ERROR_CMD12);
|
|
goto fail;
|
|
}
|
|
chipSelectHigh();
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
* Set the SPI clock rate.
|
|
*
|
|
* \param[in] sckRateID A value in the range [0, 6].
|
|
*
|
|
* The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum
|
|
* SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128
|
|
* for \a scsRateID = 6.
|
|
*
|
|
* \return The value one, true, is returned for success and the value zero,
|
|
* false, is returned for an invalid value of \a sckRateID.
|
|
*/
|
|
bool Sd2Card::setSckRate(uint8_t sckRateID) {
|
|
if (sckRateID > 6) {
|
|
error(SD_CARD_ERROR_SCK_RATE);
|
|
return false;
|
|
}
|
|
spiRate_ = sckRateID;
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// wait for card to go not busy
|
|
bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
|
|
uint16_t t0 = millis();
|
|
while (spiRec() != 0XFF) {
|
|
if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail;
|
|
}
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
* Writes a 512 byte block to an SD card.
|
|
*
|
|
* \param[in] blockNumber Logical block to be written.
|
|
* \param[in] src Pointer to the location of the data to be written.
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
|
|
// use address if not SDHC card
|
|
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
|
if (cardCommand(CMD24, blockNumber)) {
|
|
error(SD_CARD_ERROR_CMD24);
|
|
goto fail;
|
|
}
|
|
if (!writeData(DATA_START_BLOCK, src)) goto fail;
|
|
|
|
// wait for flash programming to complete
|
|
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
|
|
error(SD_CARD_ERROR_WRITE_TIMEOUT);
|
|
goto fail;
|
|
}
|
|
// response is r2 so get and check two bytes for nonzero
|
|
if (cardCommand(CMD13, 0) || spiRec()) {
|
|
error(SD_CARD_ERROR_WRITE_PROGRAMMING);
|
|
goto fail;
|
|
}
|
|
chipSelectHigh();
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Write one data block in a multiple block write sequence
|
|
* \param[in] src Pointer to the location of the data to be written.
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::writeData(const uint8_t* src) {
|
|
chipSelectLow();
|
|
// wait for previous write to finish
|
|
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
|
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto fail;
|
|
chipSelectHigh();
|
|
return true;
|
|
|
|
fail:
|
|
error(SD_CARD_ERROR_WRITE_MULTIPLE);
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// send one block of data for write block or write multiple blocks
|
|
bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
|
|
spiSendBlock(token, src);
|
|
|
|
spiSend(0xff); // dummy crc
|
|
spiSend(0xff); // dummy crc
|
|
|
|
status_ = spiRec();
|
|
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
|
|
error(SD_CARD_ERROR_WRITE);
|
|
goto fail;
|
|
}
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** Start a write multiple blocks sequence.
|
|
*
|
|
* \param[in] blockNumber Address of first block in sequence.
|
|
* \param[in] eraseCount The number of blocks to be pre-erased.
|
|
*
|
|
* \note This function is used with writeData() and writeStop()
|
|
* for optimized multiple block writes.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
|
|
// send pre-erase count
|
|
if (cardAcmd(ACMD23, eraseCount)) {
|
|
error(SD_CARD_ERROR_ACMD23);
|
|
goto fail;
|
|
}
|
|
// use address if not SDHC card
|
|
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
|
|
if (cardCommand(CMD25, blockNumber)) {
|
|
error(SD_CARD_ERROR_CMD25);
|
|
goto fail;
|
|
}
|
|
chipSelectHigh();
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/** End a write multiple blocks sequence.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
bool Sd2Card::writeStop() {
|
|
chipSelectLow();
|
|
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
|
spiSend(STOP_TRAN_TOKEN);
|
|
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
|
|
chipSelectHigh();
|
|
return true;
|
|
|
|
fail:
|
|
error(SD_CARD_ERROR_STOP_TRAN);
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/** Wait for start block token */
|
|
//FIXME Vojtech: Copied from a current version of Sd2Card Arduino code.
|
|
// We shall likely upgrade the rest of the Sd2Card.
|
|
uint8_t Sd2Card::waitStartBlock(void) {
|
|
uint16_t t0 = millis();
|
|
while ((status_ = spiRec()) == 0XFF) {
|
|
if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) {
|
|
error(SD_CARD_ERROR_READ_TIMEOUT);
|
|
goto fail;
|
|
}
|
|
}
|
|
if (status_ != DATA_START_BLOCK) {
|
|
error(SD_CARD_ERROR_READ);
|
|
goto fail;
|
|
}
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
|
|
// Toshiba FlashAir support, copied from
|
|
// https://flashair-developers.com/en/documents/tutorials/arduino/
|
|
|
|
//------------------------------------------------------------------------------
|
|
/** Perform Extention Read. */
|
|
uint8_t Sd2Card::readExt(uint32_t arg, uint8_t* dst, uint16_t count) {
|
|
uint16_t i;
|
|
|
|
// send command and argument.
|
|
if (cardCommand(CMD48, arg)) {
|
|
error(SD_CARD_ERROR_CMD48);
|
|
goto fail;
|
|
}
|
|
|
|
// wait for start block token.
|
|
if (!waitStartBlock()) {
|
|
goto fail;
|
|
}
|
|
|
|
// receive data
|
|
for (i = 0; i < count; ++i) {
|
|
dst[i] = spiRec();
|
|
}
|
|
|
|
// skip dummy bytes and 16-bit crc.
|
|
for (; i < 514; ++i) {
|
|
spiRec();
|
|
}
|
|
|
|
chipSelectHigh();
|
|
spiSend(0xFF); // dummy clock to force FlashAir finish the command.
|
|
return true;
|
|
|
|
fail:
|
|
chipSelectHigh();
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
* Read an extension register space.
|
|
*
|
|
* \return The value one, true, is returned for success and
|
|
* the value zero, false, is returned for failure.
|
|
*/
|
|
uint8_t Sd2Card::readExtMemory(uint8_t mio, uint8_t func,
|
|
uint32_t addr, uint16_t count, uint8_t* dst) {
|
|
uint32_t offset = addr & 0x1FF;
|
|
if (offset + count > 512) count = 512 - offset;
|
|
|
|
if (count == 0) return true;
|
|
|
|
uint32_t arg =
|
|
(((uint32_t)mio & 0x1) << 31) |
|
|
(mio ? (((uint32_t)func & 0x7) << 28) : (((uint32_t)func & 0xF) << 27)) |
|
|
((addr & 0x1FFFF) << 9) |
|
|
((count - 1) & 0x1FF);
|
|
|
|
return readExt(arg, dst, count);
|
|
}
|
|
|
|
#endif
|