mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-01 12:24:14 +01:00
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:
parent
9f412a2628
commit
059bc5f022
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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!
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
SDClass SD;
|
||||
|
||||
// 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;
|
||||
|
@ -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
|
||||
|
108
libraries/SD/examples/CardInfo/CardInfo.pde
Normal file
108
libraries/SD/examples/CardInfo/CardInfo.pde
Normal 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) {
|
||||
|
||||
}
|
77
libraries/SD/examples/listfiles/listfiles.pde
Normal file
77
libraries/SD/examples/listfiles/listfiles.pde
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user