awesome
Has any user created a SD memory shield/library from the Arduino to the Maple?
(149 posts) (39 voices)-
Posted 5 years ago #
-
Just a minor update to this, I soldered up a micro SD card adapter (Partially melted) yesterday and over the next few days/week I'll start porting over the library.
Posted 5 years ago # -
Hi! Are there any updates on your progress ? I'm also interested in porting this library, so maybe I could help out. Do you have a repo on github for this port?
Also, could anyone suggest to me how to make my own SD adapter ? :\ I guess I don't need an 5V => 3.3V converter, as Maple uses 3.3V already. I can't find one in the local stores and ordering one from the Internets is an overkill for something that small.
Posted 5 years ago # -
Quote: " I can't find one in the local stores "
Reply: A camera store would have them. It is only a micro SD to SD converter ~ $1 USD.
Posted 5 years ago # -
I'm still working on porting the SDFat library. Hitting some compiler errors (not unexpected) but I can slowly fix those. I'm trying to keep the Software SPI function too.
Posted 5 years ago # -
so I attempted to rewrite the Sd2Card.cpp from the SdFat library. What I have compiles, but I'm kind of lost otherwise...
#define SPI_MOSI_PIN 11
#define SPI_MISO_PIN 12
#define SPI_SCK_PIN 13
#define CS_PIN 10/* Definitions for MMC/SDC command */
#define CMD0 (0x40+0) /* GO_IDLE_STATE */
#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */
#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 ACMD13 (0xC0+13) /* SD_STATUS (SDC) */
#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 (MMC) */
#define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (0x40+24) /* WRITE_BLOCK */
#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */
#define CMD55 (0x40+55) /* APP_CMD */
#define CMD58 (0x40+58) /* READ_OCR *///------------------------------------------------------------------------------
/** status for card in the ready state */
#define R1_READY_STATE 0X00
/** status for card in the idle state */
#define R1_IDLE_STATE 0X01
/** status bit for illegal command */
#define R1_ILLEGAL_COMMAND 0X04
/** start data token for read or write single block*/
#define DATA_START_BLOCK 0XFE
/** stop token for write multiple blocks*/
#define STOP_TRAN_TOKEN 0XFD
/** start data token for write multiple blocks*/
#define WRITE_MULTIPLE_TOKEN 0XFC
/** mask for data response tokens after a write block operation */
#define DATA_RES_MASK 0X1F
/** write data accepted token */
#define DATA_RES_ACCEPTED 0X05/** Protect block zero from write if nonzero */
#define SD_PROTECT_BLOCK_ZERO 1
/** init timeout ms */
#define SD_INIT_TIMEOUT 2000
/** erase timeout ms */
#define SD_ERASE_TIMEOUT 10000
/** read timeout ms */
#define SD_READ_TIMEOUT 300
/** write time out ms */
#define SD_WRITE_TIMEOUT 600
//------------------------------------------------------------------------------
// SD card errors
/** timeout error for command CMD0 */
#define SD_CARD_ERROR_CMD0 0X1
/** CMD8 was not accepted - not a valid SD card*/
#define SD_CARD_ERROR_CMD8 0X2
/** card returned an error response for CMD17 (read block) */
#define SD_CARD_ERROR_CMD17 0X3
/** card returned an error response for CMD24 (write block) */
#define SD_CARD_ERROR_CMD24 0X4
/** WRITE_MULTIPLE_BLOCKS command failed */
#define SD_CARD_ERROR_CMD25 0X05
/** card returned an error response for CMD58 (read OCR) */
#define SD_CARD_ERROR_CMD58 0X06
/** SET_WR_BLK_ERASE_COUNT failed */
#define SD_CARD_ERROR_ACMD23 0X07
/** card's ACMD41 initialization process timeout */
#define SD_CARD_ERROR_ACMD41 0X08
/** card returned a bad CSR version field */
#define SD_CARD_ERROR_BAD_CSD 0X09
/** erase block group command failed */
#define SD_CARD_ERROR_ERASE 0X0A
/** card not capable of single block erase */
#define SD_CARD_ERROR_ERASE_SINGLE_BLOCK 0X0B
/** Erase sequence timed out */
#define SD_CARD_ERROR_ERASE_TIMEOUT 0X0C
/** card returned an error token instead of read data */
#define SD_CARD_ERROR_READ 0X0D
/** read CID or CSD failed */
#define SD_CARD_ERROR_READ_REG 0X0E
/** timeout while waiting for start of read data */
#define SD_CARD_ERROR_READ_TIMEOUT 0X0F
/** card did not accept STOP_TRAN_TOKEN */
#define SD_CARD_ERROR_STOP_TRAN 0X10
/** card returned an error token as a response to a write operation */
#define SD_CARD_ERROR_WRITE 0X11
/** attempt to write protected block zero */
#define SD_CARD_ERROR_WRITE_BLOCK_ZERO 0X12
/** card did not go ready for a multiple block write */
#define SD_CARD_ERROR_WRITE_MULTIPLE 0X13
/** card returned an error to a CMD13 status check after a write */
#define SD_CARD_ERROR_WRITE_PROGRAMMING 0X14
/** timeout occurred during write programming */
#define SD_CARD_ERROR_WRITE_TIMEOUT 0X15
/** incorrect rate selected */
#define SD_CARD_ERROR_SCK_RATE 0X16
//------------------------------------------------------------------------------
// card types
/** Standard capacity V1 SD card */
#define SD_CARD_TYPE_SD1 1
/** Standard capacity V2 SD card */
#define SD_CARD_TYPE_SD2 2
/** High Capacity SD card */
#define SD_CARD_TYPE_SDHC 3HardwareSPI spi(1);
int block_;
int chipSelectPin_;
int errorCode_;
int inBlock_;
int offset_;
int partialBlockRead_;
int status_ = 0x00;
int type_;// functions for hardware SPI
/** Send a byte to the card */
static void spiSend(uint8 b) {
spi.send(b);
}
static uint8 spiRec(void) {
spi.send(0XFF);
return spi.recv();
}
//------------------------------------------------------------------------------
// send command and return error code. Return zero for OK
uint8 cardCommand(uint8 cmd, uint32 arg) {// select card
digitalWrite(CS_PIN,LOW);// wait up to 300 ms if busy
waitNotBusy(300);// send command
spiSend(cmd | 0x40);// send argument
for (uint8 s = 24; s >= 0; s -= 8) spiSend(arg >> s);// send CRC
uint8 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);// wait for response
for (uint8 i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++);
return status_;
}
////---------------------------------------------------------------------------
///** 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.
// */
//uint8 (uint32 firstBlock, uint32 lastBlock) {
// if (!eraseSingleBlockEnable()) {
// 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;
// }
// digitalWrite(CS_PIN, HIGH);
// return true;
//
// fail:
// digitalWrite(CS_PIN, HIGH);
// 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.
// */
//uint8 eraseSingleBlockEnable(void) {
// csd_t csd;
// return readCSD(&csd) ? csd.v1.erase_blk_en : 0;
//}
////------------------------------------------------------------------------------
/**
* 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().
*/
uint8 init(uint8 sckRateID, uint8 chipSelectPin) {
errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0;
chipSelectPin_ = CS_PIN;
// 16-bit init start time allows over a minute
uint32 t0 = millis();
uint32 arg;spi.begin(SPI_140_625KHZ,MSBFIRST,0);
// set pin modes
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN,HIGH);
pinMode(SPI_MISO_PIN, INPUT);
pinMode(SPI_MOSI_PIN, OUTPUT);
pinMode(SPI_SCK_PIN, OUTPUT);// must supply min of 74 clock cycles with CS high.
for (uint8 i = 0; i < 10; i++) spiSend(0XFF);digitalWrite(CS_PIN, LOW);
// command to go idle in SPI mode
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
if ((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 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 = getType() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0;while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
// check for timeout
if (((uint16)millis() - t0) > SD_INIT_TIMEOUT) {
error(SD_CARD_ERROR_ACMD41);
goto fail;
}
}
// if SD2 read OCR register to check for SDHC card
if (getType() == 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 i = 0; i < 3; i++) spiRec();
}
digitalWrite(CS_PIN, HIGH);spi.begin(SPI_18MHZ,MSBFIRST, 0);
fail:
digitalWrite(CS_PIN, HIGH);
return false;
}
//------------------------------------------------------------------------------
/**
* Enable or disable partial block reads.
*
* Enabling partial block reads improves performance by allowing a block
* to be read over the SPI bus as several sub-blocks. Errors may occur
* if the time between reads is too long since the SD card may timeout.
* The SPI SS line will be held low until the entire block is read or
* readEnd() is called.
*
* Use this for applications like the Adafruit Wave Shield.
*
* \param[in] value The value TRUE (non-zero) or FALSE (zero).)
*/
void partialBlockRead(uint8 value) {
readEnd();
partialBlockRead_ = value;
}
//------------------------------------------------------------------------------
/**
* Read a 512 byte block from an SD card device.
*
* \param[in] block 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.
*/
uint8 readBlock(uint32 block, uint8* dst) {
return readData(block, 0, 512, dst);
}
//------------------------------------------------------------------------------
/**
* Read part of a 512 byte block from an SD card.
*
* \param[in] block Logical block to be read.
* \param[in] offset Number of bytes to skip at start of block
* \param[out] dst Pointer to the location that will receive the data.
* \param[in] count Number of bytes to read
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8 readData(uint32 block,
uint16 offset, uint16 count, uint8* dst) {
uint16 n;
if (count == 0) return true;
if ((count + offset) > 512) {
goto fail;
}
if (!inBlock_ || block != block_ || offset < offset_) {
block_ = block;
// use address if not SDHC card
if (getType()!= SD_CARD_TYPE_SDHC) block <<= 9;
if (cardCommand(CMD17, block)) {
error(SD_CARD_ERROR_CMD17);
goto fail;
}
if (!waitStartBlock()) {
goto fail;
}
offset_ = 0;
inBlock_ = 1;
}
offset_ += count;
if (!partialBlockRead_ || offset_ >= 512) {
// read rest of data, checksum and set chip select high
readEnd();
}
return true;fail:
digitalWrite(CS_PIN, HIGH);
return false;
}
//------------------------------------------------------------------------------
/** Skip remaining data in a block when in partial block read mode. */
void readEnd(void) {
if (inBlock_) {
// skip data and crc
digitalWrite(CS_PIN, HIGH);
inBlock_ = 0;
}
}
//------------------------------------------------------------------------------
/** read CID or CSR register */
uint8 readRegister(uint8 cmd, void* buf) {
uint8* dst = reinterpret_cast<uint8*>(buf);
if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG);
goto fail;
}
if (!waitStartBlock()) goto fail;
// transfer data
for (uint16 i = 0; i < 16; i++) dst[i] = spiRec();
spiRec(); // get first crc byte
spiRec(); // get second crc byte
digitalWrite(CS_PIN, HIGH);
return true;fail:
digitalWrite(CS_PIN, HIGH);
return false;
}// wait for card to go not busy
uint8 waitNotBusy(uint16 timeoutMillis) {
uint16 t0 = millis();
do {
if (spiRec() == 0XFF) return true;
}
while (((uint16)millis() - t0) < timeoutMillis);
return false;
}
//------------------------------------------------------------------------------
/** Wait for start block token */
uint8 waitStartBlock(void) {
uint16 t0 = millis();
while ((status_ = spiRec()) == 0XFF) {
if (((uint16)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:
digitalWrite(CS_PIN, HIGH);
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.
*/
uint8 writeBlock(uint32 blockNumber, const uint8* src) {
// use address if not SDHC card
if (getType() != 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(0X0D, 0) || spiRec()) { // 0X0D = CMD13
error(SD_CARD_ERROR_WRITE_PROGRAMMING);
goto fail;
}
digitalWrite(CS_PIN, HIGH);
return true;fail:
digitalWrite(CS_PIN, HIGH);
return false;
}
//------------------------------------------------------------------------------
/** Write one data block in a multiple block write sequence */
uint8 writeData(const uint8* src) {
// wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
digitalWrite(CS_PIN, HIGH);
return false;
}
return writeData(WRITE_MULTIPLE_TOKEN, src);
}
//------------------------------------------------------------------------------
// send one block of data for write block or write multiple blocks
uint8 writeData(uint8 token, const uint8* src) {spiSend(0xff); // dummy crc
spiSend(0xff); // dummy crcstatus_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE);
digitalWrite(CS_PIN, HIGH);
return false;
}
return true;
}
//------------------------------------------------------------------------------
/** 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.
*/
uint8 writeStart(uint32 blockNumber, uint32 eraseCount) {
// send pre-erase count
if (cardAcmd(ACMD23, eraseCount)) {
error(SD_CARD_ERROR_ACMD23);
goto fail;
}
// use address if not SDHC card
if (getType() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (cardCommand(CMD25, blockNumber)) {
error(SD_CARD_ERROR_CMD25);
goto fail;
}
return true;fail:
digitalWrite(CS_PIN, HIGH);
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.
*/
uint8 writeStop(void) {
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
spiSend(STOP_TRAN_TOKEN);
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
digitalWrite(CS_PIN, HIGH);
return true;fail:
error(SD_CARD_ERROR_STOP_TRAN);
digitalWrite(CS_PIN, HIGH);
return false;
}// private functions
uint8 cardAcmd(uint8 cmd, uint32 arg) {
cardCommand(CMD55, 0);
return cardCommand(cmd, arg);
}void error(uint8 code) {errorCode_ = code;}
void type(uint8 value) {type_ = value;}
uint8 getType(void) {return type_;}void setup()
{disk_initialize(0);
}
void loop()
{
}its ugly. if someone wants to take it and do more feel free.
Posted 4 years ago # -
I've had some success getting a port of the arduino SDfat library working. It will successfully initialize the SD card over SPI but that is about the end of it. It fails when it tries to initialize the volume. I am not an expert in file systems so this is where I am over my head. Using a logic analyzer I can see that the SD cord is responding to the block read request and the data it is responding with does appear in the cache. However it does not seem to be what the software expects and fails the volume initialization. I am trying to decipher how the structures interact with each other and where what data should be stored and it is making my head throb.
It may very well be the SanDISK micro SD that I am using but I would imagine it would not pass initializing the card for SPI mode.
I really believe that this is exceedingly close to a working library but I need some help to figure these issues out.
the svn is here
svn checkout http://maple-sdfat.googlecode.com/svn/trunk/ maple-sdfat-read-onlyor you can download the library from here directly
http://code.google.com/p/maple-sdfat/downloads/listextract the zip file into your libraries directory.
The one example in there is what I've been using to test with. The SPI bus speed can be bumped up to a frequency into the MHz range easily AFTER initialization. I kept it slow for debugging purposes.
The SS pin is hard coded for pin 10 in SD2Card.cpp, so if you decided to use SPI2 you need to either change the pin number to 31 or use pin 10.
regards,
JasonPosted 4 years ago # -
Almost forgot, you will need to copy stdint.h from an an Arduino/hardware/core director to the Maple/hardware/core directory.
Posted 4 years ago # -
Jason,
Thanks for your efforts towards porting SDfat! SD card I/O has been a hole in our library for quite some time.
Not sure what you mean about copying stdint.h, though -- we link against newlib, which provides stdint.h, so you should be able to just include it in your sketch. This works fine for me in Maple IDE 0.0.10 Beta and using our command line toolchain.
Posted 4 years ago # -
mbolivar,
You're welcome, wish I could say that I did did it just because but its a critical part of my project :).Being very new to Maple, I knew the definitions were in stdint so I just copied the file. Its been removed from the core directory and everything compiles fine.
This was compiled as well using Maple IDE 0.0.10 Beta, the new SPI interface made it VERY easy to do so thank you!
Regarding the problem with SDfat, it is my suspicion that the problem has something to do with the different sized data types between the STM32 and the AVR. The cache_t structure is where all the large 512 byte blocks are read to and it is a union of other structures some containing arrays of structures with in them. The simplest structure is data[] being a 512 byte array. Using data[] to find the values in their expected locations works, however trying to find the same value in the mbr's respective position yields a different value. So something isn't lining up.
The good news is that data IS being exchanged between the micro and the SD!
Posted 4 years ago # -
Alrighty folks. So I abducted a friend to look at this with me and after much head banging he realized that the structures were being aligned by words. A quick add of the packed attribute to the end of all the structures and everything works as it should. Big thanks to my boy Jason Von Nieda!
I only tested it to read a file, and quite frankly Im done looking at it for the day :)
The svn and download can be found here, enjoy.
http://code.google.com/p/maple-sdfat/Jason
Posted 4 years ago # -
oh boy, this is most excellent!
Posted 4 years ago # -
added to the wiki: http://wiki.leaflabs.com/index.php?title=Libraries
Posted 4 years ago # -
@Jason,
Thanks very much for your work i test it on my board MP32 : http://www.virtualrobotix.com/page/multipilot32-1 and it work fine.
I have only a problem after i open the file and write a string . In the first write i waiting a lot before to start to write the sd. I try a 2 gbyte SD i wait until 30 second. I think the problem is when searching contiguos block .. some idea ?
Other question i have a version of board that don't use SPI but SDIO do you have some patch for this ?
Best
RobertoPosted 4 years ago # -
Starter question can i use this shield with the maple board ,: http://nicegear.co.nz/arduino-shields/arduino-microsd-shield/ , I had it working perfectly with arduino, and i have just started playing with maple, do i need to remove the level shifter on the sd shield to not damage the maple board?
Posted 4 years ago #
Reply »
You must log in to post.