From 1876635c4c8d86a06b3eb8dbcc47dd8418fd50ad Mon Sep 17 00:00:00 2001 From: James Foster Date: Sat, 24 Oct 2020 19:07:38 -0700 Subject: [PATCH 01/17] Arduino CI test now runs but gets errors. --- .arduino-ci.yml | 20 ++++++++++++++++++++ .gitignore | 4 ++++ Gemfile | 2 ++ test/test.cpp | 16 ++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 .arduino-ci.yml create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 test/test.cpp diff --git a/.arduino-ci.yml b/.arduino-ci.yml new file mode 100644 index 0000000..7a4cef6 --- /dev/null +++ b/.arduino-ci.yml @@ -0,0 +1,20 @@ +platforms: + mega2560: + board: arduino:avr:mega:cpu=atmega2560 + package: arduino:avr + gcc: + features: + defines: + - __AVR_ATmega2560__ + - ARDUINO_CI + - __AVR__ + warnings: + flags: + +unittest: + platforms: + - mega2560 + +compile: + platforms: + - mega2560 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32a2490 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Gemfile.lock +.bundle +vendor +*.cpp.bin* diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fb422d1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org/' +gem 'arduino_ci' diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 0000000..28cb8fd --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,16 @@ +/* +bundle config --local path vendor/bundle +bundle install +bundle exec arduino_ci_remote.rb --skip-compilation +bundle exec arduino_ci_remote.rb --skip-unittests + */ +#include "Arduino.h" +#include "ArduinoUnitTests.h" +#include "SD.h" + +unittest(test) { + // add tests here + assertTrue(true); +} + +unittest_main() From 5a726692dd91a92b41eb280e3445607a49a26db3 Mon Sep 17 00:00:00 2001 From: James Foster Date: Sat, 24 Oct 2020 20:12:13 -0700 Subject: [PATCH 02/17] Allow testing with non-default chipSelect. --- examples/Files/Files.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Files/Files.ino b/examples/Files/Files.ino index 85faa7c..f19a9db 100644 --- a/examples/Files/Files.ino +++ b/examples/Files/Files.ino @@ -32,7 +32,7 @@ while (!Serial); Serial.print("Initializing SD card..."); - if (!SD.begin(10)) { + if (!SD.begin(chipSelect)) { Serial.println("initialization failed!"); while (1); } From 1c0cc5eb6c17e4563c3424cb37dd957667584493 Mon Sep 17 00:00:00 2001 From: James Foster Date: Sat, 24 Oct 2020 21:14:03 -0700 Subject: [PATCH 03/17] Now able to compile in Arduino CI. Workaround for #2 (but see https://github.com/Arduino-CI/arduino_ci/issues/120). --- src/File.cpp | 2 +- src/SD.h | 7 +++++++ src/utility/Sd2PinMap.cpp | 7 +++++++ src/utility/Sd2PinMap.h | 21 +++++++++++++++++++++ src/utility/SdFat.h | 7 +++++++ src/utility/SdFatUtil.h | 21 ++++++++++++++++----- 6 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 src/utility/Sd2PinMap.cpp diff --git a/src/File.cpp b/src/File.cpp index 3e27fb4..204c9c4 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -22,7 +22,7 @@ File::File(SdFile f, const char *n) { // oh man you are kidding me, new() doesn't exist? Ok we do it by hand! _file = (SdFile *)malloc(sizeof(SdFile)); if (_file) { - memcpy(_file, &f, sizeof(SdFile)); + memcpy((void*)_file, (void*)&f, sizeof(SdFile)); strncpy(_name, n, 12); _name[12] = 0; diff --git a/src/SD.h b/src/SD.h index c81a7d3..2b8f41a 100644 --- a/src/SD.h +++ b/src/SD.h @@ -53,6 +53,13 @@ namespace SDLib { void rewindDirectory(void); using Print::write; +#ifdef ARDUINO_CI + int getWriteError() { return writeError; } + void setWriteError(int value = 1) { writeError = value; } + void clearWriteError() { writeError = 0; } + private: + int writeError; +#endif }; class SDClass { diff --git a/src/utility/Sd2PinMap.cpp b/src/utility/Sd2PinMap.cpp new file mode 100644 index 0000000..af4191c --- /dev/null +++ b/src/utility/Sd2PinMap.cpp @@ -0,0 +1,7 @@ +#ifdef ARDUINO_CI + +#include "Sd2PinMap.h" + +uint8_t avr_io_registers[RAMSTART]; + +#endif diff --git a/src/utility/Sd2PinMap.h b/src/utility/Sd2PinMap.h index 0609ffe..93b9be3 100644 --- a/src/utility/Sd2PinMap.h +++ b/src/utility/Sd2PinMap.h @@ -17,6 +17,27 @@ along with the Arduino SdFat Library. If not, see . */ + +#ifdef ARDUINO_CI + #include + #ifdef _SFR_IO8 + #undef _SFR_IO8 + #define _AVR_IO_REG(type, mem_addr) (*(type*)(&avr_io_registers[mem_addr])) + #define __SFR_OFFSET 0x20 + #define _SFR_IO8(io_addr) _AVR_IO_REG(uint8_t, io_addr + __SFR_OFFSET) + #define _SFR_IO16(io_addr) _AVR_IO_REG(uint16_t, io_addr + __SFR_OFFSET) + #define _SFR_MEM8(mem_addr) _AVR_IO_REG(uint8_t, mem_addr) + #define _SFR_MEM16(mem_addr) _AVR_IO_REG(uint16_t, mem_addr) + #define _SFR_MEM32(mem_addr) _AVR_IO_REG(uint32_t, mem_addr) + + extern uint8_t avr_io_registers[RAMSTART]; + #endif + #define SDCARD_SS_PIN 4 + #define SDCARD_MOSI_PIN 11 + #define SDCARD_MISO_PIN 12 + #define SDCARD_SCK_PIN 13 +#endif + #if defined(__arm__) // Arduino Due Board follows #ifndef Sd2PinMap_h diff --git a/src/utility/SdFat.h b/src/utility/SdFat.h index 46b880b..b7336a9 100644 --- a/src/utility/SdFat.h +++ b/src/utility/SdFat.h @@ -454,6 +454,13 @@ class SdFile : public Print { static uint8_t make83Name(const char* str, uint8_t* name); uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); dir_t* readDirCache(void); +#ifdef ARDUINO_CI + int writeError; + public: + int getWriteError() { return writeError; } + void setWriteError(int value = 1) { writeError = value; } + void clearWriteError() { writeError = 0; } +#endif }; //============================================================================== // SdVolume class diff --git a/src/utility/SdFatUtil.h b/src/utility/SdFatUtil.h index 4b93de8..00227ec 100644 --- a/src/utility/SdFatUtil.h +++ b/src/utility/SdFatUtil.h @@ -34,20 +34,31 @@ #endif #define NOINLINE __attribute__((noinline,unused)) #define UNUSEDOK __attribute__((unused)) + +#if UINTPTR_MAX == 0xFFFF +typedef int16_t ptr_as_int; +#elif UINTPTR_MAX == 0xFFFFFFFF +typedef int32_t ptr_as_int; +#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu +typedef int64_t ptr_as_int; +#else +#error unrecognized pointer size! +#endif + //------------------------------------------------------------------------------ /** Return the number of bytes currently free in RAM. */ static UNUSEDOK int FreeRam(void) { extern int __bss_end; extern int* __brkval; int free_memory; - if (reinterpret_cast(__brkval) == 0) { + if (reinterpret_cast(__brkval) == 0) { // if no heap use from end of bss section - free_memory = reinterpret_cast(&free_memory) - - reinterpret_cast(&__bss_end); + free_memory = reinterpret_cast(&free_memory) + - reinterpret_cast(&__bss_end); } else { // use from top of stack to heap - free_memory = reinterpret_cast(&free_memory) - - reinterpret_cast(__brkval); + free_memory = reinterpret_cast(&free_memory) + - reinterpret_cast(__brkval); } return free_memory; } From 508d67143de467bae2b4e5ad39d8f6b2b9b3f64b Mon Sep 17 00:00:00 2001 From: James Foster Date: Sat, 24 Oct 2020 22:19:20 -0700 Subject: [PATCH 04/17] I thought it was working but tried again and it wasn't. With this it does work (for now?). --- src/utility/Sd2PinMap.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utility/Sd2PinMap.cpp b/src/utility/Sd2PinMap.cpp index af4191c..daa3a98 100644 --- a/src/utility/Sd2PinMap.cpp +++ b/src/utility/Sd2PinMap.cpp @@ -1,5 +1,6 @@ #ifdef ARDUINO_CI +#include #include "Sd2PinMap.h" uint8_t avr_io_registers[RAMSTART]; From eec57a414c9bfb68c59ddb95132103364f474ebb Mon Sep 17 00:00:00 2001 From: James Foster Date: Mon, 16 Nov 2020 18:31:37 -0800 Subject: [PATCH 05/17] Use `MOCK_PINS_COUNT` instead of `ARDUINO_CI` to identify CI tests. --- .arduino-ci.yml | 12 ------------ Gemfile | 2 +- src/SD.h | 2 +- src/utility/Sd2PinMap.cpp | 2 +- src/utility/Sd2PinMap.h | 24 ++++++++++-------------- src/utility/SdFat.h | 2 +- test/test.cpp | 4 ++-- 7 files changed, 16 insertions(+), 32 deletions(-) diff --git a/.arduino-ci.yml b/.arduino-ci.yml index 7a4cef6..615161d 100644 --- a/.arduino-ci.yml +++ b/.arduino-ci.yml @@ -1,15 +1,3 @@ -platforms: - mega2560: - board: arduino:avr:mega:cpu=atmega2560 - package: arduino:avr - gcc: - features: - defines: - - __AVR_ATmega2560__ - - ARDUINO_CI - - __AVR__ - warnings: - flags: unittest: platforms: diff --git a/Gemfile b/Gemfile index fb422d1..80468b5 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.org/' -gem 'arduino_ci' +gem 'arduino_ci', git: 'https://github.com/Arduino-CI/arduino_ci.git', branch: 'master' diff --git a/src/SD.h b/src/SD.h index 2b8f41a..0a4166f 100644 --- a/src/SD.h +++ b/src/SD.h @@ -53,7 +53,7 @@ namespace SDLib { void rewindDirectory(void); using Print::write; -#ifdef ARDUINO_CI +#ifdef MOCK_PINS_COUNT int getWriteError() { return writeError; } void setWriteError(int value = 1) { writeError = value; } void clearWriteError() { writeError = 0; } diff --git a/src/utility/Sd2PinMap.cpp b/src/utility/Sd2PinMap.cpp index daa3a98..e247e91 100644 --- a/src/utility/Sd2PinMap.cpp +++ b/src/utility/Sd2PinMap.cpp @@ -1,4 +1,4 @@ -#ifdef ARDUINO_CI +#ifdef MOCK_PINS_COUNT #include #include "Sd2PinMap.h" diff --git a/src/utility/Sd2PinMap.h b/src/utility/Sd2PinMap.h index 93b9be3..c272bff 100644 --- a/src/utility/Sd2PinMap.h +++ b/src/utility/Sd2PinMap.h @@ -18,20 +18,9 @@ . */ -#ifdef ARDUINO_CI - #include - #ifdef _SFR_IO8 - #undef _SFR_IO8 - #define _AVR_IO_REG(type, mem_addr) (*(type*)(&avr_io_registers[mem_addr])) - #define __SFR_OFFSET 0x20 - #define _SFR_IO8(io_addr) _AVR_IO_REG(uint8_t, io_addr + __SFR_OFFSET) - #define _SFR_IO16(io_addr) _AVR_IO_REG(uint16_t, io_addr + __SFR_OFFSET) - #define _SFR_MEM8(mem_addr) _AVR_IO_REG(uint8_t, mem_addr) - #define _SFR_MEM16(mem_addr) _AVR_IO_REG(uint16_t, mem_addr) - #define _SFR_MEM32(mem_addr) _AVR_IO_REG(uint32_t, mem_addr) - - extern uint8_t avr_io_registers[RAMSTART]; - #endif +#include + +#ifdef MOCK_PINS_COUNT #define SDCARD_SS_PIN 4 #define SDCARD_MOSI_PIN 11 #define SDCARD_MISO_PIN 12 @@ -59,6 +48,13 @@ #include + #ifdef MOCK_PINS_COUNT + #define SDCARD_SS_PIN 4 + #define SDCARD_MOSI_PIN 11 + #define SDCARD_MISO_PIN 12 + #define SDCARD_SCK_PIN 13 + #endif + uint8_t const SS_PIN = SS; uint8_t const MOSI_PIN = MOSI; uint8_t const MISO_PIN = MISO; diff --git a/src/utility/SdFat.h b/src/utility/SdFat.h index b7336a9..33f503b 100644 --- a/src/utility/SdFat.h +++ b/src/utility/SdFat.h @@ -454,7 +454,7 @@ class SdFile : public Print { static uint8_t make83Name(const char* str, uint8_t* name); uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); dir_t* readDirCache(void); -#ifdef ARDUINO_CI +#ifdef MOCK_PINS_COUNT int writeError; public: int getWriteError() { return writeError; } diff --git a/test/test.cpp b/test/test.cpp index 28cb8fd..2eccb8a 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,8 +1,8 @@ /* bundle config --local path vendor/bundle bundle install -bundle exec arduino_ci_remote.rb --skip-compilation -bundle exec arduino_ci_remote.rb --skip-unittests +bundle exec arduino_ci.rb --skip-examples-compilation +bundle exec arduino_ci.rb --skip-unittests */ #include "Arduino.h" #include "ArduinoUnitTests.h" From dcb67bb12f887220bbf370fcfbe46e9019270570 Mon Sep 17 00:00:00 2001 From: James Foster Date: Wed, 25 Nov 2020 14:04:35 -0800 Subject: [PATCH 06/17] Use published `arduino_ci` gem (currently version 0.4.0) --- Gemfile | 2 +- scripts/test.sh | 4 ++++ scripts/testAndBuild.sh | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100755 scripts/test.sh create mode 100755 scripts/testAndBuild.sh diff --git a/Gemfile b/Gemfile index 80468b5..fb422d1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.org/' -gem 'arduino_ci', git: 'https://github.com/Arduino-CI/arduino_ci.git', branch: 'master' +gem 'arduino_ci' diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..f87e6c0 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,4 @@ +#! /bin/sh +bundle config --local path vendor/bundle +bundle install +bundle exec arduino_ci.rb --skip-examples-compilation diff --git a/scripts/testAndBuild.sh b/scripts/testAndBuild.sh new file mode 100755 index 0000000..f7c20ab --- /dev/null +++ b/scripts/testAndBuild.sh @@ -0,0 +1,4 @@ +#! /bin/sh +bundle config --local path vendor/bundle +bundle install +bundle exec arduino_ci.rb From 06ee57a0addc2d1c871e19541b86dfa536a59344 Mon Sep 17 00:00:00 2001 From: lukemusic <49703931+lukemusic@users.noreply.github.com> Date: Thu, 26 Nov 2020 23:13:02 -0800 Subject: [PATCH 07/17] Added high level continuous integration tests to the SD library (#7) * test framework compiles * standalone sd functions work * working on write * peek works and write sorta works * Help from Dr Foster wip * read, write, position, seek, peek, open, etc all work * added asserts for when File_CI is directory * added test for isDirectory() * fixing incoming changes * fixed yml file changes Co-authored-by: James Foster --- src/File.cpp | 34 +++--- src/File_CI.cpp | 240 ++++++++++++++++++++++++++++++++++++ src/SD.cpp | 56 ++++----- src/SD.h | 217 +++++++++++++++++---------------- src/SD_CI.cpp | 95 +++++++++++++++ src/SD_CI.h | 106 ++++++++++++++++ src/utility/Sd2Card.h | 6 + src/utility/Sd2PinMap.h | 30 +++-- src/utility/SdFat.h | 3 + test/test.cpp | 264 +++++++++++++++++++++++++++++++++++++++- 10 files changed, 885 insertions(+), 166 deletions(-) create mode 100644 src/File_CI.cpp create mode 100644 src/SD_CI.cpp create mode 100644 src/SD_CI.h diff --git a/src/File.cpp b/src/File.cpp index 204c9c4..4478ec1 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -18,7 +18,7 @@ uint8_t nfilecount=0; */ -File::File(SdFile f, const char *n) { +File_Base::File_Base(SdFile f, const char *n) { // oh man you are kidding me, new() doesn't exist? Ok we do it by hand! _file = (SdFile *)malloc(sizeof(SdFile)); if (_file) { @@ -37,28 +37,28 @@ File::File(SdFile f, const char *n) { } } -File::File(void) { +File_Base::File_Base(void) { _file = 0; _name[0] = 0; //Serial.print("Created empty file object"); } // returns a pointer to the file name -char *File::name(void) { +char *File_Base::name(void) { return _name; } // a directory is a special type of file -bool File::isDirectory(void) { +bool File_Base::isDirectory(void) { return (_file && _file->isDir()); } -size_t File::write(uint8_t val) { +size_t File_Base::write(uint8_t val) { return write(&val, 1); } -size_t File::write(const uint8_t *buf, size_t size) { +size_t File_Base::write(const uint8_t *buf, size_t size) { size_t t; if (!_file) { setWriteError(); @@ -73,14 +73,14 @@ size_t File::write(const uint8_t *buf, size_t size) { return t; } -int File::availableForWrite() { +int File_Base::availableForWrite() { if (_file) { return _file->availableForWrite(); } return 0; } -int File::peek() { +int File_Base::peek() { if (! _file) { return 0; } @@ -92,7 +92,7 @@ int File::peek() { return c; } -int File::read() { +int File_Base::read() { if (_file) { return _file->read(); } @@ -100,14 +100,14 @@ int File::read() { } // buffered read for more efficient, high speed reading -int File::read(void *buf, uint16_t nbyte) { +int File_Base::read(void *buf, uint16_t nbyte) { if (_file) { return _file->read(buf, nbyte); } return 0; } -int File::available() { +int File_Base::available() { if (! _file) { return 0; } @@ -117,13 +117,13 @@ int File::available() { return n > 0X7FFF ? 0X7FFF : n; } -void File::flush() { +void File_Base::flush() { if (_file) { _file->sync(); } } -bool File::seek(uint32_t pos) { +bool File_Base::seek(uint32_t pos) { if (! _file) { return false; } @@ -131,21 +131,21 @@ bool File::seek(uint32_t pos) { return _file->seekSet(pos); } -uint32_t File::position() { +uint32_t File_Base::position() { if (! _file) { return -1; } return _file->curPosition(); } -uint32_t File::size() { +uint32_t File_Base::size() { if (! _file) { return 0; } return _file->fileSize(); } -void File::close() { +void File_Base::close() { if (_file) { _file->close(); free(_file); @@ -159,7 +159,7 @@ void File::close() { } } -File::operator bool() { +File_Base::operator bool() { if (_file) { return _file->isOpen(); } diff --git a/src/File_CI.cpp b/src/File_CI.cpp new file mode 100644 index 0000000..7168712 --- /dev/null +++ b/src/File_CI.cpp @@ -0,0 +1,240 @@ +#include +#ifdef MOCK_PINS_COUNT +#include "SD_CI.h" + +#include +#include +#include +#include +// #include + +namespace SDLib { + +// File_CI::File_CI(File_Base *baseFile) { +// // this->baseFile = baseFile; +// } + +// File_CI::File_CI(SdFile f, const char *name) : File_Base(f, name) {} + +// File_CI::File_CI(void) : File_Base() {} + +File_CI::File_CI(const char *name, uint8_t mode = FILE_READ) { + _mode = mode; + strcpy(_path, name); + + // prepend base path + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, name); + + // does path exist? + if (fs::exists(newPath)) { + // if so, is it a directory? + if (fs::is_directory(newPath)) { + // if so, return a new object with the _isDirectory flag set + _isDirectory = true; + } else { + // path is an existing file + _isDirectory = false; + // if file is read, open for read + if (_mode == FILE_READ) { + finOut = new std::fstream; + finOut->open(newPath, std::fstream::binary | std::fstream::in); + } else if (_mode == FILE_WRITE) { + // else if mode is FILE_WRITE open for read and write + finOut = new std::fstream; + finOut->open(newPath, std::fstream::binary | std::fstream::in | + std::fstream::out | std::fstream::app); + } else { + // else invalid mode, assert false + assert(false); + } + _open = true; + } + } else { + // path does not exist + _isDirectory = false; + // if mode is write, open new file + if (_mode == FILE_WRITE) { + finOut = new std::fstream; + finOut->open(newPath, std::fstream::binary | std::fstream::in | + std::fstream::out | std::fstream::app); + _open = true; + } else if (_mode == FILE_READ) { + // else assert false for file doesn't exist + assert(false); + } else { + // else invalid mode. assert false + assert(false); + } + } +} + +File_CI::~File_CI() { + if (!_isDirectory) { + delete finOut; + } +} + +// size_t File_CI::write(uint8_t value) { +// // TODO +// // return File_Base::write(value); +// } + +size_t File_CI::write(const char *buf, size_t size) { + // halt if file is directory + if (_isDirectory) { + assert(false); + } + + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, _path); + + assert(_mode == FILE_WRITE); + finOut->write(buf, size); + return size; +} + +int File_CI::availableForWrite() { + // TODO + // return File_Base::availableForWrite(); +} + +/* Note for read(): the user of this function needs to append + their own null character to the end of the char array. + This function will attempt to insert the number of bytes + specified by size into the buf. So the user should pass a + size that is at most one size less than the declared size + of the buffer so the user can append a null character after + this function is used. +*/ +int File_CI::read(char *buf, size_t size) { + // halt if file is directory + if (_isDirectory) { + assert(false); + } + + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, _path); + + int remaining = fs::file_size(newPath) - finOut->tellg(); + // if size is greater than remaining, use remaining for read + if (size > remaining) { + finOut->read(buf, remaining); + } else { + // else use size + finOut->read(buf, size); + } + return 0; +} + +int File_CI::peek() { + // halt if file is directory + if (_isDirectory) { + assert(false); + } + + char byte[2]; + // save old pointer position + fpos_t oldPos = finOut->tellg(); + // read in first char + finOut->read(byte, 1); + byte[1] = '\0'; + // put file pointer back + finOut->seekg(oldPos); + + // if file pointer isn't back, assert false + if (finOut->tellg() != oldPos) { + assert(false); + } + // return first char in buffer + return byte[0]; +} + +int File_CI::available() { + // halt if file is directory + if (_isDirectory) { + assert(false); + } + + // prepend base file path + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, _path); + + return fs::file_size(newPath); +} + +void File_CI::flush() { + // return File_Base::flush(); +} + +bool File_CI::seek(uint32_t pos) { + // halt if file is directory + if (_isDirectory) { + assert(false); + } + + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, _path); + + // if pos is greater than size - 1, return false + if (pos > fs::file_size(newPath) - 1) { + // trying to seek outside of file scope + return false; + } else { + finOut->seekg(pos); + } + // check for success + if (finOut->tellg() == pos) { + return true; + } + return false; +} + +uint32_t File_CI::position() { + // halt if file is directory + if (_isDirectory) { + assert(false); + } + + return finOut->tellg(); +} + +uint32_t File_CI::size() { + // halt if file is directory + if (_isDirectory) { + assert(false); + } + + // prepend base file path + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, _path); + return fs::file_size(newPath); +} + +void File_CI::close() { + // close if not directory + if (!_isDirectory) { + finOut->close(); + } + _open = false; +} + +File_CI File_CI::openNextFile(uint8_t mode = FILE_READ) { + // TODO +} + +char *File_CI::name() { return _path; } + +File_CI::operator bool() { return File_Base::operator bool(); } + +bool File_CI::isDirectory(void) { return _isDirectory; } + +} // namespace SDLib + +#endif diff --git a/src/SD.cpp b/src/SD.cpp index 24fcb2e..636b608 100644 --- a/src/SD.cpp +++ b/src/SD.cpp @@ -285,7 +285,7 @@ namespace SDLib { 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 + Expects the context object to be an instance of `SDClass_Base` and will use the `file` property of the instance to open the requested file/directory with the associated file open mode property. @@ -296,7 +296,7 @@ namespace SDLib { from descending further. (This may be unnecessary.) if (isLastComponent) { - SDClass *p_SD = static_cast(object); + SDClass_Base *p_SD = static_cast(object); p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode); if (p_SD->fileOpenMode == FILE_WRITE) { p_SD->file.seekSet(p_SD->file.fileSize()); @@ -336,7 +336,7 @@ namespace SDLib { - bool SDClass::begin(uint8_t csPin) { + bool SDClass_Base::begin(uint8_t csPin) { if (root.isOpen()) { root.close(); } @@ -353,7 +353,7 @@ namespace SDLib { root.openRoot(volume); } - bool SDClass::begin(uint32_t clock, uint8_t csPin) { + bool SDClass_Base::begin(uint32_t clock, uint8_t csPin) { if (root.isOpen()) { root.close(); } @@ -365,12 +365,12 @@ namespace SDLib { } //call this when a card is removed. It will allow you to insert and initialise a new card. - void SDClass::end() { + void SDClass_Base::end() { root.close(); } // this little helper is used to traverse paths - SdFile SDClass::getParentDir(const char *filepath, int *index) { + SdFile SDClass_Base::getParentDir(const char *filepath, int *index) { // get parent directory SdFile d1; SdFile d2; @@ -429,13 +429,13 @@ namespace SDLib { } - File SDClass::open(const char *filepath, uint8_t mode) { + File_Base SDClass_Base::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 + the `SDClass_Base` object--this property is currently a standard `SdFile` object from `sdfatlib`. Defaults to read only. @@ -463,7 +463,7 @@ namespace SDLib { if (! filepath[0]) { // it was the directory itself! - return File(parentdir, "/"); + return File_Base(parentdir, "/"); } // Open the file itself @@ -471,11 +471,11 @@ namespace SDLib { // failed to open a subdir! if (!parentdir.isOpen()) { - return File(); + return File_Base(); } if (! file.open(parentdir, filepath, mode)) { - return File(); + return File_Base(); } // close the parent parentdir.close(); @@ -483,18 +483,18 @@ namespace SDLib { if ((mode & (O_APPEND | O_WRITE)) == (O_APPEND | O_WRITE)) { file.seekSet(file.fileSize()); } - return File(file, filepath); + return File_Base(file, filepath); } /* - File SDClass::open(char *filepath, uint8_t mode) { + File SDClass_Base::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 + the `SDClass_Base` object--this property is currently a standard `SdFile` object from `sdfatlib`. Defaults to read only. @@ -517,13 +517,13 @@ namespace SDLib { fileOpenMode = mode; walkPath(filepath, root, callback_openPath, this); - return File(); + return File_Base(); } */ - //bool SDClass::close() { + //bool SDClass_Base::close() { // /* // // Closes the file opened by the `open` method. @@ -533,7 +533,7 @@ namespace SDLib { //} - bool SDClass::exists(const char *filepath) { + bool SDClass_Base::exists(const char *filepath) { /* Returns true if the supplied file path exists. @@ -543,7 +543,7 @@ namespace SDLib { } - //bool SDClass::exists(char *filepath, SdFile& parentDir) { + //bool SDClass_Base::exists(char *filepath, SdFile& parentDir) { // /* // // Returns true if the supplied file path rooted at `parentDir` @@ -554,7 +554,7 @@ namespace SDLib { //} - bool SDClass::mkdir(const char *filepath) { + bool SDClass_Base::mkdir(const char *filepath) { /* Makes a single directory or a hierarchy of directories. @@ -565,7 +565,7 @@ namespace SDLib { return walkPath(filepath, root, callback_makeDirPath); } - bool SDClass::rmdir(const char *filepath) { + bool SDClass_Base::rmdir(const char *filepath) { /* Remove a single directory or a hierarchy of directories. @@ -576,13 +576,13 @@ namespace SDLib { return walkPath(filepath, root, callback_rmdir); } - bool SDClass::remove(const char *filepath) { + bool SDClass_Base::remove(const char *filepath) { return walkPath(filepath, root, callback_remove); } // allows you to recurse into a directory - File File::openNextFile(uint8_t mode) { + File_Base File_Base::openNextFile(uint8_t mode) { dir_t p; //Serial.print("\t\treading dir..."); @@ -591,7 +591,7 @@ namespace SDLib { // done if past last used entry if (p.name[0] == DIR_NAME_FREE) { //Serial.println("end"); - return File(); + return File_Base(); } // skip deleted entry and entries for . and .. @@ -615,23 +615,23 @@ namespace SDLib { if (f.open(_file, name, mode)) { //Serial.println("OK!"); - return File(f, name); + return File_Base(f, name); } else { //Serial.println("ugh"); - return File(); + return File_Base(); } } //Serial.println("nothing"); - return File(); + return File_Base(); } - void File::rewindDirectory(void) { + void File_Base::rewindDirectory(void) { if (isDirectory()) { _file->rewind(); } } - SDClass SD; + SDClass_Base SD; }; diff --git a/src/SD.h b/src/SD.h index 0a4166f..d7cf97f 100644 --- a/src/SD.h +++ b/src/SD.h @@ -17,6 +17,15 @@ #include +#ifdef MOCK_PINS_COUNT +#define SDClass_CI SDClass +#define File_CI File +#include +#else +#define SDClass_Base SDClass +#define File_Base File +#endif + #include "utility/SdFat.h" #include "utility/SdFatUtil.h" @@ -25,121 +34,115 @@ namespace SDLib { - 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 - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual int availableForWrite(); - virtual int read(); - virtual int peek(); - virtual int available(); - virtual void flush(); - int read(void *buf, uint16_t nbyte); - bool seek(uint32_t pos); - uint32_t position(); - uint32_t size(); - void close(); - operator bool(); - char * name(); - - bool isDirectory(void); - File openNextFile(uint8_t mode = O_RDONLY); - void rewindDirectory(void); - - using Print::write; +class File_Base : public Stream { +private: + char _name[13]; // our name + SdFile *_file; // underlying file pointer + +public: + File_Base(SdFile f, const char *name); // wraps an underlying SdFile + File_Base(void); // 'empty' constructor + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int availableForWrite(); + virtual int read(); + virtual int peek(); + virtual int available(); + virtual void flush(); + int read(void *buf, uint16_t nbyte); + bool seek(uint32_t pos); + uint32_t position(); + uint32_t size(); + void close(); + operator bool(); + char *name(); + + bool isDirectory(void); + File_Base openNextFile(uint8_t mode = O_RDONLY); + void rewindDirectory(void); + + using Print::write; + #ifdef MOCK_PINS_COUNT - int getWriteError() { return writeError; } - void setWriteError(int value = 1) { writeError = value; } - void clearWriteError() { writeError = 0; } - private: - int writeError; + virtual String className() const { return "File_Base"; } #endif - }; - - 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. - bool begin(uint8_t csPin = SD_CHIP_SELECT_PIN); - bool begin(uint32_t clock, uint8_t csPin); - - //call this when a card is removed. It will allow you to insert and initialise a new card. - void end(); - - // 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); - File open(const String &filename, uint8_t mode = FILE_READ) { - return open(filename.c_str(), mode); - } - - // Methods to determine if the requested file path exists. - bool exists(const char *filepath); - bool exists(const String &filepath) { - return exists(filepath.c_str()); - } - - // Create the requested directory heirarchy--if intermediate directories - // do not exist they will be created. - bool mkdir(const char *filepath); - bool mkdir(const String &filepath) { - return mkdir(filepath.c_str()); - } - - // Delete the file. - bool remove(const char *filepath); - bool remove(const String &filepath) { - return remove(filepath.c_str()); - } - - bool rmdir(const char *filepath); - bool rmdir(const String &filepath) { - return rmdir(filepath.c_str()); - } - - 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 bool callback_openPath(SdFile&, const char *, bool, void *); - }; - - extern SDClass SD; +}; + +class SDClass_Base { + +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. + bool begin(uint8_t csPin = SD_CHIP_SELECT_PIN); + bool begin(uint32_t clock, uint8_t csPin); + + // call this when a card is removed. It will allow you to insert and + // initialise a new card. + void end(); + + // Open the specified file/directory with the supplied mode (e.g. read or + // write, etc). Returns a File_Base object for interacting with the file. + // Note that currently only one file can be open at a time. + File_Base open(const char *filename, uint8_t mode = FILE_READ); + File_Base open(const String &filename, uint8_t mode = FILE_READ) { + return open(filename.c_str(), mode); + } + // Methods to determine if the requested file path exists. + bool exists(const char *filepath); + bool exists(const String &filepath) { return exists(filepath.c_str()); } + + // Create the requested directory heirarchy--if intermediate directories + // do not exist they will be created. + bool mkdir(const char *filepath); + bool mkdir(const String &filepath) { return mkdir(filepath.c_str()); } + + // Delete the file. + bool remove(const char *filepath); + bool remove(const String &filepath) { return remove(filepath.c_str()); } + + bool rmdir(const char *filepath); + bool rmdir(const String &filepath) { return rmdir(filepath.c_str()); } + +#ifdef MOCK_PINS_COUNT + virtual String className() const { return "SDClass_Base"; } +#endif + +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_Base; + friend bool callback_openPath(SdFile &, const char *, bool, void *); }; -// We enclose File and SD classes in namespace SDLib to avoid conflicts -// with others legacy libraries that redefines File class. +extern SDClass_Base SD; + +}; // namespace SDLib + +// We enclose File_Base and SD classes in namespace SDLib to avoid conflicts +// with others legacy libraries that redefines File_Base class. // This ensure compatibility with sketches that uses only SD library using namespace SDLib; -// This allows sketches to use SDLib::File with other libraries (in the -// sketch you must use SDFile instead of File to disambiguate) -typedef SDLib::File SDFile; -typedef SDLib::SDClass SDFileSystemClass; -#define SDFileSystem SDLib::SD +// This allows sketches to use SDLib::File_Base with other libraries (in the +// sketch you must use SDFile instead of File_Base to disambiguate) +typedef SDLib::File_Base SDFile; +typedef SDLib::SDClass_Base SDFileSystemClass; +#define SDFileSystem SDLib::SD #endif diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp new file mode 100644 index 0000000..d833e1c --- /dev/null +++ b/src/SD_CI.cpp @@ -0,0 +1,95 @@ +#include +#ifdef MOCK_PINS_COUNT +#include "SD_CI.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace SDLib { + +void File_CI::rewindDirectory(void) { + // File_Base::rewindDirectory(); +} + +// ================================================================= + +bool SDClass_CI::begin(uint8_t csPin) { + // return SDClass_Base::begin(csPin); +} + +bool SDClass_CI::begin(uint32_t clock, uint8_t csPin) { + // return SDClass_Base::begin(clock, csPin); +} + +void SDClass_CI::end() { + // SDClass_Base::end(); +} + +File_CI SDClass_CI::open(const char *filename, uint8_t mode) { + return File_CI(filename, mode); +} + +bool SDClass_CI::exists(const char *filepath) { + // prepend base file path + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, filepath); + + return fs::exists(newPath); +} + +bool SDClass_CI::mkdir(const char *filepath) { + // prepend base file path + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, filepath); + + if (strchr(filepath, '/') == nullptr) { + // Creating a single directory + try { + fs::create_directory(newPath); + } catch (fs::filesystem_error &excpt) { + std::cerr << excpt.what() << std::endl; + return false; + } + return true; + } else { + // creating multiple directories + try { + fs::create_directories(newPath); + } catch (fs::filesystem_error &excpt) { + std::cerr << excpt.what() << std::endl; + return false; + } + return true; + } +} + +bool SDClass_CI::remove(const char *filepath) { + // prepend base file path + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, filepath); + + return fs::remove(newPath); +} + +bool SDClass_CI::rmdir(const char *filepath) { + // prepend base file path + char newPath[FILE_PATH_SIZE]; + strcpy(newPath, BASE_PATH); + strcat(newPath, filepath); + + return fs::remove_all(newPath); +} + +SDClass_CI sd_ci; + +} // namespace SDLib + +#endif diff --git a/src/SD_CI.h b/src/SD_CI.h new file mode 100644 index 0000000..67fc2fb --- /dev/null +++ b/src/SD_CI.h @@ -0,0 +1,106 @@ +#pragma once +#include +#ifdef MOCK_PINS_COUNT +#include +#include +#include + +namespace fs = std::filesystem; + +namespace SDLib { + +#define BASE_PATH "fileSystem/" +#define FILE_PATH_SIZE 255 + +class File_CI : public File_Base { +private: + std::fstream *finOut; + // int _pos; + char _path[FILE_PATH_SIZE]; + uint8_t _mode; + bool _open; + bool _isDirectory; + +public: + // File_CI(File_Base *baseFile); + // File_CI(SdFile f, const char *name); // wraps an underlying SdFile + // File_CI(void); // 'empty' constructor + File_CI(const char *name, uint8_t mode); + ~File_CI(); + // template size_t write1(T data); + virtual size_t write(const char *buf, size_t size); + virtual int availableForWrite(); + virtual int peek(); + virtual int available(); + virtual void flush(); + int read(char *buf, size_t size); + bool seek(uint32_t pos); + uint32_t position(); + uint32_t size(); + void close(); + operator bool(); + char *name(); + bool isOpen() { return _open; } + + bool isDirectory(void); + File_CI openNextFile(uint8_t mode); + void rewindDirectory(void); + + using Print::write; + // testing functions + int getWriteError() { return writeError; } + void setWriteError(int value = 1) { writeError = value; } + void clearWriteError() { writeError = 0; } + +private: + int writeError; +}; + +class SDClass_CI : public SDClass_Base { + +private: +public: + // This needs to be called to set up the connection to the SD card + // before other methods are used. + bool begin(uint8_t csPin = SD_CHIP_SELECT_PIN); + bool begin(uint32_t clock, uint8_t csPin); + + // call this when a card is removed. It will allow you to insert and + // initialise a new card. + void end(); + + // 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_CI open(const char *filename, uint8_t mode); + File_CI open(const String &filename, uint8_t mode = FILE_READ) { + return open(filename.c_str(), mode); + } + + // Methods to determine if the requested file path exists. + bool exists(const char *filepath); + bool exists(const String &filepath) { return exists(filepath.c_str()); } + + // Create the requested directory heirarchy--if intermediate directories + // do not exist they will be created. + bool mkdir(const char *filepath); + bool mkdir(const String &filepath) { return mkdir(filepath.c_str()); } + + // Delete the file. + bool remove(const char *filepath); + bool remove(const String &filepath) { return remove(filepath.c_str()); } + + bool rmdir(const char *filepath); + bool rmdir(const String &filepath) { return rmdir(filepath.c_str()); } + + virtual String className() const { return "SDClass_Base"; } + +private: + friend class File; +}; + +extern SDClass_CI sd_ci; + +} // namespace SDLib + +#endif diff --git a/src/utility/Sd2Card.h b/src/utility/Sd2Card.h index 5d91ebf..cb55ff4 100644 --- a/src/utility/Sd2Card.h +++ b/src/utility/Sd2Card.h @@ -57,6 +57,12 @@ uint8_t const SPI_QUARTER_SPEED = 2; // include pins_arduino.h or variant.h depending on architecture, via Arduino.h #include + #if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) + #define SDCARD_SS_PIN 4 + #define SDCARD_MOSI_PIN 11 + #define SDCARD_MISO_PIN 12 + #define SDCARD_SCK_PIN 13 + #endif /** SD Chip Select pin diff --git a/src/utility/Sd2PinMap.h b/src/utility/Sd2PinMap.h index c272bff..af261f4 100644 --- a/src/utility/Sd2PinMap.h +++ b/src/utility/Sd2PinMap.h @@ -18,21 +18,31 @@ . */ -#include - -#ifdef MOCK_PINS_COUNT - #define SDCARD_SS_PIN 4 - #define SDCARD_MOSI_PIN 11 - #define SDCARD_MISO_PIN 12 - #define SDCARD_SCK_PIN 13 -#endif - #if defined(__arm__) // Arduino Due Board follows #ifndef Sd2PinMap_h #define Sd2PinMap_h #include + #if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) + #include + // #ifdef _SFR_IO8 + // #undef _SFR_IO8 + // #define _AVR_IO_REG(type, mem_addr) (*(type*)(&avr_io_registers[mem_addr])) + // #define __SFR_OFFSET 0x20 + // #define _SFR_IO8(io_addr) _AVR_IO_REG(uint8_t, io_addr + __SFR_OFFSET) + // #define _SFR_IO16(io_addr) _AVR_IO_REG(uint16_t, io_addr + __SFR_OFFSET) + // #define _SFR_MEM8(mem_addr) _AVR_IO_REG(uint8_t, mem_addr) + // #define _SFR_MEM16(mem_addr) _AVR_IO_REG(uint16_t, mem_addr) + // #define _SFR_MEM32(mem_addr) _AVR_IO_REG(uint32_t, mem_addr) + + // extern uint8_t avr_io_registers[RAMSTART]; + // #endif + #define SDCARD_SS_PIN 4 + #define SDCARD_MOSI_PIN 11 + #define SDCARD_MISO_PIN 12 + #define SDCARD_SCK_PIN 13 + #endif uint8_t const SS_PIN = SS; uint8_t const MOSI_PIN = MOSI; @@ -48,7 +58,7 @@ #include - #ifdef MOCK_PINS_COUNT + #if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) #define SDCARD_SS_PIN 4 #define SDCARD_MOSI_PIN 11 #define SDCARD_MISO_PIN 12 diff --git a/src/utility/SdFat.h b/src/utility/SdFat.h index 33f503b..d5c48f6 100644 --- a/src/utility/SdFat.h +++ b/src/utility/SdFat.h @@ -23,6 +23,9 @@ \file SdFile and SdVolume classes */ + +#include + #if defined (__AVR__) || defined (__CPU_ARC__) #include #endif diff --git a/test/test.cpp b/test/test.cpp index 2eccb8a..d789f9a 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -6,11 +6,267 @@ bundle exec arduino_ci.rb --skip-unittests */ #include "Arduino.h" #include "ArduinoUnitTests.h" -#include "SD.h" +#include "SD_CI.h" +#include -unittest(test) { - // add tests here - assertTrue(true); +unittest(exists_works) { + assertFalse(sd_ci.exists("noSuchFile.txt")); + File_CI existingFile = sd_ci.open("existingFile.txt", FILE_WRITE); + existingFile.close(); + assertTrue(sd_ci.exists("existingFile.txt")); + sd_ci.remove("existingFile.txt"); } +unittest(mkdir_works) { + assertTrue(sd_ci.mkdir("test_directory/a/b")); + assertTrue(sd_ci.mkdir("test_directory/a/c")); + assertTrue(sd_ci.exists("test_directory")); + assertTrue(sd_ci.exists("test_directory/a/b")); + assertTrue(sd_ci.exists("test_directory/a/c")); + + // tear down + sd_ci.rmdir("test_directory"); +} + +unittest(open_works) { + // create file + File_CI writeFile = sd_ci.open("file.txt", FILE_WRITE); + writeFile.close(); + // open file for read should exist + File_CI readFile2 = sd_ci.open("file.txt", FILE_READ); + assertTrue(readFile2.isOpen()); + readFile2.close(); + // delete test file + sd_ci.remove("file.txt"); +} + +unittest(close_works) { + // close write file + File_CI file = sd_ci.open("file.txt", FILE_WRITE); + assertTrue(file.isOpen()); + file.close(); + assertFalse(file.isOpen()); + + // close read file + File_CI readFile = sd_ci.open("file.txt", FILE_READ); + assertTrue(readFile.isOpen()); + readFile.close(); + assertFalse(readFile.isOpen()); + sd_ci.remove("file.txt"); +} + +unittest(remove_works) { + // set up + File_CI file = sd_ci.open("file.txt", FILE_WRITE); + file.close(); + assertTrue(sd_ci.exists("file.txt")); + + sd_ci.remove("file.txt"); + assertFalse(sd_ci.exists("file.txt")); +} + +unittest(rmdir_works) { + // set up + sd_ci.mkdir("test_directory/a/a"); + sd_ci.mkdir("test_directory/a/b"); + sd_ci.mkdir("test_directory/a/c"); + File_CI file = sd_ci.open("test_directory/a/a/file.txt", FILE_WRITE); + file.close(); + + // remove directory + assertTrue(sd_ci.rmdir("test_directory/a/c")); + // make sure non-removed dirs still exist + assertTrue(sd_ci.exists("test_directory")); + assertTrue(sd_ci.exists("test_directory/a/a")); + // make sure removed dir no longer exists + assertFalse(sd_ci.exists("test_directory/a/c")); + + // remove directory with sub directories + assertTrue(sd_ci.rmdir("test_directory")); + assertFalse(sd_ci.exists("test_directory")); +} + +unittest(name_works) { + // set up + File_CI file = sd_ci.open("newFile.txt", FILE_WRITE); + + char expected[] = "newFile.txt"; + assertEqual(expected, file.name()); + file.close(); + + // tear down + sd_ci.remove("newFile.txt"); +} + +unittest(seek_works) { + // set up + File_CI file = sd_ci.open("seek.txt", FILE_WRITE); + char write[] = "Hide and Seek."; + file.write(write, sizeof(write) - 1); + file.close(); + File_CI read = sd_ci.open("seek.txt", FILE_READ); + + // Testing + char readFrom[4]; + char expected[] = "and"; + read.seek(5); + read.read(readFrom, 3); + readFrom[3] = '\0'; + assertEqual(expected, readFrom); + + // tear down + sd_ci.remove("seek.txt"); +} + +unittest(read_works) { + // set up + File_CI file = sd_ci.open("birthday.txt", FILE_WRITE); + char toWrite[] = "Happy Birthday to You!"; + file.write(toWrite, sizeof(toWrite) - 1); + file.close(); + + File_CI file2 = sd_ci.open("lines.txt", FILE_WRITE); + char toWrite2[] = "line 1\nline2"; + file2.write(toWrite2, sizeof(toWrite2) - 1); + file2.close(); + + // testing + File_CI readFile = sd_ci.open("birthday.txt", FILE_READ); + size_t size = readFile.size(); + char readFromFile[size + 1]; + readFile.read(readFromFile, size); + readFromFile[size] = '\0'; + readFile.close(); + + // assertEqual(toWrite, readFromFile); + + File_CI readFile2 = sd_ci.open("lines.txt", FILE_READ); + char readFromFile2[7 + 1]; + char readFromFile3[5 + 1]; + readFile2.read(readFromFile2, 7); + readFile2.read(readFromFile3, 5); + readFromFile2[7] = '\0'; + readFromFile3[5] = '\0'; + + char expected2[] = "line 1\n"; + char expected3[] = "line2"; + assertEqual(expected2, readFromFile2); + assertEqual(expected3, readFromFile3); + + // tear down + sd_ci.remove("birthday.txt"); + sd_ci.remove("lines.txt"); +} + +unittest(write_works) { + // open new file for writing + File_CI writeFile = sd_ci.open("wood.txt", FILE_WRITE); + char toWrite[] = "How much wood could a wood pecker peck?\n"; + writeFile.write(toWrite, sizeof(toWrite) - 1); + + // read from same write file + size_t size = writeFile.size(); + char readFromFile[size + 1]; + writeFile.seek(0); + writeFile.read(readFromFile, size); + readFromFile[size] = '\0'; + // assertEqual(toWrite, readFromFile); + writeFile.close(); + + // open old writing file to write at end. + File_CI writeFile2 = sd_ci.open("wood.txt", FILE_WRITE); + char toWrite2[] = "A lot of wood.\n"; + writeFile2.write(toWrite2, sizeof(toWrite2) - 1); + writeFile2.close(); + + // check old writing file + File_CI readWrite2 = sd_ci.open("wood.txt", FILE_READ); + size_t size2 = readWrite2.size(); + char toRead2[size2 + 1]; + readWrite2.read(toRead2, size2); + toRead2[size] = '\0'; + char expected2[] = + "How much wood could a wood pecker peck?\nA lot of wood.\n"; + // assertEqual(expected2, toRead2); + readWrite2.close(); + + // teardown + sd_ci.remove("wood.txt"); +} + +unittest(size_works) { + // setup + File_CI sizeFile = sd_ci.open("size.txt", FILE_WRITE); + char toWrite[] = "Test text\n"; + sizeFile.write(toWrite, sizeof(toWrite) - 1); + sizeFile.close(); + + // test + uint32_t fileSize = sizeFile.size(); + assertEqual(10, fileSize); + + // tear down + sd_ci.remove("size.txt"); +} + +unittest(peek_works) { + // set up + File_CI peekFile = sd_ci.open("peek.txt", FILE_WRITE); + char toWrite[] = "Peek file content\n"; + peekFile.write(toWrite, sizeof(toWrite) - 1); + peekFile.close(); + + // Test + File_CI readPeek = sd_ci.open("peek.txt", FILE_READ); + assertEqual('P', readPeek.peek()); + assertEqual('P', readPeek.peek()); + readPeek.close(); + + File_CI readWritePeek = sd_ci.open("peek.txt", FILE_WRITE); + readWritePeek.seek(0); + assertEqual('P', readWritePeek.peek()); + assertEqual('P', readWritePeek.peek()); + readWritePeek.close(); + + // tear down + sd_ci.remove("peek.txt"); +} + +unittest(position_works) { + // set up + File_CI posFile = sd_ci.open("pos.txt", FILE_WRITE); + char toWrite[] = "This is the position file.\n"; + posFile.write(toWrite, sizeof(toWrite) - 1); + + // test + assertEqual(27, posFile.position()); + posFile.seek(5); + assertEqual(5, posFile.position()); + posFile.close(); + + // tear down + sd_ci.remove("pos.txt"); +} + +unittest(isDirectory_works) { + // set up + sd_ci.mkdir("test"); + File_CI toRead = sd_ci.open("read.txt", FILE_WRITE); + toRead.close(); + File_CI testFile = sd_ci.open("test.txt", FILE_WRITE); + File_CI readFile = sd_ci.open("read.txt", FILE_READ); + + // test + File_CI myDir = sd_ci.open("test"); + assertTrue(myDir.isDirectory()); + assertFalse(testFile.isDirectory()); + assertFalse(readFile.isDirectory()); + + // tear down + testFile.close(); + readFile.close(); + sd_ci.remove("test.txt"); + sd_ci.remove("read.txt"); + sd_ci.rmdir("test"); +} unittest_main() From 47c6e0a2d13b8f53deb6ef2e0838655b9f1aa465 Mon Sep 17 00:00:00 2001 From: James Foster Date: Tue, 2 Mar 2021 21:16:58 -0800 Subject: [PATCH 08/17] Mock for SD (#11) * We now have an in-memory mock (test double) for the file system! * Add GitHub Action (fix #8) * Update format. * Format examples. * Add include for `assert()`. * Use proper delete to avoid AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete). * Do proper garbage collection when removing all files. * Update format. Co-authored-by: James Foster --- .github/workflows/arduino_ci.yml | 12 + .github/workflows/format.yml | 15 + .gitignore | 1 + examples/CardInfo/CardInfo.ino | 58 +- examples/Datalogger/Datalogger.ino | 16 +- examples/DumpFile/DumpFile.ino | 17 +- examples/Files/Files.ino | 8 +- .../NonBlockingWrite/NonBlockingWrite.ino | 15 +- examples/ReadWrite/ReadWrite.ino | 12 +- examples/listfiles/listfiles.ino | 22 +- src/File.cpp | 30 +- src/File_CI.cpp | 227 +---- src/SD.cpp | 810 ++++++++-------- src/SD_CI.cpp | 113 +-- src/SD_CI.h | 117 ++- src/utility/FatStructs.h | 72 +- src/utility/Sd2Card.cpp | 183 ++-- src/utility/Sd2Card.h | 282 +++--- src/utility/Sd2PinMap.cpp | 2 +- src/utility/Sd2PinMap.h | 669 ++++++------- src/utility/SdFat.h | 918 ++++++++---------- src/utility/SdFatUtil.h | 30 +- src/utility/SdFatmainpage.h | 38 +- src/utility/SdFile.cpp | 138 ++- src/utility/SdInfo.h | 18 +- src/utility/SdVolume.cpp | 38 +- test/test.cpp | 156 ++- 27 files changed, 1860 insertions(+), 2157 deletions(-) create mode 100644 .github/workflows/arduino_ci.yml create mode 100644 .github/workflows/format.yml diff --git a/.github/workflows/arduino_ci.yml b/.github/workflows/arduino_ci.yml new file mode 100644 index 0000000..a740665 --- /dev/null +++ b/.github/workflows/arduino_ci.yml @@ -0,0 +1,12 @@ +--- +name: Arduino CI + +on: [push, pull_request] + +jobs: + arduino_ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: Arduino-CI/action@stable-1.x diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..c8f556d --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,15 @@ +name: test-clang-format + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: DoozyX/clang-format-lint-action@v0.5 + with: + source: './' + extensions: 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx,ino' + clangFormatVersion: 9 diff --git a/.gitignore b/.gitignore index 32a2490..9343ab4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ Gemfile.lock .bundle vendor *.cpp.bin* +.vscode diff --git a/examples/CardInfo/CardInfo.ino b/examples/CardInfo/CardInfo.ino index 3418e92..0b9af28 100644 --- a/examples/CardInfo/CardInfo.ino +++ b/examples/CardInfo/CardInfo.ino @@ -3,15 +3,14 @@ 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. - Pin numbers reflect the default SPI pins for Uno and Nano models - The circuit: - SD card attached to SPI bus as follows: + Very useful for testing a card when you're not sure whether its working or + not. Pin numbers reflect the default SPI pins for Uno and Nano models The + circuit: SD card attached to SPI bus as follows: ** SDO - pin 11 on Arduino Uno/Duemilanove/Diecimila ** SDI - pin 12 on Arduino Uno/Duemilanove/Diecimila ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila ** CS - depends on your SD card shield or module. - Pin 10 used here for consistency with other Arduino examples + Pin 10 used here for consistency with other Arduino examples created 28 Mar 2011 by Limor Fried @@ -19,8 +18,8 @@ by Tom Igoe */ // include the SD library: -#include #include +#include // set up variables using the SD utility library functions: Sd2Card card; @@ -42,7 +41,6 @@ void setup() { ; // wait for serial port to connect. Needed for native USB port only } - Serial.print("\nInitializing SD card..."); // we'll use the initialization code from the utility libraries @@ -51,8 +49,10 @@ void setup() { Serial.println("initialization failed. Things to check:"); Serial.println("* is a card inserted?"); Serial.println("* is your wiring correct?"); - Serial.println("* did you change the chipSelect pin to match your shield or module?"); - while (1); + Serial.println( + "* did you change the chipSelect pin to match your shield or module?"); + while (1) + ; } else { Serial.println("Wiring is correct and a card is present."); } @@ -61,23 +61,26 @@ void setup() { Serial.println(); Serial.print("Card 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"); + 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 + // 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"); - while (1); + Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've " + "formatted the card"); + while (1) + ; } Serial.print("Clusters: "); @@ -94,9 +97,9 @@ void setup() { Serial.print("Volume type is: FAT"); Serial.println(volume.fatType(), DEC); - volumesize = volume.blocksPerCluster(); // clusters are collections of blocks - volumesize *= volume.clusterCount(); // we'll have a lot of clusters - volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1KB) + volumesize = volume.blocksPerCluster(); // clusters are collections of blocks + volumesize *= volume.clusterCount(); // we'll have a lot of clusters + volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1KB) Serial.print("Volume size (Kb): "); Serial.println(volumesize); Serial.print("Volume size (Mb): "); @@ -113,5 +116,4 @@ void setup() { root.close(); } -void loop(void) { -} +void loop(void) {} diff --git a/examples/Datalogger/Datalogger.ino b/examples/Datalogger/Datalogger.ino index fdbacfd..0eae279 100644 --- a/examples/Datalogger/Datalogger.ino +++ b/examples/Datalogger/Datalogger.ino @@ -12,7 +12,7 @@ ** SDI - pin 12 ** CLK - pin 13 ** CS - depends on your SD card shield or module. - Pin 10 used here for consistency with other Arduino examples + Pin 10 used here for consistency with other Arduino examples (for MKRZero SD: SDCARD_SS_PIN) created 24 Nov 2010 @@ -23,8 +23,8 @@ */ -#include #include +#include const int chipSelect = 10; @@ -32,7 +32,8 @@ void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); // wait for Serial Monitor to connect. Needed for native USB port boards only: - while (!Serial); + while (!Serial) + ; Serial.print("Initializing SD card..."); @@ -40,9 +41,12 @@ void setup() { Serial.println("initialization failed. Things to check:"); Serial.println("1. is a card inserted?"); Serial.println("2. is your wiring correct?"); - Serial.println("3. did you change the chipSelect pin to match your shield or module?"); - Serial.println("Note: press reset or reopen this serial monitor after fixing your issue!"); - while (true); + Serial.println( + "3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset or reopen this serial monitor after " + "fixing your issue!"); + while (true) + ; } Serial.println("initialization done."); diff --git a/examples/DumpFile/DumpFile.ino b/examples/DumpFile/DumpFile.ino index bb114b6..15e1213 100644 --- a/examples/DumpFile/DumpFile.ino +++ b/examples/DumpFile/DumpFile.ino @@ -11,7 +11,7 @@ ** SDI - pin 12 ** CLK - pin 13 ** CS - depends on your SD card shield or module. - Pin 10 used here for consistency with other Arduino examples + Pin 10 used here for consistency with other Arduino examples (for MKRZero SD: SDCARD_SS_PIN) created 22 December 2010 @@ -29,7 +29,8 @@ void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); // wait for Serial Monitor to connect. Needed for native USB port boards only: - while (!Serial); + while (!Serial) + ; Serial.print("Initializing SD card..."); @@ -37,9 +38,12 @@ void setup() { Serial.println("initialization failed. Things to check:"); Serial.println("1. is a card inserted?"); Serial.println("2. is your wiring correct?"); - Serial.println("3. did you change the chipSelect pin to match your shield or module?"); - Serial.println("Note: press reset or reopen this serial monitor after fixing your issue!"); - while (true); + Serial.println( + "3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset or reopen this serial monitor after " + "fixing your issue!"); + while (true) + ; } Serial.println("initialization done."); @@ -61,5 +65,4 @@ void setup() { } } -void loop() { -} +void loop() {} diff --git a/examples/Files/Files.ino b/examples/Files/Files.ino index f19a9db..650dcbc 100644 --- a/examples/Files/Files.ino +++ b/examples/Files/Files.ino @@ -9,7 +9,7 @@ ** SDI - pin 12 ** CLK - pin 13 ** CS - depends on your SD card shield or module. - Pin 10 used here for consistency with other Arduino examples + Pin 10 used here for consistency with other Arduino examples (for MKRZero SD: SDCARD_SS_PIN) created Nov 2010 @@ -28,13 +28,15 @@ void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); // wait for Serial Monitor to connect. Needed for native USB port boards only: -while (!Serial); + while (!Serial) + ; Serial.print("Initializing SD card..."); if (!SD.begin(chipSelect)) { Serial.println("initialization failed!"); - while (1); + while (1) + ; } Serial.println("initialization done."); diff --git a/examples/NonBlockingWrite/NonBlockingWrite.ino b/examples/NonBlockingWrite/NonBlockingWrite.ino index 082a5fb..87dbe2c 100644 --- a/examples/NonBlockingWrite/NonBlockingWrite.ino +++ b/examples/NonBlockingWrite/NonBlockingWrite.ino @@ -55,7 +55,8 @@ void setup() { pinMode(LED_BUILTIN, OUTPUT); // wait for Serial Monitor to connect. Needed for native USB port boards only: - while (!Serial); + while (!Serial) + ; Serial.print("Initializing SD card..."); @@ -63,9 +64,12 @@ void setup() { Serial.println("initialization failed. Things to check:"); Serial.println("1. is a card inserted?"); Serial.println("2. is your wiring correct?"); - Serial.println("3. did you change the chipSelect pin to match your shield or module?"); - Serial.println("Note: press reset or reopen this serial monitor after fixing your issue!"); - while (true); + Serial.println( + "3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset or reopen this serial monitor after " + "fixing your issue!"); + while (true) + ; } Serial.println("initialization done."); @@ -79,7 +83,8 @@ void setup() { if (!myFile) { Serial.print("error opening "); Serial.println(filename); - while (true); + while (true) + ; } // add some new lines to start diff --git a/examples/ReadWrite/ReadWrite.ino b/examples/ReadWrite/ReadWrite.ino index c15ecdb..5ebc03f 100644 --- a/examples/ReadWrite/ReadWrite.ino +++ b/examples/ReadWrite/ReadWrite.ino @@ -27,7 +27,8 @@ void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); // wait for Serial Monitor to connect. Needed for native USB port boards only: - while (!Serial); + while (!Serial) + ; Serial.print("Initializing SD card..."); @@ -35,9 +36,12 @@ void setup() { Serial.println("initialization failed. Things to check:"); Serial.println("1. is a card inserted?"); Serial.println("2. is your wiring correct?"); - Serial.println("3. did you change the chipSelect pin to match your shield or module?"); - Serial.println("Note: press reset or reopen this serial monitor after fixing your issue!"); - while (true); + Serial.println( + "3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset or reopen this serial monitor after " + "fixing your issue!"); + while (true) + ; } Serial.println("initialization done."); diff --git a/examples/listfiles/listfiles.ino b/examples/listfiles/listfiles.ino index 66eebb6..f532828 100644 --- a/examples/listfiles/listfiles.ino +++ b/examples/listfiles/listfiles.ino @@ -11,7 +11,7 @@ ** SDI - pin 12 ** CLK - pin 13 ** CS - depends on your SD card shield or module. - Pin 10 used here for consistency with other Arduino examples + Pin 10 used here for consistency with other Arduino examples (for MKRZero SD: SDCARD_SS_PIN) created Nov 2010 @@ -22,7 +22,7 @@ by Scott Fitzgerald modified 24 July 2020 by Tom Igoe - + This example code is in the public domain. */ @@ -32,10 +32,11 @@ const int chipSelect = 10; File root; void setup() { - // Open serial communications and wait for port to open: + // Open serial communications and wait for port to open: Serial.begin(9600); // wait for Serial Monitor to connect. Needed for native USB port boards only: - while (!Serial); + while (!Serial) + ; Serial.print("Initializing SD card..."); @@ -43,9 +44,12 @@ void setup() { Serial.println("initialization failed. Things to check:"); Serial.println("1. is a card inserted?"); Serial.println("2. is your wiring correct?"); - Serial.println("3. did you change the chipSelect pin to match your shield or module?"); - Serial.println("Note: press reset or reopen this serial monitor after fixing your issue!"); - while (true); + Serial.println( + "3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset or reopen this serial monitor after " + "fixing your issue!"); + while (true) + ; } Serial.println("initialization done."); @@ -64,8 +68,8 @@ void loop() { void printDirectory(File dir, int numTabs) { while (true) { - File entry = dir.openNextFile(); - if (! entry) { + File entry = dir.openNextFile(); + if (!entry) { // no more files break; } diff --git a/src/File.cpp b/src/File.cpp index 4478ec1..e875bce 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -22,7 +22,7 @@ File_Base::File_Base(SdFile f, const char *n) { // oh man you are kidding me, new() doesn't exist? Ok we do it by hand! _file = (SdFile *)malloc(sizeof(SdFile)); if (_file) { - memcpy((void*)_file, (void*)&f, sizeof(SdFile)); + memcpy((void *)_file, (void *)&f, sizeof(SdFile)); strncpy(_name, n, 12); _name[12] = 0; @@ -40,23 +40,16 @@ File_Base::File_Base(SdFile f, const char *n) { File_Base::File_Base(void) { _file = 0; _name[0] = 0; - //Serial.print("Created empty file object"); + // Serial.print("Created empty file object"); } // returns a pointer to the file name -char *File_Base::name(void) { - return _name; -} +char *File_Base::name(void) { return _name; } // a directory is a special type of file -bool File_Base::isDirectory(void) { - return (_file && _file->isDir()); -} +bool File_Base::isDirectory(void) { return (_file && _file->isDir()); } - -size_t File_Base::write(uint8_t val) { - return write(&val, 1); -} +size_t File_Base::write(uint8_t val) { return write(&val, 1); } size_t File_Base::write(const uint8_t *buf, size_t size) { size_t t; @@ -81,7 +74,7 @@ int File_Base::availableForWrite() { } int File_Base::peek() { - if (! _file) { + if (!_file) { return 0; } @@ -108,7 +101,7 @@ int File_Base::read(void *buf, uint16_t nbyte) { } int File_Base::available() { - if (! _file) { + if (!_file) { return 0; } @@ -124,7 +117,7 @@ void File_Base::flush() { } bool File_Base::seek(uint32_t pos) { - if (! _file) { + if (!_file) { return false; } @@ -132,14 +125,14 @@ bool File_Base::seek(uint32_t pos) { } uint32_t File_Base::position() { - if (! _file) { + if (!_file) { return -1; } return _file->curPosition(); } uint32_t File_Base::size() { - if (! _file) { + if (!_file) { return 0; } return _file->fileSize(); @@ -161,8 +154,7 @@ void File_Base::close() { File_Base::operator bool() { if (_file) { - return _file->isOpen(); + return _file->isOpen(); } return false; } - diff --git a/src/File_CI.cpp b/src/File_CI.cpp index 7168712..3af9bf0 100644 --- a/src/File_CI.cpp +++ b/src/File_CI.cpp @@ -6,101 +6,47 @@ #include #include #include -// #include namespace SDLib { -// File_CI::File_CI(File_Base *baseFile) { -// // this->baseFile = baseFile; -// } - -// File_CI::File_CI(SdFile f, const char *name) : File_Base(f, name) {} - -// File_CI::File_CI(void) : File_Base() {} - -File_CI::File_CI(const char *name, uint8_t mode = FILE_READ) { +File_CI::File_CI(const char *name, uint8_t mode) { + _path = name; _mode = mode; - strcpy(_path, name); - - // prepend base path - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, name); - - // does path exist? - if (fs::exists(newPath)) { - // if so, is it a directory? - if (fs::is_directory(newPath)) { - // if so, return a new object with the _isDirectory flag set - _isDirectory = true; - } else { - // path is an existing file - _isDirectory = false; - // if file is read, open for read - if (_mode == FILE_READ) { - finOut = new std::fstream; - finOut->open(newPath, std::fstream::binary | std::fstream::in); - } else if (_mode == FILE_WRITE) { - // else if mode is FILE_WRITE open for read and write - finOut = new std::fstream; - finOut->open(newPath, std::fstream::binary | std::fstream::in | - std::fstream::out | std::fstream::app); + _isOpen = true; + _pos = 0; + if (SD_CI.dirExists(name)) { + _isDir = true; + } else { + _isDir = false; + assert(mode & O_CREAT || SD_CI.fileExists(name)); + pContents = SD_CI.contentsOfNewOrExistingFileWithName(name); + if (mode & O_WRITE) { + if (mode & O_APPEND) { + _pos = pContents->size(); } else { - // else invalid mode, assert false - assert(false); + *pContents = ""; } - _open = true; - } - } else { - // path does not exist - _isDirectory = false; - // if mode is write, open new file - if (_mode == FILE_WRITE) { - finOut = new std::fstream; - finOut->open(newPath, std::fstream::binary | std::fstream::in | - std::fstream::out | std::fstream::app); - _open = true; - } else if (_mode == FILE_READ) { - // else assert false for file doesn't exist - assert(false); - } else { - // else invalid mode. assert false - assert(false); } } } -File_CI::~File_CI() { - if (!_isDirectory) { - delete finOut; +int File_CI::peek() { + if (_pos < pContents->size()) { + return pContents->at(_pos); + } else { + return EOF; } } -// size_t File_CI::write(uint8_t value) { -// // TODO -// // return File_Base::write(value); -// } - size_t File_CI::write(const char *buf, size_t size) { - // halt if file is directory - if (_isDirectory) { - assert(false); - } - - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, _path); - assert(_mode == FILE_WRITE); - finOut->write(buf, size); + for (int i = 0; i < size; ++i) { + *pContents += buf[i]; + } + _pos += size; return size; } -int File_CI::availableForWrite() { - // TODO - // return File_Base::availableForWrite(); -} - /* Note for read(): the user of this function needs to append their own null character to the end of the char array. This function will attempt to insert the number of bytes @@ -110,131 +56,12 @@ int File_CI::availableForWrite() { this function is used. */ int File_CI::read(char *buf, size_t size) { - // halt if file is directory - if (_isDirectory) { - assert(false); - } - - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, _path); - - int remaining = fs::file_size(newPath) - finOut->tellg(); - // if size is greater than remaining, use remaining for read - if (size > remaining) { - finOut->read(buf, remaining); - } else { - // else use size - finOut->read(buf, size); - } - return 0; -} - -int File_CI::peek() { - // halt if file is directory - if (_isDirectory) { - assert(false); - } - - char byte[2]; - // save old pointer position - fpos_t oldPos = finOut->tellg(); - // read in first char - finOut->read(byte, 1); - byte[1] = '\0'; - // put file pointer back - finOut->seekg(oldPos); - - // if file pointer isn't back, assert false - if (finOut->tellg() != oldPos) { - assert(false); - } - // return first char in buffer - return byte[0]; -} - -int File_CI::available() { - // halt if file is directory - if (_isDirectory) { - assert(false); - } - - // prepend base file path - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, _path); - - return fs::file_size(newPath); -} - -void File_CI::flush() { - // return File_Base::flush(); -} - -bool File_CI::seek(uint32_t pos) { - // halt if file is directory - if (_isDirectory) { - assert(false); - } - - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, _path); - - // if pos is greater than size - 1, return false - if (pos > fs::file_size(newPath) - 1) { - // trying to seek outside of file scope - return false; - } else { - finOut->seekg(pos); - } - // check for success - if (finOut->tellg() == pos) { - return true; - } - return false; -} - -uint32_t File_CI::position() { - // halt if file is directory - if (_isDirectory) { - assert(false); - } - - return finOut->tellg(); -} - -uint32_t File_CI::size() { - // halt if file is directory - if (_isDirectory) { - assert(false); - } - - // prepend base file path - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, _path); - return fs::file_size(newPath); -} - -void File_CI::close() { - // close if not directory - if (!_isDirectory) { - finOut->close(); - } - _open = false; -} - -File_CI File_CI::openNextFile(uint8_t mode = FILE_READ) { - // TODO + size = size <= available() ? size : available(); + memcpy(buf, pContents->c_str() + _pos, size); + _pos += size; + return size; } -char *File_CI::name() { return _path; } - -File_CI::operator bool() { return File_Base::operator bool(); } - -bool File_CI::isDirectory(void) { return _isDirectory; } - } // namespace SDLib #endif diff --git a/src/SD.cpp b/src/SD.cpp index 636b608..3d18889 100644 --- a/src/SD.cpp +++ b/src/SD.cpp @@ -54,584 +54,558 @@ namespace SDLib { - // Used by `getNextPathComponent` +// Used by `getNextPathComponent` #define MAX_COMPONENT_LEN 12 // What is max length? -#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1 - - bool getNextPathComponent(const 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. +#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN + 1 +bool getNextPathComponent(const char *path, unsigned int *p_offset, + char *buffer) { + /* - `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size. + Parse individual path components from a path. - `p_offset` needs to point to an integer of the offset at - which the previous path component finished. + e.g. after repeated calls '/foo/bar/baz' will be split + into 'foo', 'bar', 'baz'. - Returns `true` if more components remain. + This is similar to `strtok()` but copies the component into the + supplied buffer rather than modifying the original string. - Returns `false` if this is the last component. - (This means path ended with 'foo' or 'foo/'.) - */ + `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size. - // TODO: Have buffer local to this function, so we know it's the - // correct length? + `p_offset` needs to point to an integer of the offset at + which the previous path component finished. - int bufferOffset = 0; + Returns `true` if more components remain. - int offset = *p_offset; + Returns `false` if this is the last component. + (This means path ended with 'foo' or 'foo/'.) - // 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++]; - } + // TODO: Have buffer local to this function, so we know it's the + // correct length? - buffer[bufferOffset] = '\0'; + int bufferOffset = 0; - // Skip trailing separator so we can determine if this - // is the last component in the path or not. - if (path[offset] == '/') { - offset++; - } + int offset = *p_offset; - *p_offset = offset; - - return (path[offset] != '\0'); + // 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'; - bool walkPath(const char *filepath, SdFile& parentDir, - bool(*callback)(SdFile& parentDir, - const char *filePathComponent, - bool 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. + // Skip trailing separator so we can determine if this + // is the last component in the path or not. + if (path[offset] == '/') { + offset++; + } - e.g. given the path '/foo/bar/baz' - the callback would be called at the equivalent of - '/foo', '/foo/bar' and '/foo/bar/baz'. + *p_offset = offset; - 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. + return (path[offset] != '\0'); +} - 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. +bool walkPath(const char *filepath, SdFile &parentDir, + bool (*callback)(SdFile &parentDir, const char *filePathComponent, + bool isLastComponent, void *object), + void *object = NULL) { + /* - If a directory path doesn't exist at some point this function will - also return false and not subsequently call the callback. + 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. - 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. + 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. - SdFile subfile1; - SdFile subfile2; + If a directory path doesn't exist at some point this function will + also return false and not subsequently call the callback. - char buffer[PATH_COMPONENT_BUFFER_LEN]; + 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. - unsigned int offset = 0; + */ - SdFile *p_parent; - SdFile *p_child; + SdFile subfile1; + SdFile subfile2; - SdFile *p_tmp_sdfile; + char buffer[PATH_COMPONENT_BUFFER_LEN]; - p_child = &subfile1; + unsigned int offset = 0; - p_parent = &parentDir; + SdFile *p_parent; + SdFile *p_child; - while (true) { + SdFile *p_tmp_sdfile; - bool moreComponents = getNextPathComponent(filepath, &offset, buffer); + p_child = &subfile1; - bool shouldContinue = callback((*p_parent), buffer, !moreComponents, object); + p_parent = &parentDir; - 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; - } + while (true) { - if (!moreComponents) { - break; - } + bool moreComponents = getNextPathComponent(filepath, &offset, buffer); - bool exists = (*p_child).open(*p_parent, buffer, O_RDONLY); + bool 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; + } - // 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 (!moreComponents) { + break; } + bool 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(); // TODO: Return/ handle different? + (*p_parent).close(); } - return true; + // 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. + 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. + 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. - */ +*/ - bool callback_pathExists(SdFile& parentDir, const char *filePathComponent, - bool /* isLastComponent */, void * /* object */) { - /* +bool callback_pathExists(SdFile &parentDir, const char *filePathComponent, + bool /* isLastComponent */, void * /* object */) { + /* - Callback used to determine if a file/directory exists in parent - directory. + Callback used to determine if a file/directory exists in parent + directory. - Returns true if file path exists. + Returns true if file path exists. - */ - SdFile child; + */ + SdFile child; - bool exists = child.open(parentDir, filePathComponent, O_RDONLY); + bool exists = child.open(parentDir, filePathComponent, O_RDONLY); - if (exists) { - child.close(); - } - - return exists; + if (exists) { + child.close(); } + return exists; +} +bool callback_makeDirPath(SdFile &parentDir, const char *filePathComponent, + bool isLastComponent, void *object) { + /* - bool callback_makeDirPath(SdFile& parentDir, const char *filePathComponent, - bool 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. + Callback used to create a directory in the parent directory if + it does not already exist. - */ - bool result = false; - SdFile child; + Returns true if a directory was created or it already existed. - result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object); - if (!result) { - result = child.makeDir(parentDir, filePathComponent); - } + */ + bool result = false; + SdFile child; - return result; + result = callback_pathExists(parentDir, filePathComponent, isLastComponent, + object); + if (!result) { + result = child.makeDir(parentDir, filePathComponent); } + return result; +} - /* - - bool callback_openPath(SdFile& parentDir, char *filePathComponent, - bool 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_Base` 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 hierarchy. + bool callback_openPath(SdFile& parentDir, char *filePathComponent, + bool isLastComponent, void *object) { - Returns false once the file has been opened--to prevent the traversal - from descending further. (This may be unnecessary.) + Callback used to open a file specified by a filepath that may + specify one or more directories above it. - if (isLastComponent) { - SDClass_Base *p_SD = static_cast(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; - } - */ + Expects the context object to be an instance of `SDClass_Base` 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 hierarchy. + Returns false once the file has been opened--to prevent the traversal + from descending further. (This may be unnecessary.) - bool callback_remove(SdFile& parentDir, const char *filePathComponent, - bool isLastComponent, void * /* object */) { - if (isLastComponent) { - return SdFile::remove(parentDir, filePathComponent); - } - return true; + if (isLastComponent) { + SDClass_Base *p_SD = static_cast(object); + p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode); + if (p_SD->fileOpenMode == FILE_WRITE) { + p_SD->file.seekSet(p_SD->file.fileSize()); } - - bool callback_rmdir(SdFile& parentDir, const char *filePathComponent, - bool isLastComponent, void * /* object */) { - if (isLastComponent) { - SdFile f; - if (!f.open(parentDir, filePathComponent, O_READ)) { - return false; - } - return f.rmDir(); - } - return true; + // TODO: Return file open result? + return false; } + return true; + } +*/ - - - /* Implementation of class used to create `SDCard` object. */ - - - - bool SDClass_Base::begin(uint8_t csPin) { - if (root.isOpen()) { - root.close(); +bool callback_remove(SdFile &parentDir, const char *filePathComponent, + bool isLastComponent, void * /* object */) { + if (isLastComponent) { + return SdFile::remove(parentDir, filePathComponent); + } + return true; +} + +bool callback_rmdir(SdFile &parentDir, const char *filePathComponent, + bool 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. */ - Performs the initialisation required by the sdfatlib library. +bool SDClass_Base::begin(uint8_t csPin) { + if (root.isOpen()) { + root.close(); + } - Return true if initialization succeeds, false otherwise. + /* - */ - return card.init(SPI_HALF_SPEED, csPin) && - volume.init(card) && - root.openRoot(volume); - } + Performs the initialisation required by the sdfatlib library. - bool SDClass_Base::begin(uint32_t clock, uint8_t csPin) { - if (root.isOpen()) { - root.close(); - } + Return true if initialization succeeds, false otherwise. - return card.init(SPI_HALF_SPEED, csPin) && - card.setSpiClock(clock) && - volume.init(card) && - root.openRoot(volume); - } + */ + return card.init(SPI_HALF_SPEED, csPin) && volume.init(card) && + root.openRoot(volume); +} - //call this when a card is removed. It will allow you to insert and initialise a new card. - void SDClass_Base::end() { +bool SDClass_Base::begin(uint32_t clock, uint8_t csPin) { + if (root.isOpen()) { root.close(); } - // this little helper is used to traverse paths - SdFile SDClass_Base::getParentDir(const char *filepath, int *index) { - // get parent directory - SdFile d1; - SdFile d2; + return card.init(SPI_HALF_SPEED, csPin) && card.setSpiClock(clock) && + volume.init(card) && root.openRoot(volume); +} - d1.openRoot(volume); // start with the mostparent, root! +// call this when a card is removed. It will allow you to insert and initialise +// a new card. +void SDClass_Base::end() { root.close(); } - // we'll use the pointers to swap between the two objects - SdFile *parent = &d1; - SdFile *subdir = &d2; +// this little helper is used to traverse paths +SdFile SDClass_Base::getParentDir(const char *filepath, int *index) { + // get parent directory + SdFile d1; + SdFile d2; - const char *origpath = filepath; + d1.openRoot(volume); // start with the mostparent, root! - while (strchr(filepath, '/')) { - - // get rid of leading /'s - if (filepath[0] == '/') { - filepath++; - continue; - } + // we'll use the pointers to swap between the two objects + SdFile *parent = &d1; + SdFile *subdir = &d2; - 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; // don't 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; + const char *origpath = filepath; - // we reuse the objects, close it. - parent->close(); + while (strchr(filepath, '/')) { - // swap the pointers - SdFile *t = parent; - parent = subdir; - subdir = t; + // get rid of leading /'s + if (filepath[0] == '/') { + filepath++; + continue; } - *index = (int)(filepath - origpath); - // parent is now the parent directory of the file! - return *parent; - } - + if (!strchr(filepath, '/')) { + // it was in the root directory, so leave now + break; + } - File_Base SDClass_Base::open(const char *filepath, uint8_t mode) { - /* + // extract just the name of the next subdirectory + uint8_t idx = strchr(filepath, '/') - filepath; + if (idx > 12) { + idx = 12; // don't 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; - Open the supplied file path for reading or writing. + // we reuse the objects, close it. + parent->close(); - The file content can be accessed via the `file` property of - the `SDClass_Base` object--this property is currently - a standard `SdFile` object from `sdfatlib`. + // swap the pointers + SdFile *t = parent; + parent = subdir; + subdir = t; + } - Defaults to read only. + *index = (int)(filepath - origpath); + // parent is now the parent directory of the file! + return *parent; +} - If `write` is true, default action (when `append` is true) is to - append data to the end of the file. +File_Base SDClass_Base::open(const char *filepath, uint8_t mode) { + /* - If `append` is false then the file will be truncated first. + Open the supplied file path for reading or writing. - If the file does not exist and it is opened for writing the file - will be created. + The file content can be accessed via the `file` property of + the `SDClass_Base` object--this property is currently + a standard `SdFile` object from `sdfatlib`. - An attempt to open a file for reading that does not exist is an - error. + Defaults to read only. - */ + If `write` is true, default action (when `append` is true) is to + append data to the end of the file. - int pathidx; + If `append` is false then the file will be truncated first. - // do the interactive search - SdFile parentdir = getParentDir(filepath, &pathidx); - // no more subdirs! + If the file does not exist and it is opened for writing the file + will be created. - filepath += pathidx; + An attempt to open a file for reading that does not exist is an + error. - if (! filepath[0]) { - // it was the directory itself! - return File_Base(parentdir, "/"); - } + */ - // Open the file itself - SdFile file; + int pathidx; - // failed to open a subdir! - if (!parentdir.isOpen()) { - return File_Base(); - } + // do the interactive search + SdFile parentdir = getParentDir(filepath, &pathidx); + // no more subdirs! - if (! file.open(parentdir, filepath, mode)) { - return File_Base(); - } - // close the parent - parentdir.close(); + filepath += pathidx; - if ((mode & (O_APPEND | O_WRITE)) == (O_APPEND | O_WRITE)) { - file.seekSet(file.fileSize()); - } - return File_Base(file, filepath); + if (!filepath[0]) { + // it was the directory itself! + return File_Base(parentdir, "/"); } + // Open the file itself + SdFile file; - /* - File SDClass_Base::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_Base` object--this property is currently - a standard `SdFile` object from `sdfatlib`. - - Defaults to read only. + // failed to open a subdir! + if (!parentdir.isOpen()) { + return File_Base(); + } - If `write` is true, default action (when `append` is true) is to - append data to the end of the file. + if (!file.open(parentdir, filepath, mode)) { + return File_Base(); + } + // close the parent + parentdir.close(); - If `append` is false then the file will be truncated first. + if ((mode & (O_APPEND | O_WRITE)) == (O_APPEND | O_WRITE)) { + file.seekSet(file.fileSize()); + } + return File_Base(file, filepath); +} - If the file does not exist and it is opened for writing the file - will be created. +/* + File SDClass_Base::open(char *filepath, uint8_t mode) { + // - An attempt to open a file for reading that does not exist is an - error. + Open the supplied file path for reading or writing. - // + The file content can be accessed via the `file` property of + the `SDClass_Base` object--this property is currently + a standard `SdFile` object from `sdfatlib`. - // TODO: Allow for read&write? (Possibly not, as it requires seek.) + Defaults to read only. - fileOpenMode = mode; - walkPath(filepath, root, callback_openPath, this); + If `write` is true, default action (when `append` is true) is to + append data to the end of the file. - return File_Base(); + 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. - //bool SDClass_Base::close() { - // /* - // - // Closes the file opened by the `open` method. - // - // */ - // file.close(); - //} + // + // TODO: Allow for read&write? (Possibly not, as it requires seek.) - bool SDClass_Base::exists(const char *filepath) { - /* + fileOpenMode = mode; + walkPath(filepath, root, callback_openPath, this); - Returns true if the supplied file path exists. + return File_Base(); - */ - return walkPath(filepath, root, callback_pathExists); } +*/ +// bool SDClass_Base::close() { +// /* +// +// Closes the file opened by the `open` method. +// +// */ +// file.close(); +//} - //bool SDClass_Base::exists(char *filepath, SdFile& parentDir) { - // /* - // - // Returns true if the supplied file path rooted at `parentDir` - // exists. - // - // */ - // return walkPath(filepath, parentDir, callback_pathExists); - //} - - - bool SDClass_Base::mkdir(const char *filepath) { - /* +bool SDClass_Base::exists(const char *filepath) { + /* - Makes a single directory or a hierarchy of directories. + Returns true if the supplied file path exists. - A rough equivalent to `mkdir -p`. + */ + return walkPath(filepath, root, callback_pathExists); +} + +// bool SDClass_Base::exists(char *filepath, SdFile& parentDir) { +// /* +// +// Returns true if the supplied file path rooted at `parentDir` +// exists. +// +// */ +// return walkPath(filepath, parentDir, callback_pathExists); +//} + +bool SDClass_Base::mkdir(const char *filepath) { + /* - */ - return walkPath(filepath, root, callback_makeDirPath); - } + Makes a single directory or a hierarchy of directories. - bool SDClass_Base::rmdir(const char *filepath) { - /* + A rough equivalent to `mkdir -p`. - Remove a single directory or a hierarchy of directories. + */ + return walkPath(filepath, root, callback_makeDirPath); +} - A rough equivalent to `rm -rf`. +bool SDClass_Base::rmdir(const char *filepath) { + /* - */ - return walkPath(filepath, root, callback_rmdir); - } + Remove a single directory or a hierarchy of directories. - bool SDClass_Base::remove(const char *filepath) { - return walkPath(filepath, root, callback_remove); - } + A rough equivalent to `rm -rf`. + */ + return walkPath(filepath, root, callback_rmdir); +} - // allows you to recurse into a directory - File_Base File_Base::openNextFile(uint8_t mode) { - dir_t p; +bool SDClass_Base::remove(const char *filepath) { + return walkPath(filepath, root, callback_remove); +} - //Serial.print("\t\treading dir..."); - while (_file->readDir(&p) > 0) { +// allows you to recurse into a directory +File_Base File_Base::openNextFile(uint8_t mode) { + dir_t p; - // done if past last used entry - if (p.name[0] == DIR_NAME_FREE) { - //Serial.println("end"); - return File_Base(); - } + // Serial.print("\t\treading dir..."); + while (_file->readDir(&p) > 0) { - // skip deleted entry and entries for . and .. - if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') { - //Serial.println("dots"); - continue; - } + // done if past last used entry + if (p.name[0] == DIR_NAME_FREE) { + // Serial.println("end"); + return File_Base(); + } - // only list subdirectories and files - if (!DIR_IS_FILE_OR_SUBDIR(&p)) { - //Serial.println("notafile"); - continue; - } + // skip deleted entry and entries for . and .. + if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') { + // Serial.println("dots"); + 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_Base(f, name); - } else { - //Serial.println("ugh"); - return File_Base(); - } + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(&p)) { + // Serial.println("notafile"); + continue; } - //Serial.println("nothing"); - return File_Base(); + // 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_Base(f, name); + } else { + // Serial.println("ugh"); + return File_Base(); + } } - void File_Base::rewindDirectory(void) { - if (isDirectory()) { - _file->rewind(); - } + // Serial.println("nothing"); + return File_Base(); +} + +void File_Base::rewindDirectory(void) { + if (isDirectory()) { + _file->rewind(); } +} - SDClass_Base SD; +SDClass_Base SD; -}; +}; // namespace SDLib diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp index d833e1c..ff677db 100644 --- a/src/SD_CI.cpp +++ b/src/SD_CI.cpp @@ -1,94 +1,65 @@ #include #ifdef MOCK_PINS_COUNT #include "SD_CI.h" - -#include -#include -#include #include -#include -#include -#include namespace SDLib { -void File_CI::rewindDirectory(void) { - // File_Base::rewindDirectory(); -} - -// ================================================================= - -bool SDClass_CI::begin(uint8_t csPin) { - // return SDClass_Base::begin(csPin); -} - -bool SDClass_CI::begin(uint32_t clock, uint8_t csPin) { - // return SDClass_Base::begin(clock, csPin); -} - -void SDClass_CI::end() { - // SDClass_Base::end(); -} - -File_CI SDClass_CI::open(const char *filename, uint8_t mode) { - return File_CI(filename, mode); -} - -bool SDClass_CI::exists(const char *filepath) { - // prepend base file path - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, filepath); - - return fs::exists(newPath); +std::string * +SDClass_CI::contentsOfNewOrExistingFileWithName(const char *filepath) { + if (!fileExists(filepath)) { + files.emplace(std::string(filepath), new std::string); + } + return files[filepath]; } bool SDClass_CI::mkdir(const char *filepath) { - // prepend base file path - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, filepath); - - if (strchr(filepath, '/') == nullptr) { - // Creating a single directory - try { - fs::create_directory(newPath); - } catch (fs::filesystem_error &excpt) { - std::cerr << excpt.what() << std::endl; - return false; - } - return true; - } else { - // creating multiple directories - try { - fs::create_directories(newPath); - } catch (fs::filesystem_error &excpt) { - std::cerr << excpt.what() << std::endl; - return false; - } - return true; + assert(!fileExists(filepath)); + if (dirExists(filepath)) { + return false; + } + char *path = new char[strlen(filepath) + 1]; + strcpy(path, filepath); + char *ptr = strchr(path, '/'); + while (ptr) { + *ptr = '\0'; + directories.insert(path); + *ptr = '/'; + ptr = strchr(ptr + 1, '/'); } + directories.insert(filepath); + delete[] path; + return true; } bool SDClass_CI::remove(const char *filepath) { - // prepend base file path - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, filepath); + if (files.count(filepath)) { + delete files[filepath]; + files.erase(filepath); + return true; + } + return false; +} - return fs::remove(newPath); +void SDClass_CI::removeAll() { + for (const auto &[key, value] : files) { + delete value; + } + directories.clear(); + files.clear(); } bool SDClass_CI::rmdir(const char *filepath) { - // prepend base file path - char newPath[FILE_PATH_SIZE]; - strcpy(newPath, BASE_PATH); - strcat(newPath, filepath); - - return fs::remove_all(newPath); + if (directories.count(filepath)) { + directories.erase(filepath); + return true; + } + return false; } -SDClass_CI sd_ci; +SDClass_CI SD_CI; +std::set SDClass_CI::directories; +std::map SDClass_CI::files; } // namespace SDLib diff --git a/src/SD_CI.h b/src/SD_CI.h index 67fc2fb..9962ba8 100644 --- a/src/SD_CI.h +++ b/src/SD_CI.h @@ -1,50 +1,35 @@ #pragma once #include #ifdef MOCK_PINS_COUNT -#include -#include -#include - -namespace fs = std::filesystem; +#include "SD.h" +#include +#include +#include +#include +#include namespace SDLib { -#define BASE_PATH "fileSystem/" -#define FILE_PATH_SIZE 255 - class File_CI : public File_Base { -private: - std::fstream *finOut; - // int _pos; - char _path[FILE_PATH_SIZE]; - uint8_t _mode; - bool _open; - bool _isDirectory; - public: - // File_CI(File_Base *baseFile); - // File_CI(SdFile f, const char *name); // wraps an underlying SdFile - // File_CI(void); // 'empty' constructor File_CI(const char *name, uint8_t mode); - ~File_CI(); - // template size_t write1(T data); - virtual size_t write(const char *buf, size_t size); - virtual int availableForWrite(); - virtual int peek(); - virtual int available(); - virtual void flush(); + size_t write(const char *buf, size_t size); + int availableForWrite() { return 4096; } + int peek(); + int available() { return pContents->size() - _pos; } + void flush() {} int read(char *buf, size_t size); - bool seek(uint32_t pos); - uint32_t position(); - uint32_t size(); - void close(); - operator bool(); - char *name(); - bool isOpen() { return _open; } - - bool isDirectory(void); - File_CI openNextFile(uint8_t mode); - void rewindDirectory(void); + bool seek(uint32_t pos) { return _pos = pos <= size() ? pos : size(); } + uint32_t position() { return _pos; } + uint32_t size() { return pContents->size(); } + void close() { _isOpen = false; } + operator bool() { return _isOpen; } + const char *name() { return _path.c_str(); } + bool isOpen() { return _isOpen; } + + bool isDirectory(void) { return _isDir; } + File_CI openNextFile(uint8_t mode) { assert(false); } + void rewindDirectory(void) { assert(false); } using Print::write; // testing functions @@ -53,53 +38,61 @@ class File_CI : public File_Base { void clearWriteError() { writeError = 0; } private: + std::string *pContents; + int _pos; // index of _next_ char to read or write + std::string _path; + uint8_t _mode; + bool _isOpen; + bool _isDir; int writeError; }; class SDClass_CI : public SDClass_Base { - -private: public: - // This needs to be called to set up the connection to the SD card - // before other methods are used. - bool begin(uint8_t csPin = SD_CHIP_SELECT_PIN); - bool begin(uint32_t clock, uint8_t csPin); - - // call this when a card is removed. It will allow you to insert and - // initialise a new card. - void end(); - - // 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_CI open(const char *filename, uint8_t mode); - File_CI open(const String &filename, uint8_t mode = FILE_READ) { - return open(filename.c_str(), mode); + // Ignore hardware-related setup + bool begin(uint8_t csPin = SD_CHIP_SELECT_PIN) { return true; } + bool begin(uint32_t clock, uint8_t csPin) { return true; } + void end() {} + + std::string *contentsOfNewOrExistingFileWithName(const char *filepath); + bool dirExists(const char *filepath) { + std::cout << "dirExists(\"" << filepath + << "\") == " << directories.count(filepath) << std::endl; + return directories.count(filepath) == 1; + } + bool exists(const char *filepath) { + // This doesn't check directories for intermediate paths + return files.count(filepath) || dirExists(filepath); } - - // Methods to determine if the requested file path exists. - bool exists(const char *filepath); bool exists(const String &filepath) { return exists(filepath.c_str()); } + bool fileExists(const char *filepath) { return files.count(filepath); } - // Create the requested directory heirarchy--if intermediate directories - // do not exist they will be created. bool mkdir(const char *filepath); bool mkdir(const String &filepath) { return mkdir(filepath.c_str()); } - // Delete the file. + File_CI open(const char *filename, uint8_t mode) { + return File_CI(filename, mode); + } + File_CI open(const String &filename, uint8_t mode = FILE_READ) { + return open(filename.c_str(), mode); + } + bool remove(const char *filepath); bool remove(const String &filepath) { return remove(filepath.c_str()); } + void removeAll(); + bool rmdir(const char *filepath); bool rmdir(const String &filepath) { return rmdir(filepath.c_str()); } virtual String className() const { return "SDClass_Base"; } private: - friend class File; + static std::set directories; + static std::map files; }; -extern SDClass_CI sd_ci; +extern SDClass_CI SD_CI; } // namespace SDLib diff --git a/src/utility/FatStructs.h b/src/utility/FatStructs.h index 84c1cc7..2d3a05e 100644 --- a/src/utility/FatStructs.h +++ b/src/utility/FatStructs.h @@ -46,12 +46,12 @@ struct partitionTable { partition. Legal values include: 0X00. Do not use for booting. 0X80 Active partition. */ - uint8_t boot; + 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; + 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. @@ -63,17 +63,17 @@ struct partitionTable { Combine beginCylinderLow with beginCylinderHigh. Legal values are 0-1023. Only used in old PC BIOS. */ - uint8_t beginCylinderLow; + uint8_t beginCylinderLow; /** Partition type. See defines that begin with PART_TYPE_ for some Microsoft partition types. */ - uint8_t type; + 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; + 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. @@ -85,7 +85,7 @@ struct partitionTable { Combine endCylinderLow with endCylinderHigh. Legal values are 0-1023. Only used in old PC BIOS. */ - uint8_t endCylinderLow; + uint8_t endCylinderLow; /** Logical block address of the first block in the partition. */ uint32_t firstSector; /** Length of the partition, in blocks. */ @@ -103,17 +103,17 @@ typedef struct partitionTable part_t; */ struct masterBootRecord { /** Code Area for master boot program. */ - uint8_t codeArea[440]; + 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]; + part_t part[4]; /** First MBR signature byte. Must be 0X55 */ - uint8_t mbrSig0; + uint8_t mbrSig0; /** Second MBR signature byte. Must be 0XAA */ - uint8_t mbrSig1; + uint8_t mbrSig1; } __attribute__((packed)); /** Type name for masterBootRecord */ typedef struct masterBootRecord mbr_t; @@ -136,7 +136,7 @@ struct biosParmBlock { power of 2 that is greater than 0. The legal values are 1, 2, 4, 8, 16, 32, 64, and 128. */ - uint8_t sectorsPerCluster; + uint8_t sectorsPerCluster; /** Number of sectors before the first FAT. This value must not be zero. @@ -145,7 +145,7 @@ struct biosParmBlock { /** 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; + 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, @@ -171,7 +171,7 @@ struct biosParmBlock { for fixed (non-removable) media. For removable media, 0xF0 is frequently used. Legal values are 0xF0 or 0xF8-0xFF. */ - uint8_t mediaType; + uint8_t mediaType; /** Count of sectors occupied by one FAT on FAT12/FAT16 volumes. On FAT32 volumes this field must be 0, and sectorsPerFat32 @@ -206,8 +206,8 @@ struct biosParmBlock { 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. + -- 1 means only one FAT is active; it is the one referenced in bits + 0-3. Bits 8-15 -- Reserved. */ uint16_t fat32Flags; /** @@ -235,7 +235,7 @@ struct biosParmBlock { 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]; + uint8_t fat32Reserved[12]; } __attribute__((packed)); /** Type name for biosParmBlock */ typedef struct biosParmBlock bpb_t; @@ -248,29 +248,29 @@ typedef struct biosParmBlock bpb_t; */ struct fat32BootSector { /** X86 jmp to boot program */ - uint8_t jmpToBootCode[3]; + uint8_t jmpToBootCode[3]; /** informational only - don't depend on it */ - char oemName[8]; + char oemName[8]; /** BIOS Parameter Block */ - bpb_t bpb; + bpb_t bpb; /** for int0x13 use value 0X80 for hard drive */ - uint8_t driveNumber; + uint8_t driveNumber; /** used by Windows NT - should be zero for FAT */ - uint8_t reserved1; + uint8_t reserved1; /** 0X29 if next three fields are valid */ - uint8_t bootSignature; + uint8_t bootSignature; /** usually generated by combining date and time */ uint32_t volumeSerialNumber; /** should match volume label in root dir */ - char volumeLabel[11]; + char volumeLabel[11]; /** informational only - don't depend on it */ - char fileSystemType[8]; + char fileSystemType[8]; /** X86 boot code */ - uint8_t bootCode[420]; + uint8_t bootCode[420]; /** must be 0X55 */ - uint8_t bootSectorSig0; + uint8_t bootSectorSig0; /** must be 0XAA */ - uint8_t bootSectorSig1; + uint8_t bootSectorSig1; } __attribute__((packed)); //------------------------------------------------------------------------------ // End Of Chain values for FAT entries @@ -324,25 +324,25 @@ struct directoryEntry { 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]; + 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; + 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; + 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; + uint8_t creationTimeTenths; /** Time file was created. */ uint16_t creationTime; /** Date file was created. */ @@ -398,21 +398,21 @@ 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) { +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) { +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) { +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) { +static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t *dir) { return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; } -#endif // FatStructs_h +#endif // FatStructs_h diff --git a/src/utility/Sd2Card.cpp b/src/utility/Sd2Card.cpp index 7cfbe73..b244ae8 100644 --- a/src/utility/Sd2Card.cpp +++ b/src/utility/Sd2Card.cpp @@ -18,43 +18,44 @@ . */ #define USE_SPI_LIB -#include #include "Sd2Card.h" +#include + //------------------------------------------------------------------------------ #ifndef SOFTWARE_SPI #ifdef USE_SPI_LIB - #ifndef SDCARD_SPI - #define SDCARD_SPI SPI - #endif +#ifndef SDCARD_SPI +#define SDCARD_SPI SPI +#endif - #include - static SPISettings settings; +#include +static SPISettings settings; #endif // functions for hardware SPI /** Send a byte to the card */ static void spiSend(uint8_t b) { - #ifndef USE_SPI_LIB +#ifndef USE_SPI_LIB SPDR = b; while (!(SPSR & (1 << SPIF))) ; - #else +#else SDCARD_SPI.transfer(b); - #endif +#endif } /** Receive a byte from the card */ -static uint8_t spiRec(void) { - #ifndef USE_SPI_LIB +static uint8_t spiRec(void) { +#ifndef USE_SPI_LIB spiSend(0XFF); return SPDR; - #else +#else return SDCARD_SPI.transfer(0xFF); - #endif +#endif } -#else // SOFTWARE_SPI +#else // SOFTWARE_SPI //------------------------------------------------------------------------------ /** nop to tune soft SPI timing */ -#define nop asm volatile ("nop\n\t") +#define nop asm volatile("nop\n\t") //------------------------------------------------------------------------------ /** Soft SPI receive */ uint8_t spiRec(void) { @@ -107,7 +108,7 @@ void spiSend(uint8_t data) { // enable interrupts sei(); } -#endif // SOFTWARE_SPI +#endif // SOFTWARE_SPI //------------------------------------------------------------------------------ // send command and return error code. Return zero for OK uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { @@ -131,10 +132,10 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { // send CRC uint8_t crc = 0XFF; if (cmd == CMD0) { - crc = 0X95; // correct crc for CMD0 with arg 0 + crc = 0X95; // correct crc for CMD0 with arg 0 } if (cmd == CMD8) { - crc = 0X87; // correct crc for CMD8 with arg 0X1AA + crc = 0X87; // correct crc for CMD8 with arg 0X1AA } spiSend(crc); @@ -157,14 +158,14 @@ uint32_t Sd2Card::cardSize(void) { } 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; + 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; + 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); @@ -176,21 +177,21 @@ static uint8_t chip_select_asserted = 0; void Sd2Card::chipSelectHigh(void) { digitalWrite(chipSelectPin_, HIGH); - #ifdef USE_SPI_LIB +#ifdef USE_SPI_LIB if (chip_select_asserted) { chip_select_asserted = 0; SDCARD_SPI.endTransaction(); } - #endif +#endif } //------------------------------------------------------------------------------ void Sd2Card::chipSelectLow(void) { - #ifdef USE_SPI_LIB +#ifdef USE_SPI_LIB if (!chip_select_asserted) { chip_select_asserted = 1; SDCARD_SPI.beginTransaction(settings); } - #endif +#endif digitalWrite(chipSelectPin_, LOW); } //------------------------------------------------------------------------------ @@ -216,9 +217,8 @@ uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { firstBlock <<= 9; lastBlock <<= 9; } - if (cardCommand(CMD32, firstBlock) - || cardCommand(CMD33, lastBlock) - || cardCommand(CMD38, 0)) { + if (cardCommand(CMD32, firstBlock) || cardCommand(CMD33, lastBlock) || + cardCommand(CMD38, 0)) { error(SD_CARD_ERROR_ERASE); goto fail; } @@ -264,14 +264,14 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { // set pin modes pinMode(chipSelectPin_, OUTPUT); digitalWrite(chipSelectPin_, HIGH); - #ifndef USE_SPI_LIB +#ifndef USE_SPI_LIB pinMode(SPI_MISO_PIN, INPUT); pinMode(SPI_MOSI_PIN, OUTPUT); pinMode(SPI_SCK_PIN, OUTPUT); - #endif +#endif - #ifndef SOFTWARE_SPI - #ifndef USE_SPI_LIB +#ifndef SOFTWARE_SPI +#ifndef USE_SPI_LIB // 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 @@ -279,22 +279,22 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // clear double speed SPSR &= ~(1 << SPI2X); - #else // USE_SPI_LIB +#else // USE_SPI_LIB SDCARD_SPI.begin(); settings = SPISettings(250000, MSBFIRST, SPI_MODE0); - #endif // USE_SPI_LIB - #endif // SOFTWARE_SPI +#endif // USE_SPI_LIB +#endif // SOFTWARE_SPI - // must supply min of 74 clock cycles with CS high. - #ifdef USE_SPI_LIB +// must supply min of 74 clock cycles with CS high. +#ifdef USE_SPI_LIB SDCARD_SPI.beginTransaction(settings); - #endif +#endif for (uint8_t i = 0; i < 10; i++) { spiSend(0XFF); } - #ifdef USE_SPI_LIB +#ifdef USE_SPI_LIB SDCARD_SPI.endTransaction(); - #endif +#endif chipSelectLow(); @@ -347,11 +347,11 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { } chipSelectHigh(); - #ifndef SOFTWARE_SPI +#ifndef SOFTWARE_SPI return setSckRate(sckRateID); - #else // SOFTWARE_SPI +#else // SOFTWARE_SPI return true; - #endif // SOFTWARE_SPI +#endif // SOFTWARE_SPI fail: chipSelectHigh(); @@ -385,7 +385,7 @@ void Sd2Card::partialBlockRead(uint8_t value) { \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) { +uint8_t Sd2Card::readBlock(uint32_t block, uint8_t *dst) { return readData(block, 0, 512, dst); } //------------------------------------------------------------------------------ @@ -399,8 +399,8 @@ uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) { \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) { +uint8_t Sd2Card::readData(uint32_t block, uint16_t offset, uint16_t count, + uint8_t *dst) { if (count == 0) { return true; } @@ -424,7 +424,7 @@ uint8_t Sd2Card::readData(uint32_t block, inBlock_ = 1; } - #ifdef OPTIMIZE_HARDWARE_SPI +#ifdef OPTIMIZE_HARDWARE_SPI // start first spi transfer SPDR = 0XFF; @@ -447,7 +447,7 @@ uint8_t Sd2Card::readData(uint32_t block, ; dst[n] = SPDR; - #else // OPTIMIZE_HARDWARE_SPI +#else // OPTIMIZE_HARDWARE_SPI // skip data before offset for (; offset_ < offset; offset_++) { @@ -457,7 +457,7 @@ uint8_t Sd2Card::readData(uint32_t block, for (uint16_t i = 0; i < count; i++) { dst[i] = spiRec(); } - #endif // OPTIMIZE_HARDWARE_SPI +#endif // OPTIMIZE_HARDWARE_SPI offset_ += count; if (!partialBlockRead_ || offset_ >= 512) { @@ -474,8 +474,8 @@ uint8_t Sd2Card::readData(uint32_t block, /** 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 +// skip data and crc +#ifdef OPTIMIZE_HARDWARE_SPI // optimize skip for hardware SPDR = 0XFF; while (offset_++ < 513) { @@ -486,19 +486,19 @@ void Sd2Card::readEnd(void) { // wait for last crc byte while (!(SPSR & (1 << SPIF))) ; - #else // OPTIMIZE_HARDWARE_SPI +#else // OPTIMIZE_HARDWARE_SPI while (offset_++ < 514) { spiRec(); } - #endif // OPTIMIZE_HARDWARE_SPI +#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(buf); +uint8_t Sd2Card::readRegister(uint8_t cmd, void *buf) { + uint8_t *dst = reinterpret_cast(buf); if (cardCommand(cmd, 0)) { error(SD_CARD_ERROR_READ_REG); goto fail; @@ -510,8 +510,8 @@ uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) { for (uint16_t i = 0; i < 16; i++) { dst[i] = spiRec(); } - spiRec(); // get first crc byte - spiRec(); // get second crc byte + spiRec(); // get first crc byte + spiRec(); // get second crc byte chipSelectHigh(); return true; @@ -537,7 +537,7 @@ uint8_t Sd2Card::setSckRate(uint8_t sckRateID) { error(SD_CARD_ERROR_SCK_RATE); return false; } - #ifndef USE_SPI_LIB +#ifndef USE_SPI_LIB // see avr processor datasheet for SPI register bit definitions if ((sckRateID & 1) || sckRateID == 6) { SPSR &= ~(1 << SPI2X); @@ -545,19 +545,31 @@ uint8_t Sd2Card::setSckRate(uint8_t sckRateID) { SPSR |= (1 << SPI2X); } SPCR &= ~((1 << SPR1) | (1 << SPR0)); - SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0) - | (sckRateID & 2 ? (1 << SPR0) : 0); - #else // USE_SPI_LIB + SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0) | (sckRateID & 2 ? (1 << SPR0) : 0); +#else // USE_SPI_LIB switch (sckRateID) { - case 0: settings = SPISettings(25000000, MSBFIRST, SPI_MODE0); break; - case 1: settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); break; - case 2: settings = SPISettings(2000000, MSBFIRST, SPI_MODE0); break; - case 3: settings = SPISettings(1000000, MSBFIRST, SPI_MODE0); break; - case 4: settings = SPISettings(500000, MSBFIRST, SPI_MODE0); break; - case 5: settings = SPISettings(250000, MSBFIRST, SPI_MODE0); break; - default: settings = SPISettings(125000, MSBFIRST, SPI_MODE0); - } - #endif // USE_SPI_LIB + case 0: + settings = SPISettings(25000000, MSBFIRST, SPI_MODE0); + break; + case 1: + settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); + break; + case 2: + settings = SPISettings(2000000, MSBFIRST, SPI_MODE0); + break; + case 3: + settings = SPISettings(1000000, MSBFIRST, SPI_MODE0); + break; + case 4: + settings = SPISettings(500000, MSBFIRST, SPI_MODE0); + break; + case 5: + settings = SPISettings(250000, MSBFIRST, SPI_MODE0); + break; + default: + settings = SPISettings(125000, MSBFIRST, SPI_MODE0); + } +#endif // USE_SPI_LIB return true; } #ifdef USE_SPI_LIB @@ -612,14 +624,15 @@ uint8_t Sd2Card::waitStartBlock(void) { \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, uint8_t blocking) { - #if SD_PROTECT_BLOCK_ZERO +uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t *src, + uint8_t blocking) { +#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 +#endif // SD_PROTECT_BLOCK_ZERO // use address if not SDHC card if (type() != SD_CARD_TYPE_SDHC) { @@ -653,7 +666,7 @@ uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src, uint8_t bl } //------------------------------------------------------------------------------ /** Write one data block in a multiple block write sequence */ -uint8_t Sd2Card::writeData(const uint8_t* src) { +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); @@ -664,8 +677,8 @@ uint8_t Sd2Card::writeData(const uint8_t* 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 +uint8_t Sd2Card::writeData(uint8_t token, const uint8_t *src) { +#ifdef OPTIMIZE_HARDWARE_SPI // send data - optimized loop SPDR = token; @@ -684,14 +697,14 @@ uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) { while (!(SPSR & (1 << SPIF))) ; - #else // OPTIMIZE_HARDWARE_SPI +#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 +#endif // OPTIMIZE_HARDWARE_SPI + spiSend(0xff); // dummy crc + spiSend(0xff); // dummy crc status_ = spiRec(); if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) { @@ -714,13 +727,13 @@ uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) { the value zero, false, is returned for failure. */ uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { - #if SD_PROTECT_BLOCK_ZERO +#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 +#endif // SD_PROTECT_BLOCK_ZERO // send pre-erase count if (cardAcmd(ACMD23, eraseCount)) { error(SD_CARD_ERROR_ACMD23); diff --git a/src/utility/Sd2Card.h b/src/utility/Sd2Card.h index cb55ff4..bf8d60b 100644 --- a/src/utility/Sd2Card.h +++ b/src/utility/Sd2Card.h @@ -46,69 +46,71 @@ uint8_t const SPI_QUARTER_SPEED = 2; */ #define MEGA_SOFT_SPI 0 //------------------------------------------------------------------------------ -#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__)) - #define SOFTWARE_SPI -#endif // MEGA_SOFT_SPI +#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 +// hardware pin defs - // include pins_arduino.h or variant.h depending on architecture, via Arduino.h - #include - #if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) - #define SDCARD_SS_PIN 4 - #define SDCARD_MOSI_PIN 11 - #define SDCARD_MISO_PIN 12 - #define SDCARD_SCK_PIN 13 - #endif +// include pins_arduino.h or variant.h depending on architecture, via Arduino.h +#include +#if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) +#define SDCARD_SS_PIN 4 +#define SDCARD_MOSI_PIN 11 +#define SDCARD_MISO_PIN 12 +#define SDCARD_SCK_PIN 13 +#endif - /** - SD Chip Select pin +/** +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. - */ - #ifndef SDCARD_SS_PIN - /** The default chip select pin for the SD card is SS. */ - uint8_t const SD_CHIP_SELECT_PIN = SS; - #else - uint8_t const SD_CHIP_SELECT_PIN = SDCARD_SS_PIN; - #endif +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. +*/ +#ifndef SDCARD_SS_PIN +/** The default chip select pin for the SD card is SS. */ +uint8_t const SD_CHIP_SELECT_PIN = SS; +#else +uint8_t const SD_CHIP_SELECT_PIN = SDCARD_SS_PIN; +#endif - // The following three pins must not be redefined for hardware SPI, - // so ensure that they are taken from pins_arduino.h or variant.h, depending on architecture. - #ifndef SDCARD_MOSI_PIN - /** SPI Master Out Slave In pin */ - uint8_t const SPI_MOSI_PIN = MOSI; - /** SPI Master In Slave Out pin */ - uint8_t const SPI_MISO_PIN = MISO; - /** SPI Clock pin */ - uint8_t const SPI_SCK_PIN = SCK; - #else - uint8_t const SPI_MOSI_PIN = SDCARD_MOSI_PIN; - uint8_t const SPI_MISO_PIN = SDCARD_MISO_PIN; - uint8_t const SPI_SCK_PIN = SDCARD_SCK_PIN; - #endif +// The following three pins must not be redefined for hardware SPI, +// so ensure that they are taken from pins_arduino.h or variant.h, depending on +// architecture. +#ifndef SDCARD_MOSI_PIN +/** SPI Master Out Slave In pin */ +uint8_t const SPI_MOSI_PIN = MOSI; +/** SPI Master In Slave Out pin */ +uint8_t const SPI_MISO_PIN = MISO; +/** SPI Clock pin */ +uint8_t const SPI_SCK_PIN = SCK; +#else +uint8_t const SPI_MOSI_PIN = SDCARD_MOSI_PIN; +uint8_t const SPI_MISO_PIN = SDCARD_MISO_PIN; +uint8_t const SPI_SCK_PIN = SDCARD_SCK_PIN; +#endif - /** optimize loops for hardware SPI */ - #ifndef USE_SPI_LIB - #define OPTIMIZE_HARDWARE_SPI - #endif +/** optimize loops for hardware SPI */ +#ifndef USE_SPI_LIB +#define OPTIMIZE_HARDWARE_SPI +#endif #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 +// 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 @@ -180,100 +182,84 @@ uint8_t const SD_CARD_TYPE_SDHC = 3; \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); - #ifdef USE_SPI_LIB - uint8_t setSpiClock(uint32_t clock); - #endif - /** 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 blocking = 1); - uint8_t writeData(const uint8_t* src); - uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount); - uint8_t writeStop(void); - uint8_t isBusy(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(unsigned int timeoutMillis); - uint8_t writeData(uint8_t token, const uint8_t* src); - uint8_t waitStartBlock(void); +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); +#ifdef USE_SPI_LIB + uint8_t setSpiClock(uint32_t clock); +#endif + /** 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 blocking = 1); + uint8_t writeData(const uint8_t *src); + uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount); + uint8_t writeStop(void); + uint8_t isBusy(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(unsigned int timeoutMillis); + uint8_t writeData(uint8_t token, const uint8_t *src); + uint8_t waitStartBlock(void); }; -#endif // Sd2Card_h +#endif // Sd2Card_h diff --git a/src/utility/Sd2PinMap.cpp b/src/utility/Sd2PinMap.cpp index e247e91..9a27a49 100644 --- a/src/utility/Sd2PinMap.cpp +++ b/src/utility/Sd2PinMap.cpp @@ -1,7 +1,7 @@ #ifdef MOCK_PINS_COUNT -#include #include "Sd2PinMap.h" +#include uint8_t avr_io_registers[RAMSTART]; diff --git a/src/utility/Sd2PinMap.h b/src/utility/Sd2PinMap.h index af261f4..de6df23 100644 --- a/src/utility/Sd2PinMap.h +++ b/src/utility/Sd2PinMap.h @@ -21,54 +21,54 @@ #if defined(__arm__) // Arduino Due Board follows #ifndef Sd2PinMap_h - #define Sd2PinMap_h - - #include - #if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) - #include - // #ifdef _SFR_IO8 - // #undef _SFR_IO8 - // #define _AVR_IO_REG(type, mem_addr) (*(type*)(&avr_io_registers[mem_addr])) - // #define __SFR_OFFSET 0x20 - // #define _SFR_IO8(io_addr) _AVR_IO_REG(uint8_t, io_addr + __SFR_OFFSET) - // #define _SFR_IO16(io_addr) _AVR_IO_REG(uint16_t, io_addr + __SFR_OFFSET) - // #define _SFR_MEM8(mem_addr) _AVR_IO_REG(uint8_t, mem_addr) - // #define _SFR_MEM16(mem_addr) _AVR_IO_REG(uint16_t, mem_addr) - // #define _SFR_MEM32(mem_addr) _AVR_IO_REG(uint32_t, mem_addr) - - // extern uint8_t avr_io_registers[RAMSTART]; - // #endif - #define SDCARD_SS_PIN 4 - #define SDCARD_MOSI_PIN 11 - #define SDCARD_MISO_PIN 12 - #define SDCARD_SCK_PIN 13 - #endif - - uint8_t const SS_PIN = SS; - uint8_t const MOSI_PIN = MOSI; - uint8_t const MISO_PIN = MISO; - uint8_t const SCK_PIN = SCK; +#define Sd2PinMap_h + +#include +#if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) +#include +// #ifdef _SFR_IO8 +// #undef _SFR_IO8 +// #define _AVR_IO_REG(type, mem_addr) (*(type*)(&avr_io_registers[mem_addr])) +// #define __SFR_OFFSET 0x20 +// #define _SFR_IO8(io_addr) _AVR_IO_REG(uint8_t, io_addr + __SFR_OFFSET) +// #define _SFR_IO16(io_addr) _AVR_IO_REG(uint16_t, io_addr + __SFR_OFFSET) +// #define _SFR_MEM8(mem_addr) _AVR_IO_REG(uint8_t, mem_addr) +// #define _SFR_MEM16(mem_addr) _AVR_IO_REG(uint16_t, mem_addr) +// #define _SFR_MEM32(mem_addr) _AVR_IO_REG(uint32_t, mem_addr) + +// extern uint8_t avr_io_registers[RAMSTART]; +// #endif +#define SDCARD_SS_PIN 4 +#define SDCARD_MOSI_PIN 11 +#define SDCARD_MISO_PIN 12 +#define SDCARD_SCK_PIN 13 +#endif + +uint8_t const SS_PIN = SS; +uint8_t const MOSI_PIN = MOSI; +uint8_t const MISO_PIN = MISO; +uint8_t const SCK_PIN = SCK; #endif // Sd2PinMap_h #elif defined(__AVR_ATmega4809__) // Arduino UNO WiFI Rev2 follows #ifndef Sd2PinMap_h - #define Sd2PinMap_h +#define Sd2PinMap_h - #include +#include - #if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) - #define SDCARD_SS_PIN 4 - #define SDCARD_MOSI_PIN 11 - #define SDCARD_MISO_PIN 12 - #define SDCARD_SCK_PIN 13 - #endif +#if defined(MOCK_PINS_COUNT) & !defined(SDCARD_SS_PIN) +#define SDCARD_SS_PIN 4 +#define SDCARD_MOSI_PIN 11 +#define SDCARD_MISO_PIN 12 +#define SDCARD_SCK_PIN 13 +#endif - uint8_t const SS_PIN = SS; - uint8_t const MOSI_PIN = MOSI; - uint8_t const MISO_PIN = MISO; - uint8_t const SCK_PIN = SCK; +uint8_t const SS_PIN = SS; +uint8_t const MOSI_PIN = MOSI; +uint8_t const MISO_PIN = MISO; +uint8_t const SCK_PIN = SCK; #endif // Sd2PinMap_h @@ -82,9 +82,9 @@ //------------------------------------------------------------------------------ /** struct for mapping digital pins */ struct pin_map_t { - volatile uint8_t* ddr; - volatile uint8_t* pin; - volatile uint8_t* port; + volatile uint8_t *ddr; + volatile uint8_t *pin; + volatile uint8_t *port; uint8_t bit; }; //------------------------------------------------------------------------------ @@ -102,79 +102,80 @@ 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 + {&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_ATmega1284P__)) && defined(CORE_MICRODUINO) +#elif (defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && \ + defined(CORE_MICRODUINO) // Microduino Core+ // Two Wire (aka I2C) ports @@ -188,38 +189,38 @@ uint8_t const MISO_PIN = 12; uint8_t const SCK_PIN = 13; static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 PD0 - {&DDRD, &PIND, &PORTD, 1}, // D1 PD1 - {&DDRD, &PIND, &PORTD, 2}, // D2 PD2 - {&DDRD, &PIND, &PORTD, 3}, // D3 PD3 - {&DDRB, &PINB, &PORTB, 0}, // D4 PB0 - {&DDRB, &PINB, &PORTB, 1}, // D5 PB1 - {&DDRB, &PINB, &PORTB, 2}, // D6 PB2 - {&DDRB, &PINB, &PORTB, 3}, // D7 PB3 - {&DDRD, &PIND, &PORTD, 6}, // D8 PD6 - {&DDRD, &PIND, &PORTD, 5}, // D9 PD5 - {&DDRB, &PINB, &PORTB, 4}, // D10 PB4 - {&DDRB, &PINB, &PORTB, 5}, // D11 PB5 - {&DDRB, &PINB, &PORTB, 6}, // D12 PB6 - {&DDRB, &PINB, &PORTB, 7}, // D13 PB7 - {&DDRC, &PINC, &PORTC, 7}, // D14 PC7 - {&DDRC, &PINC, &PORTC, 6}, // D15 PC6 - {&DDRC, &PINC, &PORTC, 5}, // D16 PC5 - {&DDRC, &PINC, &PORTC, 4}, // D17 PC4 - {&DDRC, &PINC, &PORTC, 3}, // D18 PC3 - {&DDRC, &PINC, &PORTC, 2}, // D19 PC2 - {&DDRC, &PINC, &PORTC, 1}, // D20 PC1 - {&DDRC, &PINC, &PORTC, 0}, // D21 PC0 - {&DDRD, &PIND, &PORTD, 4}, // D22 PD4 - {&DDRD, &PIND, &PORTD, 7}, // D23 PD7 - {&DDRA, &PINA, &PORTA, 7}, // D24 PA7 - {&DDRA, &PINA, &PORTA, 6}, // D25 PA6 - {&DDRA, &PINA, &PORTA, 5}, // D26 PA5 - {&DDRA, &PINA, &PORTA, 4}, // D27 PA4 - {&DDRA, &PINA, &PORTA, 3}, // D28 PA3 - {&DDRA, &PINA, &PORTA, 2}, // D29 PA2 - {&DDRA, &PINA, &PORTA, 1}, // D30 PA1 - {&DDRA, &PINA, &PORTA, 0} // D31 PA0 + {&DDRD, &PIND, &PORTD, 0}, // D0 PD0 + {&DDRD, &PIND, &PORTD, 1}, // D1 PD1 + {&DDRD, &PIND, &PORTD, 2}, // D2 PD2 + {&DDRD, &PIND, &PORTD, 3}, // D3 PD3 + {&DDRB, &PINB, &PORTB, 0}, // D4 PB0 + {&DDRB, &PINB, &PORTB, 1}, // D5 PB1 + {&DDRB, &PINB, &PORTB, 2}, // D6 PB2 + {&DDRB, &PINB, &PORTB, 3}, // D7 PB3 + {&DDRD, &PIND, &PORTD, 6}, // D8 PD6 + {&DDRD, &PIND, &PORTD, 5}, // D9 PD5 + {&DDRB, &PINB, &PORTB, 4}, // D10 PB4 + {&DDRB, &PINB, &PORTB, 5}, // D11 PB5 + {&DDRB, &PINB, &PORTB, 6}, // D12 PB6 + {&DDRB, &PINB, &PORTB, 7}, // D13 PB7 + {&DDRC, &PINC, &PORTC, 7}, // D14 PC7 + {&DDRC, &PINC, &PORTC, 6}, // D15 PC6 + {&DDRC, &PINC, &PORTC, 5}, // D16 PC5 + {&DDRC, &PINC, &PORTC, 4}, // D17 PC4 + {&DDRC, &PINC, &PORTC, 3}, // D18 PC3 + {&DDRC, &PINC, &PORTC, 2}, // D19 PC2 + {&DDRC, &PINC, &PORTC, 1}, // D20 PC1 + {&DDRC, &PINC, &PORTC, 0}, // D21 PC0 + {&DDRD, &PIND, &PORTD, 4}, // D22 PD4 + {&DDRD, &PIND, &PORTD, 7}, // D23 PD7 + {&DDRA, &PINA, &PORTA, 7}, // D24 PA7 + {&DDRA, &PINA, &PORTA, 6}, // D25 PA6 + {&DDRA, &PINA, &PORTA, 5}, // D26 PA5 + {&DDRA, &PINA, &PORTA, 4}, // D27 PA4 + {&DDRA, &PINA, &PORTA, 3}, // D28 PA3 + {&DDRA, &PINA, &PORTA, 2}, // D29 PA2 + {&DDRA, &PINA, &PORTA, 1}, // D30 PA1 + {&DDRA, &PINA, &PORTA, 0} // D31 PA0 }; //------------------------------------------------------------------------------ #elif defined(__AVR_ATmega128RFA1__) && defined(CORE_MICRODUINO) @@ -236,28 +237,28 @@ uint8_t const MISO_PIN = 12; uint8_t const SCK_PIN = 13; static const pin_map_t digitalPinMap[] = { - {&DDRD, &PINE, &PORTE, 0}, // D0 PE0 - {&DDRD, &PINE, &PORTE, 1}, // D1 PE1 - {&DDRD, &PIND, &PORTD, 2}, // D2 PD2 - {&DDRD, &PIND, &PORTD, 3}, // D3 PD3 - {&DDRB, &PINE, &PORTE, 3}, // D4 PE3 - {&DDRB, &PINE, &PORTE, 4}, // D5 PE4 - {&DDRB, &PINE, &PORTE, 5}, // D6 PE5 - {&DDRB, &PINB, &PORTB, 7}, // D7 PB7 - {&DDRD, &PINB, &PORTB, 6}, // D8 PB6 - {&DDRD, &PINB, &PORTB, 5}, // D9 PB5 - {&DDRB, &PINB, &PORTB, 4}, // D10 PB4 - {&DDRB, &PINB, &PORTB, 2}, // D11 PB2 - {&DDRB, &PINB, &PORTB, 3}, // D12 PB3 - {&DDRB, &PINB, &PORTB, 1}, // D13 PB1 - {&DDRF, &PINF, &PORTF, 7}, // D14 PF7 - {&DDRF, &PINF, &PORTF, 6}, // D15 PF6 - {&DDRF, &PINF, &PORTF, 5}, // D16 PF5 - {&DDRF, &PINF, &PORTF, 4}, // D17 PF4 - {&DDRD, &PIND, &PORTD, 1}, // D18 PD1 - {&DDRD, &PIND, &PORTD, 0}, // D19 PD0 - {&DDRF, &PINF, &PORTF, 3}, // D20 PF3 - {&DDRF, &PINF, &PORTF, 2}, // D21 PF2 + {&DDRD, &PINE, &PORTE, 0}, // D0 PE0 + {&DDRD, &PINE, &PORTE, 1}, // D1 PE1 + {&DDRD, &PIND, &PORTD, 2}, // D2 PD2 + {&DDRD, &PIND, &PORTD, 3}, // D3 PD3 + {&DDRB, &PINE, &PORTE, 3}, // D4 PE3 + {&DDRB, &PINE, &PORTE, 4}, // D5 PE4 + {&DDRB, &PINE, &PORTE, 5}, // D6 PE5 + {&DDRB, &PINB, &PORTB, 7}, // D7 PB7 + {&DDRD, &PINB, &PORTB, 6}, // D8 PB6 + {&DDRD, &PINB, &PORTB, 5}, // D9 PB5 + {&DDRB, &PINB, &PORTB, 4}, // D10 PB4 + {&DDRB, &PINB, &PORTB, 2}, // D11 PB2 + {&DDRB, &PINB, &PORTB, 3}, // D12 PB3 + {&DDRB, &PINB, &PORTB, 1}, // D13 PB1 + {&DDRF, &PINF, &PORTF, 7}, // D14 PF7 + {&DDRF, &PINF, &PORTF, 6}, // D15 PF6 + {&DDRF, &PINF, &PORTF, 5}, // D16 PF5 + {&DDRF, &PINF, &PORTF, 4}, // D17 PF4 + {&DDRD, &PIND, &PORTD, 1}, // D18 PD1 + {&DDRD, &PIND, &PORTD, 0}, // D19 PD0 + {&DDRF, &PINF, &PORTF, 3}, // D20 PF3 + {&DDRF, &PINF, &PORTF, 2}, // D21 PF2 }; //------------------------------------------------------------------------------ #elif defined(__AVR_ATmega32U4__) && defined(CORE_MICRODUINO) @@ -274,28 +275,28 @@ uint8_t const MISO_PIN = 12; uint8_t const SCK_PIN = 13; static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 2}, // D0 - PD2 - {&DDRD, &PIND, &PORTD, 3}, // D1 - PD3 - {&DDRE, &PINE, &PORTE, 6}, // D2 - PE6 - {&DDRD, &PIND, &PORTD, 6}, // D3 - PD6 - {&DDRD, &PIND, &PORTD, 7}, // D4 - PD7 - {&DDRC, &PINC, &PORTC, 6}, // D5 - PC6 - {&DDRC, &PINC, &PORTC, 7}, // D6 - PC7 - {&DDRE, &PINE, &PORTE, 7}, // D7 - PE7 - {&DDRB, &PINB, &PORTB, 6}, // D8 - PB6 - {&DDRB, &PINB, &PORTB, 5}, // D9 - PB5 - {&DDRB, &PINB, &PORTB, 0}, // D10 - PB0 - {&DDRB, &PINB, &PORTB, 2}, // D11 - MOSI - PB2 - {&DDRB, &PINB, &PORTB, 3}, // D12 -MISO - PB3 - {&DDRB, &PINB, &PORTB, 1}, // D13 -SCK - PB1 - {&DDRF, &PINF, &PORTF, 7}, // D14 - A0 - PF7 - {&DDRF, &PINF, &PORTF, 6}, // D15 - A1 - PF6 - {&DDRF, &PINF, &PORTF, 5}, // D16 - A2 - PF5 - {&DDRF, &PINF, &PORTF, 4}, // D17 - A3 - PF4 - {&DDRD, &PIND, &PORTD, 1}, // D18 - PD1 - {&DDRD, &PIND, &PORTD, 0}, // D19 - PD0 - {&DDRF, &PINF, &PORTF, 1}, // D20 - A6 - PF1 - {&DDRF, &PINF, &PORTF, 0}, // D21 - A7 - PF0 + {&DDRD, &PIND, &PORTD, 2}, // D0 - PD2 + {&DDRD, &PIND, &PORTD, 3}, // D1 - PD3 + {&DDRE, &PINE, &PORTE, 6}, // D2 - PE6 + {&DDRD, &PIND, &PORTD, 6}, // D3 - PD6 + {&DDRD, &PIND, &PORTD, 7}, // D4 - PD7 + {&DDRC, &PINC, &PORTC, 6}, // D5 - PC6 + {&DDRC, &PINC, &PORTC, 7}, // D6 - PC7 + {&DDRE, &PINE, &PORTE, 7}, // D7 - PE7 + {&DDRB, &PINB, &PORTB, 6}, // D8 - PB6 + {&DDRB, &PINB, &PORTB, 5}, // D9 - PB5 + {&DDRB, &PINB, &PORTB, 0}, // D10 - PB0 + {&DDRB, &PINB, &PORTB, 2}, // D11 - MOSI - PB2 + {&DDRB, &PINB, &PORTB, 3}, // D12 -MISO - PB3 + {&DDRB, &PINB, &PORTB, 1}, // D13 -SCK - PB1 + {&DDRF, &PINF, &PORTF, 7}, // D14 - A0 - PF7 + {&DDRF, &PINF, &PORTF, 6}, // D15 - A1 - PF6 + {&DDRF, &PINF, &PORTF, 5}, // D16 - A2 - PF5 + {&DDRF, &PINF, &PORTF, 4}, // D17 - A3 - PF4 + {&DDRD, &PIND, &PORTD, 1}, // D18 - PD1 + {&DDRD, &PIND, &PORTD, 0}, // D19 - PD0 + {&DDRF, &PINF, &PORTF, 1}, // D20 - A6 - PF1 + {&DDRF, &PINF, &PORTF, 0}, // D21 - A7 - PF0 }; //------------------------------------------------------------------------------ #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) @@ -312,38 +313,38 @@ 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 - {&DDRB, &PINB, &PORTB, 5}, // B5 5 - {&DDRB, &PINB, &PORTB, 6}, // B6 6 - {&DDRB, &PINB, &PORTB, 7}, // B7 7 - {&DDRD, &PIND, &PORTD, 0}, // D0 8 - {&DDRD, &PIND, &PORTD, 1}, // D1 9 - {&DDRD, &PIND, &PORTD, 2}, // D2 10 - {&DDRD, &PIND, &PORTD, 3}, // D3 11 - {&DDRD, &PIND, &PORTD, 4}, // D4 12 - {&DDRD, &PIND, &PORTD, 5}, // D5 13 - {&DDRD, &PIND, &PORTD, 6}, // D6 14 - {&DDRD, &PIND, &PORTD, 7}, // D7 15 - {&DDRC, &PINC, &PORTC, 0}, // C0 16 - {&DDRC, &PINC, &PORTC, 1}, // C1 17 - {&DDRC, &PINC, &PORTC, 2}, // C2 18 - {&DDRC, &PINC, &PORTC, 3}, // C3 19 - {&DDRC, &PINC, &PORTC, 4}, // C4 20 - {&DDRC, &PINC, &PORTC, 5}, // C5 21 - {&DDRC, &PINC, &PORTC, 6}, // C6 22 - {&DDRC, &PINC, &PORTC, 7}, // C7 23 - {&DDRA, &PINA, &PORTA, 7}, // A7 24 - {&DDRA, &PINA, &PORTA, 6}, // A6 25 - {&DDRA, &PINA, &PORTA, 5}, // A5 26 - {&DDRA, &PINA, &PORTA, 4}, // A4 27 - {&DDRA, &PINA, &PORTA, 3}, // A3 28 - {&DDRA, &PINA, &PORTA, 2}, // A2 29 - {&DDRA, &PINA, &PORTA, 1}, // A1 30 - {&DDRA, &PINA, &PORTA, 0} // A0 31 + {&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 + {&DDRB, &PINB, &PORTB, 5}, // B5 5 + {&DDRB, &PINB, &PORTB, 6}, // B6 6 + {&DDRB, &PINB, &PORTB, 7}, // B7 7 + {&DDRD, &PIND, &PORTD, 0}, // D0 8 + {&DDRD, &PIND, &PORTD, 1}, // D1 9 + {&DDRD, &PIND, &PORTD, 2}, // D2 10 + {&DDRD, &PIND, &PORTD, 3}, // D3 11 + {&DDRD, &PIND, &PORTD, 4}, // D4 12 + {&DDRD, &PIND, &PORTD, 5}, // D5 13 + {&DDRD, &PIND, &PORTD, 6}, // D6 14 + {&DDRD, &PIND, &PORTD, 7}, // D7 15 + {&DDRC, &PINC, &PORTC, 0}, // C0 16 + {&DDRC, &PINC, &PORTC, 1}, // C1 17 + {&DDRC, &PINC, &PORTC, 2}, // C2 18 + {&DDRC, &PINC, &PORTC, 3}, // C3 19 + {&DDRC, &PINC, &PORTC, 4}, // C4 20 + {&DDRC, &PINC, &PORTC, 5}, // C5 21 + {&DDRC, &PINC, &PORTC, 6}, // C6 22 + {&DDRC, &PINC, &PORTC, 7}, // C7 23 + {&DDRA, &PINA, &PORTA, 7}, // A7 24 + {&DDRA, &PINA, &PORTA, 6}, // A6 25 + {&DDRA, &PINA, &PORTA, 5}, // A5 26 + {&DDRA, &PINA, &PORTA, 4}, // A4 27 + {&DDRA, &PINA, &PORTA, 3}, // A3 28 + {&DDRA, &PINA, &PORTA, 2}, // A2 29 + {&DDRA, &PINA, &PORTA, 1}, // A1 30 + {&DDRA, &PINA, &PORTA, 0} // A0 31 }; //------------------------------------------------------------------------------ #elif defined(__AVR_ATmega32U4__) @@ -360,30 +361,30 @@ uint8_t const MISO_PIN = 14; uint8_t const SCK_PIN = 15; static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 2}, // D2 0 - {&DDRD, &PIND, &PORTD, 3}, // D3 1 - {&DDRD, &PIND, &PORTD, 1}, // D1 2 - {&DDRD, &PIND, &PORTD, 0}, // D0 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRC, &PINC, &PORTC, 6}, // C6 5 - {&DDRD, &PIND, &PORTD, 7}, // D7 6 - {&DDRE, &PINE, &PORTE, 6}, // E6 7 - {&DDRB, &PINB, &PORTB, 4}, // B4 8 - {&DDRB, &PINB, &PORTB, 5}, // B5 9 - {&DDRB, &PINB, &PORTB, 6}, // B6 10 - {&DDRB, &PINB, &PORTB, 7}, // B7 11 - {&DDRD, &PIND, &PORTD, 6}, // D6 12 - {&DDRC, &PINC, &PORTC, 7}, // C7 13 - {&DDRB, &PINB, &PORTB, 3}, // B3 14 - {&DDRB, &PINB, &PORTB, 1}, // B1 15 - {&DDRB, &PINB, &PORTB, 2}, // B2 16 - {&DDRB, &PINB, &PORTB, 0}, // B0 17 - {&DDRF, &PINF, &PORTF, 7}, // F7 18 - {&DDRF, &PINF, &PORTF, 6}, // F6 19 - {&DDRF, &PINF, &PORTF, 5}, // F5 20 - {&DDRF, &PINF, &PORTF, 4}, // F4 21 - {&DDRF, &PINF, &PORTF, 1}, // F1 22 - {&DDRF, &PINF, &PORTF, 0}, // F0 23 + {&DDRD, &PIND, &PORTD, 2}, // D2 0 + {&DDRD, &PIND, &PORTD, 3}, // D3 1 + {&DDRD, &PIND, &PORTD, 1}, // D1 2 + {&DDRD, &PIND, &PORTD, 0}, // D0 3 + {&DDRD, &PIND, &PORTD, 4}, // D4 4 + {&DDRC, &PINC, &PORTC, 6}, // C6 5 + {&DDRD, &PIND, &PORTD, 7}, // D7 6 + {&DDRE, &PINE, &PORTE, 6}, // E6 7 + {&DDRB, &PINB, &PORTB, 4}, // B4 8 + {&DDRB, &PINB, &PORTB, 5}, // B5 9 + {&DDRB, &PINB, &PORTB, 6}, // B6 10 + {&DDRB, &PINB, &PORTB, 7}, // B7 11 + {&DDRD, &PIND, &PORTD, 6}, // D6 12 + {&DDRC, &PINC, &PORTC, 7}, // C7 13 + {&DDRB, &PINB, &PORTB, 3}, // B3 14 + {&DDRB, &PINB, &PORTB, 1}, // B1 15 + {&DDRB, &PINB, &PORTB, 2}, // B2 16 + {&DDRB, &PINB, &PORTB, 0}, // B0 17 + {&DDRF, &PINF, &PORTF, 7}, // F7 18 + {&DDRF, &PINF, &PORTF, 6}, // F6 19 + {&DDRF, &PINF, &PORTF, 5}, // F5 20 + {&DDRF, &PINF, &PORTF, 4}, // F4 21 + {&DDRF, &PINF, &PORTF, 1}, // F1 22 + {&DDRF, &PINF, &PORTF, 0}, // F0 23 }; //------------------------------------------------------------------------------ #elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) @@ -400,52 +401,52 @@ uint8_t const MISO_PIN = 23; uint8_t const SCK_PIN = 21; static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 0 - {&DDRD, &PIND, &PORTD, 1}, // D1 1 - {&DDRD, &PIND, &PORTD, 2}, // D2 2 - {&DDRD, &PIND, &PORTD, 3}, // D3 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRD, &PIND, &PORTD, 5}, // D5 5 - {&DDRD, &PIND, &PORTD, 6}, // D6 6 - {&DDRD, &PIND, &PORTD, 7}, // D7 7 - {&DDRE, &PINE, &PORTE, 0}, // E0 8 - {&DDRE, &PINE, &PORTE, 1}, // E1 9 - {&DDRC, &PINC, &PORTC, 0}, // C0 10 - {&DDRC, &PINC, &PORTC, 1}, // C1 11 - {&DDRC, &PINC, &PORTC, 2}, // C2 12 - {&DDRC, &PINC, &PORTC, 3}, // C3 13 - {&DDRC, &PINC, &PORTC, 4}, // C4 14 - {&DDRC, &PINC, &PORTC, 5}, // C5 15 - {&DDRC, &PINC, &PORTC, 6}, // C6 16 - {&DDRC, &PINC, &PORTC, 7}, // C7 17 - {&DDRE, &PINE, &PORTE, 6}, // E6 18 - {&DDRE, &PINE, &PORTE, 7}, // E7 19 - {&DDRB, &PINB, &PORTB, 0}, // B0 20 - {&DDRB, &PINB, &PORTB, 1}, // B1 21 - {&DDRB, &PINB, &PORTB, 2}, // B2 22 - {&DDRB, &PINB, &PORTB, 3}, // B3 23 - {&DDRB, &PINB, &PORTB, 4}, // B4 24 - {&DDRB, &PINB, &PORTB, 5}, // B5 25 - {&DDRB, &PINB, &PORTB, 6}, // B6 26 - {&DDRB, &PINB, &PORTB, 7}, // B7 27 - {&DDRA, &PINA, &PORTA, 0}, // A0 28 - {&DDRA, &PINA, &PORTA, 1}, // A1 29 - {&DDRA, &PINA, &PORTA, 2}, // A2 30 - {&DDRA, &PINA, &PORTA, 3}, // A3 31 - {&DDRA, &PINA, &PORTA, 4}, // A4 32 - {&DDRA, &PINA, &PORTA, 5}, // A5 33 - {&DDRA, &PINA, &PORTA, 6}, // A6 34 - {&DDRA, &PINA, &PORTA, 7}, // A7 35 - {&DDRE, &PINE, &PORTE, 4}, // E4 36 - {&DDRE, &PINE, &PORTE, 5}, // E5 37 - {&DDRF, &PINF, &PORTF, 0}, // F0 38 - {&DDRF, &PINF, &PORTF, 1}, // F1 39 - {&DDRF, &PINF, &PORTF, 2}, // F2 40 - {&DDRF, &PINF, &PORTF, 3}, // F3 41 - {&DDRF, &PINF, &PORTF, 4}, // F4 42 - {&DDRF, &PINF, &PORTF, 5}, // F5 43 - {&DDRF, &PINF, &PORTF, 6}, // F6 44 - {&DDRF, &PINF, &PORTF, 7} // F7 45 + {&DDRD, &PIND, &PORTD, 0}, // D0 0 + {&DDRD, &PIND, &PORTD, 1}, // D1 1 + {&DDRD, &PIND, &PORTD, 2}, // D2 2 + {&DDRD, &PIND, &PORTD, 3}, // D3 3 + {&DDRD, &PIND, &PORTD, 4}, // D4 4 + {&DDRD, &PIND, &PORTD, 5}, // D5 5 + {&DDRD, &PIND, &PORTD, 6}, // D6 6 + {&DDRD, &PIND, &PORTD, 7}, // D7 7 + {&DDRE, &PINE, &PORTE, 0}, // E0 8 + {&DDRE, &PINE, &PORTE, 1}, // E1 9 + {&DDRC, &PINC, &PORTC, 0}, // C0 10 + {&DDRC, &PINC, &PORTC, 1}, // C1 11 + {&DDRC, &PINC, &PORTC, 2}, // C2 12 + {&DDRC, &PINC, &PORTC, 3}, // C3 13 + {&DDRC, &PINC, &PORTC, 4}, // C4 14 + {&DDRC, &PINC, &PORTC, 5}, // C5 15 + {&DDRC, &PINC, &PORTC, 6}, // C6 16 + {&DDRC, &PINC, &PORTC, 7}, // C7 17 + {&DDRE, &PINE, &PORTE, 6}, // E6 18 + {&DDRE, &PINE, &PORTE, 7}, // E7 19 + {&DDRB, &PINB, &PORTB, 0}, // B0 20 + {&DDRB, &PINB, &PORTB, 1}, // B1 21 + {&DDRB, &PINB, &PORTB, 2}, // B2 22 + {&DDRB, &PINB, &PORTB, 3}, // B3 23 + {&DDRB, &PINB, &PORTB, 4}, // B4 24 + {&DDRB, &PINB, &PORTB, 5}, // B5 25 + {&DDRB, &PINB, &PORTB, 6}, // B6 26 + {&DDRB, &PINB, &PORTB, 7}, // B7 27 + {&DDRA, &PINA, &PORTA, 0}, // A0 28 + {&DDRA, &PINA, &PORTA, 1}, // A1 29 + {&DDRA, &PINA, &PORTA, 2}, // A2 30 + {&DDRA, &PINA, &PORTA, 3}, // A3 31 + {&DDRA, &PINA, &PORTA, 4}, // A4 32 + {&DDRA, &PINA, &PORTA, 5}, // A5 33 + {&DDRA, &PINA, &PORTA, 6}, // A6 34 + {&DDRA, &PINA, &PORTA, 7}, // A7 35 + {&DDRE, &PINE, &PORTE, 4}, // E4 36 + {&DDRE, &PINE, &PORTE, 5}, // E5 37 + {&DDRF, &PINF, &PORTF, 0}, // F0 38 + {&DDRF, &PINF, &PORTF, 1}, // F1 39 + {&DDRF, &PINF, &PORTF, 2}, // F2 40 + {&DDRF, &PINF, &PORTF, 3}, // F3 41 + {&DDRF, &PINF, &PORTF, 4}, // F4 42 + {&DDRF, &PINF, &PORTF, 5}, // F5 43 + {&DDRF, &PINF, &PORTF, 6}, // F6 44 + {&DDRF, &PINF, &PORTF, 7} // F7 45 }; //------------------------------------------------------------------------------ #else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) @@ -462,44 +463,44 @@ uint8_t const MISO_PIN = 12; uint8_t const SCK_PIN = 13; static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 0 - {&DDRD, &PIND, &PORTD, 1}, // D1 1 - {&DDRD, &PIND, &PORTD, 2}, // D2 2 - {&DDRD, &PIND, &PORTD, 3}, // D3 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRD, &PIND, &PORTD, 5}, // D5 5 - {&DDRD, &PIND, &PORTD, 6}, // D6 6 - {&DDRD, &PIND, &PORTD, 7}, // D7 7 - {&DDRB, &PINB, &PORTB, 0}, // B0 8 - {&DDRB, &PINB, &PORTB, 1}, // B1 9 - {&DDRB, &PINB, &PORTB, 2}, // B2 10 - {&DDRB, &PINB, &PORTB, 3}, // B3 11 - {&DDRB, &PINB, &PORTB, 4}, // B4 12 - {&DDRB, &PINB, &PORTB, 5}, // B5 13 - {&DDRC, &PINC, &PORTC, 0}, // C0 14 - {&DDRC, &PINC, &PORTC, 1}, // C1 15 - {&DDRC, &PINC, &PORTC, 2}, // C2 16 - {&DDRC, &PINC, &PORTC, 3}, // C3 17 - {&DDRC, &PINC, &PORTC, 4}, // C4 18 - {&DDRC, &PINC, &PORTC, 5} // C5 19 + {&DDRD, &PIND, &PORTD, 0}, // D0 0 + {&DDRD, &PIND, &PORTD, 1}, // D1 1 + {&DDRD, &PIND, &PORTD, 2}, // D2 2 + {&DDRD, &PIND, &PORTD, 3}, // D3 3 + {&DDRD, &PIND, &PORTD, 4}, // D4 4 + {&DDRD, &PIND, &PORTD, 5}, // D5 5 + {&DDRD, &PIND, &PORTD, 6}, // D6 6 + {&DDRD, &PIND, &PORTD, 7}, // D7 7 + {&DDRB, &PINB, &PORTB, 0}, // B0 8 + {&DDRB, &PINB, &PORTB, 1}, // B1 9 + {&DDRB, &PINB, &PORTB, 2}, // B2 10 + {&DDRB, &PINB, &PORTB, 3}, // B3 11 + {&DDRB, &PINB, &PORTB, 4}, // B4 12 + {&DDRB, &PINB, &PORTB, 5}, // B5 13 + {&DDRC, &PINC, &PORTC, 0}, // C0 14 + {&DDRC, &PINC, &PORTC, 1}, // C1 15 + {&DDRC, &PINC, &PORTC, 2}, // C2 16 + {&DDRC, &PINC, &PORTC, 3}, // C3 17 + {&DDRC, &PINC, &PORTC, 4}, // C4 18 + {&DDRC, &PINC, &PORTC, 5} // C5 19 }; -#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //------------------------------------------------------------------------------ -static const uint8_t digitalPinCount = sizeof(digitalPinMap) / sizeof(pin_map_t); +static const uint8_t digitalPinCount = + sizeof(digitalPinMap) / sizeof(pin_map_t); uint8_t badPinNumber(void) -__attribute__((error("Pin number is too large or not a constant"))); + __attribute__((error("Pin number is too large or not a constant"))); -static inline __attribute__((always_inline)) -uint8_t getPinMode(uint8_t pin) { +static inline __attribute__((always_inline)) uint8_t getPinMode(uint8_t pin) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1; } else { return badPinNumber(); } } -static inline __attribute__((always_inline)) -void setPinMode(uint8_t pin, uint8_t mode) { +static inline __attribute__((always_inline)) void setPinMode(uint8_t pin, + uint8_t mode) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { if (mode) { *digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit; @@ -510,16 +511,16 @@ void setPinMode(uint8_t pin, uint8_t mode) { badPinNumber(); } } -static inline __attribute__((always_inline)) -uint8_t fastDigitalRead(uint8_t pin) { +static inline __attribute__((always_inline)) uint8_t +fastDigitalRead(uint8_t pin) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1; } else { return badPinNumber(); } } -static inline __attribute__((always_inline)) -void fastDigitalWrite(uint8_t pin, uint8_t value) { +static inline __attribute__((always_inline)) void +fastDigitalWrite(uint8_t pin, uint8_t value) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { if (value) { *digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit; @@ -530,22 +531,22 @@ void fastDigitalWrite(uint8_t pin, uint8_t value) { badPinNumber(); } } -#endif // Sd2PinMap_h +#endif // Sd2PinMap_h -#elif defined (__CPU_ARC__) +#elif defined(__CPU_ARC__) -#if defined (__ARDUINO_ARC__) - // Two Wire (aka I2C) ports - uint8_t const SDA_PIN = 18; - uint8_t const SCL_PIN = 19; +#if defined(__ARDUINO_ARC__) +// Two Wire (aka I2C) ports +uint8_t const SDA_PIN = 18; +uint8_t const SCL_PIN = 19; - // SPI port - uint8_t const SS_PIN = 10; - uint8_t const MOSI_PIN = 11; - uint8_t const MISO_PIN = 12; - uint8_t const SCK_PIN = 13; +// SPI port +uint8_t const SS_PIN = 10; +uint8_t const MOSI_PIN = 11; +uint8_t const MISO_PIN = 12; +uint8_t const SCK_PIN = 13; -#endif // Arduino ARC +#endif // Arduino ARC #else #error Architecture or board not supported. diff --git a/src/utility/SdFat.h b/src/utility/SdFat.h index d5c48f6..0f6ccd5 100644 --- a/src/utility/SdFat.h +++ b/src/utility/SdFat.h @@ -26,12 +26,13 @@ #include -#if defined (__AVR__) || defined (__CPU_ARC__) - #include +#if defined(__AVR__) || defined(__CPU_ARC__) +#include #endif -#include "Sd2Card.h" #include "FatStructs.h" +#include "Sd2Card.h" #include + //------------------------------------------------------------------------------ /** Allow use of deprecated functions if non-zero @@ -43,18 +44,18 @@ class SdVolume; //============================================================================== // SdFile class -#ifdef O_RDONLY //ARDUINO_ARCH_MBED - #undef O_READ - #undef O_RDONLY - #undef O_WRITE - #undef O_WRONLY - #undef O_RDWR - #undef O_ACCMODE - #undef O_APPEND - #undef O_SYNC - #undef O_CREAT - #undef O_EXCL - #undef O_TRUNC +#ifdef O_RDONLY // ARDUINO_ARCH_MBED +#undef O_READ +#undef O_RDONLY +#undef O_WRITE +#undef O_WRONLY +#undef O_RDWR +#undef O_ACCMODE +#undef O_APPEND +#undef O_SYNC +#undef O_CREAT +#undef O_EXCL +#undef O_TRUNC #endif // flags for ls() @@ -123,17 +124,13 @@ static inline uint8_t FAT_MONTH(uint16_t fatDate) { return (fatDate >> 5) & 0XF; } /** day part of FAT directory date field */ -static inline uint8_t FAT_DAY(uint16_t fatDate) { - return fatDate & 0X1F; -} +static inline uint8_t FAT_DAY(uint16_t fatDate) { return fatDate & 0X1F; } /** time field for FAT directory entry */ static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { return hour << 11 | minute << 5 | second >> 1; } /** hour part of FAT directory time field */ -static inline uint8_t FAT_HOUR(uint16_t fatTime) { - return fatTime >> 11; -} +static inline uint8_t FAT_HOUR(uint16_t fatTime) { return fatTime >> 11; } /** minute part of FAT directory time field */ static inline uint8_t FAT_MINUTE(uint16_t fatTime) { return (fatTime >> 5) & 0X3F; @@ -152,317 +149,286 @@ uint16_t const FAT_DEFAULT_TIME = (1 << 11); \brief Access FAT16 and FAT32 files on SD and SDHC cards. */ class SdFile : public Print { - public: - /** Create an instance of SdFile. */ - SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {} - /** - writeError is set to true if an error occurs during a write(). - Set writeError to false before calling print() and/or write() and check - for true after calls to print() and/or write(). - */ - //bool writeError; - /** - Cancel unbuffered reads for this file. - See setUnbufferedRead() - */ - void clearUnbufferedRead(void) { - flags_ &= ~F_FILE_UNBUFFERED_READ; - } - uint8_t close(void); - uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); - uint8_t createContiguous(SdFile* dirFile, - const char* fileName, uint32_t size); - /** \return The current cluster number for a file or directory. */ - uint32_t curCluster(void) const { - return curCluster_; - } - /** \return The current position for a file or directory. */ - uint32_t curPosition(void) const { - return curPosition_; - } - /** - Set the date/time callback function - - \param[in] dateTime The user's call back function. The callback - function is of the form: - - \code - void dateTime(uint16_t* date, uint16_t* time) { - uint16_t year; - uint8_t month, day, hour, minute, second; - - // User gets date and time from GPS or real-time clock here - - // return date using FAT_DATE macro to format fields - * *date = FAT_DATE(year, month, day); - - // return time using FAT_TIME macro to format fields - * *time = FAT_TIME(hour, minute, second); - } - \endcode - - Sets the function that is called when a file is created or when - a file's directory entry is modified by sync(). All timestamps, - access, creation, and modify, are set when a file is created. - sync() maintains the last access date and last modify date/time. - - See the timestamp() function. - */ - static void dateTimeCallback( - void (*dateTime)(uint16_t* date, uint16_t* time)) { - dateTime_ = dateTime; - } - /** - Cancel the date/time callback function. - */ - static void dateTimeCallbackCancel(void) { - // use explicit zero since NULL is not defined for Sanguino - dateTime_ = 0; - } - /** \return Address of the block that contains this file's directory. */ - uint32_t dirBlock(void) const { - return dirBlock_; - } - uint8_t dirEntry(dir_t* dir); - /** \return Index of this file's directory in the block dirBlock. */ - uint8_t dirIndex(void) const { - return dirIndex_; - } - static void dirName(const dir_t& dir, char* name); - /** \return The total number of bytes in a file or directory. */ - uint32_t fileSize(void) const { - return fileSize_; - } - /** \return The first cluster number for a file or directory. */ - uint32_t firstCluster(void) const { - return firstCluster_; - } - /** \return True if this is a SdFile for a directory else false. */ - uint8_t isDir(void) const { - return type_ >= FAT_FILE_TYPE_MIN_DIR; - } - /** \return True if this is a SdFile for a file else false. */ - uint8_t isFile(void) const { - return type_ == FAT_FILE_TYPE_NORMAL; - } - /** \return True if this is a SdFile for an open file/directory else false. */ - uint8_t isOpen(void) const { - return type_ != FAT_FILE_TYPE_CLOSED; - } - /** \return True if this is a SdFile for a subdirectory else false. */ - uint8_t isSubDir(void) const { - return type_ == FAT_FILE_TYPE_SUBDIR; - } - /** \return True if this is a SdFile for the root directory. */ - uint8_t isRoot(void) const { - return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32; - } - void ls(uint8_t flags = 0, uint8_t indent = 0); - uint8_t makeDir(SdFile* dir, const char* dirName); - uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag); - uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag); - - uint8_t openRoot(SdVolume* vol); - static void printDirName(const dir_t& dir, uint8_t width); - static void printFatDate(uint16_t fatDate); - static void printFatTime(uint16_t fatTime); - static void printTwoDigits(uint8_t v); - /** - Read the next byte from a file. - - \return For success read returns the next byte in the file as an int. - If an error occurs or end of file is reached -1 is returned. - */ - int16_t read(void) { - uint8_t b; - return read(&b, 1) == 1 ? b : -1; - } - int16_t read(void* buf, uint16_t nbyte); - int8_t readDir(dir_t* dir); - static uint8_t remove(SdFile* dirFile, const char* fileName); - uint8_t remove(void); - /** Set the file's current position to zero. */ - void rewind(void) { - curPosition_ = curCluster_ = 0; - } - uint8_t rmDir(void); - uint8_t rmRfStar(void); - /** Set the files position to current position + \a pos. See seekSet(). */ - uint8_t seekCur(uint32_t pos) { - return seekSet(curPosition_ + pos); - } - /** - Set the files current position to end of file. Useful to position - a file for append. See seekSet(). - */ - uint8_t seekEnd(void) { - return seekSet(fileSize_); - } - uint8_t seekSet(uint32_t pos); - /** - Use unbuffered reads to access this file. Used with Wave - Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP. - - Not recommended for normal applications. - */ - void setUnbufferedRead(void) { - if (isFile()) { - flags_ |= F_FILE_UNBUFFERED_READ; - } - } - uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, - uint8_t hour, uint8_t minute, uint8_t second); - uint8_t sync(uint8_t blocking = 1); - /** Type of this SdFile. You should use isFile() or isDir() instead of type() - if possible. - - \return The file or directory type. - */ - uint8_t type(void) const { - return type_; - } - uint8_t truncate(uint32_t size); - /** \return Unbuffered read flag. */ - uint8_t unbufferedRead(void) const { - return flags_ & F_FILE_UNBUFFERED_READ; - } - /** \return SdVolume that contains this file. */ - SdVolume* volume(void) const { - return vol_; - } - size_t write(uint8_t b); - size_t write(const void* buf, uint16_t nbyte); - size_t write(const char* str); - #ifdef __AVR__ - void write_P(PGM_P str); - void writeln_P(PGM_P str); - #endif - int availableForWrite(void); - //------------------------------------------------------------------------------ - #if ALLOW_DEPRECATED_FUNCTIONS - // Deprecated functions - suppress cpplint warnings with NOLINT comment - /** \deprecated Use: - uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); - */ - uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT - return contiguousRange(&bgnBlock, &endBlock); - } - /** \deprecated Use: - uint8_t SdFile::createContiguous(SdFile* dirFile, - const char* fileName, uint32_t size) - */ - uint8_t createContiguous(SdFile& dirFile, // NOLINT - const char* fileName, uint32_t size) { - return createContiguous(&dirFile, fileName, size); - } - - /** - \deprecated Use: - static void SdFile::dateTimeCallback( - void (*dateTime)(uint16_t* date, uint16_t* time)); - */ - static void dateTimeCallback( - void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT - oldDateTime_ = dateTime; - dateTime_ = dateTime ? oldToNew : 0; - } - /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */ - uint8_t dirEntry(dir_t& dir) { - return dirEntry(&dir); // NOLINT - } - /** \deprecated Use: - uint8_t SdFile::makeDir(SdFile* dir, const char* dirName); - */ - uint8_t makeDir(SdFile& dir, const char* dirName) { // NOLINT - return makeDir(&dir, dirName); - } - /** \deprecated Use: - uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag); - */ - uint8_t open(SdFile& dirFile, // NOLINT - const char* fileName, uint8_t oflag) { - return open(&dirFile, fileName, oflag); - } - /** \deprecated Do not use in new apps */ - uint8_t open(SdFile& dirFile, const char* fileName) { // NOLINT - return open(dirFile, fileName, O_RDWR); - } - /** \deprecated Use: - uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag); - */ - uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT - return open(&dirFile, index, oflag); - } - /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */ - uint8_t openRoot(SdVolume& vol) { - return openRoot(&vol); // NOLINT - } - - /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */ - int8_t readDir(dir_t& dir) { - return readDir(&dir); // NOLINT - } - /** \deprecated Use: - static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName); - */ - static uint8_t remove(SdFile& dirFile, const char* fileName) { // NOLINT - return remove(&dirFile, fileName); - } - //------------------------------------------------------------------------------ - // rest are private - private: - static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT - static void oldToNew(uint16_t* date, uint16_t* time) { - uint16_t d; - uint16_t t; - oldDateTime_(d, t); - *date = d; - *time = t; - } - #endif // ALLOW_DEPRECATED_FUNCTIONS - private: - // bits defined in flags_ - // should be 0XF - static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); - // available bits - static uint8_t const F_FILE_NON_BLOCKING_WRITE = 0X10; - // a new cluster was added to the file - static uint8_t const F_FILE_CLUSTER_ADDED = 0X20; - // use unbuffered SD read - static uint8_t const F_FILE_UNBUFFERED_READ = 0X40; - // sync of directory entry required - static uint8_t const F_FILE_DIR_DIRTY = 0X80; - - // make sure F_OFLAG is ok - #if ((F_FILE_NON_BLOCKING_WRITE | F_FILE_CLUSTER_ADDED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG) +public: + /** Create an instance of SdFile. */ + SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {} + /** + writeError is set to true if an error occurs during a write(). + Set writeError to false before calling print() and/or write() and check + for true after calls to print() and/or write(). + */ + // bool writeError; + /** + Cancel unbuffered reads for this file. + See setUnbufferedRead() + */ + void clearUnbufferedRead(void) { flags_ &= ~F_FILE_UNBUFFERED_READ; } + uint8_t close(void); + uint8_t contiguousRange(uint32_t *bgnBlock, uint32_t *endBlock); + uint8_t createContiguous(SdFile *dirFile, const char *fileName, + uint32_t size); + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster(void) const { return curCluster_; } + /** \return The current position for a file or directory. */ + uint32_t curPosition(void) const { return curPosition_; } + /** + Set the date/time callback function + + \param[in] dateTime The user's call back function. The callback + function is of the form: + + \code + void dateTime(uint16_t* date, uint16_t* time) { + uint16_t year; + uint8_t month, day, hour, minute, second; + + // User gets date and time from GPS or real-time clock here + + // return date using FAT_DATE macro to format fields + * *date = FAT_DATE(year, month, day); + + // return time using FAT_TIME macro to format fields + * *time = FAT_TIME(hour, minute, second); + } + \endcode + + Sets the function that is called when a file is created or when + a file's directory entry is modified by sync(). All timestamps, + access, creation, and modify, are set when a file is created. + sync() maintains the last access date and last modify date/time. + + See the timestamp() function. + */ + static void dateTimeCallback(void (*dateTime)(uint16_t *date, + uint16_t *time)) { + dateTime_ = dateTime; + } + /** + Cancel the date/time callback function. + */ + static void dateTimeCallbackCancel(void) { + // use explicit zero since NULL is not defined for Sanguino + dateTime_ = 0; + } + /** \return Address of the block that contains this file's directory. */ + uint32_t dirBlock(void) const { return dirBlock_; } + uint8_t dirEntry(dir_t *dir); + /** \return Index of this file's directory in the block dirBlock. */ + uint8_t dirIndex(void) const { return dirIndex_; } + static void dirName(const dir_t &dir, char *name); + /** \return The total number of bytes in a file or directory. */ + uint32_t fileSize(void) const { return fileSize_; } + /** \return The first cluster number for a file or directory. */ + uint32_t firstCluster(void) const { return firstCluster_; } + /** \return True if this is a SdFile for a directory else false. */ + uint8_t isDir(void) const { return type_ >= FAT_FILE_TYPE_MIN_DIR; } + /** \return True if this is a SdFile for a file else false. */ + uint8_t isFile(void) const { return type_ == FAT_FILE_TYPE_NORMAL; } + /** \return True if this is a SdFile for an open file/directory else false. */ + uint8_t isOpen(void) const { return type_ != FAT_FILE_TYPE_CLOSED; } + /** \return True if this is a SdFile for a subdirectory else false. */ + uint8_t isSubDir(void) const { return type_ == FAT_FILE_TYPE_SUBDIR; } + /** \return True if this is a SdFile for the root directory. */ + uint8_t isRoot(void) const { + return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32; + } + void ls(uint8_t flags = 0, uint8_t indent = 0); + uint8_t makeDir(SdFile *dir, const char *dirName); + uint8_t open(SdFile *dirFile, uint16_t index, uint8_t oflag); + uint8_t open(SdFile *dirFile, const char *fileName, uint8_t oflag); + + uint8_t openRoot(SdVolume *vol); + static void printDirName(const dir_t &dir, uint8_t width); + static void printFatDate(uint16_t fatDate); + static void printFatTime(uint16_t fatTime); + static void printTwoDigits(uint8_t v); + /** + Read the next byte from a file. + + \return For success read returns the next byte in the file as an int. + If an error occurs or end of file is reached -1 is returned. + */ + int16_t read(void) { + uint8_t b; + return read(&b, 1) == 1 ? b : -1; + } + int16_t read(void *buf, uint16_t nbyte); + int8_t readDir(dir_t *dir); + static uint8_t remove(SdFile *dirFile, const char *fileName); + uint8_t remove(void); + /** Set the file's current position to zero. */ + void rewind(void) { curPosition_ = curCluster_ = 0; } + uint8_t rmDir(void); + uint8_t rmRfStar(void); + /** Set the files position to current position + \a pos. See seekSet(). */ + uint8_t seekCur(uint32_t pos) { return seekSet(curPosition_ + pos); } + /** + Set the files current position to end of file. Useful to position + a file for append. See seekSet(). + */ + uint8_t seekEnd(void) { return seekSet(fileSize_); } + uint8_t seekSet(uint32_t pos); + /** + Use unbuffered reads to access this file. Used with Wave + Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP. + + Not recommended for normal applications. + */ + void setUnbufferedRead(void) { + if (isFile()) { + flags_ |= F_FILE_UNBUFFERED_READ; + } + } + uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second); + uint8_t sync(uint8_t blocking = 1); + /** Type of this SdFile. You should use isFile() or isDir() instead of type() + if possible. + + \return The file or directory type. + */ + uint8_t type(void) const { return type_; } + uint8_t truncate(uint32_t size); + /** \return Unbuffered read flag. */ + uint8_t unbufferedRead(void) const { return flags_ & F_FILE_UNBUFFERED_READ; } + /** \return SdVolume that contains this file. */ + SdVolume *volume(void) const { return vol_; } + size_t write(uint8_t b); + size_t write(const void *buf, uint16_t nbyte); + size_t write(const char *str); +#ifdef __AVR__ + void write_P(PGM_P str); + void writeln_P(PGM_P str); +#endif + int availableForWrite(void); +//------------------------------------------------------------------------------ +#if ALLOW_DEPRECATED_FUNCTIONS + // Deprecated functions - suppress cpplint warnings with NOLINT comment + /** \deprecated Use: + uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + */ + uint8_t contiguousRange(uint32_t &bgnBlock, uint32_t &endBlock) { // NOLINT + return contiguousRange(&bgnBlock, &endBlock); + } + /** \deprecated Use: + uint8_t SdFile::createContiguous(SdFile* dirFile, + const char* fileName, uint32_t size) + */ + uint8_t createContiguous(SdFile &dirFile, // NOLINT + const char *fileName, uint32_t size) { + return createContiguous(&dirFile, fileName, size); + } + + /** + \deprecated Use: + static void SdFile::dateTimeCallback( + void (*dateTime)(uint16_t* date, uint16_t* time)); + */ + static void dateTimeCallback(void (*dateTime)(uint16_t &date, + uint16_t &time)) { // NOLINT + oldDateTime_ = dateTime; + dateTime_ = dateTime ? oldToNew : 0; + } + /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */ + uint8_t dirEntry(dir_t &dir) { + return dirEntry(&dir); // NOLINT + } + /** \deprecated Use: + uint8_t SdFile::makeDir(SdFile* dir, const char* dirName); + */ + uint8_t makeDir(SdFile &dir, const char *dirName) { // NOLINT + return makeDir(&dir, dirName); + } + /** \deprecated Use: + uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag); + */ + uint8_t open(SdFile &dirFile, // NOLINT + const char *fileName, uint8_t oflag) { + return open(&dirFile, fileName, oflag); + } + /** \deprecated Do not use in new apps */ + uint8_t open(SdFile &dirFile, const char *fileName) { // NOLINT + return open(dirFile, fileName, O_RDWR); + } + /** \deprecated Use: + uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag); + */ + uint8_t open(SdFile &dirFile, uint16_t index, uint8_t oflag) { // NOLINT + return open(&dirFile, index, oflag); + } + /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */ + uint8_t openRoot(SdVolume &vol) { + return openRoot(&vol); // NOLINT + } + + /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */ + int8_t readDir(dir_t &dir) { + return readDir(&dir); // NOLINT + } + /** \deprecated Use: + static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName); + */ + static uint8_t remove(SdFile &dirFile, const char *fileName) { // NOLINT + return remove(&dirFile, fileName); + } + //------------------------------------------------------------------------------ + // rest are private +private: + static void (*oldDateTime_)(uint16_t &date, uint16_t &time); // NOLINT + static void oldToNew(uint16_t *date, uint16_t *time) { + uint16_t d; + uint16_t t; + oldDateTime_(d, t); + *date = d; + *time = t; + } +#endif // ALLOW_DEPRECATED_FUNCTIONS +private: + // bits defined in flags_ + // should be 0XF + static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); + // available bits + static uint8_t const F_FILE_NON_BLOCKING_WRITE = 0X10; + // a new cluster was added to the file + static uint8_t const F_FILE_CLUSTER_ADDED = 0X20; + // use unbuffered SD read + static uint8_t const F_FILE_UNBUFFERED_READ = 0X40; + // sync of directory entry required + static uint8_t const F_FILE_DIR_DIRTY = 0X80; + +// make sure F_OFLAG is ok +#if ((F_FILE_NON_BLOCKING_WRITE | F_FILE_CLUSTER_ADDED | \ + F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & \ + F_OFLAG) #error flags_ bits conflict - #endif // flags_ bits - - // private data - uint8_t flags_; // See above for definition of flags_ bits - uint8_t type_; // type of file see above for values - uint32_t curCluster_; // cluster for current file position - uint32_t curPosition_; // current file position in bytes from beginning - uint32_t dirBlock_; // SD block that contains directory entry for file - uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF - uint32_t fileSize_; // file size in bytes - uint32_t firstCluster_; // first cluster of file - SdVolume* vol_; // volume where file is located - - // private functions - uint8_t addCluster(void); - uint8_t addDirCluster(void); - dir_t* cacheDirEntry(uint8_t action); - static void (*dateTime_)(uint16_t* date, uint16_t* time); - static uint8_t make83Name(const char* str, uint8_t* name); - uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); - dir_t* readDirCache(void); +#endif // flags_ bits + + // private data + uint8_t flags_; // See above for definition of flags_ bits + uint8_t type_; // type of file see above for values + uint32_t curCluster_; // cluster for current file position + uint32_t curPosition_; // current file position in bytes from beginning + uint32_t dirBlock_; // SD block that contains directory entry for file + uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF + uint32_t fileSize_; // file size in bytes + uint32_t firstCluster_; // first cluster of file + SdVolume *vol_; // volume where file is located + + // private functions + uint8_t addCluster(void); + uint8_t addDirCluster(void); + dir_t *cacheDirEntry(uint8_t action); + static void (*dateTime_)(uint16_t *date, uint16_t *time); + static uint8_t make83Name(const char *str, uint8_t *name); + uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); + dir_t *readDirCache(void); #ifdef MOCK_PINS_COUNT - int writeError; - public: - int getWriteError() { return writeError; } - void setWriteError(int value = 1) { writeError = value; } - void clearWriteError() { writeError = 0; } + int writeError; + +public: + int getWriteError() { return writeError; } + void setWriteError(int value = 1) { writeError = value; } + void clearWriteError() { writeError = 0; } #endif }; //============================================================================== @@ -472,17 +438,17 @@ class SdFile : public Print { */ union cache_t { /** Used to access cached file data blocks. */ - uint8_t data[512]; + uint8_t data[512]; /** Used to access cached FAT16 entries. */ uint16_t fat16[256]; /** Used to access cached FAT32 entries. */ uint32_t fat32[128]; /** Used to access cached directory entries. */ - dir_t dir[16]; + dir_t dir[16]; /** Used to access a cached MasterBoot Record. */ - mbr_t mbr; + mbr_t mbr; /** Used to access to a cached FAT boot sector. */ - fbs_t fbs; + fbs_t fbs; }; //------------------------------------------------------------------------------ /** @@ -490,162 +456,130 @@ union cache_t { \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. */ class SdVolume { - public: - /** Create an instance of SdVolume */ - SdVolume(void) : allocSearchStart_(2), fatType_(0) {} - /** Clear the cache and returns a pointer to the cache. Used by the WaveRP - recorder to do raw write to the SD card. Not for normal apps. - */ - static uint8_t* cacheClear(void) { - cacheFlush(); - cacheBlockNumber_ = 0XFFFFFFFF; - return cacheBuffer_.data; - } - /** - Initialize a FAT volume. Try partition one first then try super - floppy format. - - \param[in] dev The Sd2Card where the volume is located. - - \return The value one, true, is returned for success and - the value zero, false, is returned for failure. Reasons for - failure include not finding a valid partition, not finding a valid - FAT file system or an I/O error. - */ - uint8_t init(Sd2Card* dev) { - return init(dev, 1) ? true : init(dev, 0); - } - uint8_t init(Sd2Card* dev, uint8_t part); - - // inline functions that return volume info - /** \return The volume's cluster size in blocks. */ - uint8_t blocksPerCluster(void) const { - return blocksPerCluster_; - } - /** \return The number of blocks in one FAT. */ - uint32_t blocksPerFat(void) const { - return blocksPerFat_; - } - /** \return The total number of clusters in the volume. */ - uint32_t clusterCount(void) const { - return clusterCount_; - } - /** \return The shift count required to multiply by blocksPerCluster. */ - uint8_t clusterSizeShift(void) const { - return clusterSizeShift_; - } - /** \return The logical block number for the start of file data. */ - uint32_t dataStartBlock(void) const { - return dataStartBlock_; - } - /** \return The number of FAT structures on the volume. */ - uint8_t fatCount(void) const { - return fatCount_; - } - /** \return The logical block number for the start of the first FAT. */ - uint32_t fatStartBlock(void) const { - return fatStartBlock_; - } - /** \return The FAT type of the volume. Values are 12, 16 or 32. */ - uint8_t fatType(void) const { - return fatType_; - } - /** \return The number of entries in the root directory for FAT16 volumes. */ - uint32_t rootDirEntryCount(void) const { - return rootDirEntryCount_; - } - /** \return The logical block number for the start of the root directory - on FAT16 volumes or the first cluster number on FAT32 volumes. */ - uint32_t rootDirStart(void) const { - return rootDirStart_; - } - /** return a pointer to the Sd2Card object for this volume */ - static Sd2Card* sdCard(void) { - return sdCard_; - } - //------------------------------------------------------------------------------ - #if ALLOW_DEPRECATED_FUNCTIONS - // Deprecated functions - suppress cpplint warnings with NOLINT comment - /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */ - uint8_t init(Sd2Card& dev) { - return init(&dev); // NOLINT - } - - /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */ - uint8_t init(Sd2Card& dev, uint8_t part) { // NOLINT - return init(&dev, part); - } - #endif // ALLOW_DEPRECATED_FUNCTIONS - //------------------------------------------------------------------------------ - private: - // Allow SdFile access to SdVolume private data. - friend class SdFile; - - // value for action argument in cacheRawBlock to indicate read from cache - static uint8_t const CACHE_FOR_READ = 0; - // value for action argument in cacheRawBlock to indicate cache dirty - static uint8_t const CACHE_FOR_WRITE = 1; - - static cache_t cacheBuffer_; // 512 byte cache for device blocks - static uint32_t cacheBlockNumber_; // Logical number of block in the cache - static Sd2Card* sdCard_; // Sd2Card object for cache - static uint8_t cacheDirty_; // cacheFlush() will write block if true - static uint32_t cacheMirrorBlock_; // block number for mirror FAT - // - uint32_t allocSearchStart_; // start cluster for alloc search - uint8_t blocksPerCluster_; // cluster size in blocks - uint32_t blocksPerFat_; // FAT size in blocks - uint32_t clusterCount_; // clusters in one FAT - uint8_t clusterSizeShift_; // shift to convert cluster count to block count - uint32_t dataStartBlock_; // first data block number - uint8_t fatCount_; // number of FATs on volume - uint32_t fatStartBlock_; // start block for first FAT - uint8_t fatType_; // volume type (12, 16, OR 32) - uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir - uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 - //---------------------------------------------------------------------------- - uint8_t allocContiguous(uint32_t count, uint32_t* curCluster); - uint8_t blockOfCluster(uint32_t position) const { - return (position >> 9) & (blocksPerCluster_ - 1); - } - uint32_t clusterStartBlock(uint32_t cluster) const { - return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_); - } - uint32_t blockNumber(uint32_t cluster, uint32_t position) const { - return clusterStartBlock(cluster) + blockOfCluster(position); - } - static uint8_t cacheFlush(uint8_t blocking = 1); - static uint8_t cacheMirrorBlockFlush(uint8_t blocking); - static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action); - static void cacheSetDirty(void) { - cacheDirty_ |= CACHE_FOR_WRITE; - } - static uint8_t cacheZeroBlock(uint32_t blockNumber); - uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const; - uint8_t fatGet(uint32_t cluster, uint32_t* value) const; - uint8_t fatPut(uint32_t cluster, uint32_t value); - uint8_t fatPutEOC(uint32_t cluster) { - return fatPut(cluster, 0x0FFFFFFF); - } - uint8_t freeChain(uint32_t cluster); - uint8_t isEOC(uint32_t cluster) const { - return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN); - } - uint8_t readBlock(uint32_t block, uint8_t* dst) { - return sdCard_->readBlock(block, dst); - } - uint8_t readData(uint32_t block, uint16_t offset, - uint16_t count, uint8_t* dst) { - return sdCard_->readData(block, offset, count, dst); - } - uint8_t writeBlock(uint32_t block, const uint8_t* dst, uint8_t blocking = 1) { - return sdCard_->writeBlock(block, dst, blocking); - } - uint8_t isBusy(void) { - return sdCard_->isBusy(); - } - uint8_t isCacheMirrorBlockDirty(void) { - return (cacheMirrorBlock_ != 0); - } +public: + /** Create an instance of SdVolume */ + SdVolume(void) : allocSearchStart_(2), fatType_(0) {} + /** Clear the cache and returns a pointer to the cache. Used by the WaveRP + recorder to do raw write to the SD card. Not for normal apps. + */ + static uint8_t *cacheClear(void) { + cacheFlush(); + cacheBlockNumber_ = 0XFFFFFFFF; + return cacheBuffer_.data; + } + /** + Initialize a FAT volume. Try partition one first then try super + floppy format. + + \param[in] dev The Sd2Card where the volume is located. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. Reasons for + failure include not finding a valid partition, not finding a valid + FAT file system or an I/O error. + */ + uint8_t init(Sd2Card *dev) { return init(dev, 1) ? true : init(dev, 0); } + uint8_t init(Sd2Card *dev, uint8_t part); + + // inline functions that return volume info + /** \return The volume's cluster size in blocks. */ + uint8_t blocksPerCluster(void) const { return blocksPerCluster_; } + /** \return The number of blocks in one FAT. */ + uint32_t blocksPerFat(void) const { return blocksPerFat_; } + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount(void) const { return clusterCount_; } + /** \return The shift count required to multiply by blocksPerCluster. */ + uint8_t clusterSizeShift(void) const { return clusterSizeShift_; } + /** \return The logical block number for the start of file data. */ + uint32_t dataStartBlock(void) const { return dataStartBlock_; } + /** \return The number of FAT structures on the volume. */ + uint8_t fatCount(void) const { return fatCount_; } + /** \return The logical block number for the start of the first FAT. */ + uint32_t fatStartBlock(void) const { return fatStartBlock_; } + /** \return The FAT type of the volume. Values are 12, 16 or 32. */ + uint8_t fatType(void) const { return fatType_; } + /** \return The number of entries in the root directory for FAT16 volumes. */ + uint32_t rootDirEntryCount(void) const { return rootDirEntryCount_; } + /** \return The logical block number for the start of the root directory + on FAT16 volumes or the first cluster number on FAT32 volumes. */ + uint32_t rootDirStart(void) const { return rootDirStart_; } + /** return a pointer to the Sd2Card object for this volume */ + static Sd2Card *sdCard(void) { return sdCard_; } +//------------------------------------------------------------------------------ +#if ALLOW_DEPRECATED_FUNCTIONS + // Deprecated functions - suppress cpplint warnings with NOLINT comment + /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */ + uint8_t init(Sd2Card &dev) { + return init(&dev); // NOLINT + } + + /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */ + uint8_t init(Sd2Card &dev, uint8_t part) { // NOLINT + return init(&dev, part); + } +#endif // ALLOW_DEPRECATED_FUNCTIONS + //------------------------------------------------------------------------------ +private: + // Allow SdFile access to SdVolume private data. + friend class SdFile; + + // value for action argument in cacheRawBlock to indicate read from cache + static uint8_t const CACHE_FOR_READ = 0; + // value for action argument in cacheRawBlock to indicate cache dirty + static uint8_t const CACHE_FOR_WRITE = 1; + + static cache_t cacheBuffer_; // 512 byte cache for device blocks + static uint32_t cacheBlockNumber_; // Logical number of block in the cache + static Sd2Card *sdCard_; // Sd2Card object for cache + static uint8_t cacheDirty_; // cacheFlush() will write block if true + static uint32_t cacheMirrorBlock_; // block number for mirror FAT + // + uint32_t allocSearchStart_; // start cluster for alloc search + uint8_t blocksPerCluster_; // cluster size in blocks + uint32_t blocksPerFat_; // FAT size in blocks + uint32_t clusterCount_; // clusters in one FAT + uint8_t clusterSizeShift_; // shift to convert cluster count to block count + uint32_t dataStartBlock_; // first data block number + uint8_t fatCount_; // number of FATs on volume + uint32_t fatStartBlock_; // start block for first FAT + uint8_t fatType_; // volume type (12, 16, OR 32) + uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir + uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 + //---------------------------------------------------------------------------- + uint8_t allocContiguous(uint32_t count, uint32_t *curCluster); + uint8_t blockOfCluster(uint32_t position) const { + return (position >> 9) & (blocksPerCluster_ - 1); + } + uint32_t clusterStartBlock(uint32_t cluster) const { + return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_); + } + uint32_t blockNumber(uint32_t cluster, uint32_t position) const { + return clusterStartBlock(cluster) + blockOfCluster(position); + } + static uint8_t cacheFlush(uint8_t blocking = 1); + static uint8_t cacheMirrorBlockFlush(uint8_t blocking); + static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action); + static void cacheSetDirty(void) { cacheDirty_ |= CACHE_FOR_WRITE; } + static uint8_t cacheZeroBlock(uint32_t blockNumber); + uint8_t chainSize(uint32_t beginCluster, uint32_t *size) const; + uint8_t fatGet(uint32_t cluster, uint32_t *value) const; + uint8_t fatPut(uint32_t cluster, uint32_t value); + uint8_t fatPutEOC(uint32_t cluster) { return fatPut(cluster, 0x0FFFFFFF); } + uint8_t freeChain(uint32_t cluster); + uint8_t isEOC(uint32_t cluster) const { + return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN); + } + uint8_t readBlock(uint32_t block, uint8_t *dst) { + return sdCard_->readBlock(block, dst); + } + uint8_t readData(uint32_t block, uint16_t offset, uint16_t count, + uint8_t *dst) { + return sdCard_->readData(block, offset, count, dst); + } + uint8_t writeBlock(uint32_t block, const uint8_t *dst, uint8_t blocking = 1) { + return sdCard_->writeBlock(block, dst, blocking); + } + uint8_t isBusy(void) { return sdCard_->isBusy(); } + uint8_t isCacheMirrorBlockDirty(void) { return (cacheMirrorBlock_ != 0); } }; -#endif // SdFat_h +#endif // SdFat_h diff --git a/src/utility/SdFatUtil.h b/src/utility/SdFatUtil.h index 00227ec..0422e5c 100644 --- a/src/utility/SdFatUtil.h +++ b/src/utility/SdFatUtil.h @@ -25,14 +25,14 @@ */ #include #ifdef __AVR__ - #include - /** Store and print a string in flash memory.*/ - #define PgmPrint(x) SerialPrint_P(PSTR(x)) - /** Store and print a string in flash memory followed by a CR/LF.*/ - #define PgmPrintln(x) SerialPrintln_P(PSTR(x)) - /** Defined so doxygen works for function definitions. */ +#include +/** Store and print a string in flash memory.*/ +#define PgmPrint(x) SerialPrint_P(PSTR(x)) +/** Store and print a string in flash memory followed by a CR/LF.*/ +#define PgmPrintln(x) SerialPrintln_P(PSTR(x)) +/** Defined so doxygen works for function definitions. */ #endif -#define NOINLINE __attribute__((noinline,unused)) +#define NOINLINE __attribute__((noinline, unused)) #define UNUSEDOK __attribute__((unused)) #if UINTPTR_MAX == 0xFFFF @@ -48,17 +48,17 @@ typedef int64_t ptr_as_int; //------------------------------------------------------------------------------ /** Return the number of bytes currently free in RAM. */ static UNUSEDOK int FreeRam(void) { - extern int __bss_end; - extern int* __brkval; + extern int __bss_end; + extern int *__brkval; int free_memory; if (reinterpret_cast(__brkval) == 0) { // if no heap use from end of bss section - free_memory = reinterpret_cast(&free_memory) - - reinterpret_cast(&__bss_end); + free_memory = reinterpret_cast(&free_memory) - + reinterpret_cast(&__bss_end); } else { // use from top of stack to heap - free_memory = reinterpret_cast(&free_memory) - - reinterpret_cast(__brkval); + free_memory = reinterpret_cast(&free_memory) - + reinterpret_cast(__brkval); } return free_memory; } @@ -84,5 +84,5 @@ static NOINLINE void SerialPrintln_P(PGM_P str) { SerialPrint_P(str); Serial.println(); } -#endif // __AVR__ -#endif // #define SdFatUtil_h +#endif // __AVR__ +#endif // #define SdFatUtil_h diff --git a/src/utility/SdFatmainpage.h b/src/utility/SdFatmainpage.h index 1e6d96f..d72119b 100644 --- a/src/utility/SdFatmainpage.h +++ b/src/utility/SdFatmainpage.h @@ -45,11 +45,11 @@ A number of example are provided in the SdFat/examples folder. These were developed to test SdFat and illustrate its use. - SdFat was developed for high speed data recording. SdFat was used to implement - an audio record/play class, WaveRP, for the Adafruit Wave Shield. This - application uses special Sd2Card calls to write to contiguous files in raw mode. - These functions reduce write latency so that audio can be recorded with the - small amount of RAM in the Arduino. + SdFat was developed for high speed data recording. SdFat was used to + implement an audio record/play class, WaveRP, for the Adafruit Wave Shield. + This application uses special Sd2Card calls to write to contiguous files in + raw mode. These functions reduce write latency so that audio can be recorded + with the small amount of RAM in the Arduino. \section SDcard SD\SDHC Cards @@ -79,9 +79,9 @@ controllers when resistor voltage dividers are used. The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the - 74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield - uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the - 74LCX245. + 74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave + Shield uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based + on the 74LCX245. If you are using a resistor based level shifter and are having problems try setting the SPI bus frequency to 4 MHz. This can be done by using @@ -89,7 +89,8 @@ \section comment Bugs and Comments - If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. + If you wish to report bugs or have comments, send email to + fat16lib@sbcglobal.net. \section SdFatClass SdFat Usage @@ -109,16 +110,16 @@ \note The Arduino Print class uses character - at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink - function to control when data is written to the SD card. + at a time writes so it was necessary to use a \link SdFile::sync() sync() + \endlink function to control when data is written to the SD card. \par - An application which writes to a file using \link Print::print() print()\endlink, - \link Print::println() println() \endlink - or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink + An application which writes to a file using \link Print::print() + print()\endlink, \link Print::println() println() \endlink or \link + SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink at the appropriate time to force data and directory information to be written - to the SD Card. Data and directory information are also written to the SD card - when \link SdFile::close() close() \endlink is called. + to the SD Card. Data and directory information are also written to the SD + card when \link SdFile::close() close() \endlink is called. \par Applications must use care calling \link SdFile::sync() sync() \endlink @@ -127,8 +128,9 @@ the block that contains the directory entry for update, writing the directory block back and reading back the current data block. - It is possible to open a file with two or more instances of SdFile. A file may - be corrupted if data is written to the file by more than one instance of SdFile. + It is possible to open a file with two or more instances of SdFile. A file + may be corrupted if data is written to the file by more than one instance of + SdFile. \section HowTo How to format SD Cards as FAT Volumes diff --git a/src/utility/SdFile.cpp b/src/utility/SdFile.cpp index 18a1db6..16e01ec 100644 --- a/src/utility/SdFile.cpp +++ b/src/utility/SdFile.cpp @@ -19,17 +19,17 @@ */ #include "SdFat.h" #ifdef __AVR__ - #include +#include #endif #include //------------------------------------------------------------------------------ // callback function for date/time -void (*SdFile::dateTime_)(uint16_t* date, uint16_t* time) = NULL; +void (*SdFile::dateTime_)(uint16_t *date, uint16_t *time) = NULL; #if ALLOW_DEPRECATED_FUNCTIONS - // suppress cpplint warnings with NOLINT comment - void (*SdFile::oldDateTime_)(uint16_t& date, uint16_t& time) = NULL; // NOLINT -#endif // ALLOW_DEPRECATED_FUNCTIONS +// suppress cpplint warnings with NOLINT comment +void (*SdFile::oldDateTime_)(uint16_t &date, uint16_t &time) = NULL; // NOLINT +#endif // ALLOW_DEPRECATED_FUNCTIONS //------------------------------------------------------------------------------ // add a cluster to a file uint8_t SdFile::addCluster() { @@ -67,7 +67,7 @@ uint8_t SdFile::addDirCluster(void) { //------------------------------------------------------------------------------ // cache a file's directory entry // return pointer to cached entry or null for failure -dir_t* SdFile::cacheDirEntry(uint8_t action) { +dir_t *SdFile::cacheDirEntry(uint8_t action) { if (!SdVolume::cacheRawBlock(dirBlock_, action)) { return NULL; } @@ -101,13 +101,13 @@ uint8_t SdFile::close(void) { Reasons for failure include file is not contiguous, file has zero length or an I/O error occurred. */ -uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { +uint8_t SdFile::contiguousRange(uint32_t *bgnBlock, uint32_t *endBlock) { // error if no blocks if (firstCluster_ == 0) { return false; } - for (uint32_t c = firstCluster_; ; c++) { + for (uint32_t c = firstCluster_;; c++) { uint32_t next; if (!vol_->fatGet(c, &next)) { return false; @@ -120,8 +120,7 @@ uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { return false; } *bgnBlock = vol_->clusterStartBlock(firstCluster_); - *endBlock = vol_->clusterStartBlock(c) - + vol_->blocksPerCluster_ - 1; + *endBlock = vol_->clusterStartBlock(c) + vol_->blocksPerCluster_ - 1; return true; } } @@ -145,8 +144,8 @@ uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { directory is full or an I/O error. */ -uint8_t SdFile::createContiguous(SdFile* dirFile, - const char* fileName, uint32_t size) { +uint8_t SdFile::createContiguous(SdFile *dirFile, const char *fileName, + uint32_t size) { // don't allow zero length file if (size == 0) { return false; @@ -178,14 +177,14 @@ uint8_t SdFile::createContiguous(SdFile* dirFile, \return The value one, true, is returned for success and the value zero, false, is returned for failure. */ -uint8_t SdFile::dirEntry(dir_t* dir) { +uint8_t SdFile::dirEntry(dir_t *dir) { // make sure fields on SD are correct if (!sync()) { return false; } // read entry - dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_READ); + dir_t *p = cacheDirEntry(SdVolume::CACHE_FOR_READ); if (!p) { return false; } @@ -202,7 +201,7 @@ uint8_t SdFile::dirEntry(dir_t* dir) { \param[in] dir The directory structure containing the name. \param[out] name A 13 byte char array for the formatted name. */ -void SdFile::dirName(const dir_t& dir, char* name) { +void SdFile::dirName(const dir_t &dir, char *name) { uint8_t j = 0; for (uint8_t i = 0; i < 11; i++) { if (dir.name[i] == ' ') { @@ -230,7 +229,7 @@ void SdFile::dirName(const dir_t& dir, char* name) { list to indicate subdirectory level. */ void SdFile::ls(uint8_t flags, uint8_t indent) { - dir_t* p; + dir_t *p; rewind(); while ((p = readDirCache())) { @@ -283,9 +282,9 @@ void SdFile::ls(uint8_t flags, uint8_t indent) { } //------------------------------------------------------------------------------ // format directory name field from a 8.3 name string -uint8_t SdFile::make83Name(const char* str, uint8_t* name) { +uint8_t SdFile::make83Name(const char *str, uint8_t *name) { uint8_t c; - uint8_t n = 7; // max index for part before dot + uint8_t n = 7; // max index for part before dot uint8_t i = 0; // blank fill name and extension while (i < 11) { @@ -295,31 +294,33 @@ uint8_t SdFile::make83Name(const char* str, uint8_t* name) { while ((c = *str++) != '\0') { if (c == '.') { if (n == 10) { - return false; // only one dot allowed + return false; // only one dot allowed } - n = 10; // max index for full 8.3 name - i = 8; // place for extension + n = 10; // max index for full 8.3 name + i = 8; // place for extension } else { // illegal FAT characters uint8_t b; - #if defined(__AVR__) +#if defined(__AVR__) PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); - while ((b = pgm_read_byte(p++))) if (b == c) { + while ((b = pgm_read_byte(p++))) + if (b == c) { return false; } - #elif defined(__arm__) +#elif defined(__arm__) const uint8_t valid[] = "|<>^+=?/[];,*\"\\"; const uint8_t *p = valid; - while ((b = *p++)) if (b == c) { + while ((b = *p++)) + if (b == c) { return false; } - #endif +#endif // check size and only allow ASCII printable characters if (i > n || c < 0X21 || c > 0X7E) { return false; } // only upper case allowed in 8.3 names - convert lower to upper - name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); + name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); } } // must have a file name, extension is optional @@ -338,7 +339,7 @@ uint8_t SdFile::make83Name(const char* str, uint8_t* name) { Reasons for failure include this SdFile is already open, \a dir is not a directory, \a dirName is invalid or already exists in \a dir. */ -uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) { +uint8_t SdFile::makeDir(SdFile *dir, const char *dirName) { dir_t d; // create a normal file @@ -361,7 +362,7 @@ uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) { } // cache entry - should already be in cache due to sync() call - dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + dir_t *p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); if (!p) { return false; } @@ -450,9 +451,9 @@ uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) { a directory, \a fileName is invalid, the file does not exist or can't be opened in the access mode specified by oflag. */ -uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { +uint8_t SdFile::open(SdFile *dirFile, const char *fileName, uint8_t oflag) { uint8_t dname[11]; - dir_t* p; + dir_t *p; // error if already open if (isOpen()) { @@ -562,7 +563,7 @@ uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { See open() by fileName for definition of flags and return values. */ -uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) { +uint8_t SdFile::open(SdFile *dirFile, uint16_t index, uint8_t oflag) { // error if already open if (isOpen()) { return false; @@ -581,14 +582,14 @@ uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) { } // read entry into cache - dir_t* p = dirFile->readDirCache(); + dir_t *p = dirFile->readDirCache(); if (p == NULL) { return false; } // error if empty slot or '.' or '..' - if (p->name[0] == DIR_NAME_FREE || - p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED || + p->name[0] == '.') { return false; } // open cached entry @@ -598,7 +599,7 @@ uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) { // open a cached directory entry. Assumes vol_ is initializes uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { // location of entry in cache - dir_t* p = SdVolume::cacheBuffer_.dir + dirIndex; + dir_t *p = SdVolume::cacheBuffer_.dir + dirIndex; // write or truncate is an error for a directory or read-only file if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) { @@ -650,7 +651,7 @@ uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { Reasons for failure include the FAT volume has not been initialized or it a FAT12 volume. */ -uint8_t SdFile::openRoot(SdVolume* vol) { +uint8_t SdFile::openRoot(SdVolume *vol) { // error if file is already open if (isOpen()) { return false; @@ -689,7 +690,7 @@ uint8_t SdFile::openRoot(SdVolume* vol) { \param[in] dir The directory structure containing the name. \param[in] width Blank fill name if length is less than \a width. */ -void SdFile::printDirName(const dir_t& dir, uint8_t width) { +void SdFile::printDirName(const dir_t &dir, uint8_t width) { uint8_t w = 0; for (uint8_t i = 0; i < 11; i++) { if (dir.name[i] == ' ') { @@ -766,8 +767,8 @@ void SdFile::printTwoDigits(uint8_t v) { read() called before a file has been opened, corrupt file system or an I/O error occurred. */ -int16_t SdFile::read(void* buf, uint16_t nbyte) { - uint8_t* dst = reinterpret_cast(buf); +int16_t SdFile::read(void *buf, uint16_t nbyte) { + uint8_t *dst = reinterpret_cast(buf); // error if not open or write only if (!isOpen() || !(flags_ & O_READ)) { @@ -782,8 +783,8 @@ int16_t SdFile::read(void* buf, uint16_t nbyte) { // amount left to read uint16_t toRead = nbyte; while (toRead > 0) { - uint32_t block; // raw device block number - uint16_t offset = curPosition_ & 0X1FF; // offset in block + uint32_t block; // raw device block number + uint16_t offset = curPosition_ & 0X1FF; // offset in block if (type_ == FAT_FILE_TYPE_ROOT16) { block = vol_->rootDirStart() + (curPosition_ >> 9); } else { @@ -821,8 +822,8 @@ int16_t SdFile::read(void* buf, uint16_t nbyte) { if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) { return -1; } - uint8_t* src = SdVolume::cacheBuffer_.data + offset; - uint8_t* end = src + n; + uint8_t *src = SdVolume::cacheBuffer_.data + offset; + uint8_t *end = src + n; while (src != end) { *dst++ = *src++; } @@ -844,7 +845,7 @@ int16_t SdFile::read(void* buf, uint16_t nbyte) { readDir() called before a directory has been opened, this is not a directory file or an I/O error occurred. */ -int8_t SdFile::readDir(dir_t* dir) { +int8_t SdFile::readDir(dir_t *dir) { int8_t n; // if not a directory file or miss-positioned return an error if (!isDir() || (0X1F & curPosition_)) { @@ -871,7 +872,7 @@ int8_t SdFile::readDir(dir_t* dir) { //------------------------------------------------------------------------------ // Read next directory entry into the cache // Assumes file is correctly positioned -dir_t* SdFile::readDirCache(void) { +dir_t *SdFile::readDirCache(void) { // error if not directory if (!isDir()) { return NULL; @@ -913,7 +914,7 @@ uint8_t SdFile::remove(void) { } // cache directory entry - dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); if (!d) { return false; } @@ -946,7 +947,7 @@ uint8_t SdFile::remove(void) { \a dirFile is not a directory, \a fileName is not found or an I/O error occurred. */ -uint8_t SdFile::remove(SdFile* dirFile, const char* fileName) { +uint8_t SdFile::remove(SdFile *dirFile, const char *fileName) { SdFile file; if (!file.open(dirFile, fileName, O_WRITE)) { return false; @@ -979,7 +980,7 @@ uint8_t SdFile::rmDir(void) { // make sure directory is empty while (curPosition_ < fileSize_) { - dir_t* p = readDirCache(); + dir_t *p = readDirCache(); if (p == NULL) { return false; } @@ -1025,7 +1026,7 @@ uint8_t SdFile::rmRfStar(void) { // remember position uint16_t index = curPosition_ / 32; - dir_t* p = readDirCache(); + dir_t *p = readDirCache(); if (!p) { return false; } @@ -1136,7 +1137,7 @@ uint8_t SdFile::sync(uint8_t blocking) { } if (flags_ & F_FILE_DIR_DIRTY) { - dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); if (!d) { return false; } @@ -1201,20 +1202,13 @@ uint8_t SdFile::sync(uint8_t blocking) { the value zero, false, is returned for failure. */ uint8_t SdFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, - uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { - if (!isOpen() - || year < 1980 - || year > 2107 - || month < 1 - || month > 12 - || day < 1 - || day > 31 - || hour > 23 - || minute > 59 - || second > 59) { + uint8_t day, uint8_t hour, uint8_t minute, + uint8_t second) { + if (!isOpen() || year < 1980 || year > 2107 || month < 1 || month > 12 || + day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59) { return false; } - dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); if (!d) { return false; } @@ -1327,9 +1321,9 @@ uint8_t SdFile::truncate(uint32_t length) { for a read-only file, device is full, a corrupt file system or an I/O error. */ -size_t SdFile::write(const void* buf, uint16_t nbyte) { +size_t SdFile::write(const void *buf, uint16_t nbyte) { // convert void* to uint8_t* - must be before goto statements - const uint8_t* src = reinterpret_cast(buf); + const uint8_t *src = reinterpret_cast(buf); // number of bytes left to write - must be before goto statements uint16_t nToWrite = nbyte; @@ -1411,8 +1405,8 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { goto writeErrorReturn; } } - uint8_t* dst = SdVolume::cacheBuffer_.data + blockOffset; - uint8_t* end = dst + n; + uint8_t *dst = SdVolume::cacheBuffer_.data + blockOffset; + uint8_t *end = dst + n; while (dst != end) { *dst++ = *src++; } @@ -1438,7 +1432,7 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { writeErrorReturn: // return for write error - //writeError = true; + // writeError = true; setWriteError(); return 0; } @@ -1448,18 +1442,14 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { Use SdFile::writeError to check for errors. */ -size_t SdFile::write(uint8_t b) { - return write(&b, 1); -} +size_t SdFile::write(uint8_t b) { return write(&b, 1); } //------------------------------------------------------------------------------ /** Write a string to a file. Used by the Arduino Print class. Use SdFile::writeError to check for errors. */ -size_t SdFile::write(const char* str) { - return write(str, strlen(str)); -} +size_t SdFile::write(const char *str) { return write(str, strlen(str)); } #ifdef __AVR__ //------------------------------------------------------------------------------ /** diff --git a/src/utility/SdInfo.h b/src/utility/SdInfo.h index 6a0e087..71addd5 100644 --- a/src/utility/SdInfo.h +++ b/src/utility/SdInfo.h @@ -85,18 +85,18 @@ uint8_t const DATA_RES_ACCEPTED = 0X05; //------------------------------------------------------------------------------ typedef struct CID { // byte 0 - uint8_t mid; // Manufacturer ID + uint8_t mid; // Manufacturer ID // byte 1-2 - char oid[2]; // OEM/Application ID + char oid[2]; // OEM/Application ID // byte 3-7 - char pnm[5]; // Product name + char pnm[5]; // Product name // byte 8 - unsigned prv_m : 4; // Product revision n.m + unsigned prv_m : 4; // Product revision n.m unsigned prv_n : 4; // byte 9-12 - uint32_t psn; // Product serial number + uint32_t psn; // Product serial number // byte 13 - unsigned mdt_year_high : 4; // Manufacturing date + unsigned mdt_year_high : 4; // Manufacturing date unsigned reserved : 4; // byte 14 unsigned mdt_month : 4; @@ -156,7 +156,7 @@ typedef struct CSDV1 { unsigned write_partial : 1; unsigned write_bl_len_low : 2; // byte 14 - unsigned reserved5: 2; + unsigned reserved5 : 2; unsigned file_format : 2; unsigned tmp_write_protect : 1; unsigned perm_write_protect : 1; @@ -213,7 +213,7 @@ typedef struct CSDV2 { unsigned write_partial : 1; unsigned write_bl_len_low : 2; // byte 14 - unsigned reserved7: 2; + unsigned reserved7 : 2; unsigned file_format : 2; unsigned tmp_write_protect : 1; unsigned perm_write_protect : 1; @@ -229,4 +229,4 @@ union csd_t { csd1_t v1; csd2_t v2; }; -#endif // SdInfo_h +#endif // SdInfo_h diff --git a/src/utility/SdVolume.cpp b/src/utility/SdVolume.cpp index 60375c4..bc219fe 100644 --- a/src/utility/SdVolume.cpp +++ b/src/utility/SdVolume.cpp @@ -22,13 +22,13 @@ // raw block cache // init cacheBlockNumber_to invalid SD block number uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF; -cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card -Sd2Card* SdVolume::sdCard_; // pointer to SD card object -uint8_t SdVolume::cacheDirty_ = 0; // cacheFlush() will write block if true -uint32_t SdVolume::cacheMirrorBlock_ = 0; // mirror block for second FAT +cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card +Sd2Card *SdVolume::sdCard_; // pointer to SD card object +uint8_t SdVolume::cacheDirty_ = 0; // cacheFlush() will write block if true +uint32_t SdVolume::cacheMirrorBlock_ = 0; // mirror block for second FAT //------------------------------------------------------------------------------ // find a contiguous group of clusters -uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { +uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t *curCluster) { // start of group uint32_t bgnCluster; @@ -167,7 +167,7 @@ uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) { } //------------------------------------------------------------------------------ // return the size in bytes of a cluster chain -uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const { +uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t *size) const { uint32_t s = 0; do { if (!fatGet(cluster, &cluster)) { @@ -180,7 +180,7 @@ uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const { } //------------------------------------------------------------------------------ // Fetch a FAT entry -uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const { +uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t *value) const { if (cluster > (clusterCount_ + 1)) { return false; } @@ -272,7 +272,7 @@ uint8_t SdVolume::freeChain(uint32_t cluster) { failure include not finding a valid partition, not finding a valid FAT file system in the specified partition or an I/O error. */ -uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { +uint8_t SdVolume::init(Sd2Card *dev, uint8_t part) { uint32_t volumeStartBlock = 0; sdCard_ = dev; // if part == 0 assume super floppy with FAT boot sector in block zero @@ -284,10 +284,8 @@ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) { return false; } - part_t* p = &cacheBuffer_.mbr.part[part - 1]; - if ((p->boot & 0X7F) != 0 || - p->totalSectors < 100 || - p->firstSector == 0) { + part_t *p = &cacheBuffer_.mbr.part[part - 1]; + if ((p->boot & 0X7F) != 0 || p->totalSectors < 100 || p->firstSector == 0) { // not a valid partition return false; } @@ -296,11 +294,9 @@ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) { return false; } - bpb_t* bpb = &cacheBuffer_.fbs.bpb; - if (bpb->bytesPerSector != 512 || - bpb->fatCount == 0 || - bpb->reservedSectorCount == 0 || - bpb->sectorsPerCluster == 0) { + bpb_t *bpb = &cacheBuffer_.fbs.bpb; + if (bpb->bytesPerSector != 512 || bpb->fatCount == 0 || + bpb->reservedSectorCount == 0 || bpb->sectorsPerCluster == 0) { // not valid FAT volume return false; } @@ -315,8 +311,8 @@ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { return false; } } - blocksPerFat_ = bpb->sectorsPerFat16 ? - bpb->sectorsPerFat16 : bpb->sectorsPerFat32; + blocksPerFat_ = + bpb->sectorsPerFat16 ? bpb->sectorsPerFat16 : bpb->sectorsPerFat32; fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount; @@ -330,8 +326,8 @@ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511) / 512); // total blocks for FAT16 or FAT32 - uint32_t totalBlocks = bpb->totalSectors16 ? - bpb->totalSectors16 : bpb->totalSectors32; + uint32_t totalBlocks = + bpb->totalSectors16 ? bpb->totalSectors16 : bpb->totalSectors32; // total data blocks clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); diff --git a/test/test.cpp b/test/test.cpp index d789f9a..8211223 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -9,102 +9,96 @@ bundle exec arduino_ci.rb --skip-unittests #include "SD_CI.h" #include -unittest(exists_works) { - assertFalse(sd_ci.exists("noSuchFile.txt")); - File_CI existingFile = sd_ci.open("existingFile.txt", FILE_WRITE); +unittest_setup() { SD_CI.removeAll(); } + +unittest_teardown() { SD_CI.removeAll(); } + +unittest(exists) { + assertFalse(SD_CI.exists("noSuchFile.txt")); + File_CI existingFile = SD_CI.open("existingFile.txt", FILE_WRITE); existingFile.close(); - assertTrue(sd_ci.exists("existingFile.txt")); - sd_ci.remove("existingFile.txt"); + assertTrue(SD_CI.exists("existingFile.txt")); } -unittest(mkdir_works) { - assertTrue(sd_ci.mkdir("test_directory/a/b")); - assertTrue(sd_ci.mkdir("test_directory/a/c")); - assertTrue(sd_ci.exists("test_directory")); - assertTrue(sd_ci.exists("test_directory/a/b")); - assertTrue(sd_ci.exists("test_directory/a/c")); - - // tear down - sd_ci.rmdir("test_directory"); +unittest(mkdir) { + assertTrue(SD_CI.mkdir("test_directory/a/b")); + assertTrue(SD_CI.mkdir("test_directory/a/c")); + assertTrue(SD_CI.exists("test_directory")); + assertTrue(SD_CI.exists("test_directory/a/b")); + assertTrue(SD_CI.exists("test_directory/a/c")); } -unittest(open_works) { +unittest(open) { // create file - File_CI writeFile = sd_ci.open("file.txt", FILE_WRITE); + File_CI writeFile = SD_CI.open("file.txt", FILE_WRITE); writeFile.close(); // open file for read should exist - File_CI readFile2 = sd_ci.open("file.txt", FILE_READ); + File_CI readFile2 = SD_CI.open("file.txt", FILE_READ); assertTrue(readFile2.isOpen()); readFile2.close(); - // delete test file - sd_ci.remove("file.txt"); } -unittest(close_works) { +unittest(close) { // close write file - File_CI file = sd_ci.open("file.txt", FILE_WRITE); + File_CI file = SD_CI.open("file.txt", FILE_WRITE); assertTrue(file.isOpen()); file.close(); assertFalse(file.isOpen()); // close read file - File_CI readFile = sd_ci.open("file.txt", FILE_READ); + File_CI readFile = SD_CI.open("file.txt", FILE_READ); assertTrue(readFile.isOpen()); readFile.close(); assertFalse(readFile.isOpen()); - sd_ci.remove("file.txt"); } -unittest(remove_works) { +unittest(remove) { // set up - File_CI file = sd_ci.open("file.txt", FILE_WRITE); + File_CI file = SD_CI.open("file.txt", FILE_WRITE); file.close(); - assertTrue(sd_ci.exists("file.txt")); + assertTrue(SD_CI.exists("file.txt")); - sd_ci.remove("file.txt"); - assertFalse(sd_ci.exists("file.txt")); + SD_CI.remove("file.txt"); + assertFalse(SD_CI.exists("file.txt")); } -unittest(rmdir_works) { +unittest(rmdir) { // set up - sd_ci.mkdir("test_directory/a/a"); - sd_ci.mkdir("test_directory/a/b"); - sd_ci.mkdir("test_directory/a/c"); - File_CI file = sd_ci.open("test_directory/a/a/file.txt", FILE_WRITE); + SD_CI.mkdir("test_directory/a/a"); + SD_CI.mkdir("test_directory/a/b"); + SD_CI.mkdir("test_directory/a/c"); + File_CI file = SD_CI.open("test_directory/a/a/file.txt", FILE_WRITE); file.close(); // remove directory - assertTrue(sd_ci.rmdir("test_directory/a/c")); + assertTrue(SD_CI.rmdir("test_directory/a/c")); // make sure non-removed dirs still exist - assertTrue(sd_ci.exists("test_directory")); - assertTrue(sd_ci.exists("test_directory/a/a")); + assertTrue(SD_CI.exists("test_directory")); + assertTrue(SD_CI.exists("test_directory/a/a")); // make sure removed dir no longer exists - assertFalse(sd_ci.exists("test_directory/a/c")); + assertFalse(SD_CI.exists("test_directory/a/c")); // remove directory with sub directories - assertTrue(sd_ci.rmdir("test_directory")); - assertFalse(sd_ci.exists("test_directory")); + assertTrue(SD_CI.rmdir("test_directory")); + assertFalse(SD_CI.exists("test_directory")); } -unittest(name_works) { +unittest(name) { // set up - File_CI file = sd_ci.open("newFile.txt", FILE_WRITE); + File_CI file = SD_CI.open("newFile.txt", FILE_WRITE); char expected[] = "newFile.txt"; assertEqual(expected, file.name()); file.close(); - - // tear down - sd_ci.remove("newFile.txt"); } -unittest(seek_works) { +unittest(seek) { // set up - File_CI file = sd_ci.open("seek.txt", FILE_WRITE); + File_CI file = SD_CI.open("seek.txt", FILE_WRITE); char write[] = "Hide and Seek."; file.write(write, sizeof(write) - 1); file.close(); - File_CI read = sd_ci.open("seek.txt", FILE_READ); + File_CI read = SD_CI.open("seek.txt", FILE_READ); // Testing char readFrom[4]; @@ -113,25 +107,22 @@ unittest(seek_works) { read.read(readFrom, 3); readFrom[3] = '\0'; assertEqual(expected, readFrom); - - // tear down - sd_ci.remove("seek.txt"); } -unittest(read_works) { +unittest(read) { // set up - File_CI file = sd_ci.open("birthday.txt", FILE_WRITE); + File_CI file = SD_CI.open("birthday.txt", FILE_WRITE); char toWrite[] = "Happy Birthday to You!"; file.write(toWrite, sizeof(toWrite) - 1); file.close(); - File_CI file2 = sd_ci.open("lines.txt", FILE_WRITE); + File_CI file2 = SD_CI.open("lines.txt", FILE_WRITE); char toWrite2[] = "line 1\nline2"; file2.write(toWrite2, sizeof(toWrite2) - 1); file2.close(); // testing - File_CI readFile = sd_ci.open("birthday.txt", FILE_READ); + File_CI readFile = SD_CI.open("birthday.txt", FILE_READ); size_t size = readFile.size(); char readFromFile[size + 1]; readFile.read(readFromFile, size); @@ -140,7 +131,7 @@ unittest(read_works) { // assertEqual(toWrite, readFromFile); - File_CI readFile2 = sd_ci.open("lines.txt", FILE_READ); + File_CI readFile2 = SD_CI.open("lines.txt", FILE_READ); char readFromFile2[7 + 1]; char readFromFile3[5 + 1]; readFile2.read(readFromFile2, 7); @@ -152,15 +143,11 @@ unittest(read_works) { char expected3[] = "line2"; assertEqual(expected2, readFromFile2); assertEqual(expected3, readFromFile3); - - // tear down - sd_ci.remove("birthday.txt"); - sd_ci.remove("lines.txt"); } -unittest(write_works) { +unittest(write) { // open new file for writing - File_CI writeFile = sd_ci.open("wood.txt", FILE_WRITE); + File_CI writeFile = SD_CI.open("wood.txt", FILE_WRITE); char toWrite[] = "How much wood could a wood pecker peck?\n"; writeFile.write(toWrite, sizeof(toWrite) - 1); @@ -174,13 +161,13 @@ unittest(write_works) { writeFile.close(); // open old writing file to write at end. - File_CI writeFile2 = sd_ci.open("wood.txt", FILE_WRITE); + File_CI writeFile2 = SD_CI.open("wood.txt", FILE_WRITE); char toWrite2[] = "A lot of wood.\n"; writeFile2.write(toWrite2, sizeof(toWrite2) - 1); writeFile2.close(); // check old writing file - File_CI readWrite2 = sd_ci.open("wood.txt", FILE_READ); + File_CI readWrite2 = SD_CI.open("wood.txt", FILE_READ); size_t size2 = readWrite2.size(); char toRead2[size2 + 1]; readWrite2.read(toRead2, size2); @@ -189,14 +176,11 @@ unittest(write_works) { "How much wood could a wood pecker peck?\nA lot of wood.\n"; // assertEqual(expected2, toRead2); readWrite2.close(); - - // teardown - sd_ci.remove("wood.txt"); } -unittest(size_works) { +unittest(size) { // setup - File_CI sizeFile = sd_ci.open("size.txt", FILE_WRITE); + File_CI sizeFile = SD_CI.open("size.txt", FILE_WRITE); char toWrite[] = "Test text\n"; sizeFile.write(toWrite, sizeof(toWrite) - 1); sizeFile.close(); @@ -204,37 +188,31 @@ unittest(size_works) { // test uint32_t fileSize = sizeFile.size(); assertEqual(10, fileSize); - - // tear down - sd_ci.remove("size.txt"); } -unittest(peek_works) { +unittest(peek) { // set up - File_CI peekFile = sd_ci.open("peek.txt", FILE_WRITE); + File_CI peekFile = SD_CI.open("peek.txt", FILE_WRITE); char toWrite[] = "Peek file content\n"; peekFile.write(toWrite, sizeof(toWrite) - 1); peekFile.close(); // Test - File_CI readPeek = sd_ci.open("peek.txt", FILE_READ); + File_CI readPeek = SD_CI.open("peek.txt", FILE_READ); assertEqual('P', readPeek.peek()); assertEqual('P', readPeek.peek()); readPeek.close(); - File_CI readWritePeek = sd_ci.open("peek.txt", FILE_WRITE); + File_CI readWritePeek = SD_CI.open("peek.txt", FILE_WRITE); readWritePeek.seek(0); assertEqual('P', readWritePeek.peek()); assertEqual('P', readWritePeek.peek()); readWritePeek.close(); - - // tear down - sd_ci.remove("peek.txt"); } -unittest(position_works) { +unittest(position) { // set up - File_CI posFile = sd_ci.open("pos.txt", FILE_WRITE); + File_CI posFile = SD_CI.open("pos.txt", FILE_WRITE); char toWrite[] = "This is the position file.\n"; posFile.write(toWrite, sizeof(toWrite) - 1); @@ -243,21 +221,18 @@ unittest(position_works) { posFile.seek(5); assertEqual(5, posFile.position()); posFile.close(); - - // tear down - sd_ci.remove("pos.txt"); } -unittest(isDirectory_works) { +unittest(isDirectory) { // set up - sd_ci.mkdir("test"); - File_CI toRead = sd_ci.open("read.txt", FILE_WRITE); + SD_CI.mkdir("test"); + File_CI toRead = SD_CI.open("read.txt", FILE_WRITE); toRead.close(); - File_CI testFile = sd_ci.open("test.txt", FILE_WRITE); - File_CI readFile = sd_ci.open("read.txt", FILE_READ); + File_CI testFile = SD_CI.open("test.txt", FILE_WRITE); + File_CI readFile = SD_CI.open("read.txt", FILE_READ); // test - File_CI myDir = sd_ci.open("test"); + File_CI myDir = SD_CI.open("test"); assertTrue(myDir.isDirectory()); assertFalse(testFile.isDirectory()); assertFalse(readFile.isDirectory()); @@ -265,8 +240,5 @@ unittest(isDirectory_works) { // tear down testFile.close(); readFile.close(); - sd_ci.remove("test.txt"); - sd_ci.remove("read.txt"); - sd_ci.rmdir("test"); } unittest_main() From 06c2a4573e23f1252f86f594a79f0fc5ce09d108 Mon Sep 17 00:00:00 2001 From: James Foster Date: Tue, 9 Mar 2021 23:01:26 -0800 Subject: [PATCH 09/17] Use in-memory test double (#14) * We now have an in-memory mock (test double) for the file system! * Fix #13 Co-authored-by: James Foster --- src/File_CI.cpp | 108 +++++++++++++++++++++++++++++++----------- src/SD_CI.cpp | 123 ++++++++++++++++++++++++++++++++++-------------- src/SD_CI.h | 120 ++++++++++++++++++++++++---------------------- test/test.cpp | 63 +++++++++++++++++++++---- 4 files changed, 287 insertions(+), 127 deletions(-) diff --git a/src/File_CI.cpp b/src/File_CI.cpp index 3af9bf0..f97316a 100644 --- a/src/File_CI.cpp +++ b/src/File_CI.cpp @@ -4,47 +4,55 @@ #include #include +#include #include #include namespace SDLib { -File_CI::File_CI(const char *name, uint8_t mode) { - _path = name; - _mode = mode; +// create a directory reference +File_CI::File_CI(const std::string &path) { + assert(path.size()); + assert(path.back() == '/'); + assert(SD_CI.exists(path)); _isOpen = true; - _pos = 0; - if (SD_CI.dirExists(name)) { - _isDir = true; - } else { - _isDir = false; - assert(mode & O_CREAT || SD_CI.fileExists(name)); - pContents = SD_CI.contentsOfNewOrExistingFileWithName(name); - if (mode & O_WRITE) { - if (mode & O_APPEND) { - _pos = pContents->size(); - } else { - *pContents = ""; - } - } - } + _path = path; + _current = path; } -int File_CI::peek() { - if (_pos < pContents->size()) { - return pContents->at(_pos); +// create a file reference +File_CI::File_CI(const std::string &path, uint8_t mode) { + assert(path.size()); + assert(path.back() != '/'); + assert(SD_CI.exists(path)); + _isOpen = true; + _mode = mode; + _path = path; +} + +// find the file name in the full path +const char *File_CI::name() const { + size_t index; + if (isDirectory()) { + index = _path.find_last_of('/', _path.size() - 2); } else { - return EOF; + index = _path.find_last_of('/'); } + if (index == std::string::npos) { + index = -1; + } + return _path.c_str() + index + 1; } -size_t File_CI::write(const char *buf, size_t size) { - assert(_mode == FILE_WRITE); - for (int i = 0; i < size; ++i) { - *pContents += buf[i]; +// get next char without advancing position +int File_CI::peek() const { + assert(!isDirectory()); + std::string *pContents = SD_CI.contentsOf(_path); + assert(pContents); + if (_pos < pContents->size()) { + return pContents->at(_pos); } - _pos += size; - return size; + return EOF; } /* Note for read(): the user of this function needs to append @@ -56,12 +64,56 @@ size_t File_CI::write(const char *buf, size_t size) { this function is used. */ int File_CI::read(char *buf, size_t size) { + assert(!isDirectory()); + assert(_mode & O_READ); + std::string *pContents = SD_CI.contentsOf(_path); size = size <= available() ? size : available(); memcpy(buf, pContents->c_str() + _pos, size); _pos += size; return size; } +// set position for next read +bool File_CI::seek(uint32_t pos) { + uint32_t sz = size(); + if (pos < sz) { + _pos = pos; + return true; + } + _pos = sz; + return false; +} + +uint32_t File_CI::size() const { + assert(!isDirectory()); + return SD_CI.contentsOf(_path)->size(); +} + +size_t File_CI::write(const char *buf, size_t size) { + assert(!isDirectory()); + assert(_mode & O_WRITE); + std::string *pContents = SD_CI.contentsOf(_path); + for (int i = 0; i < size; ++i) { + *pContents += buf[i]; + } + _pos += size; + return size; +} + +File_CI File_CI::openNextFile(uint8_t mode) { + assert(isDirectory()); + const std::string &name = SD_CI.nameAfter(_current, _path); + if (name.empty()) { + _current = _path; + return File_CI(); + } + _current = name; + if (name.back() == '/') { + return File_CI(name); + } + return File_CI(name, mode); +} + } // namespace SDLib #endif diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp index ff677db..6961a93 100644 --- a/src/SD_CI.cpp +++ b/src/SD_CI.cpp @@ -1,65 +1,116 @@ #include #ifdef MOCK_PINS_COUNT #include "SD_CI.h" -#include namespace SDLib { -std::string * -SDClass_CI::contentsOfNewOrExistingFileWithName(const char *filepath) { - if (!fileExists(filepath)) { - files.emplace(std::string(filepath), new std::string); +std::string *SDClass_CI::contentsOf(const std::string &path) { + std::string fullPath = normalizePath(path); + if (fileSystem.count(fullPath)) { + return &fileSystem[fullPath]; } - return files[filepath]; + return nullptr; } -bool SDClass_CI::mkdir(const char *filepath) { - assert(!fileExists(filepath)); - if (dirExists(filepath)) { +bool SDClass_CI::exists(const std::string &path) { + std::string fullPath = normalizePath(path); + return contentsOf(fullPath) != nullptr; +} + +bool SDClass_CI::mkdir(const std::string &path) { + std::string fullPath = normalizePath(path, true); + if (exists(fullPath)) { return false; } - char *path = new char[strlen(filepath) + 1]; - strcpy(path, filepath); - char *ptr = strchr(path, '/'); - while (ptr) { - *ptr = '\0'; - directories.insert(path); - *ptr = '/'; - ptr = strchr(ptr + 1, '/'); - } - directories.insert(filepath); - delete[] path; + size_t index = fullPath.find_last_of('/', fullPath.size() - 2); + assert(index == 0 || exists(fullPath.substr(0, index + 1))); + fileSystem.emplace(fullPath, ""); return true; } -bool SDClass_CI::remove(const char *filepath) { - if (files.count(filepath)) { - delete files[filepath]; - files.erase(filepath); - return true; +const std::string &SDClass_CI::nameAfter(const std::string fullPath, + const std::string start) { + std::map::iterator iter; + iter = fileSystem.find(fullPath); + assert(iter != fileSystem.end()); + if (iter->first != start && + fullPath.back() == '/') { // current entry is a directory + // advance to next entry after directory + while ((++iter)->first.substr(0, fullPath.size()) == fullPath) { + } + } else { + ++iter; } - return false; + if (iter == fileSystem.end()) { + return emptyString; + } + return iter->first; +} + +File_CI SDClass_CI::open(const std::string &path, uint8_t mode) { + // ensure we have a root directory + fileSystem.emplace(std::string("/"), std::string("")); + std::string fullPath = normalizePath(path); + if (exists(fullPath)) { + if (fullPath.back() == '/') { + return File_CI(fullPath); + } + return File_CI(fullPath, mode); + } + assert(fullPath.back() != '/' && mode & O_WRITE); + size_t index = fullPath.find_last_of('/'); + assert(index == 0 || exists(fullPath.substr(0, index + 1))); + fileSystem.emplace(fullPath, ""); + return File_CI(fullPath, mode); } -void SDClass_CI::removeAll() { - for (const auto &[key, value] : files) { - delete value; +bool SDClass_CI::remove(const std::string &path) { + std::string fullPath = normalizePath(path); + assert(fullPath.back() != '/'); + if (exists(fullPath)) { + fileSystem.erase(fullPath); + return true; } - directories.clear(); - files.clear(); + return false; } -bool SDClass_CI::rmdir(const char *filepath) { - if (directories.count(filepath)) { - directories.erase(filepath); +bool SDClass_CI::rmdir(const std::string &path) { + std::string fullPath = normalizePath(path, true); + if (exists(fullPath)) { + // check for children of this directory + std::string next = nameAfter(fullPath, fullPath); + assert(next == "" || next.substr(0, fullPath.size()) != fullPath); + fileSystem.erase(fullPath); return true; } return false; } +// private +std::string SDClass_CI::normalizePath(const std::string &inPath, + bool isDirectory) { + std::string fullPath = inPath; + // add leading '/' for root + if (fullPath.front() != '/') { + fullPath.insert(0, 1, '/'); + } + if (fullPath.back() == '/') { // if already a directory, then done + return fullPath; + } else if (isDirectory) { // if should be a directory, make it one + fullPath += '/'; + return fullPath; + } + fullPath += '/'; + if (exists(fullPath)) { // look for a directory + return fullPath; + } + fullPath = fullPath.substr(0, fullPath.size() - 1); + return fullPath; +} + +// static variables SDClass_CI SD_CI; -std::set SDClass_CI::directories; -std::map SDClass_CI::files; +std::map SDClass::fileSystem; } // namespace SDLib diff --git a/src/SD_CI.h b/src/SD_CI.h index 9962ba8..0de868c 100644 --- a/src/SD_CI.h +++ b/src/SD_CI.h @@ -3,93 +3,103 @@ #ifdef MOCK_PINS_COUNT #include "SD.h" #include -#include #include -#include #include namespace SDLib { +// we add a new "mode" so `find()` can remove files and directories +uint8_t const O_REMOVE = 0X80; + class File_CI : public File_Base { public: - File_CI(const char *name, uint8_t mode); - size_t write(const char *buf, size_t size); - int availableForWrite() { return 4096; } - int peek(); - int available() { return pContents->size() - _pos; } + File_CI() {} // not a file (for iteration) + File_CI(const std::string &path); // create a directory + File_CI(const std::string &path, uint8_t mode); // create a file + + // file operations + int available() const { return size() - _pos; } + int availableForWrite() const { return 4096; } void flush() {} + int peek() const; + uint32_t position() const { return _pos; } int read(char *buf, size_t size); - bool seek(uint32_t pos) { return _pos = pos <= size() ? pos : size(); } - uint32_t position() { return _pos; } - uint32_t size() { return pContents->size(); } - void close() { _isOpen = false; } - operator bool() { return _isOpen; } - const char *name() { return _path.c_str(); } - bool isOpen() { return _isOpen; } + bool seek(uint32_t pos); + uint32_t size() const; + size_t write(const char *buf, size_t size); + using Print::write; - bool isDirectory(void) { return _isDir; } - File_CI openNextFile(uint8_t mode) { assert(false); } - void rewindDirectory(void) { assert(false); } + // directory operations + bool isDirectory(void) const { return _path.back() == '/'; } + File_CI openNextFile(uint8_t mode = O_RDONLY); + void rewindDirectory() { _current = _path; } + + // common operations + void close() { _isOpen = false; } + bool isOpen() const { return _isOpen; } + const char *name() const; + operator bool() const { return _isOpen; } - using Print::write; // testing functions - int getWriteError() { return writeError; } - void setWriteError(int value = 1) { writeError = value; } - void clearWriteError() { writeError = 0; } + int getWriteError() const { return _writeError; } + void setWriteError(int value = 1) { _writeError = value; } + void clearWriteError() { _writeError = 0; } private: - std::string *pContents; - int _pos; // index of _next_ char to read or write - std::string _path; - uint8_t _mode; - bool _isOpen; - bool _isDir; - int writeError; + std::string _path; // full path + int _pos = 0; // index of _next_ char to read or write + uint8_t _mode = 0; + bool _isOpen = false; + int _writeError = 0; + std::string _current; // used by directory iterator }; class SDClass_CI : public SDClass_Base { public: - // Ignore hardware-related setup + // hardware-related setup can be ignored for test double bool begin(uint8_t csPin = SD_CHIP_SELECT_PIN) { return true; } bool begin(uint32_t clock, uint8_t csPin) { return true; } void end() {} - std::string *contentsOfNewOrExistingFileWithName(const char *filepath); - bool dirExists(const char *filepath) { - std::cout << "dirExists(\"" << filepath - << "\") == " << directories.count(filepath) << std::endl; - return directories.count(filepath) == 1; - } - bool exists(const char *filepath) { - // This doesn't check directories for intermediate paths - return files.count(filepath) || dirExists(filepath); - } - bool exists(const String &filepath) { return exists(filepath.c_str()); } - bool fileExists(const char *filepath) { return files.count(filepath); } + // return a pointer rather than a reference so we can use nullptr for missing + std::string *contentsOf(const std::string &path); + + bool exists(const std::string &path); + bool exists(const char *path) { return exists(std::string(path)); } + bool exists(const String &path) { return exists(std::string(path.c_str())); } + + bool mkdir(const std::string &path); + bool mkdir(const char *path) { return mkdir(std::string(path)); } + bool mkdir(const String &path) { return mkdir(path.c_str()); } - bool mkdir(const char *filepath); - bool mkdir(const String &filepath) { return mkdir(filepath.c_str()); } + const std::string &nameAfter(const std::string current, + const std::string start); - File_CI open(const char *filename, uint8_t mode) { - return File_CI(filename, mode); + File_CI open(const std::string &path, uint8_t mode = FILE_READ); + File_CI open(const char *path, uint8_t mode) { + return open(std::string(path), mode); } - File_CI open(const String &filename, uint8_t mode = FILE_READ) { - return open(filename.c_str(), mode); + File_CI open(const String &path, uint8_t mode) { + return open(std::string(path.c_str()), mode); } - bool remove(const char *filepath); - bool remove(const String &filepath) { return remove(filepath.c_str()); } + bool remove(const std::string &path); + bool remove(const char *path) { return remove(std::string(path)); } + bool remove(const String &path) { return remove(std::string(path.c_str())); } - void removeAll(); + void removeAll() { fileSystem.clear(); } - bool rmdir(const char *filepath); - bool rmdir(const String &filepath) { return rmdir(filepath.c_str()); } + bool rmdir(const std::string &path); + bool rmdir(const char *path) { return rmdir(std::string(path)); } + bool rmdir(const String &path) { return rmdir(path.c_str()); } - virtual String className() const { return "SDClass_Base"; } + String className() const { return "SDClass_Base"; } private: - static std::set directories; - static std::map files; + static std::map fileSystem; + std::string emptyString; + std::string normalizePath(const std::string &inPath, + bool isDirectory = false); }; extern SDClass_CI SD_CI; diff --git a/test/test.cpp b/test/test.cpp index 8211223..e34db75 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -8,19 +8,22 @@ bundle exec arduino_ci.rb --skip-unittests #include "ArduinoUnitTests.h" #include "SD_CI.h" #include +#include unittest_setup() { SD_CI.removeAll(); } unittest_teardown() { SD_CI.removeAll(); } unittest(exists) { - assertFalse(SD_CI.exists("noSuchFile.txt")); - File_CI existingFile = SD_CI.open("existingFile.txt", FILE_WRITE); - existingFile.close(); - assertTrue(SD_CI.exists("existingFile.txt")); + assertFalse(SD_CI.exists("file.txt")); + File_CI file = SD_CI.open("file.txt", FILE_WRITE); + file.close(); + assertTrue(SD_CI.exists("file.txt")); } unittest(mkdir) { + assertTrue(SD_CI.mkdir("test_directory")); + assertTrue(SD_CI.mkdir("test_directory/a")); assertTrue(SD_CI.mkdir("test_directory/a/b")); assertTrue(SD_CI.mkdir("test_directory/a/c")); assertTrue(SD_CI.exists("test_directory")); @@ -64,6 +67,8 @@ unittest(remove) { unittest(rmdir) { // set up + SD_CI.mkdir("test_directory"); + SD_CI.mkdir("test_directory/a"); SD_CI.mkdir("test_directory/a/a"); SD_CI.mkdir("test_directory/a/b"); SD_CI.mkdir("test_directory/a/c"); @@ -77,10 +82,6 @@ unittest(rmdir) { assertTrue(SD_CI.exists("test_directory/a/a")); // make sure removed dir no longer exists assertFalse(SD_CI.exists("test_directory/a/c")); - - // remove directory with sub directories - assertTrue(SD_CI.rmdir("test_directory")); - assertFalse(SD_CI.exists("test_directory")); } unittest(name) { @@ -241,4 +242,50 @@ unittest(isDirectory) { testFile.close(); readFile.close(); } + +unittest(next) { + File_CI file; + + file = SD_CI.open("x", FILE_WRITE); + file.close(); + SD_CI.open("x", O_WRITE).close(); + + SD_CI.mkdir("d"); + + file = SD_CI.open("a", FILE_WRITE); + file.close(); + SD_CI.open("a", O_WRITE).close(); + + File_CI root = SD_CI.open("/"); + assertTrue(root); + assertTrue(root.isDirectory()); + + file = root.openNextFile(); + assertTrue(file); + assertEqual(std::string("a"), file.name()); + assertFalse(file.isDirectory()); + file.close(); + + file = root.openNextFile(); + assertTrue(file); + assertEqual(std::string("d/"), file.name()); + assertTrue(file.isDirectory()); + file.close(); + + file = root.openNextFile(); + assertTrue(file); + assertEqual(std::string("x"), file.name()); + assertFalse(file.isDirectory()); + file.close(); + + file = root.openNextFile(); + assertFalse(file); + + root.rewindDirectory(); + file = root.openNextFile(); + assertTrue(file); + assertEqual(std::string("a"), file.name()); + file.close(); +} + unittest_main() From 9c3f7b31505b4af6e65eb92626af01d84525fee1 Mon Sep 17 00:00:00 2001 From: James Foster Date: Wed, 10 Mar 2021 22:43:47 -0800 Subject: [PATCH 10/17] Use `SD` as name for global in both test and non-test (#15) * Use `SD` to refer to `SD_CI` in test builds and `SD_Base` in non-test builds. Unfortunately, the history seems to include a lot from the previous rewrite. --- src/File_CI.cpp | 14 +++--- src/SD.cpp | 2 +- src/SD.h | 14 +++--- test/test.cpp | 112 ++++++++++++++++++++++++------------------------ 4 files changed, 72 insertions(+), 70 deletions(-) diff --git a/src/File_CI.cpp b/src/File_CI.cpp index f97316a..4b3dfe3 100644 --- a/src/File_CI.cpp +++ b/src/File_CI.cpp @@ -14,7 +14,7 @@ namespace SDLib { File_CI::File_CI(const std::string &path) { assert(path.size()); assert(path.back() == '/'); - assert(SD_CI.exists(path)); + assert(SD.exists(path)); _isOpen = true; _path = path; _current = path; @@ -24,7 +24,7 @@ File_CI::File_CI(const std::string &path) { File_CI::File_CI(const std::string &path, uint8_t mode) { assert(path.size()); assert(path.back() != '/'); - assert(SD_CI.exists(path)); + assert(SD.exists(path)); _isOpen = true; _mode = mode; _path = path; @@ -47,7 +47,7 @@ const char *File_CI::name() const { // get next char without advancing position int File_CI::peek() const { assert(!isDirectory()); - std::string *pContents = SD_CI.contentsOf(_path); + std::string *pContents = SD.contentsOf(_path); assert(pContents); if (_pos < pContents->size()) { return pContents->at(_pos); @@ -66,7 +66,7 @@ int File_CI::peek() const { int File_CI::read(char *buf, size_t size) { assert(!isDirectory()); assert(_mode & O_READ); - std::string *pContents = SD_CI.contentsOf(_path); + std::string *pContents = SD.contentsOf(_path); size = size <= available() ? size : available(); memcpy(buf, pContents->c_str() + _pos, size); _pos += size; @@ -86,13 +86,13 @@ bool File_CI::seek(uint32_t pos) { uint32_t File_CI::size() const { assert(!isDirectory()); - return SD_CI.contentsOf(_path)->size(); + return SD.contentsOf(_path)->size(); } size_t File_CI::write(const char *buf, size_t size) { assert(!isDirectory()); assert(_mode & O_WRITE); - std::string *pContents = SD_CI.contentsOf(_path); + std::string *pContents = SD.contentsOf(_path); for (int i = 0; i < size; ++i) { *pContents += buf[i]; } @@ -102,7 +102,7 @@ size_t File_CI::write(const char *buf, size_t size) { File_CI File_CI::openNextFile(uint8_t mode) { assert(isDirectory()); - const std::string &name = SD_CI.nameAfter(_current, _path); + const std::string &name = SD.nameAfter(_current, _path); if (name.empty()) { _current = _path; return File_CI(); diff --git a/src/SD.cpp b/src/SD.cpp index 3d18889..e0005da 100644 --- a/src/SD.cpp +++ b/src/SD.cpp @@ -606,6 +606,6 @@ void File_Base::rewindDirectory(void) { } } -SDClass_Base SD; +SDClass_Base SD_Base; }; // namespace SDLib diff --git a/src/SD.h b/src/SD.h index d7cf97f..4fbbb18 100644 --- a/src/SD.h +++ b/src/SD.h @@ -20,10 +20,12 @@ #ifdef MOCK_PINS_COUNT #define SDClass_CI SDClass #define File_CI File +#define SD_CI SD #include #else #define SDClass_Base SDClass #define File_Base File +#define SD_Base SD #endif #include "utility/SdFat.h" @@ -129,20 +131,20 @@ class SDClass_Base { friend bool callback_openPath(SdFile &, const char *, bool, void *); }; -extern SDClass_Base SD; +extern SDClass_Base SD_Base; }; // namespace SDLib -// We enclose File_Base and SD classes in namespace SDLib to avoid conflicts -// with others legacy libraries that redefines File_Base class. +// We enclose File and SD classes in namespace SDLib to avoid conflicts +// with others legacy libraries that redefines File class. // This ensure compatibility with sketches that uses only SD library using namespace SDLib; -// This allows sketches to use SDLib::File_Base with other libraries (in the -// sketch you must use SDFile instead of File_Base to disambiguate) +// This allows sketches to use SDLib::File with other libraries (in the +// sketch you must use SDFile instead of File to disambiguate) typedef SDLib::File_Base SDFile; typedef SDLib::SDClass_Base SDFileSystemClass; -#define SDFileSystem SDLib::SD +#define SDFileSystem SDLib::SD_Base #endif diff --git a/test/test.cpp b/test/test.cpp index e34db75..fae10e5 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -10,46 +10,46 @@ bundle exec arduino_ci.rb --skip-unittests #include #include -unittest_setup() { SD_CI.removeAll(); } +unittest_setup() { SD.removeAll(); } -unittest_teardown() { SD_CI.removeAll(); } +unittest_teardown() { SD.removeAll(); } unittest(exists) { - assertFalse(SD_CI.exists("file.txt")); - File_CI file = SD_CI.open("file.txt", FILE_WRITE); + assertFalse(SD.exists("file.txt")); + File_CI file = SD.open("file.txt", FILE_WRITE); file.close(); - assertTrue(SD_CI.exists("file.txt")); + assertTrue(SD.exists("file.txt")); } unittest(mkdir) { - assertTrue(SD_CI.mkdir("test_directory")); - assertTrue(SD_CI.mkdir("test_directory/a")); - assertTrue(SD_CI.mkdir("test_directory/a/b")); - assertTrue(SD_CI.mkdir("test_directory/a/c")); - assertTrue(SD_CI.exists("test_directory")); - assertTrue(SD_CI.exists("test_directory/a/b")); - assertTrue(SD_CI.exists("test_directory/a/c")); + assertTrue(SD.mkdir("test_directory")); + assertTrue(SD.mkdir("test_directory/a")); + assertTrue(SD.mkdir("test_directory/a/b")); + assertTrue(SD.mkdir("test_directory/a/c")); + assertTrue(SD.exists("test_directory")); + assertTrue(SD.exists("test_directory/a/b")); + assertTrue(SD.exists("test_directory/a/c")); } unittest(open) { // create file - File_CI writeFile = SD_CI.open("file.txt", FILE_WRITE); + File_CI writeFile = SD.open("file.txt", FILE_WRITE); writeFile.close(); // open file for read should exist - File_CI readFile2 = SD_CI.open("file.txt", FILE_READ); + File_CI readFile2 = SD.open("file.txt", FILE_READ); assertTrue(readFile2.isOpen()); readFile2.close(); } unittest(close) { // close write file - File_CI file = SD_CI.open("file.txt", FILE_WRITE); + File_CI file = SD.open("file.txt", FILE_WRITE); assertTrue(file.isOpen()); file.close(); assertFalse(file.isOpen()); // close read file - File_CI readFile = SD_CI.open("file.txt", FILE_READ); + File_CI readFile = SD.open("file.txt", FILE_READ); assertTrue(readFile.isOpen()); readFile.close(); assertFalse(readFile.isOpen()); @@ -57,36 +57,36 @@ unittest(close) { unittest(remove) { // set up - File_CI file = SD_CI.open("file.txt", FILE_WRITE); + File_CI file = SD.open("file.txt", FILE_WRITE); file.close(); - assertTrue(SD_CI.exists("file.txt")); + assertTrue(SD.exists("file.txt")); - SD_CI.remove("file.txt"); - assertFalse(SD_CI.exists("file.txt")); + SD.remove("file.txt"); + assertFalse(SD.exists("file.txt")); } unittest(rmdir) { // set up - SD_CI.mkdir("test_directory"); - SD_CI.mkdir("test_directory/a"); - SD_CI.mkdir("test_directory/a/a"); - SD_CI.mkdir("test_directory/a/b"); - SD_CI.mkdir("test_directory/a/c"); - File_CI file = SD_CI.open("test_directory/a/a/file.txt", FILE_WRITE); + SD.mkdir("test_directory"); + SD.mkdir("test_directory/a"); + SD.mkdir("test_directory/a/a"); + SD.mkdir("test_directory/a/b"); + SD.mkdir("test_directory/a/c"); + File_CI file = SD.open("test_directory/a/a/file.txt", FILE_WRITE); file.close(); // remove directory - assertTrue(SD_CI.rmdir("test_directory/a/c")); + assertTrue(SD.rmdir("test_directory/a/c")); // make sure non-removed dirs still exist - assertTrue(SD_CI.exists("test_directory")); - assertTrue(SD_CI.exists("test_directory/a/a")); + assertTrue(SD.exists("test_directory")); + assertTrue(SD.exists("test_directory/a/a")); // make sure removed dir no longer exists - assertFalse(SD_CI.exists("test_directory/a/c")); + assertFalse(SD.exists("test_directory/a/c")); } unittest(name) { // set up - File_CI file = SD_CI.open("newFile.txt", FILE_WRITE); + File_CI file = SD.open("newFile.txt", FILE_WRITE); char expected[] = "newFile.txt"; assertEqual(expected, file.name()); @@ -95,11 +95,11 @@ unittest(name) { unittest(seek) { // set up - File_CI file = SD_CI.open("seek.txt", FILE_WRITE); + File_CI file = SD.open("seek.txt", FILE_WRITE); char write[] = "Hide and Seek."; file.write(write, sizeof(write) - 1); file.close(); - File_CI read = SD_CI.open("seek.txt", FILE_READ); + File_CI read = SD.open("seek.txt", FILE_READ); // Testing char readFrom[4]; @@ -112,18 +112,18 @@ unittest(seek) { unittest(read) { // set up - File_CI file = SD_CI.open("birthday.txt", FILE_WRITE); + File_CI file = SD.open("birthday.txt", FILE_WRITE); char toWrite[] = "Happy Birthday to You!"; file.write(toWrite, sizeof(toWrite) - 1); file.close(); - File_CI file2 = SD_CI.open("lines.txt", FILE_WRITE); + File_CI file2 = SD.open("lines.txt", FILE_WRITE); char toWrite2[] = "line 1\nline2"; file2.write(toWrite2, sizeof(toWrite2) - 1); file2.close(); // testing - File_CI readFile = SD_CI.open("birthday.txt", FILE_READ); + File_CI readFile = SD.open("birthday.txt", FILE_READ); size_t size = readFile.size(); char readFromFile[size + 1]; readFile.read(readFromFile, size); @@ -132,7 +132,7 @@ unittest(read) { // assertEqual(toWrite, readFromFile); - File_CI readFile2 = SD_CI.open("lines.txt", FILE_READ); + File_CI readFile2 = SD.open("lines.txt", FILE_READ); char readFromFile2[7 + 1]; char readFromFile3[5 + 1]; readFile2.read(readFromFile2, 7); @@ -148,7 +148,7 @@ unittest(read) { unittest(write) { // open new file for writing - File_CI writeFile = SD_CI.open("wood.txt", FILE_WRITE); + File_CI writeFile = SD.open("wood.txt", FILE_WRITE); char toWrite[] = "How much wood could a wood pecker peck?\n"; writeFile.write(toWrite, sizeof(toWrite) - 1); @@ -162,13 +162,13 @@ unittest(write) { writeFile.close(); // open old writing file to write at end. - File_CI writeFile2 = SD_CI.open("wood.txt", FILE_WRITE); + File_CI writeFile2 = SD.open("wood.txt", FILE_WRITE); char toWrite2[] = "A lot of wood.\n"; writeFile2.write(toWrite2, sizeof(toWrite2) - 1); writeFile2.close(); // check old writing file - File_CI readWrite2 = SD_CI.open("wood.txt", FILE_READ); + File_CI readWrite2 = SD.open("wood.txt", FILE_READ); size_t size2 = readWrite2.size(); char toRead2[size2 + 1]; readWrite2.read(toRead2, size2); @@ -181,7 +181,7 @@ unittest(write) { unittest(size) { // setup - File_CI sizeFile = SD_CI.open("size.txt", FILE_WRITE); + File_CI sizeFile = SD.open("size.txt", FILE_WRITE); char toWrite[] = "Test text\n"; sizeFile.write(toWrite, sizeof(toWrite) - 1); sizeFile.close(); @@ -193,18 +193,18 @@ unittest(size) { unittest(peek) { // set up - File_CI peekFile = SD_CI.open("peek.txt", FILE_WRITE); + File_CI peekFile = SD.open("peek.txt", FILE_WRITE); char toWrite[] = "Peek file content\n"; peekFile.write(toWrite, sizeof(toWrite) - 1); peekFile.close(); // Test - File_CI readPeek = SD_CI.open("peek.txt", FILE_READ); + File_CI readPeek = SD.open("peek.txt", FILE_READ); assertEqual('P', readPeek.peek()); assertEqual('P', readPeek.peek()); readPeek.close(); - File_CI readWritePeek = SD_CI.open("peek.txt", FILE_WRITE); + File_CI readWritePeek = SD.open("peek.txt", FILE_WRITE); readWritePeek.seek(0); assertEqual('P', readWritePeek.peek()); assertEqual('P', readWritePeek.peek()); @@ -213,7 +213,7 @@ unittest(peek) { unittest(position) { // set up - File_CI posFile = SD_CI.open("pos.txt", FILE_WRITE); + File_CI posFile = SD.open("pos.txt", FILE_WRITE); char toWrite[] = "This is the position file.\n"; posFile.write(toWrite, sizeof(toWrite) - 1); @@ -226,14 +226,14 @@ unittest(position) { unittest(isDirectory) { // set up - SD_CI.mkdir("test"); - File_CI toRead = SD_CI.open("read.txt", FILE_WRITE); + SD.mkdir("test"); + File_CI toRead = SD.open("read.txt", FILE_WRITE); toRead.close(); - File_CI testFile = SD_CI.open("test.txt", FILE_WRITE); - File_CI readFile = SD_CI.open("read.txt", FILE_READ); + File_CI testFile = SD.open("test.txt", FILE_WRITE); + File_CI readFile = SD.open("read.txt", FILE_READ); // test - File_CI myDir = SD_CI.open("test"); + File_CI myDir = SD.open("test"); assertTrue(myDir.isDirectory()); assertFalse(testFile.isDirectory()); assertFalse(readFile.isDirectory()); @@ -246,17 +246,17 @@ unittest(isDirectory) { unittest(next) { File_CI file; - file = SD_CI.open("x", FILE_WRITE); + file = SD.open("x", FILE_WRITE); file.close(); - SD_CI.open("x", O_WRITE).close(); + SD.open("x", O_WRITE).close(); - SD_CI.mkdir("d"); + SD.mkdir("d"); - file = SD_CI.open("a", FILE_WRITE); + file = SD.open("a", FILE_WRITE); file.close(); - SD_CI.open("a", O_WRITE).close(); + SD.open("a", O_WRITE).close(); - File_CI root = SD_CI.open("/"); + File_CI root = SD.open("/"); assertTrue(root); assertTrue(root.isDirectory()); From f7f98e4a971734448de227769a6b57449f064e14 Mon Sep 17 00:00:00 2001 From: James Foster Date: Wed, 10 Mar 2021 23:18:07 -0800 Subject: [PATCH 11/17] Fix directory name * Remove trailing slash from directory name. (#16) * Use `File` instead of `File_CI` for class name. --- src/File_CI.cpp | 9 +++---- src/SD_CI.cpp | 4 ++- test/test.cpp | 65 ++++++++++++++++++++++++++----------------------- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/File_CI.cpp b/src/File_CI.cpp index 4b3dfe3..754a51b 100644 --- a/src/File_CI.cpp +++ b/src/File_CI.cpp @@ -33,15 +33,12 @@ File_CI::File_CI(const std::string &path, uint8_t mode) { // find the file name in the full path const char *File_CI::name() const { size_t index; - if (isDirectory()) { - index = _path.find_last_of('/', _path.size() - 2); + if (isDirectory()) { // name saved as "contents" + return SD.contentsOf(_path)->c_str(); } else { index = _path.find_last_of('/'); + return _path.c_str() + index + 1; } - if (index == std::string::npos) { - index = -1; - } - return _path.c_str() + index + 1; } // get next char without advancing position diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp index 6961a93..f066824 100644 --- a/src/SD_CI.cpp +++ b/src/SD_CI.cpp @@ -24,7 +24,9 @@ bool SDClass_CI::mkdir(const std::string &path) { } size_t index = fullPath.find_last_of('/', fullPath.size() - 2); assert(index == 0 || exists(fullPath.substr(0, index + 1))); - fileSystem.emplace(fullPath, ""); + // save directory base name as "file contents" to simplify name() lookup + std::string dirName = fullPath.substr(index + 1, fullPath.size() - index - 2); + fileSystem.emplace(fullPath, dirName); return true; } diff --git a/test/test.cpp b/test/test.cpp index fae10e5..1bab4bd 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -16,7 +16,7 @@ unittest_teardown() { SD.removeAll(); } unittest(exists) { assertFalse(SD.exists("file.txt")); - File_CI file = SD.open("file.txt", FILE_WRITE); + File file = SD.open("file.txt", FILE_WRITE); file.close(); assertTrue(SD.exists("file.txt")); } @@ -33,23 +33,23 @@ unittest(mkdir) { unittest(open) { // create file - File_CI writeFile = SD.open("file.txt", FILE_WRITE); + File writeFile = SD.open("file.txt", FILE_WRITE); writeFile.close(); // open file for read should exist - File_CI readFile2 = SD.open("file.txt", FILE_READ); + File readFile2 = SD.open("file.txt", FILE_READ); assertTrue(readFile2.isOpen()); readFile2.close(); } unittest(close) { // close write file - File_CI file = SD.open("file.txt", FILE_WRITE); + File file = SD.open("file.txt", FILE_WRITE); assertTrue(file.isOpen()); file.close(); assertFalse(file.isOpen()); // close read file - File_CI readFile = SD.open("file.txt", FILE_READ); + File readFile = SD.open("file.txt", FILE_READ); assertTrue(readFile.isOpen()); readFile.close(); assertFalse(readFile.isOpen()); @@ -57,7 +57,7 @@ unittest(close) { unittest(remove) { // set up - File_CI file = SD.open("file.txt", FILE_WRITE); + File file = SD.open("file.txt", FILE_WRITE); file.close(); assertTrue(SD.exists("file.txt")); @@ -72,7 +72,7 @@ unittest(rmdir) { SD.mkdir("test_directory/a/a"); SD.mkdir("test_directory/a/b"); SD.mkdir("test_directory/a/c"); - File_CI file = SD.open("test_directory/a/a/file.txt", FILE_WRITE); + File file = SD.open("test_directory/a/a/file.txt", FILE_WRITE); file.close(); // remove directory @@ -86,20 +86,23 @@ unittest(rmdir) { unittest(name) { // set up - File_CI file = SD.open("newFile.txt", FILE_WRITE); + File file = SD.open("newFile.txt", FILE_WRITE); + assertEqual("newFile.txt", file.name()); + file.close(); - char expected[] = "newFile.txt"; - assertEqual(expected, file.name()); + SD.mkdir("dir"); + file = SD.open("dir"); + assertEqual("dir", file.name()); file.close(); } unittest(seek) { // set up - File_CI file = SD.open("seek.txt", FILE_WRITE); + File file = SD.open("seek.txt", FILE_WRITE); char write[] = "Hide and Seek."; file.write(write, sizeof(write) - 1); file.close(); - File_CI read = SD.open("seek.txt", FILE_READ); + File read = SD.open("seek.txt", FILE_READ); // Testing char readFrom[4]; @@ -112,18 +115,18 @@ unittest(seek) { unittest(read) { // set up - File_CI file = SD.open("birthday.txt", FILE_WRITE); + File file = SD.open("birthday.txt", FILE_WRITE); char toWrite[] = "Happy Birthday to You!"; file.write(toWrite, sizeof(toWrite) - 1); file.close(); - File_CI file2 = SD.open("lines.txt", FILE_WRITE); + File file2 = SD.open("lines.txt", FILE_WRITE); char toWrite2[] = "line 1\nline2"; file2.write(toWrite2, sizeof(toWrite2) - 1); file2.close(); // testing - File_CI readFile = SD.open("birthday.txt", FILE_READ); + File readFile = SD.open("birthday.txt", FILE_READ); size_t size = readFile.size(); char readFromFile[size + 1]; readFile.read(readFromFile, size); @@ -132,7 +135,7 @@ unittest(read) { // assertEqual(toWrite, readFromFile); - File_CI readFile2 = SD.open("lines.txt", FILE_READ); + File readFile2 = SD.open("lines.txt", FILE_READ); char readFromFile2[7 + 1]; char readFromFile3[5 + 1]; readFile2.read(readFromFile2, 7); @@ -148,7 +151,7 @@ unittest(read) { unittest(write) { // open new file for writing - File_CI writeFile = SD.open("wood.txt", FILE_WRITE); + File writeFile = SD.open("wood.txt", FILE_WRITE); char toWrite[] = "How much wood could a wood pecker peck?\n"; writeFile.write(toWrite, sizeof(toWrite) - 1); @@ -162,13 +165,13 @@ unittest(write) { writeFile.close(); // open old writing file to write at end. - File_CI writeFile2 = SD.open("wood.txt", FILE_WRITE); + File writeFile2 = SD.open("wood.txt", FILE_WRITE); char toWrite2[] = "A lot of wood.\n"; writeFile2.write(toWrite2, sizeof(toWrite2) - 1); writeFile2.close(); // check old writing file - File_CI readWrite2 = SD.open("wood.txt", FILE_READ); + File readWrite2 = SD.open("wood.txt", FILE_READ); size_t size2 = readWrite2.size(); char toRead2[size2 + 1]; readWrite2.read(toRead2, size2); @@ -181,7 +184,7 @@ unittest(write) { unittest(size) { // setup - File_CI sizeFile = SD.open("size.txt", FILE_WRITE); + File sizeFile = SD.open("size.txt", FILE_WRITE); char toWrite[] = "Test text\n"; sizeFile.write(toWrite, sizeof(toWrite) - 1); sizeFile.close(); @@ -193,18 +196,18 @@ unittest(size) { unittest(peek) { // set up - File_CI peekFile = SD.open("peek.txt", FILE_WRITE); + File peekFile = SD.open("peek.txt", FILE_WRITE); char toWrite[] = "Peek file content\n"; peekFile.write(toWrite, sizeof(toWrite) - 1); peekFile.close(); // Test - File_CI readPeek = SD.open("peek.txt", FILE_READ); + File readPeek = SD.open("peek.txt", FILE_READ); assertEqual('P', readPeek.peek()); assertEqual('P', readPeek.peek()); readPeek.close(); - File_CI readWritePeek = SD.open("peek.txt", FILE_WRITE); + File readWritePeek = SD.open("peek.txt", FILE_WRITE); readWritePeek.seek(0); assertEqual('P', readWritePeek.peek()); assertEqual('P', readWritePeek.peek()); @@ -213,7 +216,7 @@ unittest(peek) { unittest(position) { // set up - File_CI posFile = SD.open("pos.txt", FILE_WRITE); + File posFile = SD.open("pos.txt", FILE_WRITE); char toWrite[] = "This is the position file.\n"; posFile.write(toWrite, sizeof(toWrite) - 1); @@ -227,13 +230,13 @@ unittest(position) { unittest(isDirectory) { // set up SD.mkdir("test"); - File_CI toRead = SD.open("read.txt", FILE_WRITE); + File toRead = SD.open("read.txt", FILE_WRITE); toRead.close(); - File_CI testFile = SD.open("test.txt", FILE_WRITE); - File_CI readFile = SD.open("read.txt", FILE_READ); + File testFile = SD.open("test.txt", FILE_WRITE); + File readFile = SD.open("read.txt", FILE_READ); // test - File_CI myDir = SD.open("test"); + File myDir = SD.open("test"); assertTrue(myDir.isDirectory()); assertFalse(testFile.isDirectory()); assertFalse(readFile.isDirectory()); @@ -244,7 +247,7 @@ unittest(isDirectory) { } unittest(next) { - File_CI file; + File file; file = SD.open("x", FILE_WRITE); file.close(); @@ -256,7 +259,7 @@ unittest(next) { file.close(); SD.open("a", O_WRITE).close(); - File_CI root = SD.open("/"); + File root = SD.open("/"); assertTrue(root); assertTrue(root.isDirectory()); @@ -268,7 +271,7 @@ unittest(next) { file = root.openNextFile(); assertTrue(file); - assertEqual(std::string("d/"), file.name()); + assertEqual(std::string("d"), file.name()); assertTrue(file.isDirectory()); file.close(); From 164dee454e2da1391487330ec842a9715d3c10d5 Mon Sep 17 00:00:00 2001 From: James Foster Date: Thu, 11 Mar 2021 11:56:39 -0800 Subject: [PATCH 12/17] Directory scan fix (#17) * Fix problem with end of directory in scan. --- src/SD_CI.cpp | 7 ++++- test/test.cpp | 82 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp index f066824..ff70893 100644 --- a/src/SD_CI.cpp +++ b/src/SD_CI.cpp @@ -40,12 +40,17 @@ const std::string &SDClass_CI::nameAfter(const std::string fullPath, // advance to next entry after directory while ((++iter)->first.substr(0, fullPath.size()) == fullPath) { } - } else { + } else { // advance to next entry ++iter; } + // are we at the end of the file system? if (iter == fileSystem.end()) { return emptyString; } + // have we finished the current directory? + if (iter->first.substr(0, start.size()) != start) { + return emptyString; + } return iter->first; } diff --git a/test/test.cpp b/test/test.cpp index 1bab4bd..a57f7b0 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -247,48 +247,62 @@ unittest(isDirectory) { } unittest(next) { - File file; - - file = SD.open("x", FILE_WRITE); - file.close(); - SD.open("x", O_WRITE).close(); - + /* + * /c + * /d/ + * /d/x + * /d/y + * /e + */ + // create things out-of-order to confirm that we find in order + SD.open("e", O_WRITE).close(); SD.mkdir("d"); - - file = SD.open("a", FILE_WRITE); - file.close(); - SD.open("a", O_WRITE).close(); + SD.open("c", O_WRITE).close(); + SD.open("d/x", O_WRITE).close(); + SD.open("d/y", O_WRITE).close(); File root = SD.open("/"); assertTrue(root); assertTrue(root.isDirectory()); - file = root.openNextFile(); - assertTrue(file); - assertEqual(std::string("a"), file.name()); - assertFalse(file.isDirectory()); - file.close(); - - file = root.openNextFile(); - assertTrue(file); - assertEqual(std::string("d"), file.name()); - assertTrue(file.isDirectory()); - file.close(); - - file = root.openNextFile(); - assertTrue(file); - assertEqual(std::string("x"), file.name()); - assertFalse(file.isDirectory()); - file.close(); - - file = root.openNextFile(); - assertFalse(file); + File c = root.openNextFile(); + assertTrue(c); + assertEqual(std::string("c"), c.name()); + assertFalse(c.isDirectory()); + c.close(); + + File d = root.openNextFile(); + assertTrue(d); + assertEqual(std::string("d"), d.name()); + assertTrue(d.isDirectory()); + File d1 = d.openNextFile(); + assertTrue(d1); + assertEqual(std::string("x"), d1.name()); + assertFalse(d1.isDirectory()); + d1.close(); + File d2 = d.openNextFile(); + assertTrue(d2); + assertEqual(std::string("y"), d2.name()); + assertFalse(d2.isDirectory()); + d2.close(); + File end = d.openNextFile(); + assertFalse(end); + d.close(); + + File e = root.openNextFile(); + assertTrue(e); + assertEqual(std::string("e"), e.name()); + assertFalse(e.isDirectory()); + e.close(); + + end = root.openNextFile(); + assertFalse(end); root.rewindDirectory(); - file = root.openNextFile(); - assertTrue(file); - assertEqual(std::string("a"), file.name()); - file.close(); + c = root.openNextFile(); + assertTrue(c); + assertEqual(std::string("c"), c.name()); + c.close(); } unittest_main() From f4b2e629a9fc9af7c5150ba530b223696a277037 Mon Sep 17 00:00:00 2001 From: James Foster Date: Thu, 11 Mar 2021 14:42:32 -0800 Subject: [PATCH 13/17] Use `String` instead of `std::string`. --- src/File_CI.cpp | 13 ++++++------- src/SD_CI.cpp | 42 +++++++++++++++++++-------------------- src/SD_CI.h | 52 ++++++++++++++++++++----------------------------- test/test.cpp | 12 ++++++------ 4 files changed, 53 insertions(+), 66 deletions(-) diff --git a/src/File_CI.cpp b/src/File_CI.cpp index 754a51b..12685ea 100644 --- a/src/File_CI.cpp +++ b/src/File_CI.cpp @@ -6,12 +6,11 @@ #include #include #include -#include namespace SDLib { // create a directory reference -File_CI::File_CI(const std::string &path) { +File_CI::File_CI(const String &path) { assert(path.size()); assert(path.back() == '/'); assert(SD.exists(path)); @@ -21,7 +20,7 @@ File_CI::File_CI(const std::string &path) { } // create a file reference -File_CI::File_CI(const std::string &path, uint8_t mode) { +File_CI::File_CI(const String &path, uint8_t mode) { assert(path.size()); assert(path.back() != '/'); assert(SD.exists(path)); @@ -44,7 +43,7 @@ const char *File_CI::name() const { // get next char without advancing position int File_CI::peek() const { assert(!isDirectory()); - std::string *pContents = SD.contentsOf(_path); + String *pContents = SD.contentsOf(_path); assert(pContents); if (_pos < pContents->size()) { return pContents->at(_pos); @@ -63,7 +62,7 @@ int File_CI::peek() const { int File_CI::read(char *buf, size_t size) { assert(!isDirectory()); assert(_mode & O_READ); - std::string *pContents = SD.contentsOf(_path); + String *pContents = SD.contentsOf(_path); size = size <= available() ? size : available(); memcpy(buf, pContents->c_str() + _pos, size); _pos += size; @@ -89,7 +88,7 @@ uint32_t File_CI::size() const { size_t File_CI::write(const char *buf, size_t size) { assert(!isDirectory()); assert(_mode & O_WRITE); - std::string *pContents = SD.contentsOf(_path); + String *pContents = SD.contentsOf(_path); for (int i = 0; i < size; ++i) { *pContents += buf[i]; } @@ -99,7 +98,7 @@ size_t File_CI::write(const char *buf, size_t size) { File_CI File_CI::openNextFile(uint8_t mode) { assert(isDirectory()); - const std::string &name = SD.nameAfter(_current, _path); + const String &name = SD.nameAfter(_current, _path); if (name.empty()) { _current = _path; return File_CI(); diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp index ff70893..721b617 100644 --- a/src/SD_CI.cpp +++ b/src/SD_CI.cpp @@ -4,35 +4,34 @@ namespace SDLib { -std::string *SDClass_CI::contentsOf(const std::string &path) { - std::string fullPath = normalizePath(path); +String *SDClass_CI::contentsOf(const String &path) { + String fullPath = normalizePath(path); if (fileSystem.count(fullPath)) { return &fileSystem[fullPath]; } return nullptr; } -bool SDClass_CI::exists(const std::string &path) { - std::string fullPath = normalizePath(path); +bool SDClass_CI::exists(const String &path) { + String fullPath = normalizePath(path); return contentsOf(fullPath) != nullptr; } -bool SDClass_CI::mkdir(const std::string &path) { - std::string fullPath = normalizePath(path, true); +bool SDClass_CI::mkdir(const String &path) { + String fullPath = normalizePath(path, true); if (exists(fullPath)) { return false; } size_t index = fullPath.find_last_of('/', fullPath.size() - 2); assert(index == 0 || exists(fullPath.substr(0, index + 1))); // save directory base name as "file contents" to simplify name() lookup - std::string dirName = fullPath.substr(index + 1, fullPath.size() - index - 2); + String dirName = fullPath.substr(index + 1, fullPath.size() - index - 2); fileSystem.emplace(fullPath, dirName); return true; } -const std::string &SDClass_CI::nameAfter(const std::string fullPath, - const std::string start) { - std::map::iterator iter; +const String &SDClass_CI::nameAfter(const String fullPath, const String start) { + std::map::iterator iter; iter = fileSystem.find(fullPath); assert(iter != fileSystem.end()); if (iter->first != start && @@ -54,10 +53,10 @@ const std::string &SDClass_CI::nameAfter(const std::string fullPath, return iter->first; } -File_CI SDClass_CI::open(const std::string &path, uint8_t mode) { +File_CI SDClass_CI::open(const String &path, uint8_t mode) { // ensure we have a root directory - fileSystem.emplace(std::string("/"), std::string("")); - std::string fullPath = normalizePath(path); + fileSystem.emplace(String("/"), String("")); + String fullPath = normalizePath(path); if (exists(fullPath)) { if (fullPath.back() == '/') { return File_CI(fullPath); @@ -71,8 +70,8 @@ File_CI SDClass_CI::open(const std::string &path, uint8_t mode) { return File_CI(fullPath, mode); } -bool SDClass_CI::remove(const std::string &path) { - std::string fullPath = normalizePath(path); +bool SDClass_CI::remove(const String &path) { + String fullPath = normalizePath(path); assert(fullPath.back() != '/'); if (exists(fullPath)) { fileSystem.erase(fullPath); @@ -81,11 +80,11 @@ bool SDClass_CI::remove(const std::string &path) { return false; } -bool SDClass_CI::rmdir(const std::string &path) { - std::string fullPath = normalizePath(path, true); +bool SDClass_CI::rmdir(const String &path) { + String fullPath = normalizePath(path, true); if (exists(fullPath)) { // check for children of this directory - std::string next = nameAfter(fullPath, fullPath); + String next = nameAfter(fullPath, fullPath); assert(next == "" || next.substr(0, fullPath.size()) != fullPath); fileSystem.erase(fullPath); return true; @@ -94,9 +93,8 @@ bool SDClass_CI::rmdir(const std::string &path) { } // private -std::string SDClass_CI::normalizePath(const std::string &inPath, - bool isDirectory) { - std::string fullPath = inPath; +String SDClass_CI::normalizePath(const String &inPath, bool isDirectory) { + String fullPath = inPath; // add leading '/' for root if (fullPath.front() != '/') { fullPath.insert(0, 1, '/'); @@ -117,7 +115,7 @@ std::string SDClass_CI::normalizePath(const std::string &inPath, // static variables SDClass_CI SD_CI; -std::map SDClass::fileSystem; +std::map SDClass::fileSystem; } // namespace SDLib diff --git a/src/SD_CI.h b/src/SD_CI.h index 0de868c..a72c52e 100644 --- a/src/SD_CI.h +++ b/src/SD_CI.h @@ -4,7 +4,6 @@ #include "SD.h" #include #include -#include namespace SDLib { @@ -13,9 +12,9 @@ uint8_t const O_REMOVE = 0X80; class File_CI : public File_Base { public: - File_CI() {} // not a file (for iteration) - File_CI(const std::string &path); // create a directory - File_CI(const std::string &path, uint8_t mode); // create a file + File_CI() {} // not a file (for iteration) + File_CI(const String &path); // create a directory + File_CI(const String &path, uint8_t mode); // create a file // file operations int available() const { return size() - _pos; } @@ -46,12 +45,12 @@ class File_CI : public File_Base { void clearWriteError() { _writeError = 0; } private: - std::string _path; // full path - int _pos = 0; // index of _next_ char to read or write + String _path; // full path + int _pos = 0; // index of _next_ char to read or write uint8_t _mode = 0; bool _isOpen = false; int _writeError = 0; - std::string _current; // used by directory iterator + String _current; // used by directory iterator }; class SDClass_CI : public SDClass_Base { @@ -62,44 +61,35 @@ class SDClass_CI : public SDClass_Base { void end() {} // return a pointer rather than a reference so we can use nullptr for missing - std::string *contentsOf(const std::string &path); + String *contentsOf(const String &path); - bool exists(const std::string &path); - bool exists(const char *path) { return exists(std::string(path)); } - bool exists(const String &path) { return exists(std::string(path.c_str())); } + bool exists(const String &path); + bool exists(const char *path) { return exists(String(path)); } - bool mkdir(const std::string &path); - bool mkdir(const char *path) { return mkdir(std::string(path)); } - bool mkdir(const String &path) { return mkdir(path.c_str()); } + bool mkdir(const String &path); + bool mkdir(const char *path) { return mkdir(String(path)); } - const std::string &nameAfter(const std::string current, - const std::string start); + const String &nameAfter(const String current, const String start); - File_CI open(const std::string &path, uint8_t mode = FILE_READ); + File_CI open(const String &path, uint8_t mode = FILE_READ); File_CI open(const char *path, uint8_t mode) { - return open(std::string(path), mode); - } - File_CI open(const String &path, uint8_t mode) { - return open(std::string(path.c_str()), mode); + return open(String(path), mode); } - bool remove(const std::string &path); - bool remove(const char *path) { return remove(std::string(path)); } - bool remove(const String &path) { return remove(std::string(path.c_str())); } + bool remove(const String &path); + bool remove(const char *path) { return remove(String(path)); } void removeAll() { fileSystem.clear(); } - bool rmdir(const std::string &path); - bool rmdir(const char *path) { return rmdir(std::string(path)); } - bool rmdir(const String &path) { return rmdir(path.c_str()); } + bool rmdir(const String &path); + bool rmdir(const char *path) { return rmdir(String(path)); } String className() const { return "SDClass_Base"; } private: - static std::map fileSystem; - std::string emptyString; - std::string normalizePath(const std::string &inPath, - bool isDirectory = false); + static std::map fileSystem; + String emptyString; + String normalizePath(const String &inPath, bool isDirectory = false); }; extern SDClass_CI SD_CI; diff --git a/test/test.cpp b/test/test.cpp index a57f7b0..9f6791c 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -267,22 +267,22 @@ unittest(next) { File c = root.openNextFile(); assertTrue(c); - assertEqual(std::string("c"), c.name()); + assertEqual(String("c"), c.name()); assertFalse(c.isDirectory()); c.close(); File d = root.openNextFile(); assertTrue(d); - assertEqual(std::string("d"), d.name()); + assertEqual(String("d"), d.name()); assertTrue(d.isDirectory()); File d1 = d.openNextFile(); assertTrue(d1); - assertEqual(std::string("x"), d1.name()); + assertEqual(String("x"), d1.name()); assertFalse(d1.isDirectory()); d1.close(); File d2 = d.openNextFile(); assertTrue(d2); - assertEqual(std::string("y"), d2.name()); + assertEqual(String("y"), d2.name()); assertFalse(d2.isDirectory()); d2.close(); File end = d.openNextFile(); @@ -291,7 +291,7 @@ unittest(next) { File e = root.openNextFile(); assertTrue(e); - assertEqual(std::string("e"), e.name()); + assertEqual(String("e"), e.name()); assertFalse(e.isDirectory()); e.close(); @@ -301,7 +301,7 @@ unittest(next) { root.rewindDirectory(); c = root.openNextFile(); assertTrue(c); - assertEqual(std::string("c"), c.name()); + assertEqual(String("c"), c.name()); c.close(); } From 3475a145571ac157320057e76753ce27c08bc7e0 Mon Sep 17 00:00:00 2001 From: James Foster Date: Sun, 18 Apr 2021 20:10:09 -0700 Subject: [PATCH 14/17] Check for out-of-range when iterating through files in a directory. --- .gitignore | 1 + src/SD_CI.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9343ab4..0bcfe10 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ Gemfile.lock vendor *.cpp.bin* .vscode +libarduino.so* diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp index 721b617..6ff424a 100644 --- a/src/SD_CI.cpp +++ b/src/SD_CI.cpp @@ -37,7 +37,8 @@ const String &SDClass_CI::nameAfter(const String fullPath, const String start) { if (iter->first != start && fullPath.back() == '/') { // current entry is a directory // advance to next entry after directory - while ((++iter)->first.substr(0, fullPath.size()) == fullPath) { + while (++iter != fileSystem.end() && + iter->first.substr(0, fullPath.size()) == fullPath) { } } else { // advance to next entry ++iter; From e68d9aa50d9f396322ee042f5eaa70f84ce762fb Mon Sep 17 00:00:00 2001 From: James Foster Date: Sun, 6 Jun 2021 13:40:23 -0700 Subject: [PATCH 15/17] WIP: add comments --- src/SD_CI.cpp | 33 ++++++++++++++++++++++++++++++++- src/SD_CI.h | 11 ++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/SD_CI.cpp b/src/SD_CI.cpp index 6ff424a..ee2040e 100644 --- a/src/SD_CI.cpp +++ b/src/SD_CI.cpp @@ -12,16 +12,46 @@ String *SDClass_CI::contentsOf(const String &path) { return nullptr; } +/** + * exists() + * + * Tests whether a file or directory exists on the SD card. + * + * path: the name of the file to test for existence, which + * can include directories (delimited by forward-slashes ('/'). + */ bool SDClass_CI::exists(const String &path) { String fullPath = normalizePath(path); return contentsOf(fullPath) != nullptr; } +/** + * mkdir() + * + * Create a directory on the SD card. This will also create any + * intermediate directories that don't already exists; e.g. + * SD.mkdir("a/b/c") will create a, b, and c. + * + * path: the name of the directory to create, with sub-directories + * separated by forward-slashes ('/') + */ bool SDClass_CI::mkdir(const String &path) { String fullPath = normalizePath(path, true); if (exists(fullPath)) { return false; } + // int i = 1; + // int j = fullPath.indexOf('/', i); + // while (j > 0) { + // String intermediatePath = fullPath.substring(0, j); + // String name = fullPath.substring(i, j - 1); + // if (!exists(intermediatePath)) { + // fileSystem.emplace(intermediatePath, fullPath.substring(i, j - 1)); + // i = j + 1; + // j = fullPath.indexOf('/', i + 1); + // } + // } + size_t index = fullPath.find_last_of('/', fullPath.size() - 2); assert(index == 0 || exists(fullPath.substr(0, index + 1))); // save directory base name as "file contents" to simplify name() lookup @@ -64,7 +94,8 @@ File_CI SDClass_CI::open(const String &path, uint8_t mode) { } return File_CI(fullPath, mode); } - assert(fullPath.back() != '/' && mode & O_WRITE); + assert(fullPath.back() != '/'); + assert(mode & O_WRITE); size_t index = fullPath.find_last_of('/'); assert(index == 0 || exists(fullPath.substr(0, index + 1))); fileSystem.emplace(fullPath, ""); diff --git a/src/SD_CI.h b/src/SD_CI.h index a72c52e..81e7068 100644 --- a/src/SD_CI.h +++ b/src/SD_CI.h @@ -5,6 +5,15 @@ #include #include +/** + * The mock SD has an internal map + * key = normalized full path + * * path always begins with a forward slash ('/') + * * trailing forward slash ('/') indicates a directory + * value = contents or name + * * binary or text data for a file + * * simple name for a directory (easier for name lookup) + */ namespace SDLib { // we add a new "mode" so `find()` can remove files and directories @@ -84,7 +93,7 @@ class SDClass_CI : public SDClass_Base { bool rmdir(const String &path); bool rmdir(const char *path) { return rmdir(String(path)); } - String className() const { return "SDClass_Base"; } + String className() const { return "SDClass_CI"; } private: static std::map fileSystem; From 32ccba37572a494321285df4373f8d86093a553b Mon Sep 17 00:00:00 2001 From: James Foster Date: Mon, 2 Aug 2021 07:02:33 -0700 Subject: [PATCH 16/17] Remove "Report Size Deltas" GitHub Action since it is being run every 5 minutes! --- .github/workflows/report-size-deltas.yml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .github/workflows/report-size-deltas.yml diff --git a/.github/workflows/report-size-deltas.yml b/.github/workflows/report-size-deltas.yml deleted file mode 100644 index 652be5d..0000000 --- a/.github/workflows/report-size-deltas.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Report Size Deltas - -# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows -on: - push: - paths: - - ".github/workflows/report-size-deltas.yml" - schedule: - # Run at the minimum interval allowed by GitHub Actions. - # Note: GitHub Actions periodically has outages which result in workflow failures. - # In this event, the workflows will start passing again once the service recovers. - - cron: "*/5 * * * *" - workflow_dispatch: - repository_dispatch: - -jobs: - report: - runs-on: ubuntu-latest - steps: - - name: Comment size deltas reports to PRs - uses: arduino/report-size-deltas@v1 - with: - # The name of the workflow artifact created by the sketch compilation workflow - sketches-reports-source: sketches-reports From cf1d6ba55dcb8b96bd58b3003beb5199626adbe7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 14:03:17 +0000 Subject: [PATCH 17/17] Bump DoozyX/clang-format-lint-action from 0.5 to 0.12 Bumps [DoozyX/clang-format-lint-action](https://github.com/DoozyX/clang-format-lint-action) from 0.5 to 0.12. - [Release notes](https://github.com/DoozyX/clang-format-lint-action/releases) - [Commits](https://github.com/DoozyX/clang-format-lint-action/compare/v0.5...v0.12) --- updated-dependencies: - dependency-name: DoozyX/clang-format-lint-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index c8f556d..8e4bfa4 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: DoozyX/clang-format-lint-action@v0.5 + - uses: DoozyX/clang-format-lint-action@v0.12 with: source: './' extensions: 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx,ino'