Incorporating ladyada's SD changes (multifile & directories).

Grabbed a patch from this repository: https://github.com/adafruit/SD and updated it for Arduino 1.0 (using Arduino.h instead of WProgram.h).
This commit is contained in:
David A. Mellis 2011-04-18 17:58:54 -04:00
parent 9f412a2628
commit 059bc5f022
6 changed files with 472 additions and 16 deletions

View File

@ -14,52 +14,130 @@
#include <SD.h>
/* for debugging file open/close leaks
uint8_t nfilecount=0;
*/
File::File(SdFile f, char *n) {
// oh man you are kidding me, new() doesnt exist? Ok we do it by hand!
_file = (SdFile *)malloc(sizeof(SdFile));
if (_file) {
memcpy(_file, &f, sizeof(SdFile));
strncpy(_name, n, 12);
_name[12] = 0;
/* for debugging file open/close leaks
nfilecount++;
Serial.print("Created \"");
Serial.print(n);
Serial.print("\": ");
Serial.println(nfilecount, DEC);
*/
}
}
File::File(void) {
_file = 0;
_name[0] = 0;
//Serial.print("Created empty file object");
}
File::~File(void) {
// Serial.print("Deleted file object");
}
// returns a pointer to the file name
char *File::name(void) {
return _name;
}
// a directory is a special type of file
boolean File::isDirectory(void) {
return (_file && _file->isDir());
}
void File::write(uint8_t val) {
SD.file.write(val);
if (_file)
_file->write(val);
}
void File::write(const char *str) {
SD.file.write(str);
if (_file)
_file->write(str);
}
void File::write(const uint8_t *buf, size_t size) {
SD.file.write(buf, size);
if (_file)
_file->write(buf, size);
}
int File::peek() {
char c = SD.file.read();
if (c != -1) SD.file.seekCur(-1);
if (! _file)
return 0;
int c = _file->read();
if (c != -1) _file->seekCur(-1);
return c;
}
int File::read() {
return SD.file.read();
if (_file)
return _file->read();
return -1;
}
// buffered read for more efficient, high speed reading
int File::read(void *buf, uint16_t nbyte) {
if (_file)
return _file->read(buf, nbyte);
return 0;
}
int File::available() {
if (! _file) return 0;
return size() - position();
}
void File::flush() {
SD.file.sync();
if (_file)
_file->sync();
}
boolean File::seek(uint32_t pos) {
return SD.file.seekSet(pos);
if (! _file) return false;
return _file->seekSet(pos);
}
uint32_t File::position() {
return SD.file.curPosition();
if (! _file) return -1;
return _file->curPosition();
}
uint32_t File::size() {
return SD.file.fileSize();
if (! _file) return 0;
return _file->fileSize();
}
void File::close() {
SD.file.close();
if (_file) {
_file->close();
free(_file);
_file = 0;
/* for debugging file open/close leaks
nfilecount--;
Serial.print("Deleted ");
Serial.println(nfilecount, DEC);
*/
}
}
File::operator bool() {
return SD.file.isOpen();
if (_file)
return _file->isOpen();
return false;
}

View File

@ -9,3 +9,5 @@ License: GNU General Public License V3
(C) Copyright 2010 SparkFun Electronics
Now better than ever with optimization, multiple file support, directory handling, etc - ladyada!

View File

