Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First Commit

  • Loading branch information...
commit bd7200b2a2c0a519969926bafdb6252c89245183 0 parents
@infomaniac50 authored
188 Logger.cpp
@@ -0,0 +1,188 @@
+/*
+ Logger.cpp - Library for formatting and writing data to various outputs.
+ Created by Derek Chafin, December 5, 2011
+ Released into the public domain.
+*/
+
+#include "Arduino.h"
+#include "Logger.h"
+#include <LoggerSD.h>
+
+#define CS 4
+
+
+Logger::Logger(log_outputs output, data_formats format, boolean add_line_break)
+{
+ _output = output;
+ _format = format;
+ _add_line_break = add_line_break;
+ _filename = "LOGGER00.CSV";
+}
+
+int Logger::init()
+{
+ int result;
+
+ switch(_output)
+ {
+ case SD_OUTPUT:
+ result = initSD();
+ break;
+ case SERIAL_OUTPUT:
+ result = initSerial();
+ break;
+ }
+
+ return result;
+}
+
+int Logger::initSD()
+{
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+ //Hardware SS pin on the Arduino Mega is pin 53
+ pinMode(53, OUTPUT);
+#else
+ pinMode(10, OUTPUT);
+#endif
+
+ if (!SD.begin(CS)) {
+ return 1;
+ }
+
+ // create a new file
+ for (uint8_t i = 0; i < 100; i++) {
+ _filename[6] = i/10 + '0';
+ _filename[7] = i%10 + '0';
+ if (!SD.exists(_filename)) {
+ break; // leave the loop!
+ }
+ }
+
+ _file = SD.open(_filename, FILE_WRITE);
+
+ return 0;
+}
+
+int Logger::initSerial()
+{
+ return 0;
+}
+
+void Logger::checkAddLineBreak()
+{
+ if (_format != BINARY)
+ {
+ if (_add_line_break)
+ {
+ switch(_output)
+ {
+ case SD_OUTPUT:
+ _file.println("");
+ break;
+ case SERIAL_OUTPUT:
+ Serial.println("");
+ break;
+ }
+ }
+ else
+ {
+ switch(_output)
+ {
+ case SD_OUTPUT:
+ _file.print(',');
+ break;
+ case SERIAL_OUTPUT:
+ Serial.print(',');
+ break;
+ }
+ }
+
+ }
+}
+
+void Logger::logAsciiByte(byte data)
+{
+ switch(_output)
+ {
+ case SD_OUTPUT:
+ _file.print(data, DEC);
+ break;
+ case SERIAL_OUTPUT:
+ Serial.print(data, DEC);
+ break;
+ }
+}
+
+void Logger::logBinary(byte data)
+{
+ switch(_output)
+ {
+ case SD_OUTPUT:
+ _file.write(data);
+ break;
+ case SERIAL_OUTPUT:
+ Serial.write(data);
+ break;
+ }
+}
+
+void Logger::logAsciiBoolean(byte data)
+{
+ for (int i=0; i < 8; i++)
+ {
+ switch(_output)
+ {
+ case SD_OUTPUT:
+ _file.print(bitRead(data, i), DEC);
+ break;
+ case SERIAL_OUTPUT:
+ Serial.print(bitRead(data, i), DEC);
+ break;
+ }
+ }
+}
+
+void Logger::logData(byte data)
+{
+ static int sync_counter = 0;
+
+ switch(_format)
+ {
+ case ASCII_BYTE:
+ logAsciiByte(data);
+ break;
+ case BINARY:
+ logBinary(data);
+ break;
+ case ASCII_BOOL:
+ logAsciiBoolean(data);
+ break;
+ }
+
+ checkAddLineBreak();
+
+ if (_output == SERIAL_OUTPUT)
+ {
+ Serial.flush();
+ }
+
+ if (_output == SD_OUTPUT)
+ {
+ sync_counter++;
+ if (sync_counter >= 2048)
+ {
+ _file.flush();
+ sync_counter = 0;
+ }
+ }
+}
+
+// void Logger::logData(int data)
+// {
+
+// }
+
+// void Logger::logData(long data)
+// {
+
+// }
47 Logger.h
@@ -0,0 +1,47 @@
+/*
+ Logger.h - Library for formatting and writing data to various outputs.
+ Created by Derek Chafin, December 5, 2011
+ Released into the public domain.
+*/
+
+#ifndef Logger_h
+#define Logger_h
+
+#include "Arduino.h"
+#include <LoggerSD.h>
+
+enum log_outputs
+{
+ SERIAL_OUTPUT,
+ SD_OUTPUT
+};
+
+enum data_formats
+{
+ ASCII_BYTE,
+ BINARY,
+ ASCII_BOOL
+};
+
+class Logger
+{
+ public:
+ Logger(log_outputs output, data_formats format, boolean add_line_break);
+ void logData(byte data);
+ int init();
+// void logData(int data);
+// void logData(long data);
+ private:
+ int initSD();
+ int initSerial();
+ void checkAddLineBreak();
+ void logAsciiByte(byte data);
+ void logBinary(byte data);
+ void logAsciiBoolean(byte data);
+ char* _filename;
+ File _file;
+ log_outputs _output;
+ data_formats _format;
+ boolean _add_line_break;
+};
+#endif
150 LoggerFile.cpp
@@ -0,0 +1,150 @@
+/*
+
+ SD - a slightly more friendly wrapper for sdfatlib
+
+ This library aims to expose a subset of SD card functionality
+ in the form of a higher level "wrapper" object.
+
+ License: GNU General Public License V3
+ (Because sdfatlib is licensed with this.)
+
+ (C) Copyright 2010 SparkFun Electronics
+
+ */
+
+#include <LoggerSD.h>
+
+/* for debugging file open/close leaks
+ uint8_t nfilecount=0;
+*/
+
+File::File(SdFile f, const 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());
+}
+
+
+size_t File::write(uint8_t val) {
+ return write(&val, 1);
+}
+
+size_t File::write(const uint8_t *buf, size_t size) {
+ size_t t;
+ if (!_file) {
+ setWriteError();
+ return 0;
+ }
+ _file->clearWriteError();
+ t = _file->write(buf, size);
+ if (_file->getWriteError()) {
+ setWriteError();
+ return 0;
+ }
+ return t;
+}
+
+int File::peek() {
+ if (! _file)
+ return 0;
+
+ int c = _file->read();
+ if (c != -1) _file->seekCur(-1);
+ return c;
+}
+
+int 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;
+
+ uint32_t n = size() - position();
+
+ return n > 0X7FFF ? 0X7FFF : n;
+}
+
+void File::flush() {
+ if (_file)
+ _file->sync();
+}
+
+boolean File::seek(uint32_t pos) {
+ if (! _file) return false;
+
+ return _file->seekSet(pos);
+}
+
+uint32_t File::position() {
+ if (! _file) return -1;
+ return _file->curPosition();
+}
+
+uint32_t File::size() {
+ if (! _file) return 0;
+ return _file->fileSize();
+}
+
+void 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() {
+ if (_file)
+ return _file->isOpen();
+ return false;
+}
+
616 LoggerSD.cpp
@@ -0,0 +1,616 @@
+/*
+
+ SD - a slightly more friendly wrapper for sdfatlib
+
+ This library aims to expose a subset of SD card functionality
+ in the form of a higher level "wrapper" object.
+
+ License: GNU General Public License V3
+ (Because sdfatlib is licensed with this.)
+
+ (C) Copyright 2010 SparkFun Electronics
+
+
+ This library provides four key benefits:
+
+ * Including `SD.h` automatically creates a global
+ `SD` object which can be interacted with in a similar
+ manner to other standard global objects like `Serial` and `Ethernet`.
+
+ * Boilerplate initialisation code is contained in one method named
+ `begin` and no further objects need to be created in order to access
+ the SD card.
+
+ * Calls to `open` can supply a full path name including parent
+ directories which simplifies interacting with files in subdirectories.
+
+ * Utility methods are provided to determine whether a file exists
+ and to create a directory heirarchy.
+
+
+ Note however that not all functionality provided by the underlying
+ sdfatlib library is exposed.
+
+ */
+
+/*
+
+ Implementation Notes
+
+ In order to handle multi-directory path traversal, functionality that
+ requires this ability is implemented as callback functions.
+
+ Individual methods call the `walkPath` function which performs the actual
+ directory traversal (swapping between two different directory/file handles
+ along the way) and at each level calls the supplied callback function.
+
+ Some types of functionality will take an action at each level (e.g. exists
+ or make directory) which others will only take an action at the bottom
+ level (e.g. open).
+
+ */
+
+#include "LoggerSD.h"
+
+// Used by `getNextPathComponent`
+#define MAX_COMPONENT_LEN 12 // What is max length?
+#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1
+
+bool getNextPathComponent(char *path, unsigned int *p_offset,
+ char *buffer) {
+ /*
+
+ Parse individual path components from a path.
+
+ e.g. after repeated calls '/foo/bar/baz' will be split
+ into 'foo', 'bar', 'baz'.
+
+ This is similar to `strtok()` but copies the component into the
+ supplied buffer rather than modifying the original string.
+
+
+ `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.
+
+ `p_offset` needs to point to an integer of the offset at
+ which the previous path component finished.
+
+ Returns `true` if more components remain.
+
+ Returns `false` if this is the last component.
+ (This means path ended with 'foo' or 'foo/'.)
+
+ */
+
+ // TODO: Have buffer local to this function, so we know it's the
+ // correct length?
+
+ int bufferOffset = 0;
+
+ int offset = *p_offset;
+
+ // Skip root or other separator
+ if (path[offset] == '/') {
+ offset++;
+ }
+
+ // Copy the next next path segment
+ while (bufferOffset < MAX_COMPONENT_LEN
+ && (path[offset] != '/')
+ && (path[offset] != '\0')) {
+ buffer[bufferOffset++] = path[offset++];
+ }
+
+ buffer[bufferOffset] = '\0';
+
+ // Skip trailing separator so we can determine if this
+ // is the last component in the path or not.
+ if (path[offset] == '/') {
+ offset++;
+ }
+
+ *p_offset = offset;
+
+ return (path[offset] != '\0');
+}
+
+
+
+boolean walkPath(char *filepath, SdFile& parentDir,
+ boolean (*callback)(SdFile& parentDir,
+ char *filePathComponent,
+ boolean isLastComponent,
+ void *object),
+ void *object = NULL) {
+ /*
+
+ When given a file path (and parent directory--normally root),
+ this function traverses the directories in the path and at each
+ level calls the supplied callback function while also providing
+ the supplied object for context if required.
+
+ e.g. given the path '/foo/bar/baz'
+ the callback would be called at the equivalent of
+ '/foo', '/foo/bar' and '/foo/bar/baz'.
+
+ The implementation swaps between two different directory/file
+ handles as it traverses the directories and does not use recursion
+ in an attempt to use memory efficiently.
+
+ If a callback wishes to stop the directory traversal it should
+ return false--in this case the function will stop the traversal,
+ tidy up and return false.
+
+ If a directory path doesn't exist at some point this function will
+ also return false and not subsequently call the callback.
+
+ If a directory path specified is complete, valid and the callback
+ did not indicate the traversal should be interrupted then this
+ function will return true.
+
+ */
+
+
+ SdFile subfile1;
+ SdFile subfile2;
+
+ char buffer[PATH_COMPONENT_BUFFER_LEN];
+
+ unsigned int offset = 0;
+
+ SdFile *p_parent;
+ SdFile *p_child;
+
+ SdFile *p_tmp_sdfile;
+
+ p_child = &subfile1;
+
+ p_parent = &parentDir;
+
+ while (true) {
+
+ boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);
+
+ boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);
+
+ if (!shouldContinue) {
+ // TODO: Don't repeat this code?
+ // If it's one we've created then we
+ // don't need the parent handle anymore.
+ if (p_parent != &parentDir) {
+ (*p_parent).close();
+ }
+ return false;
+ }
+
+ if (!moreComponents) {
+ break;
+ }
+
+ boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);
+
+ // If it's one we've created then we
+ // don't need the parent handle anymore.
+ if (p_parent != &parentDir) {
+ (*p_parent).close();
+ }
+
+ // Handle case when it doesn't exist and we can't continue...
+ if (exists) {
+ // We alternate between two file handles as we go down
+ // the path.
+ if (p_parent == &parentDir) {
+ p_parent = &subfile2;
+ }
+
+ p_tmp_sdfile = p_parent;
+ p_parent = p_child;
+ p_child = p_tmp_sdfile;
+ } else {
+ return false;
+ }
+ }
+
+ if (p_parent != &parentDir) {
+ (*p_parent).close(); // TODO: Return/ handle different?
+ }
+
+ return true;
+}
+
+
+
+/*
+
+ The callbacks used to implement various functionality follow.
+
+ Each callback is supplied with a parent directory handle,
+ character string with the name of the current file path component,
+ a flag indicating if this component is the last in the path and
+ a pointer to an arbitrary object used for context.
+
+ */
+
+boolean callback_pathExists(SdFile& parentDir, char *filePathComponent,
+ boolean isLastComponent, void *object) {
+ /*
+
+ Callback used to determine if a file/directory exists in parent
+ directory.
+
+ Returns true if file path exists.
+
+ */
+ SdFile child;
+
+ boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);
+
+ if (exists) {
+ child.close();
+ }
+
+ return exists;
+}
+
+
+
+boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent,
+ boolean isLastComponent, void *object) {
+ /*
+
+ Callback used to create a directory in the parent directory if
+ it does not already exist.
+
+ Returns true if a directory was created or it already existed.
+
+ */
+ boolean result = false;
+ SdFile child;
+
+ result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
+ if (!result) {
+ result = child.makeDir(parentDir, filePathComponent);
+ }
+
+ return result;
+}
+
+
+ /*
+
+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.
+
+ Expects the context object to be an instance of `SDClass` and
+ will use the `file` property of the instance to open the requested
+ file/directory with the associated file open mode property.
+
+ Always returns true if the directory traversal hasn't reached the
+ bottom of the directory heirarchy.
+
+ 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);
+ if (p_SD->fileOpenMode == FILE_WRITE) {
+ p_SD->file.seekSet(p_SD->file.fileSize());
+ }
+ // TODO: Return file open result?
+ return false;
+ }
+ return true;
+}
+ */
+
+
+
+boolean callback_remove(SdFile& parentDir, char *filePathComponent,
+ boolean isLastComponent, void *object) {
+ if (isLastComponent) {
+ return SdFile::remove(parentDir, filePathComponent);
+ }
+ return true;
+}
+
+boolean callback_rmdir(SdFile& parentDir, char *filePathComponent,
+ boolean isLastComponent, void *object) {
+ if (isLastComponent) {
+ SdFile f;
+ if (!f.open(parentDir, filePathComponent, O_READ)) return false;
+ return f.rmDir();
+ }
+ return true;
+}
+
+
+
+/* Implementation of class used to create `SDCard` object. */
+
+
+
+boolean SDClass::begin(uint8_t csPin) {
+ /*
+
+ Performs the initialisation required by the sdfatlib library.
+
+ Return true if initialization succeeds, false otherwise.
+
+ */
+ return card.init(SPI_HALF_SPEED, csPin) &&
+ volume.init(card) &&
+ root.openRoot(volume);
+}
+
+
+
+// this little helper is used to traverse paths
+SdFile SDClass::getParentDir(const 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;
+
+ const 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(const 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.
+
+ */
+
+ 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;
+ walkPath(filepath, root, callback_openPath, this);
+
+ return File();
+
+}
+*/
+
+
+//boolean SDClass::close() {
+// /*
+//
+// Closes the file opened by the `open` method.
+//
+// */
+// file.close();
+//}
+
+
+boolean SDClass::exists(char *filepath) {
+ /*
+
+ Returns true if the supplied file path exists.
+
+ */
+ return walkPath(filepath, root, callback_pathExists);
+}
+
+
+//boolean SDClass::exists(char *filepath, SdFile& parentDir) {
+// /*
+//
+// Returns true if the supplied file path rooted at `parentDir`
+// exists.
+//
+// */
+// return walkPath(filepath, parentDir, callback_pathExists);
+//}
+
+
+boolean SDClass::mkdir(char *filepath) {
+ /*
+
+ Makes a single directory or a heirarchy of directories.
+
+ A rough equivalent to `mkdir -p`.
+
+ */
+ return walkPath(filepath, root, callback_makeDirPath);
+}
+
+boolean SDClass::rmdir(char *filepath) {
+ /*
+
+ Makes a single directory or a heirarchy of directories.
+
+ A rough equivalent to `mkdir -p`.
+
+ */
+ return walkPath(filepath, root, callback_rmdir);
+}
+
+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;
103 LoggerSD.h
@@ -0,0 +1,103 @@
+/*
+
+ SD - a slightly more friendly wrapper for sdfatlib
+
+ This library aims to expose a subset of SD card functionality
+ in the form of a higher level "wrapper" object.
+
+ License: GNU General Public License V3
+ (Because sdfatlib is licensed with this.)
+
+ (C) Copyright 2010 SparkFun Electronics
+
+ */
+
+#ifndef __SD_H__
+#define __SD_H__
+
+#include <Arduino.h>
+
+#include <utility/SdFat.h>
+#include <utility/SdFatUtil.h>
+
+#define FILE_READ O_READ
+#define FILE_WRITE (O_READ | O_WRITE | O_CREAT)
+
+class File : public Stream {
+ private:
+ char _name[13]; // our name
+ SdFile *_file; // underlying file pointer
+
+public:
+ File(SdFile f, const char *name); // wraps an underlying SdFile
+ File(void); // 'empty' constructor
+ ~File(void); // destructor
+ virtual size_t write(uint8_t);
+ virtual size_t write(const uint8_t *buf, size_t size);
+ virtual int read();
+ 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);
+
+ using Print::write;
+};
+
+class SDClass {
+
+private:
+ // These are required for initialisation and use of sdfatlib
+ Sd2Card card;
+ SdVolume volume;
+ SdFile root;
+
+ // my quick&dirty iterator, should be replaced
+ SdFile getParentDir(const char *filepath, int *indx);
+public:
+ // This needs to be called to set up the connection to the SD card
+ // before other methods are used.
+ boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN);
+
+ // Open the specified file/directory with the supplied mode (e.g. read or
+ // write, etc). Returns a File object for interacting with the file.
+ // Note that currently only one file can be open at a time.
+ File open(const char *filename, uint8_t mode = FILE_READ);
+
+ // Methods to determine if the requested file path exists.
+ boolean exists(char *filepath);
+
+ // Create the requested directory heirarchy--if intermediate directories
+ // do not exist they will be created.
+ boolean mkdir(char *filepath);
+
+ // Delete the file.
+ boolean remove(char *filepath);
+
+ boolean rmdir(char *filepath);
+
+private:
+
+ // This is used to determine the mode used to open a file
+ // it's here because it's the easiest place to pass the
+ // information through the directory walking function. But
+ // it's probably not the best place for it.
+ // It shouldn't be set directly--it is set via the parameters to `open`.
+ int fileOpenMode;
+
+ friend class File;
+ friend boolean callback_openPath(SdFile&, char *, boolean, void *);
+};
+
+extern SDClass SD;
+
+#endif
5 README
@@ -0,0 +1,5 @@
+Logger - A library for formatting and writing data to various outputs.
+Created by Derek Chafin
+December 5, 2011
+
+The logger library is capable of logging to serial or and sd card with minimal user code.
24 keywords.txt
@@ -0,0 +1,24 @@
+#######################################
+# Syntax Coloring Map SD
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+Logger KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+init KEYWORD2
+logData KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+SERIAL_OUTPUT LITERAL1
+SD_OUTPUT LITERAL1
+ASCII_BYTE LITERAL1
+BINARY LITERAL1
+ASCII_BOOL LITERAL1
418 utility/FatStructs.h
@@ -0,0 +1,418 @@
+/* Arduino SdFat Library
+ * Copyright (C) 2009 by William Greiman
+ *
+ * This file is part of the Arduino SdFat 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 SdFat Library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#ifndef FatStructs_h
+#define FatStructs_h
+/**
+ * \file
+ * FAT file structures
+ */
+/*
+ * mostly from Microsoft document fatgen103.doc
+ * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
+ */
+//------------------------------------------------------------------------------
+/** Value for byte 510 of boot block or MBR */
+uint8_t const BOOTSIG0 = 0X55;
+/** Value for byte 511 of boot block or MBR */
+uint8_t const BOOTSIG1 = 0XAA;
+//------------------------------------------------------------------------------
+/**
+ * \struct partitionTable
+ * \brief MBR partition table entry
+ *
+ * A partition table entry for a MBR formatted storage device.
+ * The MBR partition table has four entries.
+ */
+struct partitionTable {
+ /**
+ * Boot Indicator . Indicates whether the volume is the active
+ * partition. Legal values include: 0X00. Do not use for booting.
+ * 0X80 Active partition.
+ */
+ uint8_t boot;
+ /**
+ * Head part of Cylinder-head-sector address of the first block in
+ * the partition. Legal values are 0-255. Only used in old PC BIOS.
+ */
+ uint8_t beginHead;
+ /**
+ * Sector part of Cylinder-head-sector address of the first block in
+ * the partition. Legal values are 1-63. Only used in old PC BIOS.
+ */
+ unsigned beginSector : 6;
+ /** High bits cylinder for first block in partition. */
+ unsigned beginCylinderHigh : 2;
+ /**
+ * Combine beginCylinderLow with beginCylinderHigh. Legal values
+ * are 0-1023. Only used in old PC BIOS.
+ */
+ uint8_t beginCylinderLow;
+ /**
+ * Partition type. See defines that begin with PART_TYPE_ for
+ * some Microsoft partition types.
+ */
+ uint8_t type;
+ /**
+ * head part of cylinder-head-sector address of the last sector in the
+ * partition. Legal values are 0-255. Only used in old PC BIOS.
+ */
+ uint8_t endHead;
+ /**
+ * Sector part of cylinder-head-sector address of the last sector in
+ * the partition. Legal values are 1-63. Only used in old PC BIOS.
+ */
+ unsigned endSector : 6;
+ /** High bits of end cylinder */
+ unsigned endCylinderHigh : 2;
+ /**
+ * Combine endCylinderLow with endCylinderHigh. Legal values
+ * are 0-1023. Only used in old PC BIOS.
+ */
+ uint8_t endCylinderLow;
+ /** Logical block address of the first block in the partition. */
+ uint32_t firstSector;
+ /** Length of the partition, in blocks. */
+ uint32_t totalSectors;
+};
+/** Type name for partitionTable */
+typedef struct partitionTable part_t;
+//------------------------------------------------------------------------------
+/**
+ * \struct masterBootRecord
+ *
+ * \brief Master Boot Record
+ *
+ * The first block of a storage device that is formatted with a MBR.
+ */
+struct masterBootRecord {
+ /** Code Area for master boot program. */
+ uint8_t codeArea[440];
+ /** Optional WindowsNT disk signature. May contain more boot code. */
+ uint32_t diskSignature;
+ /** Usually zero but may be more boot code. */
+ uint16_t usuallyZero;
+ /** Partition tables. */
+ part_t part[4];
+ /** First MBR signature byte. Must be 0X55 */
+ uint8_t mbrSig0;
+ /** Second MBR signature byte. Must be 0XAA */
+ uint8_t mbrSig1;
+};
+/** Type name for masterBootRecord */
+typedef struct masterBootRecord mbr_t;
+//------------------------------------------------------------------------------
+/**
+ * \struct biosParmBlock
+ *
+ * \brief BIOS parameter block
+ *
+ * The BIOS parameter block describes the physical layout of a FAT volume.
+ */
+struct biosParmBlock {
+ /**
+ * Count of bytes per sector. This value may take on only the
+ * following values: 512, 1024, 2048 or 4096
+ */
+ uint16_t bytesPerSector;
+ /**
+ * Number of sectors per allocation unit. This value must be a
+ * power of 2 that is greater than 0. The legal values are
+ * 1, 2, 4, 8, 16, 32, 64, and 128.
+ */
+ uint8_t sectorsPerCluster;
+ /**
+ * Number of sectors before the first FAT.
+ * This value must not be zero.
+ */
+ uint16_t reservedSectorCount;
+ /** The count of FAT data structures on the volume. This field should
+ * always contain the value 2 for any FAT volume of any type.
+ */
+ uint8_t fatCount;
+ /**
+ * For FAT12 and FAT16 volumes, this field contains the count of
+ * 32-byte directory entries in the root directory. For FAT32 volumes,
+ * this field must be set to 0. For FAT12 and FAT16 volumes, this
+ * value should always specify a count that when multiplied by 32
+ * results in a multiple of bytesPerSector. FAT16 volumes should
+ * use the value 512.
+ */
+ uint16_t rootDirEntryCount;
+ /**
+ * This field is the old 16-bit total count of sectors on the volume.
+ * This count includes the count of all sectors in all four regions
+ * of the volume. This field can be 0; if it is 0, then totalSectors32
+ * must be non-zero. For FAT32 volumes, this field must be 0. For
+ * FAT12 and FAT16 volumes, this field contains the sector count, and
+ * totalSectors32 is 0 if the total sector count fits
+ * (is less than 0x10000).
+ */
+ uint16_t totalSectors16;
+ /**
+ * This dates back to the old MS-DOS 1.x media determination and is
+ * no longer usually used for anything. 0xF8 is the standard value
+ * for fixed (non-removable) media. For removable media, 0xF0 is
+ * frequently used. Legal values are 0xF0 or 0xF8-0xFF.
+ */
+ uint8_t mediaType;
+ /**
+ * Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
+ * On FAT32 volumes this field must be 0, and sectorsPerFat32
+ * contains the FAT size count.
+ */
+ uint16_t sectorsPerFat16;
+ /** Sectors per track for interrupt 0x13. Not used otherwise. */
+ uint16_t sectorsPerTrtack;
+ /** Number of heads for interrupt 0x13. Not used otherwise. */
+ uint16_t headCount;
+ /**
+ * Count of hidden sectors preceding the partition that contains this
+ * FAT volume. This field is generally only relevant for media
+ * visible on interrupt 0x13.
+ */
+ uint32_t hidddenSectors;
+ /**
+ * This field is the new 32-bit total count of sectors on the volume.
+ * This count includes the count of all sectors in all four regions
+ * of the volume. This field can be 0; if it is 0, then
+ * totalSectors16 must be non-zero.
+ */
+ uint32_t totalSectors32;
+ /**
+ * Count of sectors occupied by one FAT on FAT32 volumes.
+ */
+ uint32_t sectorsPerFat32;
+ /**
+ * This field is only defined for FAT32 media and does not exist on
+ * FAT12 and FAT16 media.
+ * Bits 0-3 -- Zero-based number of active FAT.
+ * Only valid if mirroring is disabled.
+ * Bits 4-6 -- Reserved.
+ * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
+ * -- 1 means only one FAT is active; it is the one referenced in bits 0-3.
+ * Bits 8-15 -- Reserved.
+ */
+ uint16_t fat32Flags;
+ /**
+ * FAT32 version. High byte is major revision number.
+ * Low byte is minor revision number. Only 0.0 define.
+ */
+ uint16_t fat32Version;
+ /**
+ * Cluster number of the first cluster of the root directory for FAT32.
+ * This usually 2 but not required to be 2.
+ */
+ uint32_t fat32RootCluster;
+ /**
+ * Sector number of FSINFO structure in the reserved area of the
+ * FAT32 volume. Usually 1.
+ */
+ uint16_t fat32FSInfo;
+ /**
+ * If non-zero, indicates the sector number in the reserved area
+ * of the volume of a copy of the boot record. Usually 6.
+ * No value other than 6 is recommended.
+ */
+ uint16_t fat32BackBootBlock;
+ /**
+ * Reserved for future expansion. Code that formats FAT32 volumes
+ * should always set all of the bytes of this field to 0.
+ */
+ uint8_t fat32Reserved[12];
+};
+/** Type name for biosParmBlock */
+typedef struct biosParmBlock bpb_t;
+//------------------------------------------------------------------------------
+/**
+ * \struct fat32BootSector
+ *
+ * \brief Boot sector for a FAT16 or FAT32 volume.
+ *
+ */
+struct fat32BootSector {
+ /** X86 jmp to boot program */
+ uint8_t jmpToBootCode[3];
+ /** informational only - don't depend on it */
+ char oemName[8];
+ /** BIOS Parameter Block */
+ bpb_t bpb;
+ /** for int0x13 use value 0X80 for hard drive */
+ uint8_t driveNumber;
+ /** used by Windows NT - should be zero for FAT */
+ uint8_t reserved1;
+ /** 0X29 if next three fields are valid */
+ uint8_t bootSignature;
+ /** usually generated by combining date and time */
+ uint32_t volumeSerialNumber;
+ /** should match volume label in root dir */
+ char volumeLabel[11];
+ /** informational only - don't depend on it */
+ char fileSystemType[8];
+ /** X86 boot code */
+ uint8_t bootCode[420];
+ /** must be 0X55 */
+ uint8_t bootSectorSig0;
+ /** must be 0XAA */
+ uint8_t bootSectorSig1;
+};
+//------------------------------------------------------------------------------
+// End Of Chain values for FAT entries
+/** FAT16 end of chain value used by Microsoft. */
+uint16_t const FAT16EOC = 0XFFFF;
+/** Minimum value for FAT16 EOC. Use to test for EOC. */
+uint16_t const FAT16EOC_MIN = 0XFFF8;
+/** FAT32 end of chain value used by Microsoft. */
+uint32_t const FAT32EOC = 0X0FFFFFFF;
+/** Minimum value for FAT32 EOC. Use to test for EOC. */
+uint32_t const FAT32EOC_MIN = 0X0FFFFFF8;
+/** Mask a for FAT32 entry. Entries are 28 bits. */
+uint32_t const FAT32MASK = 0X0FFFFFFF;
+
+/** Type name for fat32BootSector */
+typedef struct fat32BootSector fbs_t;
+//------------------------------------------------------------------------------
+/**
+ * \struct directoryEntry
+ * \brief FAT short directory entry
+ *
+ * Short means short 8.3 name, not the entry size.
+ *
+ * Date Format. A FAT directory entry date stamp is a 16-bit field that is
+ * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
+ * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
+ * 16-bit word):
+ *
+ * Bits 9-15: Count of years from 1980, valid value range 0-127
+ * inclusive (1980-2107).
+ *
+ * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
+ *
+ * Bits 0-4: Day of month, valid value range 1-31 inclusive.
+ *
+ * Time Format. A FAT directory entry time stamp is a 16-bit field that has
+ * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
+ * 16-bit word, bit 15 is the MSB of the 16-bit word).
+ *
+ * Bits 11-15: Hours, valid value range 0-23 inclusive.
+ *
+ * Bits 5-10: Minutes, valid value range 0-59 inclusive.
+ *
+ * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
+ *
+ * The valid time range is from Midnight 00:00:00 to 23:59:58.
+ */
+struct directoryEntry {
+ /**
+ * Short 8.3 name.
+ * The first eight bytes contain the file name with blank fill.
+ * The last three bytes contain the file extension with blank fill.
+ */
+ uint8_t name[11];
+ /** Entry attributes.
+ *
+ * The upper two bits of the attribute byte are reserved and should
+ * always be set to 0 when a file is created and never modified or
+ * looked at after that. See defines that begin with DIR_ATT_.
+ */
+ uint8_t attributes;
+ /**
+ * Reserved for use by Windows NT. Set value to 0 when a file is
+ * created and never modify or look at it after that.
+ */
+ uint8_t reservedNT;
+ /**
+ * The granularity of the seconds part of creationTime is 2 seconds
+ * so this field is a count of tenths of a second and its valid
+ * value range is 0-199 inclusive. (WHG note - seems to be hundredths)
+ */
+ uint8_t creationTimeTenths;
+ /** Time file was created. */
+ uint16_t creationTime;
+ /** Date file was created. */
+ uint16_t creationDate;
+ /**
+ * Last access date. Note that there is no last access time, only
+ * a date. This is the date of last read or write. In the case of
+ * a write, this should be set to the same date as lastWriteDate.
+ */
+ uint16_t lastAccessDate;
+ /**
+ * High word of this entry's first cluster number (always 0 for a
+ * FAT12 or FAT16 volume).
+ */
+ uint16_t firstClusterHigh;
+ /** Time of last write. File creation is considered a write. */
+ uint16_t lastWriteTime;
+ /** Date of last write. File creation is considered a write. */
+ uint16_t lastWriteDate;
+ /** Low word of this entry's first cluster number. */
+ uint16_t firstClusterLow;
+ /** 32-bit unsigned holding this file's size in bytes. */
+ uint32_t fileSize;
+};
+//------------------------------------------------------------------------------
+// Definitions for directory entries
+//
+/** Type name for directoryEntry */
+typedef struct directoryEntry dir_t;
+/** escape for name[0] = 0XE5 */
+uint8_t const DIR_NAME_0XE5 = 0X05;
+/** name[0] value for entry that is free after being "deleted" */
+uint8_t const DIR_NAME_DELETED = 0XE5;
+/** name[0] value for entry that is free and no allocated entries follow */
+uint8_t const DIR_NAME_FREE = 0X00;
+/** file is read-only */
+uint8_t const DIR_ATT_READ_ONLY = 0X01;
+/** File should hidden in directory listings */
+uint8_t const DIR_ATT_HIDDEN = 0X02;
+/** Entry is for a system file */
+uint8_t const DIR_ATT_SYSTEM = 0X04;
+/** Directory entry contains the volume label */
+uint8_t const DIR_ATT_VOLUME_ID = 0X08;
+/** Entry is for a directory */
+uint8_t const DIR_ATT_DIRECTORY = 0X10;
+/** Old DOS archive bit for backup support */
+uint8_t const DIR_ATT_ARCHIVE = 0X20;
+/** Test value for long name entry. Test is
+ (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */
+uint8_t const DIR_ATT_LONG_NAME = 0X0F;
+/** Test mask for long name entry */
+uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F;
+/** defined attribute bits */
+uint8_t const DIR_ATT_DEFINED_BITS = 0X3F;
+/** Directory entry is part of a long name */
+static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
+ return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
+}
+/** Mask for file/subdirectory tests */
+uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
+/** Directory entry is for a file */
+static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
+ return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
+}
+/** Directory entry is for a subdirectory */
+static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
+ return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
+}
+/** Directory entry is for a file or subdirectory */
+static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
+ return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
+}
+#endif // FatStructs_h
644 utility/Sd2Card.cpp
@@ -0,0 +1,644 @@
+/* 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 <Arduino.h>
+#include "Sd2Card.h"
+//------------------------------------------------------------------------------
+#ifndef SOFTWARE_SPI
+// functions for hardware SPI
+/** Send a byte to the card */
+static void spiSend(uint8_t b) {
+ SPDR = b;
+ while (!(SPSR & (1 << SPIF)));
+}
+/** Receive a byte from the card */
+static uint8_t spiRec(void) {
+ spiSend(0XFF);
+ return SPDR;
+}
+#else // SOFTWARE_SPI
+//------------------------------------------------------------------------------
+/** nop to tune soft SPI timing */
+#define nop asm volatile ("nop\n\t")
+//------------------------------------------------------------------------------
+/** Soft SPI receive */
+uint8_t spiRec(void) {
+ 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 send */
+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();
+}
+#endif // SOFTWARE_SPI
+//------------------------------------------------------------------------------
+// send command and return error code. Return zero for OK
+uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
+ // end read if in partialBlockRead mode
+ readEnd();
+
+ // 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);
+
+ // wait for response
+ for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++);
+ 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(void) {
+ 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(void) {
+ digitalWrite(chipSelectPin_, HIGH);
+}
+//------------------------------------------------------------------------------
+void Sd2Card::chipSelectLow(void) {
+ 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.
+ */
+uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t 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;
+ }
+ 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.
+ */
+uint8_t Sd2Card::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_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
+ errorCode_ = inBlock_ = partialBlockRead_ = 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);
+ digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin
+ // Enable SPI, Master, clock rate f_osc/128
+ SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
+ // clear double speed
+ SPSR &= ~(1 << SPI2X);
+#endif // SOFTWARE_SPI
+
+ // must supply min of 74 clock cycles with CS high.
+ for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
+
+ chipSelectLow();
+
+ // 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;
+}
+//------------------------------------------------------------------------------
+/**
+ * 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 Sd2Card::partialBlockRead(uint8_t 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_t Sd2Card::readBlock(uint32_t block, uint8_t* 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_t Sd2Card::readData(uint32_t block,
+ uint16_t offset, uint16_t count, uint8_t* dst) {
+ uint16_t 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 (type()!= 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;
+ }
+
+#ifdef OPTIMIZE_HARDWARE_SPI
+ // start first spi transfer
+ SPDR = 0XFF;
+
+ // skip data before offset
+ for (;offset_ < offset; offset_++) {
+ while (!(SPSR & (1 << SPIF)));
+ SPDR = 0XFF;
+ }
+ // transfer data
+ n = count - 1;
+ for (uint16_t i = 0; i < n; i++) {
+ while (!(SPSR & (1 << SPIF)));
+ dst[i] = SPDR;
+ SPDR = 0XFF;
+ }
+ // wait for last byte
+ while (!(SPSR & (1 << SPIF)));
+ dst[n] = SPDR;
+
+#else // OPTIMIZE_HARDWARE_SPI
+
+ // skip data before offset
+ for (;offset_ < offset; offset_++) {
+ spiRec();
+ }
+ // transfer data
+ for (uint16_t i = 0; i < count; i++) {
+ dst[i] = spiRec();
+ }
+#endif // OPTIMIZE_HARDWARE_SPI
+
+ offset_ += count;
+ if (!partialBlockRead_ || offset_ >= 512) {
+ // read rest of data, checksum and set chip select high
+ readEnd();
+ }
+ return true;
+
+ fail:
+ chipSelectHigh();
+ return false;
+}
+//------------------------------------------------------------------------------
+/** Skip remaining data in a block when in partial block read mode. */
+void Sd2Card::readEnd(void) {
+ if (inBlock_) {
+ // skip data and crc
+#ifdef OPTIMIZE_HARDWARE_SPI
+ // optimize skip for hardware
+ SPDR = 0XFF;
+ while (offset_++ < 513) {
+ while (!(SPSR & (1 << SPIF)));
+ SPDR = 0XFF;
+ }
+ // wait for last crc byte
+ while (!(SPSR & (1 << SPIF)));
+#else // OPTIMIZE_HARDWARE_SPI
+ while (offset_++ < 514) spiRec();
+#endif // OPTIMIZE_HARDWARE_SPI
+ chipSelectHigh();
+ inBlock_ = 0;
+ }
+}
+//------------------------------------------------------------------------------
+/** read CID or CSR register */
+uint8_t 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;
+ }
+ if (!waitStartBlock()) goto fail;
+ // transfer data
+ for (uint16_t i = 0; i < 16; i++) dst[i] = spiRec();
+ spiRec(); // get first crc byte
+ spiRec(); // get second crc byte
+ 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.
+ */
+uint8_t Sd2Card::setSckRate(uint8_t sckRateID) {
+ if (sckRateID > 6) {
+ error(SD_CARD_ERROR_SCK_RATE);
+ return false;
+ }
+ // see avr processor datasheet for SPI register bit definitions
+ if ((sckRateID & 1) || sckRateID == 6) {
+ SPSR &= ~(1 << SPI2X);
+ } else {
+ SPSR |= (1 << SPI2X);
+ }
+ SPCR &= ~((1 <<SPR1) | (1 << SPR0));
+ SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0)
+ | (sckRateID & 2 ? (1 << SPR0) : 0);
+ return true;
+}
+//------------------------------------------------------------------------------
+// wait for card to go not busy
+uint8_t Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
+ uint16_t t0 = millis();
+ do {
+ if (spiRec() == 0XFF) return true;
+ }
+ while (((uint16_t)millis() - t0) < timeoutMillis);
+ return false;
+}
+//------------------------------------------------------------------------------
+/** Wait for start block token */
+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;
+}
+//------------------------------------------------------------------------------
+/**
+ * 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_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
+#if SD_PROTECT_BLOCK_ZERO
+ // don't allow write to first block
+ if (blockNumber == 0) {
+ error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
+ goto fail;
+ }
+#endif // SD_PROTECT_BLOCK_ZERO
+
+ // 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 */
+uint8_t Sd2Card::writeData(const uint8_t* src) {
+ // wait for previous write to finish
+ if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+ error(SD_CARD_ERROR_WRITE_MULTIPLE);
+ chipSelectHigh();
+ return false;
+ }
+ return writeData(WRITE_MULTIPLE_TOKEN, src);
+}
+//------------------------------------------------------------------------------
+// send one block of data for write block or write multiple blocks
+uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) {
+#ifdef OPTIMIZE_HARDWARE_SPI
+
+ // send data - optimized loop
+ SPDR = token;
+
+ // send two byte per iteration
+ for (uint16_t i = 0; i < 512; i += 2) {
+ while (!(SPSR & (1 << SPIF)));
+ SPDR = src[i];
+ while (!(SPSR & (1 << SPIF)));
+ SPDR = src[i+1];
+ }
+
+ // wait for last data byte
+ while (!(SPSR & (1 << SPIF)));
+
+#else // OPTIMIZE_HARDWARE_SPI
+ spiSend(token);
+ for (uint16_t i = 0; i < 512; i++) {
+ spiSend(src[i]);
+ }
+#endif // OPTIMIZE_HARDWARE_SPI
+ spiSend(0xff); // dummy crc
+ spiSend(0xff); // dummy crc
+
+ status_ = spiRec();
+ if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
+ error(SD_CARD_ERROR_WRITE);
+ chipSelectHigh();
+ 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_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
+#if SD_PROTECT_BLOCK_ZERO
+ // don't allow write to first block
+ if (blockNumber == 0) {
+ error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
+ goto fail;
+ }
+#endif // SD_PROTECT_BLOCK_ZERO
+ // 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;
+ }
+ 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.
+ */
+uint8_t Sd2Card::writeStop(void) {
+ 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;
+}
233 utility/Sd2Card.h
@@ -0,0 +1,233 @@
+/* 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/>.
+ */
+#ifndef Sd2Card_h
+#define Sd2Card_h
+/**
+ * \file
+ * Sd2Card class
+ */
+#include "Sd2PinMap.h"
+#include "SdInfo.h"
+/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */
+uint8_t const SPI_FULL_SPEED = 0;
+/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */
+uint8_t const SPI_HALF_SPEED = 1;
+/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */
+uint8_t const SPI_QUARTER_SPEED = 2;
+/**
+ * Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos.
+ * Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
+ *
+ * MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used
+ * on Mega Arduinos. Software SPI works well with GPS Shield V1.1
+ * but many SD cards will fail with GPS Shield V1.0.
+ */
+#define MEGA_SOFT_SPI 0
+//------------------------------------------------------------------------------
+#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__))
+#define SOFTWARE_SPI
+#endif // MEGA_SOFT_SPI
+//------------------------------------------------------------------------------
+// SPI pin definitions
+//
+#ifndef SOFTWARE_SPI
+// hardware pin defs
+/**
+ * SD Chip Select pin
+ *
+ * Warning if this pin is redefined the hardware SS will pin will be enabled
+ * as an output by init(). An avr processor will not function as an SPI
+ * master unless SS is set to output mode.
+ */
+/** The default chip select pin for the SD card is SS. */
+uint8_t const SD_CHIP_SELECT_PIN = SS_PIN;
+// The following three pins must not be redefined for hardware SPI.
+/** SPI Master Out Slave In pin */
+uint8_t const SPI_MOSI_PIN = MOSI_PIN;
+/** SPI Master In Slave Out pin */
+uint8_t const SPI_MISO_PIN = MISO_PIN;
+/** SPI Clock pin */
+uint8_t const SPI_SCK_PIN = SCK_PIN;
+/** optimize loops for hardware SPI */
+#define OPTIMIZE_HARDWARE_SPI
+
+#else // SOFTWARE_SPI
+// define software SPI pins so Mega can use unmodified GPS Shield
+/** SPI chip select pin */
+uint8_t const SD_CHIP_SELECT_PIN = 10;
+/** SPI Master Out Slave In pin */
+uint8_t const SPI_MOSI_PIN = 11;
+/** SPI Master In Slave Out pin */
+uint8_t const SPI_MISO_PIN = 12;
+/** SPI Clock pin */
+uint8_t const SPI_SCK_PIN = 13;
+#endif // SOFTWARE_SPI
+//------------------------------------------------------------------------------
+/** Protect block zero from write if nonzero */
+#define SD_PROTECT_BLOCK_ZERO 1
+/** init timeout ms */
+uint16_t const SD_INIT_TIMEOUT = 2000;
+/** erase timeout ms */
+uint16_t const SD_ERASE_TIMEOUT = 10000;
+/** read timeout ms */
+uint16_t const SD_READ_TIMEOUT = 300;
+/** write time out ms */
+uint16_t const SD_WRITE_TIMEOUT = 600;
+//------------------------------------------------------------------------------
+// SD card errors
+/** timeout error for command CMD0 */
+uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
+/** CMD8 was not accepted - not a valid SD card*/
+uint8_t const SD_CARD_ERROR_CMD8 = 0X2;
+/** card returned an error response for CMD17 (read block) */
+uint8_t const SD_CARD_ERROR_CMD17 = 0X3;
+/** card returned an error response for CMD24 (write block) */
+uint8_t const SD_CARD_ERROR_CMD24 = 0X4;
+/** WRITE_MULTIPLE_BLOCKS command failed */
+uint8_t const SD_CARD_ERROR_CMD25 = 0X05;
+/** card returned an error response for CMD58 (read OCR) */
+uint8_t const SD_CARD_ERROR_CMD58 = 0X06;
+/** SET_WR_BLK_ERASE_COUNT failed */
+uint8_t const SD_CARD_ERROR_ACMD23 = 0X07;
+/** card's ACMD41 initialization process timeout */
+uint8_t const SD_CARD_ERROR_ACMD41 = 0X08;
+/** card returned a bad CSR version field */
+uint8_t const SD_CARD_ERROR_BAD_CSD = 0X09;
+/** erase block group command failed */
+uint8_t const SD_CARD_ERROR_ERASE = 0X0A;
+/** card not capable of single block erase */
+uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B;
+/** Erase sequence timed out */
+uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C;
+/** card returned an error token instead of read data */
+uint8_t const SD_CARD_ERROR_READ = 0X0D;
+/** read CID or CSD failed */
+uint8_t const SD_CARD_ERROR_READ_REG = 0X0E;
+/** timeout while waiting for start of read data */
+uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X0F;
+/** card did not accept STOP_TRAN_TOKEN */
+uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X10;
+/** card returned an error token as a response to a write operation */
+uint8_t const SD_CARD_ERROR_WRITE = 0X11;
+/** attempt to write protected block zero */
+uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12;
+/** card did not go ready for a multiple block write */
+uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13;
+/** card returned an error to a CMD13 status check after a write */
+uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14;
+/** timeout occurred during write programming */
+uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15;
+/** incorrect rate selected */
+uint8_t const SD_CARD_ERROR_SCK_RATE = 0X16;
+//------------------------------------------------------------------------------
+// card types
+/** Standard capacity V1 SD card */
+uint8_t const SD_CARD_TYPE_SD1 = 1;
+/** Standard capacity V2 SD card */
+uint8_t const SD_CARD_TYPE_SD2 = 2;
+/** High Capacity SD card */
+uint8_t const SD_CARD_TYPE_SDHC = 3;
+//------------------------------------------------------------------------------
+/**
+ * \class Sd2Card
+ * \brief Raw access to SD and SDHC flash memory cards.
+ */
+class Sd2Card {
+ public:
+ /** Construct an instance of Sd2Card. */
+ Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {}
+ uint32_t cardSize(void);
+ uint8_t erase(uint32_t firstBlock, uint32_t lastBlock);
+ uint8_t eraseSingleBlockEnable(void);
+ /**
+ * \return error code for last error. See Sd2Card.h for a list of error codes.
+ */
+ uint8_t errorCode(void) const {return errorCode_;}
+ /** \return error data for last error. */
+ uint8_t errorData(void) const {return status_;}
+ /**
+ * Initialize an SD flash memory card with default clock rate and chip
+ * select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
+ */
+ uint8_t init(void) {
+ return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN);
+ }
+ /**
+ * Initialize an SD flash memory card with the selected SPI clock rate
+ * and the default SD chip select pin.
+ * See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
+ */
+ uint8_t init(uint8_t sckRateID) {
+ return init(sckRateID, SD_CHIP_SELECT_PIN);
+ }
+ uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin);
+ void partialBlockRead(uint8_t value);
+ /** Returns the current value, true or false, for partial block read. */
+ uint8_t partialBlockRead(void) const {return partialBlockRead_;}
+ uint8_t readBlock(uint32_t block, uint8_t* dst);
+ uint8_t readData(uint32_t block,
+ uint16_t offset, uint16_t count, uint8_t* dst);
+ /**
+ * Read a cards CID register. The CID contains card identification
+ * information such as Manufacturer ID, Product name, Product serial
+ * number and Manufacturing date. */
+ uint8_t readCID(cid_t* cid) {
+ return readRegister(CMD10, cid);
+ }
+ /**
+ * Read a cards CSD register. The CSD contains Card-Specific Data that
+ * provides information regarding access to the card's contents. */
+ uint8_t readCSD(csd_t* csd) {
+ return readRegister(CMD9, csd);
+ }
+ void readEnd(void);
+ uint8_t setSckRate(uint8_t sckRateID);
+ /** Return the card type: SD V1, SD V2 or SDHC */
+ uint8_t type(void) const {return type_;}
+ uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src);
+ uint8_t writeData(const uint8_t* src);
+ uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount);
+ uint8_t writeStop(void);
+ private:
+ uint32_t block_;
+ uint8_t chipSelectPin_;
+ uint8_t errorCode_;
+ uint8_t inBlock_;
+ uint16_t offset_;
+ uint8_t partialBlockRead_;
+ uint8_t status_;
+ uint8_t type_;
+ // private functions
+ uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
+ cardCommand(CMD55, 0);
+ return cardCommand(cmd, arg);
+ }
+ uint8_t cardCommand(uint8_t cmd, uint32_t arg);
+ void error(uint8_t code) {errorCode_ = code;}
+ uint8_t readRegister(uint8_t cmd, void* buf);
+ uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount);
+ void chipSelectHigh(void);
+ void chipSelectLow(void);
+ void type(uint8_t value) {type_ = value;}
+ uint8_t waitNotBusy(uint16_t timeoutMillis);
+ uint8_t writeData(uint8_t token, const uint8_t* src);
+ uint8_t waitStartBlock(void);
+};
+#endif // Sd2Card_h
353 utility/Sd2PinMap.h
@@ -0,0 +1,353 @@
+/* Arduino SdFat Library
+ * Copyright (C) 2010 by William Greiman
+ *
+ * This file is part of the Arduino SdFat 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 SdFat Library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+// Warning this file was generated by a program.
+#ifndef Sd2PinMap_h
+#define Sd2PinMap_h
+#include <avr/io.h>
+
+//------------------------------------------------------------------------------
+/** struct for mapping digital pins */
+struct pin_map_t {
+ volatile uint8_t* ddr;
+ volatile uint8_t* pin;
+ volatile uint8_t* port;
+ uint8_t bit;
+};
+//------------------------------------------------------------------------------
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+// Mega
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 20;
+uint8_t const SCL_PIN = 21;
+
+// SPI port
+uint8_t const SS_PIN = 53;
+uint8_t const MOSI_PIN = 51;
+uint8_t const MISO_PIN = 50;
+uint8_t const SCK_PIN = 52;
+
+static const pin_map_t digitalPinMap[] = {
+ {&DDRE, &PINE, &PORTE, 0}, // E0 0
+ {&DDRE, &PINE, &PORTE, 1}, // E1 1
+ {&DDRE, &PINE, &PORTE, 4}, // E4 2
+ {&DDRE, &PINE, &PORTE, 5}, // E5 3
+ {&DDRG, &PING, &PORTG, 5}, // G5 4
+ {&DDRE, &PINE, &PORTE, 3}, // E3 5
+ {&DDRH, &PINH, &PORTH, 3}, // H3 6
+ {&DDRH, &PINH, &PORTH, 4}, // H4 7
+ {&DDRH, &PINH, &PORTH, 5}, // H5 8
+ {&DDRH, &PINH, &PORTH, 6}, // H6 9
+ {&DDRB, &PINB, &PORTB, 4}, // B4 10
+ {&DDRB, &PINB, &PORTB, 5}, // B5 11
+ {&DDRB, &PINB, &PORTB, 6}, // B6 12
+ {&DDRB, &PINB, &PORTB, 7}, // B7 13
+ {&DDRJ, &PINJ, &PORTJ, 1}, // J1 14
+ {&DDRJ, &PINJ, &PORTJ, 0}, // J0 15
+ {&DDRH, &PINH, &PORTH, 1}, // H1 16
+ {&DDRH, &PINH, &PORTH, 0}, // H0 17
+ {&DDRD, &PIND, &PORTD, 3}, // D3 18
+ {&DDRD, &PIND, &PORTD, 2}, // D2 19
+ {&DDRD, &PIND, &PORTD, 1}, // D1 20
+ {&DDRD, &PIND, &PORTD, 0}, // D0 21
+ {&DDRA, &PINA, &PORTA, 0}, // A0 22
+ {&DDRA, &PINA, &PORTA, 1}, // A1 23
+ {&DDRA, &PINA, &PORTA, 2}, // A2 24
+ {&DDRA, &PINA, &PORTA, 3}, // A3 25
+ {&DDRA, &PINA, &PORTA, 4}, // A4 26
+ {&DDRA, &PINA, &PORTA, 5}, // A5 27
+ {&DDRA, &PINA, &PORTA, 6}, // A6 28
+ {&DDRA, &PINA, &PORTA, 7}, // A7 29
+ {&DDRC, &PINC, &PORTC, 7}, // C7 30
+ {&DDRC, &PINC, &PORTC, 6}, // C6 31
+ {&DDRC, &PINC, &PORTC, 5}, // C5 32
+ {&DDRC, &PINC, &PORTC, 4}, // C4 33
+ {&DDRC, &PINC, &PORTC, 3}, // C3 34
+ {&DDRC, &PINC, &PORTC, 2}, // C2 35
+ {&DDRC, &PINC, &PORTC, 1}, // C1 36
+ {&DDRC, &PINC, &PORTC, 0}, // C0 37
+ {&DDRD, &PIND, &PORTD, 7}, // D7 38
+ {&DDRG, &PING, &PORTG, 2}, // G2 39
+ {&DDRG, &PING, &PORTG, 1}, // G1 40
+ {&DDRG, &PING, &PORTG, 0}, // G0 41
+ {&DDRL, &PINL, &PORTL, 7}, // L7 42
+ {&DDRL, &PINL, &PORTL, 6}, // L6 43
+ {&DDRL, &PINL, &PORTL, 5}, // L5 44
+ {&DDRL, &PINL, &PORTL, 4}, // L4 45
+ {&DDRL, &PINL, &PORTL, 3}, // L3 46
+ {&DDRL, &PINL, &PORTL, 2}, // L2 47
+ {&DDRL, &PINL, &PORTL, 1}, // L1 48
+ {&DDRL, &PINL, &PORTL, 0}, // L0 49
+ {&DDRB, &PINB, &PORTB, 3}, // B3 50
+ {&DDRB, &PINB, &PORTB, 2}, // B2 51
+ {&DDRB, &PINB, &PORTB, 1}, // B1 52
+ {&DDRB, &PINB, &PORTB, 0}, // B0 53
+ {&DDRF, &PINF, &PORTF, 0}, // F0 54
+ {&DDRF, &PINF, &PORTF, 1}, // F1 55
+ {&DDRF, &PINF, &PORTF, 2}, // F2 56
+ {&DDRF, &PINF, &PORTF, 3}, // F3 57
+ {&DDRF, &PINF, &PORTF, 4}, // F4 58
+ {&DDRF, &PINF, &PORTF, 5}, // F5 59
+ {&DDRF, &PINF, &PORTF, 6}, // F6 60
+ {&DDRF, &PINF, &PORTF, 7}, // F7 61
+ {&DDRK, &PINK, &PORTK, 0}, // K0 62
+ {&DDRK, &PINK, &PORTK, 1}, // K1 63
+ {&DDRK, &PINK, &PORTK, 2}, // K2 64
+ {&DDRK, &PINK, &PORTK, 3}, // K3 65
+ {&DDRK, &PINK, &PORTK, 4}, // K4 66
+ {&DDRK, &PINK, &PORTK, 5}, // K5 67
+ {&DDRK, &PINK, &PORTK, 6}, // K6 68
+ {&DDRK, &PINK, &PORTK, 7} // K7 69
+};
+//------------------------------------------------------------------------------
+#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
+// Sanguino
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 17;
+uint8_t const SCL_PIN = 18;
+
+// SPI port
+uint8_t const SS_PIN = 4;
+uint8_t const MOSI_PIN = 5;
+uint8_t const MISO_PIN = 6;
+uint8_t const SCK_PIN = 7;
+
+static const pin_map_t digitalPinMap[] = {
+ {&DDRB, &PINB, &PORTB, 0}, // B0 0
+ {&DDRB, &PINB, &PORTB, 1}, // B1 1
+ {&DDRB, &PINB, &PORTB, 2}, // B2 2
+ {&DDRB, &PINB, &PORTB, 3}, // B3 3
+ {&DDRB, &PINB, &PORTB, 4}, // B4 4