@ -275,10 +275,10 @@ boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent,
}
/*
boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
/*
Callback used to open a file specified by a filepath that may
specify one or more directories above it.
@ -293,7 +293,6 @@ boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
Returns false once the file has been opened--to prevent the traversal
from descending further. (This may be unnecessary.)
*/
if (isLastComponent) {
SDClass *p_SD = static_cast<SDClass*>(object);
p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
@ -305,6 +304,8 @@ boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
}
return true;
}
*/
boolean callback_remove(SdFile& parentDir, char *filePathComponent,
@ -345,6 +346,64 @@ boolean SDClass::begin(uint8_t csPin) {
}
// this little helper is used to traverse paths
SdFile SDClass::getParentDir(char *filepath, int *index) {
// get parent directory
SdFile d1 = root; // start with the mostparent, root!
SdFile d2;
// we'll use the pointers to swap between the two objects
SdFile *parent = &d1;
SdFile *subdir = &d2;
char *origpath = filepath;
while (strchr(filepath, '/')) {
// get rid of leading /'s
if (filepath[0] == '/') {
filepath++;
continue;
}
if (! strchr(filepath, '/')) {
// it was in the root directory, so leave now
break;
}
// extract just the name of the next subdirectory
uint8_t idx = strchr(filepath, '/') - filepath;
if (idx > 12)
idx = 12; // dont let them specify long names
char subdirname[13];
strncpy(subdirname, filepath, idx);
subdirname[idx] = 0;
// close the subdir (we reuse them) if open
subdir->close();
if (! subdir->open(parent, subdirname, O_READ)) {
// failed to open one of the subdirectories
return SdFile();
}
// move forward to the next subdirectory
filepath += idx;
// we reuse the objects, close it.
parent->close();
// swap the pointers
SdFile *t = parent;
parent = subdir;
subdir = t;
}
*index = (int)(filepath - origpath);
// parent is now the parent diretory of the file!
return *parent;
}
File SDClass::open(char *filepath, uint8_t mode) {
/*
@ -369,6 +428,72 @@ File SDClass::open(char *filepath, uint8_t mode) {
*/
int pathidx;
// do the interative search
SdFile parentdir = getParentDir(filepath, &pathidx);
// no more subdirs!
filepath += pathidx;
if (! filepath[0]) {
// it was the directory itself!
return File(parentdir, "/");
}
// Open the file itself
SdFile file;
// failed to open a subdir!
if (!parentdir.isOpen())
return File();
// there is a special case for the Root directory since its a static dir
if (parentdir.isRoot()) {
if ( ! file.open(SD.root, filepath, mode)) {
// failed to open the file :(
return File();
}
// dont close the root!
} else {
if ( ! file.open(parentdir, filepath, mode)) {
return File();
}
// close the parent
parentdir.close();
}
if (mode & (O_APPEND | O_WRITE))
file.seekSet(file.fileSize());
return File(file, filepath);
}
/*
File SDClass::open(char *filepath, uint8_t mode) {
//
Open the supplied file path for reading or writing.
The file content can be accessed via the `file` property of
the `SDClass` object--this property is currently
a standard `SdFile` object from `sdfatlib`.
Defaults to read only.
If `write` is true, default action (when `append` is true) is to
append data to the end of the file.
If `append` is false then the file will be truncated first.
If the file does not exist and it is opened for writing the file
will be created.
An attempt to open a file for reading that does not exist is an
error.
//
// TODO: Allow for read&write? (Possibly not, as it requires seek.)
fileOpenMode = mode;
@ -377,6 +502,7 @@ File SDClass::open(char *filepath, uint8_t mode) {
return File();
}
*/
//boolean SDClass::close() {
@ -436,4 +562,55 @@ boolean SDClass::remove(char *filepath) {
return walkPath(filepath, root, callback_remove);
}
// allows you to recurse into a directory
File File::openNextFile(uint8_t mode) {
dir_t p;
//Serial.print("\t\treading dir...");
while (_file->readDir(&p) > 0) {
// done if past last used entry
if (p.name[0] == DIR_NAME_FREE) {
//Serial.println("end");
return File();
}
// skip deleted entry and entries for . and ..
if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') {
//Serial.println("dots");
continue;
}
// only list subdirectories and files
if (!DIR_IS_FILE_OR_SUBDIR(&p)) {
//Serial.println("notafile");
continue;
}
// print file name with possible blank fill
SdFile f;
char name[13];
_file->dirName(p, name);
//Serial.print("try to open file ");
//Serial.println(name);
if (f.open(_file, name, mode)) {
//Serial.println("OK!");
return File(f, name);
} else {
//Serial.println("ugh");
return File();
}
}
//Serial.println("nothing");
return File();
}
void File::rewindDirectory(void) {
if (isDirectory())
_file->rewind();
}
SDClass SD;

View File

@ -24,7 +24,14 @@
#define FILE_WRITE (O_READ | O_WRITE | O_CREAT | O_SYNC)
class File : public Stream {
private:
char _name[13]; // our name
SdFile *_file; // underlying file pointer
public:
File(SdFile f, char *name); // wraps an underlying SdFile
File(void); // 'empty' constructor
~File(void); // destructor
virtual void write(uint8_t);
virtual void write(const char *str);
virtual void write(const uint8_t *buf, size_t size);
@ -32,11 +39,17 @@ public:
virtual int peek();
virtual int available();
virtual void flush();
int read(void *buf, uint16_t nbyte);
boolean seek(uint32_t pos);
uint32_t position();
uint32_t size();
void close();
operator bool();
char * name();
boolean isDirectory(void);
File openNextFile(uint8_t mode = O_RDONLY);
void rewindDirectory(void);
};
class SDClass {
@ -47,6 +60,8 @@ private:
SdVolume volume;
SdFile root;
// my quick&dirty iterator, should be replaced
SdFile getParentDir(char *filepath, int *indx);
public:
// This needs to be called to set up the connection to the SD card
// before other methods are used.
@ -70,7 +85,6 @@ public:
boolean rmdir(char *filepath);
private:
SdFile file;
// This is used to determine the mode used to open a file
// it's here because it's the easiest place to pass the

View File

@ -0,0 +1,108 @@
/*
SD card test
This example shows how use the utility libraries on which the'
SD library is based in order to get info about your SD card.
Very useful for testing a card when you're not sure whether its working or not.
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
** CS - depends on your SD card shield or module
created 28 Mar 2011
by Limor Fried
*/
// include the SD library:
#include <SD.h>
// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
const int chipSelect = 8;
void setup()
{
Serial.begin(9600);
Serial.print("\nInitializing SD card...");
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
// Note that even if it's not used as the CS pin, the hardware SS pin
// (10 on most Arduino boards, 53 on the Mega) must be left as an output
// or the SD library functions will not work.
pinMode(10, OUTPUT); // change this to 53 on a mega
// we'll use the initialization code from the utility libraries
// since we're just testing if the card is working!
if (!card.init(SPI_HALF_SPEED, chipSelect)) {
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card is inserted?");
Serial.println("* Is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
return;
} else {
Serial.println("Wiring is correct and a card is present.");
}
// print the type of card
Serial.print("\nCard type: ");
switch(card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Unknown");
}
// Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
if (!volume.init(card)) {
Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
return;
}
// print the type and size of the first FAT-type volume
long volumesize;
Serial.print("\nVolume type is FAT");
Serial.println(volume.fatType(), DEC);
Serial.println();
volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
volumesize *= volume.clusterCount(); // we'll have a lot of clusters
volumesize *= 512; // SD card blocks are always 512 bytes
Serial.print("Volume size (bytes): ");
Serial.println(volumesize);
Serial.print("Volume size (Kbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Mbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.println("\nFiles found on the card (name, date and size in bytes): ");
root.openRoot(volume);
// list all files in the card with date and size
root.ls(LS_R | LS_DATE | LS_SIZE);
}
void loop(void) {
}

View File

@ -0,0 +1,77 @@
/*
SD card basic file example
This example shows how to create and destroy an SD card file
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4
created Nov 2010
by David A. Mellis
updated 2 Dec 2010
by Tom Igoe
This example code is in the public domain.
*/
#include <SD.h>
File root;
void setup()
{
Serial.begin(9600);
Serial.print("Initializing SD card...");
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
// Note that even if it's not used as the CS pin, the hardware SS pin
// (10 on most Arduino boards, 53 on the Mega) must be left as an output
// or the SD library functions will not work.
pinMode(10, OUTPUT);
if (!SD.begin(10)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
root = SD.open("/");
printDirectory(root, 0);
Serial.println("done!");
}
void loop()
{
// nothing happens after setup finishes.
}
void printDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
//Serial.println("**nomorefiles**");
break;
}
for (uint8_t i=0; i<numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs+1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
}
}