From 3e49e3fa2f62190fbc6caeb9c0b3c2d6997500ae Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Aug 2015 17:47:15 +1000 Subject: [PATCH] Import the mslogger-lcd-sd-blu-uno project and fix up a few lib and mega board issues --- .gitignore | 4 + LICENSE | 22 + README.md | 18 + lib/Adafruit-GFX/Adafruit_GFX.cpp | 468 +++++++ lib/Adafruit-GFX/Adafruit_GFX.h | 111 ++ lib/Adafruit-GFX/glcdfont.c | 273 ++++ lib/Adafruit-GPS-Library/Adafruit_GPS.cpp | 500 +++++++ lib/Adafruit-GPS-Library/Adafruit_GPS.h | 168 +++ lib/Adafruit-TFTLCD/Adafruit_TFTLCD.cpp | 803 +++++++++++ lib/Adafruit-TFTLCD/Adafruit_TFTLCD.h | 88 ++ lib/Adafruit-TFTLCD/pin_magic.h | 284 ++++ lib/SdFat/MinimumSerial.cpp | 58 + lib/SdFat/MinimumSerial.h | 50 + lib/SdFat/SdFat.h | 341 +++++ lib/SdFat/SdFatBase.cpp | 99 ++ lib/SdFat/SdFatConfig.h | 188 +++ lib/SdFat/SdFatUtil.cpp | 51 + lib/SdFat/SdFatUtil.h | 67 + lib/SdFat/SdFatmainpage.h | 392 ++++++ lib/SdFat/SdInfo.h | 381 ++++++ lib/SdFat/SdSpi.h | 370 +++++ lib/SdFat/SdSpiCard.cpp | 636 +++++++++ lib/SdFat/SdSpiCard.h | 308 +++++ lib/SdFat/SdSpiSAM3X.cpp | 218 +++ lib/SdFat/SdSpiSTM32F1.cpp | 171 +++ lib/SdFat/SdSpiTeensy3.cpp | 301 +++++ lib/SdFat/SdVolume.h | 71 + lib/SdFat/utility/ArduinoFiles.h | 248 ++++ lib/SdFat/utility/ArduinoStream.h | 141 ++ lib/SdFat/utility/DigitalPin.h | 702 ++++++++++ lib/SdFat/utility/FatApiConstants.h | 67 + lib/SdFat/utility/FatFile.cpp | 1494 +++++++++++++++++++++ lib/SdFat/utility/FatFile.h | 990 ++++++++++++++ lib/SdFat/utility/FatFileLFN.cpp | 683 ++++++++++ lib/SdFat/utility/FatFilePrint.cpp | 248 ++++ lib/SdFat/utility/FatFileSFN.cpp | 273 ++++ lib/SdFat/utility/FatFileSystem.h | 310 +++++ lib/SdFat/utility/FatLib.h | 33 + lib/SdFat/utility/FatLibConfig.h | 143 ++ lib/SdFat/utility/FatStructs.h | 755 +++++++++++ lib/SdFat/utility/FatVolume.cpp | 585 ++++++++ lib/SdFat/utility/FatVolume.h | 338 +++++ lib/SdFat/utility/FmtNumber.cpp | 455 +++++++ lib/SdFat/utility/FmtNumber.h | 38 + lib/SdFat/utility/SoftSPI.h | 170 +++ lib/SdFat/utility/StdioStream.cpp | 606 +++++++++ lib/SdFat/utility/StdioStream.h | 670 +++++++++ lib/SdFat/utility/bufstream.h | 163 +++ lib/SdFat/utility/fstream.cpp | 167 +++ lib/SdFat/utility/fstream.h | 315 +++++ lib/SdFat/utility/ios.h | 418 ++++++ lib/SdFat/utility/iostream.h | 153 +++ lib/SdFat/utility/istream.cpp | 381 ++++++ lib/SdFat/utility/istream.h | 379 ++++++ lib/SdFat/utility/ostream.cpp | 191 +++ lib/SdFat/utility/ostream.h | 295 ++++ lib/TouchScreen/TouchScreen.cpp | 248 ++++ lib/TouchScreen/TouchScreen.h | 39 + platformio.ini | 25 + src/GfxArrowButton.cpp | 46 + src/GfxArrowButton.h | 27 + src/GfxButton.cpp | 72 + src/GfxButton.h | 47 + src/GfxDataField.cpp | 42 + src/GfxDataField.h | 32 + src/GfxIndicator.cpp | 32 + src/GfxIndicator.h | 30 + src/GfxLabel.cpp | 28 + src/GfxLabel.h | 27 + src/GfxTextButton.cpp | 38 + src/GfxTextButton.h | 23 + src/MegaSquirt.cpp | 114 ++ src/MegaSquirt.h | 42 + src/TempSensor.cpp | 123 ++ src/TempSensor.h | 28 + src/debug.h | 9 + src/main.cpp | 487 +++++++ 77 files changed, 19411 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 lib/Adafruit-GFX/Adafruit_GFX.cpp create mode 100644 lib/Adafruit-GFX/Adafruit_GFX.h create mode 100644 lib/Adafruit-GFX/glcdfont.c create mode 100644 lib/Adafruit-GPS-Library/Adafruit_GPS.cpp create mode 100644 lib/Adafruit-GPS-Library/Adafruit_GPS.h create mode 100644 lib/Adafruit-TFTLCD/Adafruit_TFTLCD.cpp create mode 100644 lib/Adafruit-TFTLCD/Adafruit_TFTLCD.h create mode 100644 lib/Adafruit-TFTLCD/pin_magic.h create mode 100644 lib/SdFat/MinimumSerial.cpp create mode 100644 lib/SdFat/MinimumSerial.h create mode 100644 lib/SdFat/SdFat.h create mode 100644 lib/SdFat/SdFatBase.cpp create mode 100644 lib/SdFat/SdFatConfig.h create mode 100644 lib/SdFat/SdFatUtil.cpp create mode 100644 lib/SdFat/SdFatUtil.h create mode 100644 lib/SdFat/SdFatmainpage.h create mode 100644 lib/SdFat/SdInfo.h create mode 100644 lib/SdFat/SdSpi.h create mode 100644 lib/SdFat/SdSpiCard.cpp create mode 100644 lib/SdFat/SdSpiCard.h create mode 100644 lib/SdFat/SdSpiSAM3X.cpp create mode 100644 lib/SdFat/SdSpiSTM32F1.cpp create mode 100644 lib/SdFat/SdSpiTeensy3.cpp create mode 100644 lib/SdFat/SdVolume.h create mode 100644 lib/SdFat/utility/ArduinoFiles.h create mode 100644 lib/SdFat/utility/ArduinoStream.h create mode 100644 lib/SdFat/utility/DigitalPin.h create mode 100644 lib/SdFat/utility/FatApiConstants.h create mode 100644 lib/SdFat/utility/FatFile.cpp create mode 100644 lib/SdFat/utility/FatFile.h create mode 100644 lib/SdFat/utility/FatFileLFN.cpp create mode 100644 lib/SdFat/utility/FatFilePrint.cpp create mode 100644 lib/SdFat/utility/FatFileSFN.cpp create mode 100644 lib/SdFat/utility/FatFileSystem.h create mode 100644 lib/SdFat/utility/FatLib.h create mode 100644 lib/SdFat/utility/FatLibConfig.h create mode 100644 lib/SdFat/utility/FatStructs.h create mode 100644 lib/SdFat/utility/FatVolume.cpp create mode 100644 lib/SdFat/utility/FatVolume.h create mode 100644 lib/SdFat/utility/FmtNumber.cpp create mode 100644 lib/SdFat/utility/FmtNumber.h create mode 100644 lib/SdFat/utility/SoftSPI.h create mode 100644 lib/SdFat/utility/StdioStream.cpp create mode 100644 lib/SdFat/utility/StdioStream.h create mode 100644 lib/SdFat/utility/bufstream.h create mode 100644 lib/SdFat/utility/fstream.cpp create mode 100644 lib/SdFat/utility/fstream.h create mode 100644 lib/SdFat/utility/ios.h create mode 100644 lib/SdFat/utility/iostream.h create mode 100644 lib/SdFat/utility/istream.cpp create mode 100644 lib/SdFat/utility/istream.h create mode 100644 lib/SdFat/utility/ostream.cpp create mode 100644 lib/SdFat/utility/ostream.h create mode 100644 lib/TouchScreen/TouchScreen.cpp create mode 100644 lib/TouchScreen/TouchScreen.h create mode 100644 platformio.ini create mode 100644 src/GfxArrowButton.cpp create mode 100644 src/GfxArrowButton.h create mode 100644 src/GfxButton.cpp create mode 100644 src/GfxButton.h create mode 100644 src/GfxDataField.cpp create mode 100644 src/GfxDataField.h create mode 100644 src/GfxIndicator.cpp create mode 100644 src/GfxIndicator.h create mode 100644 src/GfxLabel.cpp create mode 100644 src/GfxLabel.h create mode 100644 src/GfxTextButton.cpp create mode 100644 src/GfxTextButton.h create mode 100644 src/MegaSquirt.cpp create mode 100644 src/MegaSquirt.h create mode 100644 src/TempSensor.cpp create mode 100644 src/TempSensor.h create mode 100644 src/debug.h create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7abe35e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.pioenvs/ +.cproject +.project +.sconsign.dblite \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..81f7353 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Grant Slender + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index 5e4fb0e..abc8cba 100644 --- a/README.md +++ b/README.md @@ -1 +1,19 @@ # mslogger-lcd-gps-sd-mega + +An Arduino ATmega2560 microcontroller application that logs Megasquirt (via Serial) and uses a GPS+SD shield to log data together. Also uses LCD shield to display information including a RPM limiter and menu to add/remove displayed items, start/stop logging etc. + +Current project is based on Platformio's code builder - see http://platformio.org/ + +After pulling down the project's repo, run the following platformio command +`platformio init --board megaatmega2560 --ide eclipse` + +To build the project (and upload) you run `platformio run -t upload` + +The Arduino board actually used is the Seeeduino Mega (same size as an Uno) - we've then got the following additions... + +1) Adafruit's ILI9328 TFT LCD Display and Touch screen shield - uses digital pins D5-D13 and analog A0-A3. +2) Adafruit's Ultimate GPS Logger shield - using pins D18(TX1) and D19(RX1) by running wires from GPS. +3) DB9 Serial TTL board wired to digital pins D0-D1 for Hardware Serial for Megasquirt logging. +3) DB9 Serial TTL board wired to digital pins D2-D3 Software Serial for debuging. + +.. more details to follow soon .. \ No newline at end of file diff --git a/lib/Adafruit-GFX/Adafruit_GFX.cpp b/lib/Adafruit-GFX/Adafruit_GFX.cpp new file mode 100644 index 0000000..901e0de --- /dev/null +++ b/lib/Adafruit-GFX/Adafruit_GFX.cpp @@ -0,0 +1,468 @@ +/****************************************************************** + This is the core graphics library for all our displays, providing + basic graphics primitives (points, lines, circles, etc.). It needs + to be paired with a hardware-specific library for each display + device we carry (handling the lower-level functions). + + Adafruit invests time and resources providing this open + source code, please support Adafruit and open-source hardware + by purchasing products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, check license.txt for more information. + All text above must be included in any redistribution. + ******************************************************************/ + +#include "Adafruit_GFX.h" +#include "glcdfont.c" +#include + +void Adafruit_GFX::constructor(int16_t w, int16_t h) { + _width = WIDTH = w; + _height = HEIGHT = h; + + rotation = 0; + cursor_y = cursor_x = 0; + textsize = 1; + textcolor = textbgcolor = 0xFFFF; + wrap = true; +} + + +// draw a circle outline +void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + drawPixel(x0, y0+r, color); + drawPixel(x0, y0-r, color); + drawPixel(x0+r, y0, color); + drawPixel(x0-r, y0, color); + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 + x, y0 - y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + y, y0 + x, color); + drawPixel(x0 - y, y0 + x, color); + drawPixel(x0 + y, y0 - x, color); + drawPixel(x0 - y, y0 - x, color); + + } +} + +void Adafruit_GFX::drawCircleHelper( int16_t x0, int16_t y0, + int16_t r, uint8_t cornername, uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (cornername & 0x4) { + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 + y, y0 + x, color); + } + if (cornername & 0x2) { + drawPixel(x0 + x, y0 - y, color); + drawPixel(x0 + y, y0 - x, color); + } + if (cornername & 0x8) { + drawPixel(x0 - y, y0 + x, color); + drawPixel(x0 - x, y0 + y, color); + } + if (cornername & 0x1) { + drawPixel(x0 - y, y0 - x, color); + drawPixel(x0 - x, y0 - y, color); + } + } +} + +void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { + drawFastVLine(x0, y0-r, 2*r+1, color); + fillCircleHelper(x0, y0, r, 3, 0, color); +} + +// used to do circles and roundrects! +void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, + uint8_t cornername, int16_t delta, uint16_t color) { + + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + if (cornername & 0x1) { + drawFastVLine(x0+x, y0-y, 2*y+1+delta, color); + drawFastVLine(x0+y, y0-x, 2*x+1+delta, color); + } + if (cornername & 0x2) { + drawFastVLine(x0-x, y0-y, 2*y+1+delta, color); + drawFastVLine(x0-y, y0-x, 2*x+1+delta, color); + } + } +} + +// bresenham's algorithm - thx wikpedia +void Adafruit_GFX::drawLine(int16_t x0, int16_t y0, + int16_t x1, int16_t y1, + uint16_t color) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + swap(x0, y0); + swap(x1, y1); + } + + if (x0 > x1) { + swap(x0, x1); + swap(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + drawPixel(y0, x0, color); + } else { + drawPixel(x0, y0, color); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + + +// draw a rectangle +void Adafruit_GFX::drawRect(int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t color) { + drawFastHLine(x, y, w, color); + drawFastHLine(x, y+h-1, w, color); + drawFastVLine(x, y, h, color); + drawFastVLine(x+w-1, y, h, color); +} + +void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y, + int16_t h, uint16_t color) { + // stupidest version - update in subclasses if desired! + drawLine(x, y, x, y+h-1, color); +} + + +void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y, + int16_t w, uint16_t color) { + // stupidest version - update in subclasses if desired! + drawLine(x, y, x+w-1, y, color); +} + +void Adafruit_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + // stupidest version - update in subclasses if desired! + for (int16_t i=x; i= y1 >= y0) + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + if (y1 > y2) { + swap(y2, y1); swap(x2, x1); + } + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + + if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if(x1 < a) a = x1; + else if(x1 > b) b = x1; + if(x2 < a) a = x2; + else if(x2 > b) b = x2; + drawFastHLine(a, y0, b-a+1, color); + return; + } + + int16_t + dx01 = x1 - x0, + dy01 = y1 - y0, + dx02 = x2 - x0, + dy02 = y2 - y0, + dx12 = x2 - x1, + dy12 = y2 - y1, + sa = 0, + sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y1 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y0=y1 + // (flat-topped triangle). + if(y1 == y2) last = y1; // Include y1 scanline + else last = y1-1; // Skip it + + for(y=y0; y<=last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + /* longhand: + a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + drawFastHLine(a, y, b-a+1, color); + } + + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y1=y2. + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + for(; y<=y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + drawFastHLine(a, y, b-a+1, color); + } +} + +void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, + const uint8_t *bitmap, int16_t w, int16_t h, + uint16_t color) { + + int16_t i, j, byteWidth = (w + 7) / 8; + + for(j=0; j> (i & 7))) { + drawPixel(x+i, y+j, color); + } + } + } +} + + +#if ARDUINO >= 100 +size_t Adafruit_GFX::write(uint8_t c) { +#else +void Adafruit_GFX::write(uint8_t c) { +#endif + if (c == '\n') { + cursor_y += textsize*8; + cursor_x = 0; + } else if (c == '\r') { + // skip em + } else { + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); + cursor_x += textsize*6; + if (wrap && (cursor_x > (_width - textsize*6))) { + cursor_y += textsize*8; + cursor_x = 0; + } + } +#if ARDUINO >= 100 + return 1; +#endif +} + +// draw a character +void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg, uint8_t size) { + + if((x >= _width) || // Clip right + (y >= _height) || // Clip bottom + ((x + 5 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; + + for (int8_t i=0; i<6; i++ ) { + uint8_t line; + if (i == 5) + line = 0x0; + else + line = pgm_read_byte(font+(c*5)+i); + for (int8_t j = 0; j<8; j++) { + if (line & 0x1) { + if (size == 1) // default size + drawPixel(x+i, y+j, color); + else { // big size + fillRect(x+(i*size), y+(j*size), size, size, color); + } + } else if (bg != color) { + if (size == 1) // default size + drawPixel(x+i, y+j, bg); + else { // big size + fillRect(x+i*size, y+j*size, size, size, bg); + } + } + line >>= 1; + } + } +} + +void Adafruit_GFX::setCursor(int16_t x, int16_t y) { + cursor_x = x; + cursor_y = y; +} + + +void Adafruit_GFX::setTextSize(uint8_t s) { + textsize = (s > 0) ? s : 1; +} + + +void Adafruit_GFX::setTextColor(uint16_t c) { + textcolor = c; + textbgcolor = c; + // for 'transparent' background, we'll set the bg + // to the same as fg instead of using a flag +} + + void Adafruit_GFX::setTextColor(uint16_t c, uint16_t b) { + textcolor = c; + textbgcolor = b; + } + +void Adafruit_GFX::setTextWrap(boolean w) { + wrap = w; +} + +uint8_t Adafruit_GFX::getRotation(void) { + rotation %= 4; + return rotation; +} + +void Adafruit_GFX::setRotation(uint8_t x) { + x %= 4; // cant be higher than 3 + rotation = x; + switch (x) { + case 0: + case 2: + _width = WIDTH; + _height = HEIGHT; + break; + case 1: + case 3: + _width = HEIGHT; + _height = WIDTH; + break; + } +} + +void Adafruit_GFX::invertDisplay(boolean i) { + // do nothing, can be subclassed +} + + +// return the size of the display which depends on the rotation! +int16_t Adafruit_GFX::width(void) { + return _width; +} + +int16_t Adafruit_GFX::height(void) { + return _height; +} diff --git a/lib/Adafruit-GFX/Adafruit_GFX.h b/lib/Adafruit-GFX/Adafruit_GFX.h new file mode 100644 index 0000000..30f4723 --- /dev/null +++ b/lib/Adafruit-GFX/Adafruit_GFX.h @@ -0,0 +1,111 @@ +/****************************************************************** + This is the core graphics library for all our displays, providing + basic graphics primitives (points, lines, circles, etc.). It needs + to be paired with a hardware-specific library for each display + device we carry (handling the lower-level functions). + + Adafruit invests time and resources providing this open + source code, please support Adafruit and open-source hardware + by purchasing products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, check license.txt for more information. + All text above must be included in any redistribution. + ******************************************************************/ + +#ifndef _ADAFRUIT_GFX_H +#define _ADAFRUIT_GFX_H + +#if ARDUINO >= 100 + #include "Arduino.h" + #include "Print.h" +#else + #include "WProgram.h" +#endif + +#define swap(a, b) { int16_t t = a; a = b; b = t; } + +// Assign human-readable names to some common 16-bit color values: +#define BLACK 0x0000 +#define DRKGRAY 0x2124 +#define LTGRAY 0xBDD7 +#define BLUE 0x001F +#define LTBLUE 0x94DF +#define RED 0xF800 +#define GREEN 0x07E0 +#define CYAN 0x07FF +#define MAGENTA 0xF81F +#define YELLOW 0xFFE0 +#define WHITE 0xFFFF + +class Adafruit_GFX : public Print { + public: + + //Adafruit_GFX(); + // i have no idea why we have to formally call the constructor. kinda sux + void constructor(int16_t w, int16_t h); + + // this must be defined by the subclass + virtual void drawPixel(int16_t x, int16_t y, uint16_t color); + virtual void invertDisplay(boolean i); + + // these are 'generic' drawing functions, so we can share them! + virtual void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color); + virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + virtual void fillScreen(uint16_t color); + + void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); + void drawCircleHelper(int16_t x0, int16_t y0, + int16_t r, uint8_t cornername, uint16_t color); + void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); + void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, + uint8_t cornername, int16_t delta, uint16_t color); + + void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color); + void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color); + void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color); + void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color); + + void drawBitmap(int16_t x, int16_t y, + const uint8_t *bitmap, int16_t w, int16_t h, + uint16_t color); + void drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg, uint8_t size); +#if ARDUINO >= 100 + virtual size_t write(uint8_t); +#else + virtual void write(uint8_t); +#endif + void setCursor(int16_t x, int16_t y); + void setTextColor(uint16_t c); + void setTextColor(uint16_t c, uint16_t bg); + void setTextSize(uint8_t s); + void setTextWrap(boolean w); + + int16_t height(void); + int16_t width(void); + + void setRotation(uint8_t r); + uint8_t getRotation(void); + + protected: + int16_t WIDTH, HEIGHT; // this is the 'raw' display w/h - never changes + int16_t _width, _height; // dependent on rotation + int16_t cursor_x, cursor_y; + uint16_t textcolor, textbgcolor; + uint8_t textsize; + uint8_t rotation; + boolean wrap; // If set, 'wrap' text at right edge of display +}; + +#endif diff --git a/lib/Adafruit-GFX/glcdfont.c b/lib/Adafruit-GFX/glcdfont.c new file mode 100644 index 0000000..b7be2a7 --- /dev/null +++ b/lib/Adafruit-GFX/glcdfont.c @@ -0,0 +1,273 @@ +#ifndef FONT5X7_H +#define FONT5X7_H + +#ifdef __AVR__ + #include + #include +#elif defined(ESP8266) + #include +#else + #define PROGMEM +#endif + +// Standard ASCII 5x7 font + +static const unsigned char font[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, + 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, + 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, + 0x18, 0x3C, 0x7E, 0x3C, 0x18, + 0x1C, 0x57, 0x7D, 0x57, 0x1C, + 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, + 0x00, 0x18, 0x3C, 0x18, 0x00, + 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, + 0x00, 0x18, 0x24, 0x18, 0x00, + 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, + 0x30, 0x48, 0x3A, 0x06, 0x0E, + 0x26, 0x29, 0x79, 0x29, 0x26, + 0x40, 0x7F, 0x05, 0x05, 0x07, + 0x40, 0x7F, 0x05, 0x25, 0x3F, + 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, + 0x7F, 0x3E, 0x1C, 0x1C, 0x08, + 0x08, 0x1C, 0x1C, 0x3E, 0x7F, + 0x14, 0x22, 0x7F, 0x22, 0x14, + 0x5F, 0x5F, 0x00, 0x5F, 0x5F, + 0x06, 0x09, 0x7F, 0x01, 0x7F, + 0x00, 0x66, 0x89, 0x95, 0x6A, + 0x60, 0x60, 0x60, 0x60, 0x60, + 0x94, 0xA2, 0xFF, 0xA2, 0x94, + 0x08, 0x04, 0x7E, 0x04, 0x08, + 0x10, 0x20, 0x7E, 0x20, 0x10, + 0x08, 0x08, 0x2A, 0x1C, 0x08, + 0x08, 0x1C, 0x2A, 0x08, 0x08, + 0x1E, 0x10, 0x10, 0x10, 0x10, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, + 0x30, 0x38, 0x3E, 0x38, 0x30, + 0x06, 0x0E, 0x3E, 0x0E, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x07, 0x00, + 0x14, 0x7F, 0x14, 0x7F, 0x14, + 0x24, 0x2A, 0x7F, 0x2A, 0x12, + 0x23, 0x13, 0x08, 0x64, 0x62, + 0x36, 0x49, 0x56, 0x20, 0x50, + 0x00, 0x08, 0x07, 0x03, 0x00, + 0x00, 0x1C, 0x22, 0x41, 0x00, + 0x00, 0x41, 0x22, 0x1C, 0x00, + 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, + 0x08, 0x08, 0x3E, 0x08, 0x08, + 0x00, 0x80, 0x70, 0x30, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x60, 0x60, 0x00, + 0x20, 0x10, 0x08, 0x04, 0x02, + 0x3E, 0x51, 0x49, 0x45, 0x3E, + 0x00, 0x42, 0x7F, 0x40, 0x00, + 0x72, 0x49, 0x49, 0x49, 0x46, + 0x21, 0x41, 0x49, 0x4D, 0x33, + 0x18, 0x14, 0x12, 0x7F, 0x10, + 0x27, 0x45, 0x45, 0x45, 0x39, + 0x3C, 0x4A, 0x49, 0x49, 0x31, + 0x41, 0x21, 0x11, 0x09, 0x07, + 0x36, 0x49, 0x49, 0x49, 0x36, + 0x46, 0x49, 0x49, 0x29, 0x1E, + 0x00, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x40, 0x34, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x41, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x00, 0x41, 0x22, 0x14, 0x08, + 0x02, 0x01, 0x59, 0x09, 0x06, + 0x3E, 0x41, 0x5D, 0x59, 0x4E, + 0x7C, 0x12, 0x11, 0x12, 0x7C, + 0x7F, 0x49, 0x49, 0x49, 0x36, + 0x3E, 0x41, 0x41, 0x41, 0x22, + 0x7F, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x49, 0x49, 0x49, 0x41, + 0x7F, 0x09, 0x09, 0x09, 0x01, + 0x3E, 0x41, 0x41, 0x51, 0x73, + 0x7F, 0x08, 0x08, 0x08, 0x7F, + 0x00, 0x41, 0x7F, 0x41, 0x00, + 0x20, 0x40, 0x41, 0x3F, 0x01, + 0x7F, 0x08, 0x14, 0x22, 0x41, + 0x7F, 0x40, 0x40, 0x40, 0x40, + 0x7F, 0x02, 0x1C, 0x02, 0x7F, + 0x7F, 0x04, 0x08, 0x10, 0x7F, + 0x3E, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x09, 0x09, 0x09, 0x06, + 0x3E, 0x41, 0x51, 0x21, 0x5E, + 0x7F, 0x09, 0x19, 0x29, 0x46, + 0x26, 0x49, 0x49, 0x49, 0x32, + 0x03, 0x01, 0x7F, 0x01, 0x03, + 0x3F, 0x40, 0x40, 0x40, 0x3F, + 0x1F, 0x20, 0x40, 0x20, 0x1F, + 0x3F, 0x40, 0x38, 0x40, 0x3F, + 0x63, 0x14, 0x08, 0x14, 0x63, + 0x03, 0x04, 0x78, 0x04, 0x03, + 0x61, 0x59, 0x49, 0x4D, 0x43, + 0x00, 0x7F, 0x41, 0x41, 0x41, + 0x02, 0x04, 0x08, 0x10, 0x20, + 0x00, 0x41, 0x41, 0x41, 0x7F, + 0x04, 0x02, 0x01, 0x02, 0x04, + 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x03, 0x07, 0x08, 0x00, + 0x20, 0x54, 0x54, 0x78, 0x40, + 0x7F, 0x28, 0x44, 0x44, 0x38, + 0x38, 0x44, 0x44, 0x44, 0x28, + 0x38, 0x44, 0x44, 0x28, 0x7F, + 0x38, 0x54, 0x54, 0x54, 0x18, + 0x00, 0x08, 0x7E, 0x09, 0x02, + 0x18, 0xA4, 0xA4, 0x9C, 0x78, + 0x7F, 0x08, 0x04, 0x04, 0x78, + 0x00, 0x44, 0x7D, 0x40, 0x00, + 0x20, 0x40, 0x40, 0x3D, 0x00, + 0x7F, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x41, 0x7F, 0x40, 0x00, + 0x7C, 0x04, 0x78, 0x04, 0x78, + 0x7C, 0x08, 0x04, 0x04, 0x78, + 0x38, 0x44, 0x44, 0x44, 0x38, + 0xFC, 0x18, 0x24, 0x24, 0x18, + 0x18, 0x24, 0x24, 0x18, 0xFC, + 0x7C, 0x08, 0x04, 0x04, 0x08, + 0x48, 0x54, 0x54, 0x54, 0x24, + 0x04, 0x04, 0x3F, 0x44, 0x24, + 0x3C, 0x40, 0x40, 0x20, 0x7C, + 0x1C, 0x20, 0x40, 0x20, 0x1C, + 0x3C, 0x40, 0x30, 0x40, 0x3C, + 0x44, 0x28, 0x10, 0x28, 0x44, + 0x4C, 0x90, 0x90, 0x90, 0x7C, + 0x44, 0x64, 0x54, 0x4C, 0x44, + 0x00, 0x08, 0x36, 0x41, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, + 0x00, 0x41, 0x36, 0x08, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x02, + 0x3C, 0x26, 0x23, 0x26, 0x3C, + 0x1E, 0xA1, 0xA1, 0x61, 0x12, + 0x3A, 0x40, 0x40, 0x20, 0x7A, + 0x38, 0x54, 0x54, 0x55, 0x59, + 0x21, 0x55, 0x55, 0x79, 0x41, + 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut + 0x21, 0x55, 0x54, 0x78, 0x40, + 0x20, 0x54, 0x55, 0x79, 0x40, + 0x0C, 0x1E, 0x52, 0x72, 0x12, + 0x39, 0x55, 0x55, 0x55, 0x59, + 0x39, 0x54, 0x54, 0x54, 0x59, + 0x39, 0x55, 0x54, 0x54, 0x58, + 0x00, 0x00, 0x45, 0x7C, 0x41, + 0x00, 0x02, 0x45, 0x7D, 0x42, + 0x00, 0x01, 0x45, 0x7C, 0x40, + 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut + 0xF0, 0x28, 0x25, 0x28, 0xF0, + 0x7C, 0x54, 0x55, 0x45, 0x00, + 0x20, 0x54, 0x54, 0x7C, 0x54, + 0x7C, 0x0A, 0x09, 0x7F, 0x49, + 0x32, 0x49, 0x49, 0x49, 0x32, + 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut + 0x32, 0x4A, 0x48, 0x48, 0x30, + 0x3A, 0x41, 0x41, 0x21, 0x7A, + 0x3A, 0x42, 0x40, 0x20, 0x78, + 0x00, 0x9D, 0xA0, 0xA0, 0x7D, + 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut + 0x3D, 0x40, 0x40, 0x40, 0x3D, + 0x3C, 0x24, 0xFF, 0x24, 0x24, + 0x48, 0x7E, 0x49, 0x43, 0x66, + 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, + 0xFF, 0x09, 0x29, 0xF6, 0x20, + 0xC0, 0x88, 0x7E, 0x09, 0x03, + 0x20, 0x54, 0x54, 0x79, 0x41, + 0x00, 0x00, 0x44, 0x7D, 0x41, + 0x30, 0x48, 0x48, 0x4A, 0x32, + 0x38, 0x40, 0x40, 0x22, 0x7A, + 0x00, 0x7A, 0x0A, 0x0A, 0x72, + 0x7D, 0x0D, 0x19, 0x31, 0x7D, + 0x26, 0x29, 0x29, 0x2F, 0x28, + 0x26, 0x29, 0x29, 0x29, 0x26, + 0x30, 0x48, 0x4D, 0x40, 0x20, + 0x38, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x38, + 0x2F, 0x10, 0xC8, 0xAC, 0xBA, + 0x2F, 0x10, 0x28, 0x34, 0xFA, + 0x00, 0x00, 0x7B, 0x00, 0x00, + 0x08, 0x14, 0x2A, 0x14, 0x22, + 0x22, 0x14, 0x2A, 0x14, 0x08, + 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code + 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block + 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block + 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x10, 0x10, 0x10, 0xFF, 0x00, + 0x14, 0x14, 0x14, 0xFF, 0x00, + 0x10, 0x10, 0xFF, 0x00, 0xFF, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x14, 0x14, 0x14, 0xFC, 0x00, + 0x14, 0x14, 0xF7, 0x00, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x14, 0x14, 0xF4, 0x04, 0xFC, + 0x14, 0x14, 0x17, 0x10, 0x1F, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0x1F, 0x00, + 0x10, 0x10, 0x10, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0xF0, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0xFF, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x14, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x1F, 0x10, 0x17, + 0x00, 0x00, 0xFC, 0x04, 0xF4, + 0x14, 0x14, 0x17, 0x10, 0x17, + 0x14, 0x14, 0xF4, 0x04, 0xF4, + 0x00, 0x00, 0xFF, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0xF7, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x17, 0x14, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0xF4, 0x14, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x00, 0x00, 0x1F, 0x10, 0x1F, + 0x00, 0x00, 0x00, 0x1F, 0x14, + 0x00, 0x00, 0x00, 0xFC, 0x14, + 0x00, 0x00, 0xF0, 0x10, 0xF0, + 0x10, 0x10, 0xFF, 0x10, 0xFF, + 0x14, 0x14, 0x14, 0xFF, 0x14, + 0x10, 0x10, 0x10, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x10, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x38, 0x44, 0x44, 0x38, 0x44, + 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta + 0x7E, 0x02, 0x02, 0x06, 0x06, + 0x02, 0x7E, 0x02, 0x7E, 0x02, + 0x63, 0x55, 0x49, 0x41, 0x63, + 0x38, 0x44, 0x44, 0x3C, 0x04, + 0x40, 0x7E, 0x20, 0x1E, 0x20, + 0x06, 0x02, 0x7E, 0x02, 0x02, + 0x99, 0xA5, 0xE7, 0xA5, 0x99, + 0x1C, 0x2A, 0x49, 0x2A, 0x1C, + 0x4C, 0x72, 0x01, 0x72, 0x4C, + 0x30, 0x4A, 0x4D, 0x4D, 0x30, + 0x30, 0x48, 0x78, 0x48, 0x30, + 0xBC, 0x62, 0x5A, 0x46, 0x3D, + 0x3E, 0x49, 0x49, 0x49, 0x00, + 0x7E, 0x01, 0x01, 0x01, 0x7E, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x44, 0x44, 0x5F, 0x44, 0x44, + 0x40, 0x51, 0x4A, 0x44, 0x40, + 0x40, 0x44, 0x4A, 0x51, 0x40, + 0x00, 0x00, 0xFF, 0x01, 0x03, + 0xE0, 0x80, 0xFF, 0x00, 0x00, + 0x08, 0x08, 0x6B, 0x6B, 0x08, + 0x36, 0x12, 0x36, 0x24, 0x36, + 0x06, 0x0F, 0x09, 0x0F, 0x06, + 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x00, + 0x30, 0x40, 0xFF, 0x01, 0x01, + 0x00, 0x1F, 0x01, 0x01, 0x1E, + 0x00, 0x19, 0x1D, 0x17, 0x12, + 0x00, 0x3C, 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP +}; +#endif // FONT5X7_H diff --git a/lib/Adafruit-GPS-Library/Adafruit_GPS.cpp b/lib/Adafruit-GPS-Library/Adafruit_GPS.cpp new file mode 100644 index 0000000..0c655a3 --- /dev/null +++ b/lib/Adafruit-GPS-Library/Adafruit_GPS.cpp @@ -0,0 +1,500 @@ +/*********************************** +This is our GPS library + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information +All text above must be included in any redistribution +****************************************/ +#ifdef __AVR__ + // Only include software serial on AVR platforms (i.e. not on Due). + #include +#endif +#include + +// how long are max NMEA lines to parse? +#define MAXLINELENGTH 120 + +// we double buffer: read one line in and leave one for the main program +volatile char line1[MAXLINELENGTH]; +volatile char line2[MAXLINELENGTH]; +// our index into filling the current line +volatile uint8_t lineidx=0; +// pointers to the double buffers +volatile char *currentline; +volatile char *lastline; +volatile boolean recvdflag; +volatile boolean inStandbyMode; + + +boolean Adafruit_GPS::parse(char *nmea) { + // do checksum check + + // first look if we even have one + if (nmea[strlen(nmea)-4] == '*') { + uint16_t sum = parseHex(nmea[strlen(nmea)-3]) * 16; + sum += parseHex(nmea[strlen(nmea)-2]); + + // check checksum + for (uint8_t i=1; i < (strlen(nmea)-4); i++) { + sum ^= nmea[i]; + } + if (sum != 0) { + // bad checksum :( + //return false; + } + } + int32_t degree; + long minutes; + char degreebuff[10]; + // look for a few common sentences + if (strstr(nmea, "$GPGGA")) { + // found GGA + char *p = nmea; + // get time + p = strchr(p, ',')+1; + float timef = atof(p); + uint32_t time = timef; + hour = time / 10000; + minute = (time % 10000) / 100; + seconds = (time % 100); + + milliseconds = fmod(timef, 1.0) * 1000; + + // parse out latitude + p = strchr(p, ',')+1; + if (',' != *p) + { + strncpy(degreebuff, p, 2); + p += 2; + degreebuff[2] = '\0'; + degree = atol(degreebuff) * 10000000; + strncpy(degreebuff, p, 2); // minutes + p += 3; // skip decimal point + strncpy(degreebuff + 2, p, 4); + degreebuff[6] = '\0'; + minutes = 50 * atol(degreebuff) / 3; + latitude_fixed = degree + minutes; + latitude = degree / 100000 + minutes * 0.000006F; + latitudeDegrees = (latitude-100*int(latitude/100))/60.0; + latitudeDegrees += int(latitude/100); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + if (p[0] == 'S') latitudeDegrees *= -1.0; + if (p[0] == 'N') lat = 'N'; + else if (p[0] == 'S') lat = 'S'; + else if (p[0] == ',') lat = 0; + else return false; + } + + // parse out longitude + p = strchr(p, ',')+1; + if (',' != *p) + { + strncpy(degreebuff, p, 3); + p += 3; + degreebuff[3] = '\0'; + degree = atol(degreebuff) * 10000000; + strncpy(degreebuff, p, 2); // minutes + p += 3; // skip decimal point + strncpy(degreebuff + 2, p, 4); + degreebuff[6] = '\0'; + minutes = 50 * atol(degreebuff) / 3; + longitude_fixed = degree + minutes; + longitude = degree / 100000 + minutes * 0.000006F; + longitudeDegrees = (longitude-100*int(longitude/100))/60.0; + longitudeDegrees += int(longitude/100); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + if (p[0] == 'W') longitudeDegrees *= -1.0; + if (p[0] == 'W') lon = 'W'; + else if (p[0] == 'E') lon = 'E'; + else if (p[0] == ',') lon = 0; + else return false; + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + fixquality = atoi(p); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + satellites = atoi(p); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + HDOP = atof(p); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + altitude = atof(p); + } + + p = strchr(p, ',')+1; + p = strchr(p, ',')+1; + if (',' != *p) + { + geoidheight = atof(p); + } + return true; + } + if (strstr(nmea, "$GPRMC")) { + // found RMC + char *p = nmea; + + // get time + p = strchr(p, ',')+1; + float timef = atof(p); + uint32_t time = timef; + hour = time / 10000; + minute = (time % 10000) / 100; + seconds = (time % 100); + + milliseconds = fmod(timef, 1.0) * 1000; + + p = strchr(p, ',')+1; + // Serial.println(p); + if (p[0] == 'A') + fix = true; + else if (p[0] == 'V') + fix = false; + else + return false; + + // parse out latitude + p = strchr(p, ',')+1; + if (',' != *p) + { + strncpy(degreebuff, p, 2); + p += 2; + degreebuff[2] = '\0'; + long degree = atol(degreebuff) * 10000000; + strncpy(degreebuff, p, 2); // minutes + p += 3; // skip decimal point + strncpy(degreebuff + 2, p, 4); + degreebuff[6] = '\0'; + long minutes = 50 * atol(degreebuff) / 3; + latitude_fixed = degree + minutes; + latitude = degree / 100000 + minutes * 0.000006F; + latitudeDegrees = (latitude-100*int(latitude/100))/60.0; + latitudeDegrees += int(latitude/100); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + if (p[0] == 'S') latitudeDegrees *= -1.0; + if (p[0] == 'N') lat = 'N'; + else if (p[0] == 'S') lat = 'S'; + else if (p[0] == ',') lat = 0; + else return false; + } + + // parse out longitude + p = strchr(p, ',')+1; + if (',' != *p) + { + strncpy(degreebuff, p, 3); + p += 3; + degreebuff[3] = '\0'; + degree = atol(degreebuff) * 10000000; + strncpy(degreebuff, p, 2); // minutes + p += 3; // skip decimal point + strncpy(degreebuff + 2, p, 4); + degreebuff[6] = '\0'; + minutes = 50 * atol(degreebuff) / 3; + longitude_fixed = degree + minutes; + longitude = degree / 100000 + minutes * 0.000006F; + longitudeDegrees = (longitude-100*int(longitude/100))/60.0; + longitudeDegrees += int(longitude/100); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + if (p[0] == 'W') longitudeDegrees *= -1.0; + if (p[0] == 'W') lon = 'W'; + else if (p[0] == 'E') lon = 'E'; + else if (p[0] == ',') lon = 0; + else return false; + } + // speed + p = strchr(p, ',')+1; + if (',' != *p) + { + speed = atof(p); + } + + // angle + p = strchr(p, ',')+1; + if (',' != *p) + { + angle = atof(p); + } + + p = strchr(p, ',')+1; + if (',' != *p) + { + uint32_t fulldate = atof(p); + day = fulldate / 10000; + month = (fulldate % 10000) / 100; + year = (fulldate % 100); + } + // we dont parse the remaining, yet! + return true; + } + + return false; +} + +char Adafruit_GPS::read(void) { + char c = 0; + + if (paused) return c; + +#ifdef __AVR__ + if(gpsSwSerial) { + if(!gpsSwSerial->available()) return c; + c = gpsSwSerial->read(); + } else +#endif + { + if(!gpsHwSerial->available()) return c; + c = gpsHwSerial->read(); + } + + //Serial.print(c); + +// if (c == '$') { //please don't eat the dollar sign - rdl 9/15/14 +// currentline[lineidx] = 0; +// lineidx = 0; +// } + if (c == '\n') { + currentline[lineidx] = 0; + + if (currentline == line1) { + currentline = line2; + lastline = line1; + } else { + currentline = line1; + lastline = line2; + } + + //Serial.println("----"); + //Serial.println((char *)lastline); + //Serial.println("----"); + lineidx = 0; + recvdflag = true; + } + + currentline[lineidx++] = c; + if (lineidx >= MAXLINELENGTH) + lineidx = MAXLINELENGTH-1; + + return c; +} + +#ifdef __AVR__ +// Constructor when using SoftwareSerial or NewSoftSerial +#if ARDUINO >= 100 +Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser) +#else +Adafruit_GPS::Adafruit_GPS(NewSoftSerial *ser) +#endif +{ + common_init(); // Set everything to common state, then... + gpsSwSerial = ser; // ...override gpsSwSerial with value passed. +} +#endif + +// Constructor when using HardwareSerial +Adafruit_GPS::Adafruit_GPS(HardwareSerial *ser) { + common_init(); // Set everything to common state, then... + gpsHwSerial = ser; // ...override gpsHwSerial with value passed. +} + +// Initialization code used by all constructor types +void Adafruit_GPS::common_init(void) { +#ifdef __AVR__ + gpsSwSerial = NULL; // Set both to NULL, then override correct +#endif + gpsHwSerial = NULL; // port pointer in corresponding constructor + recvdflag = false; + paused = false; + lineidx = 0; + currentline = line1; + lastline = line2; + + hour = minute = seconds = year = month = day = + fixquality = satellites = 0; // uint8_t + lat = lon = mag = 0; // char + fix = false; // boolean + milliseconds = 0; // uint16_t + latitude = longitude = geoidheight = altitude = + speed = angle = magvariation = HDOP = 0.0; // float +} + +void Adafruit_GPS::begin(uint16_t baud) +{ +#ifdef __AVR__ + if(gpsSwSerial) + gpsSwSerial->begin(baud); + else + gpsHwSerial->begin(baud); +#endif + + delay(10); +} + +void Adafruit_GPS::sendCommand(const char *str) { +#ifdef __AVR__ + if(gpsSwSerial) + gpsSwSerial->println(str); + else +#endif + gpsHwSerial->println(str); +} + +boolean Adafruit_GPS::newNMEAreceived(void) { + return recvdflag; +} + +void Adafruit_GPS::pause(boolean p) { + paused = p; +} + +char *Adafruit_GPS::lastNMEA(void) { + recvdflag = false; + return (char *)lastline; +} + +// read a Hex value and return the decimal equivalent +uint8_t Adafruit_GPS::parseHex(char c) { + if (c < '0') + return 0; + if (c <= '9') + return c - '0'; + if (c < 'A') + return 0; + if (c <= 'F') + return (c - 'A')+10; + // if (c > 'F') + return 0; +} + +boolean Adafruit_GPS::waitForSentence(const char *wait4me, uint8_t max) { + char str[20]; + + uint8_t i=0; + while (i < max) { + if (newNMEAreceived()) { + char *nmea = lastNMEA(); + strncpy(str, nmea, 20); + str[19] = 0; + i++; + + if (strstr(str, wait4me)) + return true; + } + } + + return false; +} + +boolean Adafruit_GPS::LOCUS_StartLogger(void) { + sendCommand(PMTK_LOCUS_STARTLOG); + recvdflag = false; + return waitForSentence(PMTK_LOCUS_STARTSTOPACK); +} + +boolean Adafruit_GPS::LOCUS_StopLogger(void) { + sendCommand(PMTK_LOCUS_STOPLOG); + recvdflag = false; + return waitForSentence(PMTK_LOCUS_STARTSTOPACK); +} + +boolean Adafruit_GPS::LOCUS_ReadStatus(void) { + sendCommand(PMTK_LOCUS_QUERY_STATUS); + + if (! waitForSentence("$PMTKLOG")) + return false; + + char *response = lastNMEA(); + uint16_t parsed[10]; + uint8_t i; + + for (i=0; i<10; i++) parsed[i] = -1; + + response = strchr(response, ','); + for (i=0; i<10; i++) { + if (!response || (response[0] == 0) || (response[0] == '*')) + break; + response++; + parsed[i]=0; + while ((response[0] != ',') && + (response[0] != '*') && (response[0] != 0)) { + parsed[i] *= 10; + char c = response[0]; + if (isDigit(c)) + parsed[i] += c - '0'; + else + parsed[i] = c; + response++; + } + } + LOCUS_serial = parsed[0]; + LOCUS_type = parsed[1]; + if (isAlpha(parsed[2])) { + parsed[2] = parsed[2] - 'a' + 10; + } + LOCUS_mode = parsed[2]; + LOCUS_config = parsed[3]; + LOCUS_interval = parsed[4]; + LOCUS_distance = parsed[5]; + LOCUS_speed = parsed[6]; + LOCUS_status = !parsed[7]; + LOCUS_records = parsed[8]; + LOCUS_percent = parsed[9]; + + return true; +} + +// Standby Mode Switches +boolean Adafruit_GPS::standby(void) { + if (inStandbyMode) { + return false; // Returns false if already in standby mode, so that you do not wake it up by sending commands to GPS + } + else { + inStandbyMode = true; + sendCommand(PMTK_STANDBY); + //return waitForSentence(PMTK_STANDBY_SUCCESS); // don't seem to be fast enough to catch the message, or something else just is not working + return true; + } +} + +boolean Adafruit_GPS::wakeup(void) { + if (inStandbyMode) { + inStandbyMode = false; + sendCommand(""); // send byte to wake it up + return waitForSentence(PMTK_AWAKE); + } + else { + return false; // Returns false if not in standby mode, nothing to wakeup + } +} diff --git a/lib/Adafruit-GPS-Library/Adafruit_GPS.h b/lib/Adafruit-GPS-Library/Adafruit_GPS.h new file mode 100644 index 0000000..9ffd993 --- /dev/null +++ b/lib/Adafruit-GPS-Library/Adafruit_GPS.h @@ -0,0 +1,168 @@ +/*********************************** +This is the Adafruit GPS library - the ultimate GPS library +for the ultimate GPS module! + +Tested and works great with the Adafruit Ultimate GPS module +using MTK33x9 chipset + ------> http://www.adafruit.com/products/746 +Pick one up today at the Adafruit electronics shop +and help support open source hardware & software! -ada + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information +All text above must be included in any redistribution +****************************************/ +// Fllybob added lines 34,35 and 40,41 to add 100mHz logging capability + +#ifndef _ADAFRUIT_GPS_H +#define _ADAFRUIT_GPS_H + +#ifdef __AVR__ + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +// different commands to set the update rate from once a second (1 Hz) to 10 times a second (10Hz) +// Note that these only control the rate at which the position is echoed, to actually speed up the +// position fix you must also send one of the position fix rate commands below too. +#define PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ "$PMTK220,10000*2F" // Once every 10 seconds, 100 millihertz. +#define PMTK_SET_NMEA_UPDATE_200_MILLIHERTZ "$PMTK220,5000*1B" // Once every 5 seconds, 200 millihertz. +#define PMTK_SET_NMEA_UPDATE_1HZ "$PMTK220,1000*1F" +#define PMTK_SET_NMEA_UPDATE_5HZ "$PMTK220,200*2C" +#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F" +// Position fix update rate commands. +#define PMTK_API_SET_FIX_CTL_100_MILLIHERTZ "$PMTK300,10000,0,0,0,0*2C" // Once every 10 seconds, 100 millihertz. +#define PMTK_API_SET_FIX_CTL_200_MILLIHERTZ "$PMTK300,5000,0,0,0,0*18" // Once every 5 seconds, 200 millihertz. +#define PMTK_API_SET_FIX_CTL_1HZ "$PMTK300,1000,0,0,0,0*1C" +#define PMTK_API_SET_FIX_CTL_5HZ "$PMTK300,200,0,0,0,0*2F" +// Can't fix position faster than 5 times a second! + + +#define PMTK_SET_BAUD_57600 "$PMTK251,57600*2C" +#define PMTK_SET_BAUD_9600 "$PMTK251,9600*17" + +// turn on only the second sentence (GPRMC) +#define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" +// turn on GPRMC and GGA +#define PMTK_SET_NMEA_OUTPUT_RMCGGA "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" +// turn on ALL THE DATA +#define PMTK_SET_NMEA_OUTPUT_ALLDATA "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28" +// turn off output +#define PMTK_SET_NMEA_OUTPUT_OFF "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" + +// to generate your own sentences, check out the MTK command datasheet and use a checksum calculator +// such as the awesome http://www.hhhh.org/wiml/proj/nmeaxor.html + +#define PMTK_LOCUS_STARTLOG "$PMTK185,0*22" +#define PMTK_LOCUS_STOPLOG "$PMTK185,1*23" +#define PMTK_LOCUS_STARTSTOPACK "$PMTK001,185,3*3C" +#define PMTK_LOCUS_QUERY_STATUS "$PMTK183*38" +#define PMTK_LOCUS_ERASE_FLASH "$PMTK184,1*22" +#define LOCUS_OVERLAP 0 +#define LOCUS_FULLSTOP 1 + +#define PMTK_ENABLE_SBAS "$PMTK313,1*2E" +#define PMTK_ENABLE_WAAS "$PMTK301,2*2E" + +// standby command & boot successful message +#define PMTK_STANDBY "$PMTK161,0*28" +#define PMTK_STANDBY_SUCCESS "$PMTK001,161,3*36" // Not needed currently +#define PMTK_AWAKE "$PMTK010,002*2D" + +// ask for the release and version +#define PMTK_Q_RELEASE "$PMTK605*31" + +// request for updates on antenna status +#define PGCMD_ANTENNA "$PGCMD,33,1*6C" +#define PGCMD_NOANTENNA "$PGCMD,33,0*6D" + +// how long to wait when we're looking for a response +#define MAXWAITSENTENCE 5 + +#if ARDUINO >= 100 + #include "Arduino.h" +#if defined (__AVR__) && !defined(__AVR_ATmega32U4__) + #include "SoftwareSerial.h" +#endif +#else + #include "WProgram.h" + #include "NewSoftSerial.h" +#endif + + +class Adafruit_GPS { + public: + void begin(uint16_t baud); + +#ifdef __AVR__ + #if ARDUINO >= 100 + Adafruit_GPS(SoftwareSerial *ser); // Constructor when using SoftwareSerial + #else + Adafruit_GPS(NewSoftSerial *ser); // Constructor when using NewSoftSerial + #endif +#endif + Adafruit_GPS(HardwareSerial *ser); // Constructor when using HardwareSerial + + char *lastNMEA(void); + boolean newNMEAreceived(); + void common_init(void); + + void sendCommand(const char *); + + void pause(boolean b); + + boolean parseNMEA(char *response); + uint8_t parseHex(char c); + + char read(void); + boolean parse(char *); + void interruptReads(boolean r); + + boolean wakeup(void); + boolean standby(void); + + uint8_t hour, minute, seconds, year, month, day; + uint16_t milliseconds; + // Floating point latitude and longitude value in degrees. + float latitude, longitude; + // Fixed point latitude and longitude value with degrees stored in units of 1/100000 degrees, + // and minutes stored in units of 1/100000 degrees. See pull #13 for more details: + // https://github.com/adafruit/Adafruit-GPS-Library/pull/13 + int32_t latitude_fixed, longitude_fixed; + float latitudeDegrees, longitudeDegrees; + float geoidheight, altitude; + float speed, angle, magvariation, HDOP; + char lat, lon, mag; + boolean fix; + uint8_t fixquality, satellites; + + boolean waitForSentence(const char *wait, uint8_t max = MAXWAITSENTENCE); + boolean LOCUS_StartLogger(void); + boolean LOCUS_StopLogger(void); + boolean LOCUS_ReadStatus(void); + + uint16_t LOCUS_serial, LOCUS_records; + uint8_t LOCUS_type, LOCUS_mode, LOCUS_config, LOCUS_interval, LOCUS_distance, LOCUS_speed, LOCUS_status, LOCUS_percent; + private: + boolean paused; + + uint8_t parseResponse(char *response); +#ifdef __AVR__ + #if ARDUINO >= 100 + SoftwareSerial *gpsSwSerial; + #else + NewSoftSerial *gpsSwSerial; + #endif +#endif + HardwareSerial *gpsHwSerial; +}; + + +#endif diff --git a/lib/Adafruit-TFTLCD/Adafruit_TFTLCD.cpp b/lib/Adafruit-TFTLCD/Adafruit_TFTLCD.cpp new file mode 100644 index 0000000..5639460 --- /dev/null +++ b/lib/Adafruit-TFTLCD/Adafruit_TFTLCD.cpp @@ -0,0 +1,803 @@ +// IMPORTANT: LIBRARY MUST BE SPECIFICALLY CONFIGURED FOR EITHER TFT SHIELD +// OR BREAKOUT BOARD USAGE. SEE RELEVANT COMMENTS IN Adafruit_TFTLCD.h + +// Graphics library by ladyada/adafruit with init code from Rossum +// MIT license + +#include +#include "pins_arduino.h" +#include "wiring_private.h" +#include "Adafruit_TFTLCD.h" +#include "pin_magic.h" + +#define TFTWIDTH 240 +#define TFTHEIGHT 320 + +// LCD controller chip identifiers +#define ID_932X 0 +#define ID_7575 1 +#define ID_UNKNOWN 0xFF + +// Register names from Peter Barrett's Microtouch code +#define ILI932X_START_OSC 0x00 +#define ILI932X_DRIV_OUT_CTRL 0x01 +#define ILI932X_DRIV_WAV_CTRL 0x02 +#define ILI932X_ENTRY_MOD 0x03 +#define ILI932X_RESIZE_CTRL 0x04 +#define ILI932X_DISP_CTRL1 0x07 +#define ILI932X_DISP_CTRL2 0x08 +#define ILI932X_DISP_CTRL3 0x09 +#define ILI932X_DISP_CTRL4 0x0A +#define ILI932X_RGB_DISP_IF_CTRL1 0x0C +#define ILI932X_FRM_MARKER_POS 0x0D +#define ILI932X_RGB_DISP_IF_CTRL2 0x0F +#define ILI932X_POW_CTRL1 0x10 +#define ILI932X_POW_CTRL2 0x11 +#define ILI932X_POW_CTRL3 0x12 +#define ILI932X_POW_CTRL4 0x13 +#define ILI932X_GRAM_HOR_AD 0x20 +#define ILI932X_GRAM_VER_AD 0x21 +#define ILI932X_RW_GRAM 0x22 +#define ILI932X_POW_CTRL7 0x29 +#define ILI932X_FRM_RATE_COL_CTRL 0x2B +#define ILI932X_GAMMA_CTRL1 0x30 +#define ILI932X_GAMMA_CTRL2 0x31 +#define ILI932X_GAMMA_CTRL3 0x32 +#define ILI932X_GAMMA_CTRL4 0x35 +#define ILI932X_GAMMA_CTRL5 0x36 +#define ILI932X_GAMMA_CTRL6 0x37 +#define ILI932X_GAMMA_CTRL7 0x38 +#define ILI932X_GAMMA_CTRL8 0x39 +#define ILI932X_GAMMA_CTRL9 0x3C +#define ILI932X_GAMMA_CTRL10 0x3D +#define ILI932X_HOR_START_AD 0x50 +#define ILI932X_HOR_END_AD 0x51 +#define ILI932X_VER_START_AD 0x52 +#define ILI932X_VER_END_AD 0x53 +#define ILI932X_GATE_SCAN_CTRL1 0x60 +#define ILI932X_GATE_SCAN_CTRL2 0x61 +#define ILI932X_GATE_SCAN_CTRL3 0x6A +#define ILI932X_PART_IMG1_DISP_POS 0x80 +#define ILI932X_PART_IMG1_START_AD 0x81 +#define ILI932X_PART_IMG1_END_AD 0x82 +#define ILI932X_PART_IMG2_DISP_POS 0x83 +#define ILI932X_PART_IMG2_START_AD 0x84 +#define ILI932X_PART_IMG2_END_AD 0x85 +#define ILI932X_PANEL_IF_CTRL1 0x90 +#define ILI932X_PANEL_IF_CTRL2 0x92 +#define ILI932X_PANEL_IF_CTRL3 0x93 +#define ILI932X_PANEL_IF_CTRL4 0x95 +#define ILI932X_PANEL_IF_CTRL5 0x97 +#define ILI932X_PANEL_IF_CTRL6 0x98 + +#define HX8347G_COLADDRSTART_HI 0x02 +#define HX8347G_COLADDRSTART_LO 0x03 +#define HX8347G_COLADDREND_HI 0x04 +#define HX8347G_COLADDREND_LO 0x05 +#define HX8347G_ROWADDRSTART_HI 0x06 +#define HX8347G_ROWADDRSTART_LO 0x07 +#define HX8347G_ROWADDREND_HI 0x08 +#define HX8347G_ROWADDREND_LO 0x09 +#define HX8347G_MEMACCESS 0x16 + +// Constructor for breakout board (configurable LCD control lines). +// Can still use this w/shield, but parameters are ignored. +Adafruit_TFTLCD::Adafruit_TFTLCD( + uint8_t cs, uint8_t cd, uint8_t wr, uint8_t rd, uint8_t reset) { + +#ifndef USE_ADAFRUIT_SHIELD_PINOUT + // Convert pin numbers to registers and bitmasks + _reset = reset; + csPort = portOutputRegister(digitalPinToPort(cs)); + cdPort = portOutputRegister(digitalPinToPort(cd)); + wrPort = portOutputRegister(digitalPinToPort(wr)); + rdPort = portOutputRegister(digitalPinToPort(rd)); + csPinSet = digitalPinToBitMask(cs); + cdPinSet = digitalPinToBitMask(cd); + wrPinSet = digitalPinToBitMask(wr); + rdPinSet = digitalPinToBitMask(rd); + csPinUnset = ~csPinSet; + cdPinUnset = ~cdPinSet; + wrPinUnset = ~wrPinSet; + rdPinUnset = ~rdPinSet; + *csPort |= csPinSet; // Set all control bits to HIGH (idle) + *cdPort |= cdPinSet; // Signals are ACTIVE LOW + *wrPort |= wrPinSet; + *rdPort |= rdPinSet; + pinMode(cs, OUTPUT); // Enable outputs + pinMode(cd, OUTPUT); + pinMode(wr, OUTPUT); + pinMode(rd, OUTPUT); + if(reset) { + digitalWrite(reset, HIGH); + pinMode(reset, OUTPUT); + } +#endif + + init(); +} + +// Constructor for shield (fixed LCD control lines) +Adafruit_TFTLCD::Adafruit_TFTLCD(void) { + init(); +} + +// Initialization common to both shield & breakout configs +void Adafruit_TFTLCD::init(void) { + +#ifdef USE_ADAFRUIT_SHIELD_PINOUT + CS_IDLE; // Set all control bits to idle state + WR_IDLE; + RD_IDLE; + CD_DATA; + digitalWrite(5, HIGH); // Reset line + pinMode(A3, OUTPUT); // Enable outputs + pinMode(A2, OUTPUT); + pinMode(A1, OUTPUT); + pinMode(A0, OUTPUT); + pinMode( 5, OUTPUT); +#endif + + setWriteDir(); // Set up LCD data port(s) for WRITE operations + + rotation = 0; + cursor_y = cursor_x = 0; + textsize = 1; + textcolor = 0xFFFF; + _width = TFTWIDTH; + _height = TFTHEIGHT; +} + +// Initialization command tables for different LCD controllers +#define TFTLCD_DELAY 0xFF +static const uint8_t HX8347G_regValues[] PROGMEM = { + 0x2E , 0x89, + 0x29 , 0x8F, + 0x2B , 0x02, + 0xE2 , 0x00, + 0xE4 , 0x01, + 0xE5 , 0x10, + 0xE6 , 0x01, + 0xE7 , 0x10, + 0xE8 , 0x70, + 0xF2 , 0x00, + 0xEA , 0x00, + 0xEB , 0x20, + 0xEC , 0x3C, + 0xED , 0xC8, + 0xE9 , 0x38, + 0xF1 , 0x01, + + // skip gamma, do later + + 0x1B , 0x1A, + 0x1A , 0x02, + 0x24 , 0x61, + 0x25 , 0x5C, + + 0x18 , 0x36, + 0x19 , 0x01, + 0x1F , 0x88, + TFTLCD_DELAY , 5 , // delay 5 ms + 0x1F , 0x80, + TFTLCD_DELAY , 5 , + 0x1F , 0x90, + TFTLCD_DELAY , 5 , + 0x1F , 0xD4, + TFTLCD_DELAY , 5 , + 0x17 , 0x05, + + 0x36 , 0x09, + 0x28 , 0x38, + TFTLCD_DELAY , 40 , + 0x28 , 0x3C, + + 0x02 , 0x00, + 0x03 , 0x00, + 0x04 , 0x00, + 0x05 , 0xEF, + 0x06 , 0x00, + 0x07 , 0x00, + 0x08 , 0x01, + 0x09 , 0x3F +}; + +static const uint16_t ILI932x_regValues[] PROGMEM = { + ILI932X_START_OSC , 0x0001, // Start oscillator + TFTLCD_DELAY , 50, // 50 millisecond delay + ILI932X_DRIV_OUT_CTRL , 0x0100, + ILI932X_DRIV_WAV_CTRL , 0x0700, + ILI932X_ENTRY_MOD , 0x1030, + ILI932X_RESIZE_CTRL , 0x0000, + ILI932X_DISP_CTRL2 , 0x0202, + ILI932X_DISP_CTRL3 , 0x0000, + ILI932X_DISP_CTRL4 , 0x0000, + ILI932X_RGB_DISP_IF_CTRL1, 0x0, + ILI932X_FRM_MARKER_POS , 0x0, + ILI932X_RGB_DISP_IF_CTRL2, 0x0, + ILI932X_POW_CTRL1 , 0x0000, + ILI932X_POW_CTRL2 , 0x0007, + ILI932X_POW_CTRL3 , 0x0000, + ILI932X_POW_CTRL4 , 0x0000, + TFTLCD_DELAY , 200, + ILI932X_POW_CTRL1 , 0x1690, + ILI932X_POW_CTRL2 , 0x0227, + TFTLCD_DELAY , 50, + ILI932X_POW_CTRL3 , 0x001A, + TFTLCD_DELAY , 50, + ILI932X_POW_CTRL4 , 0x1800, + ILI932X_POW_CTRL7 , 0x002A, + TFTLCD_DELAY , 50, + ILI932X_GAMMA_CTRL1 , 0x0000, + ILI932X_GAMMA_CTRL2 , 0x0000, + ILI932X_GAMMA_CTRL3 , 0x0000, + ILI932X_GAMMA_CTRL4 , 0x0206, + ILI932X_GAMMA_CTRL5 , 0x0808, + ILI932X_GAMMA_CTRL6 , 0x0007, + ILI932X_GAMMA_CTRL7 , 0x0201, + ILI932X_GAMMA_CTRL8 , 0x0000, + ILI932X_GAMMA_CTRL9 , 0x0000, + ILI932X_GAMMA_CTRL10 , 0x0000, + ILI932X_GRAM_HOR_AD , 0x0000, + ILI932X_GRAM_VER_AD , 0x0000, + ILI932X_HOR_START_AD , 0x0000, + ILI932X_HOR_END_AD , 0x00EF, + ILI932X_VER_START_AD , 0X0000, + ILI932X_VER_END_AD , 0x013F, + ILI932X_GATE_SCAN_CTRL1 , 0xA700, // Driver Output Control (R60h) + ILI932X_GATE_SCAN_CTRL2 , 0x0003, // Driver Output Control (R61h) + ILI932X_GATE_SCAN_CTRL3 , 0x0000, // Driver Output Control (R62h) + ILI932X_PANEL_IF_CTRL1 , 0X0010, // Panel Interface Control 1 (R90h) + ILI932X_PANEL_IF_CTRL2 , 0X0000, + ILI932X_PANEL_IF_CTRL3 , 0X0003, + ILI932X_PANEL_IF_CTRL4 , 0X1100, + ILI932X_PANEL_IF_CTRL5 , 0X0000, + ILI932X_PANEL_IF_CTRL6 , 0X0000, + ILI932X_DISP_CTRL1 , 0x0133, // Main screen turn on +}; + +void Adafruit_TFTLCD::begin(uint16_t id) { + uint8_t i = 0; + + constructor(TFTWIDTH, TFTHEIGHT); + + reset(); + + if((id == 0x9325) || (id == 0x9328)) { + + uint16_t a, d; + driver = ID_932X; + CS_ACTIVE; + while(i < sizeof(ILI932x_regValues) / sizeof(uint16_t)) { + a = pgm_read_word(&ILI932x_regValues[i++]); + d = pgm_read_word(&ILI932x_regValues[i++]); + if(a == TFTLCD_DELAY) delay(d); + else writeRegister16(a, d); + } + setRotation(rotation); + setAddrWindow(0, 0, TFTWIDTH-1, TFTHEIGHT-1); + + } else if(id == 0x7575) { + + uint8_t a, d; + driver = ID_7575; + CS_ACTIVE; + while(i < sizeof(HX8347G_regValues)) { + a = pgm_read_byte(&HX8347G_regValues[i++]); + d = pgm_read_byte(&HX8347G_regValues[i++]); + if(a == TFTLCD_DELAY) delay(d); + else writeRegister8(a, d); + } + setRotation(rotation); + setLR(); // Lower-right corner of address window + + } else { + driver = ID_UNKNOWN; + return; + } +} + +void Adafruit_TFTLCD::reset(void) { + + CS_IDLE; + CD_DATA; + WR_IDLE; + RD_IDLE; + +#ifdef USE_ADAFRUIT_SHIELD_PINOUT + digitalWrite(5, LOW); + delay(2); + digitalWrite(5, HIGH); +#else + if(_reset) { + digitalWrite(_reset, LOW); + delay(2); + digitalWrite(_reset, HIGH); + } +#endif + + CS_ACTIVE; + CD_DATA; + write8(0x00); + for(uint8_t i=0; i<7; i++) WR_STROBE; + CS_IDLE; +delay(100); +} + +// Sets the LCD address window (and address counter, on 932X). +// Relevant to rect/screen fills and H/V lines. Input coordinates are +// assumed pre-sorted (e.g. x2 >= x1). +void Adafruit_TFTLCD::setAddrWindow(int x1, int y1, int x2, int y2) { + + CS_ACTIVE; + if(driver == ID_932X) { + + // Values passed are in current (possibly rotated) coordinate + // system. 932X requires hardware-native coords regardless of + // MADCTL, so rotate inputs as needed. The address counter is + // set to the top-left corner -- although fill operations can be + // done in any direction, the current screen rotation is applied + // because some users find it disconcerting when a fill does not + // occur top-to-bottom. + int x, y, t; + switch(rotation) { + default: + x = x1; + y = y1; + break; + case 1: + t = y1; + y1 = x1; + x1 = TFTWIDTH - 1 - y2; + y2 = x2; + x2 = TFTWIDTH - 1 - t; + x = x2; + y = y1; + break; + case 2: + t = x1; + x1 = TFTWIDTH - 1 - x2; + x2 = TFTWIDTH - 1 - t; + t = y1; + y1 = TFTHEIGHT - 1 - y2; + y2 = TFTHEIGHT - 1 - t; + x = x2; + y = y2; + break; + case 3: + t = x1; + x1 = y1; + y1 = TFTHEIGHT - 1 - x2; + x2 = y2; + y2 = TFTHEIGHT - 1 - t; + x = x1; + y = y2; + break; + } + writeRegister16(0x0050, x1); // Set address window + writeRegister16(0x0051, x2); + writeRegister16(0x0052, y1); + writeRegister16(0x0053, y2); + writeRegister16(0x0020, x ); // Set address counter to top left + writeRegister16(0x0021, y ); + + } else if(driver == ID_7575) { + + writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x1); + writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y1); + writeRegisterPair(HX8347G_COLADDREND_HI , HX8347G_COLADDREND_LO , x2); + writeRegisterPair(HX8347G_ROWADDREND_HI , HX8347G_ROWADDREND_LO , y2); + + } + CS_IDLE; +} + +// Unlike the 932X drivers that set the address window to the full screen +// by default (using the address counter for drawPixel operations), the +// 7575 needs the address window set on all graphics operations. In order +// to save a few register writes on each pixel drawn, the lower-right +// corner of the address window is reset after most fill operations, so +// that drawPixel only needs to change the upper left each time. +void Adafruit_TFTLCD::setLR(void) { + CS_ACTIVE; + writeRegisterPair(HX8347G_COLADDREND_HI, HX8347G_COLADDREND_LO, _width - 1); + writeRegisterPair(HX8347G_ROWADDREND_HI, HX8347G_ROWADDREND_LO, _height - 1); + CS_IDLE; +} + +// Fast block fill operation for fillScreen, fillRect, H/V line, etc. +// Requires setAddrWindow() has previously been called to set the fill +// bounds. 'len' is inclusive, MUST be >= 1. +void Adafruit_TFTLCD::flood(uint16_t color, uint32_t len) { + uint16_t blocks; + uint8_t i, hi = color >> 8, + lo = color; + + CS_ACTIVE; + CD_COMMAND; + if(driver == ID_932X) write8(0x00); // High byte of GRAM register... + write8(0x22); // Write data to GRAM + + // Write first pixel normally, decrement counter by 1 + CD_DATA; + write8(hi); + write8(lo); + len--; + + blocks = (uint16_t)(len / 64); // 64 pixels/block + if(hi == lo) { + // High and low bytes are identical. Leave prior data + // on the port(s) and just toggle the write strobe. + while(blocks--) { + i = 16; // 64 pixels/block / 4 pixels/pass + do { + WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // 2 bytes/pixel + WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // x 4 pixels + } while(--i); + } + // Fill any remaining pixels (1 to 64) + for(i = (uint8_t)len & 63; i--; ) { + WR_STROBE; + WR_STROBE; + } + } else { + while(blocks--) { + i = 16; // 64 pixels/block / 4 pixels/pass + do { + write8(hi); write8(lo); write8(hi); write8(lo); + write8(hi); write8(lo); write8(hi); write8(lo); + } while(--i); + } + for(i = (uint8_t)len & 63; i--; ) { + write8(hi); + write8(lo); + } + } + CS_IDLE; +} + +void Adafruit_TFTLCD::drawFastHLine(int16_t x, int16_t y, int16_t length, + uint16_t color) +{ + int16_t x2; + + // Initial off-screen clipping + if((length <= 0 ) || + (y < 0 ) || ( y >= _height) || + (x >= _width) || ((x2 = (x+length-1)) < 0 )) return; + + if(x < 0) { // Clip left + length += x; + x = 0; + } + if(x2 >= _width) { // Clip right + x2 = _width - 1; + length = x2 - x + 1; + } + + setAddrWindow(x, y, x2, y); + flood(color, length); + if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1); + else setLR(); +} + +void Adafruit_TFTLCD::drawFastVLine(int16_t x, int16_t y, int16_t length, + uint16_t color) +{ + int16_t y2; + + // Initial off-screen clipping + if((length <= 0 ) || + (x < 0 ) || ( x >= _width) || + (y >= _height) || ((y2 = (y+length-1)) < 0 )) return; + if(y < 0) { // Clip top + length += y; + y = 0; + } + if(y2 >= _height) { // Clip bottom + y2 = _height - 1; + length = y2 - y + 1; + } + + setAddrWindow(x, y, x, y2); + flood(color, length); + if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1); + else setLR(); +} + +void Adafruit_TFTLCD::fillRect(int16_t x1, int16_t y1, int16_t w, int16_t h, + uint16_t fillcolor) { + int16_t x2, y2; + + // Initial off-screen clipping + if( (w <= 0 ) || (h <= 0 ) || + (x1 >= _width) || (y1 >= _height) || + ((x2 = x1+w-1) < 0 ) || ((y2 = y1+h-1) < 0 )) return; + if(x1 < 0) { // Clip left + w += x1; + x1 = 0; + } + if(y1 < 0) { // Clip top + h += y1; + y1 = 0; + } + if(x2 >= _width) { // Clip right + x2 = _width - 1; + w = x2 - x1 + 1; + } + if(y2 >= _height) { // Clip bottom + y2 = _height - 1; + h = y2 - y1 + 1; + } + + setAddrWindow(x1, y1, x2, y2); + flood(fillcolor, (uint32_t)w * (uint32_t)h); + if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1); + else setLR(); +} + +void Adafruit_TFTLCD::fillScreen(uint16_t color) { + + if(driver == ID_932X) { + + // For the 932X, a full-screen address window is already the default + // state, just need to set the address pointer to the top-left corner. + // Although we could fill in any direction, the code uses the current + // screen rotation because some users find it disconcerting when a + // fill does not occur top-to-bottom. + uint16_t x, y; + switch(rotation) { + default: x = 0 ; y = 0 ; break; + case 1 : x = TFTWIDTH - 1; y = 0 ; break; + case 2 : x = TFTWIDTH - 1; y = TFTHEIGHT - 1; break; + case 3 : x = 0 ; y = TFTHEIGHT - 1; break; + } + CS_ACTIVE; + writeRegister16(0x0020, x); + writeRegister16(0x0021, y); + + } else if(driver == ID_7575) { + + // For the 7575, there is no settable address pointer, instead the + // address window must be set for each drawing operation. However, + // this display takes rotation into account for the parameters, no + // need to do extra rotation math here. + setAddrWindow(0, 0, _width - 1, _height - 1); + + } + flood(color, (long)TFTWIDTH * (long)TFTHEIGHT); +} + +void Adafruit_TFTLCD::drawPixel(int16_t x, int16_t y, uint16_t color) { + + // Clip + if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return; + + CS_ACTIVE; + if(driver == ID_932X) { + int16_t t; + switch(rotation) { + case 1: + t = x; + x = TFTWIDTH - 1 - y; + y = t; + break; + case 2: + x = TFTWIDTH - 1 - x; + y = TFTHEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = TFTHEIGHT - 1 - t; + break; + } + writeRegister16(0x0020, x); + writeRegister16(0x0021, y); + writeRegister16(0x0022, color); + + } else if(driver == ID_7575) { + + uint8_t hi, lo; + switch(rotation) { + default: lo = 0 ; break; + case 1 : lo = 0x60; break; + case 2 : lo = 0xc0; break; + case 3 : lo = 0xa0; break; + } + writeRegister8( HX8347G_MEMACCESS , lo); + // Only upper-left is set -- bottom-right is full screen default + writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x); + writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y); + hi = color >> 8; lo = color; + CD_COMMAND; write8(0x22); CD_DATA; write8(hi); write8(lo); + + } + CS_IDLE; +} + +// Issues 'raw' an array of 16-bit color values to the LCD; used +// externally by BMP examples. Assumes that setWindowAddr() has +// previously been set to define the bounds. Max 255 pixels at +// a time (BMP examples read in small chunks due to limited RAM). +void Adafruit_TFTLCD::pushColors(uint16_t *data, uint8_t len, boolean first) { + uint16_t color; + uint8_t hi, lo; + CS_ACTIVE; + if(first == true) { // Issue GRAM write command only on first call + CD_COMMAND; + if(driver == ID_932X) write8(0x00); + write8(0x22); + } + CD_DATA; + while(len--) { + color = *data++; + hi = color >> 8; // Don't simplify or merge these + lo = color; // lines, there's macro shenanigans + write8(hi); // going on. + write8(lo); + } + CS_IDLE; +} + +void Adafruit_TFTLCD::setRotation(uint8_t x) { + + // Call parent rotation func first -- sets up rotation flags, etc. + Adafruit_GFX::setRotation(x); + // Then perform hardware-specific rotation operations... + + CS_ACTIVE; + if(driver == ID_932X) { + + uint16_t t; + switch(rotation) { + default: t = 0x1030; break; + case 1 : t = 0x1028; break; + case 2 : t = 0x1000; break; + case 3 : t = 0x1018; break; + } + writeRegister16(0x0003, t ); // MADCTL + // For 932X, init default full-screen address window: + setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here + + } if(driver == ID_7575) { + + uint8_t t; + switch(rotation) { + default: t = 0 ; break; + case 1 : t = 0x60; break; + case 2 : t = 0xc0; break; + case 3 : t = 0xa0; break; + } + writeRegister8(HX8347G_MEMACCESS, t); + // 7575 has to set the address window on most drawing operations. + // drawPixel() cheats by setting only the top left...by default, + // the lower right is always reset to the corner. + setLR(); // CS_IDLE happens here + + } +} + +// Because this function is used infrequently, it configures the ports for +// the read operation, reads the data, then restores the ports to the write +// configuration. Write operations happen a LOT, so it's advantageous to +// leave the ports in that state as a default. +uint16_t Adafruit_TFTLCD::readPixel(int16_t x, int16_t y) { + + uint16_t c; + + if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return 0; + + CS_ACTIVE; + if(driver == ID_932X) { + + int16_t t; + switch(rotation) { + case 1: + t = x; + x = TFTWIDTH - 1 - y; + y = t; + break; + case 2: + x = TFTWIDTH - 1 - x; + y = TFTHEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = TFTHEIGHT - 1 - t; + break; + } + writeRegister16(0x0020, x); + writeRegister16(0x0021, y); + CD_COMMAND; write8(0x00); write8(0x22); // Read data from GRAM + } else if(driver == ID_7575) { + writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x); + writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y); + CD_COMMAND; write8(0x22); // Read data from GRAM + } + + setReadDir(); // Set up LCD data port(s) for READ operations + CD_DATA; + c = read8(); // Do not merge or otherwise simplify + c <<= 8; // these lines. It's an unfortunate + delayMicroseconds(1); // artifact of the macro substitution + c |= read8(); // shenanigans that are going on. + setWriteDir(); // Restore LCD data port(s) to WRITE configuration + CS_IDLE; + + return c; +} + +// Ditto with the read/write port directions, as above. +uint16_t Adafruit_TFTLCD::readID(void) { + + uint16_t id; + + CS_ACTIVE; + CD_COMMAND; + write8(0x00); + WR_STROBE; // Extra strobe because high, low bytes are the same + setReadDir(); // Set up LCD data port(s) for READ operations + CD_DATA; + id = read8(); // Do not merge or otherwise simplify + id <<= 8; // these lines. It's an unfortunate + delayMicroseconds(1); // artifact of the macro substitution + id |= read8(); // shenanigans that are going on. + CS_IDLE; + setWriteDir(); // Restore LCD data port(s) to WRITE configuration + + return id; +} + +// Pass 8-bit (each) R,G,B, get back 16-bit packed color +uint16_t Adafruit_TFTLCD::color565(uint8_t r, uint8_t g, uint8_t b) { + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); +} + +// For I/O macros that were left undefined, declare function +// versions that reference the inline macros just once: + +#ifndef write8 +void Adafruit_TFTLCD::write8(uint8_t value) { + write8inline(value); +} +#endif + +#ifndef read8 +uint8_t Adafruit_TFTLCD::read8(void) { + // Do not merge or simplify -- macro shenanigans going on! + uint8_t d = read8inline(); + return d; +} +#endif + +#ifndef setWriteDir +void Adafruit_TFTLCD::setWriteDir(void) { + setWriteDirInline(); +} +#endif + +#ifndef setReadDir +void Adafruit_TFTLCD::setReadDir(void) { + setReadDirInline(); +} +#endif + +#ifndef writeRegister8 +void Adafruit_TFTLCD::writeRegister8(uint8_t a, uint8_t d) { + writeRegister8inline(a, d); +} +#endif + +#ifndef writeRegister16 +void Adafruit_TFTLCD::writeRegister16(uint16_t a, uint16_t d) { + writeRegister16inline(a, d); +} +#endif + +#ifndef writeRegisterPair +void Adafruit_TFTLCD::writeRegisterPair(uint8_t aH, uint8_t aL, uint16_t d) { + writeRegisterPairInline(aH, aL, d); +} +#endif diff --git a/lib/Adafruit-TFTLCD/Adafruit_TFTLCD.h b/lib/Adafruit-TFTLCD/Adafruit_TFTLCD.h new file mode 100644 index 0000000..2396078 --- /dev/null +++ b/lib/Adafruit-TFTLCD/Adafruit_TFTLCD.h @@ -0,0 +1,88 @@ +// IMPORTANT: SEE COMMENTS @ LINE 15 REGARDING SHIELD VS BREAKOUT BOARD USAGE. + +// Graphics library by ladyada/adafruit with init code from Rossum +// MIT license + +#ifndef _ADAFRUIT_TFTLCD_H_ +#define _ADAFRUIT_TFTLCD_H_ + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif +#include "Adafruit_GFX.h" + +// **** IF USING THE LCD BREAKOUT BOARD, COMMENT OUT THIS NEXT LINE. **** +// **** IF USING THE LCD SHIELD, LEAVE THE LINE ENABLED: **** +#define USE_ADAFRUIT_SHIELD_PINOUT + +class Adafruit_TFTLCD : public Adafruit_GFX { + + public: + + Adafruit_TFTLCD(uint8_t cs, uint8_t cd, uint8_t wr, uint8_t rd, uint8_t rst); + Adafruit_TFTLCD(void); + + void begin(uint16_t id = 0x9325), + drawPixel(int16_t x, int16_t y, uint16_t color), + drawFastHLine(int16_t x0, int16_t y0, int16_t w, uint16_t color), + drawFastVLine(int16_t x0, int16_t y0, int16_t h, uint16_t color), + fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t c), + fillScreen(uint16_t color), + reset(void), + setRegisters8(uint8_t *ptr, uint8_t n), + setRegisters16(uint16_t *ptr, uint8_t n), + setRotation(uint8_t x), + // These methods are public in order for BMP examples to work: + setAddrWindow(int x1, int y1, int x2, int y2), + pushColors(uint16_t *data, uint8_t len, boolean first); + +#ifndef read8 + uint8_t read8(void); // See notes below re: macros +#endif + + uint16_t color565(uint8_t r, uint8_t g, uint8_t b), + readPixel(int16_t x, int16_t y), + readID(void); + + private: + + void init(), + // These items may have previously been defined as macros + // in pin_magic.h. If not, function versions are declared: +#ifndef write8 + write8(uint8_t value), +#endif +#ifndef setWriteDir + setWriteDir(void), +#endif +#ifndef setReadDir + setReadDir(void), +#endif +#ifndef writeRegister8 + writeRegister8(uint8_t a, uint8_t d), +#endif +#ifndef writeRegister16 + writeRegister16(uint16_t a, uint16_t d), +#endif +#ifndef writeRegisterPair + writeRegisterPair(uint8_t aH, uint8_t aL, uint16_t d), +#endif + setLR(void), + flood(uint16_t color, uint32_t len); + uint8_t driver; +#ifndef USE_ADAFRUIT_SHIELD_PINOUT + volatile uint8_t *csPort , *cdPort , *wrPort , *rdPort; + uint8_t csPinSet , cdPinSet , wrPinSet , rdPinSet , + csPinUnset, cdPinUnset, wrPinUnset, rdPinUnset, + _reset; +#endif +}; + +// For compatibility with sketches written for older versions of library. +// Color function name was changed to 'color565' for parity with 2.2" LCD +// library. +#define Color565 color565 + +#endif diff --git a/lib/Adafruit-TFTLCD/pin_magic.h b/lib/Adafruit-TFTLCD/pin_magic.h new file mode 100644 index 0000000..275dc3c --- /dev/null +++ b/lib/Adafruit-TFTLCD/pin_magic.h @@ -0,0 +1,284 @@ +#ifndef _pin_magic_ +#define _pin_magic_ + +// This header file serves two purposes: +// +// 1) Isolate non-portable MCU port- and pin-specific identifiers and +// operations so the library code itself remains somewhat agnostic +// (PORTs and pin numbers are always referenced through macros). +// +// 2) GCC doesn't always respect the "inline" keyword, so this is a +// ham-fisted manner of forcing the issue to minimize function calls. +// This sometimes makes the library a bit bigger than before, but fast++. +// However, because they're macros, we need to be SUPER CAREFUL about +// parameters -- for example, write8(x) may expand to multiple PORT +// writes that all refer to x, so it needs to be a constant or fixed +// variable and not something like *ptr++ (which, after macro +// expansion, may increment the pointer repeatedly and run off into +// la-la land). Macros also give us fune-grained control over which +// operations are inlined on which boards (balancing speed against +// available program space). + +// When using the TFT shield, control and data pins exist in set physical +// locations, but the ports and bitmasks corresponding to each vary among +// boards. A separate set of pin definitions is given for each supported +// board type. +// When using the TFT breakout board, control pins are configurable but +// the data pins are still fixed -- making every data pin configurable +// would be much too slow. The data pin layouts are not the same between +// the shield and breakout configurations -- for the latter, pins were +// chosen to keep the tutorial wiring manageable more than making optimal +// use of ports and bitmasks. So there's a second set of pin definitions +// given for each supported board. + +// Shield pin usage: +// LCD Data Bit : 7 6 5 4 3 2 1 0 +// Digital pin #: 7 6 13 4 11 10 9 8 +// Uno port/pin : PD7 PD6 PB5 PD4 PB3 PB2 PB1 PB0 +// Mega port/pin: PH4 PH3 PB7 PG5 PB5 PB4 PH6 PH5 +// Leo port/pin : PE6 PD7 PC7 PD4 PB7 PB6 PB5 PB4 +// Breakout pin usage: +// LCD Data Bit : 7 6 5 4 3 2 1 0 +// Uno dig. pin : 7 6 5 4 3 2 9 8 +// Uno port/pin : PD7 PD6 PD5 PD4 PD3 PD2 PB1 PB0 +// Mega dig. pin: 29 28 27 26 25 24 23 22 +// Mega port/pin: PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0 (one contiguous PORT) +// Leo dig. pin : 7 6 5 4 3 2 9 8 +// Leo port/pin : PE6 PD7 PC6 PD4 PD0 PD1 PB5 PB4 + +#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined (__AVR_ATmega328__) || defined(__AVR_ATmega8__) + + // Arduino Uno, Duemilanove, etc. + + #ifdef USE_ADAFRUIT_SHIELD_PINOUT + + // LCD control lines: + // RD (read), WR (write), CD (command/data), CS (chip select) + #define RD_PORT PORTC + #define WR_PORT PORTC + #define CD_PORT PORTC + #define CS_PORT PORTC + #define RD_MASK B00000001 + #define WR_MASK B00000010 + #define CD_MASK B00000100 + #define CS_MASK B00001000 + + // These are macros for I/O operations... + + // Write 8-bit value to LCD data lines + #define write8inline(d) { \ + PORTD = (PORTD & B00101111) | ((d) & B11010000); \ + PORTB = (PORTB & B11010000) | ((d) & B00101111); \ + WR_STROBE; } // STROBEs are defined later + + // Read 8-bit value from LCD data lines + #define read8inline() (RD_STROBE, (PIND & B11010000) | (PINB & B00101111)) + + // These set the PORT directions as required before the write and read + // operations. Because write operations are much more common than reads, + // the data-reading functions in the library code set the PORT(s) to + // input before a read, and restore them back to the write state before + // returning. This avoids having to set it for output inside every + // drawing method. The default state has them initialized for writes. + #define setWriteDirInline() { DDRD |= B11010000; DDRB |= B00101111; } + #define setReadDirInline() { DDRD &= ~B11010000; DDRB &= ~B00101111; } + + #else // Uno w/Breakout board + + #define write8inline(d) { \ + PORTD = (PORTD & B00000011) | ((d) & B11111100); \ + PORTB = (PORTB & B11111100) | ((d) & B00000011); \ + WR_STROBE; } + #define read8inline() (RD_STROBE, (PIND& B11111100)|(PINB& B00000011)) + #define setWriteDirInline() { DDRD|= B11111100; DDRB|= B00000011; } + #define setReadDirInline() { DDRD&=~B11111100; DDRB&=~B00000011; } + + #endif + + // As part of the inline control, macros reference other macros...if any + // of these are left undefined, an equivalent function version (non-inline) + // is declared later. The Uno has a moderate amount of program space, so + // only write8() is inlined -- that one provides the most performance + // benefit, but also generates the most bloat. + #define write8 write8inline + +#elif defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) + + // Arduino Mega, ADK, etc. + + #ifdef USE_ADAFRUIT_SHIELD_PINOUT + + #define RD_PORT PORTF + #define WR_PORT PORTF + #define CD_PORT PORTF + #define CS_PORT PORTF + #define RD_MASK B00000001 + #define WR_MASK B00000010 + #define CD_MASK B00000100 + #define CS_MASK B00001000 + + #define write8inline(d) { \ + PORTH = (PORTH & B10000111)|(((d) & B11000000)>>3)|(((d) & B00000011)<<5); \ + PORTB = (PORTB & B01001111)|(((d) & B00101100)<<2); \ + PORTG = (PORTG & B11011111)|(((d) & B00010000)<<1); \ + WR_STROBE; } + #define read8inline() (RD_STROBE, \ + ((PINH & B00011000) << 3) | ((PINB & B10110000) >> 2) | \ + ((PING & B00100000) >> 1) | ((PINH & B01100000) >> 5)) + #define setWriteDirInline() { \ + DDRH |= B01111000; DDRB |= B10110000; DDRG |= B00100000; } + #define setReadDirInline() { \ + DDRH &= ~B01111000; DDRB &= ~B10110000; DDRG &= ~B00100000; } + + // Strobe is wonky on Mega w/shield. Haven't worked out the underlying + // reason, but an interim kludge is just to use inverted levels. ??? + #define RD_STROBE RD_IDLE, RD_ACTIVE + + #else // Mega w/Breakout board + + #define write8inline(d) { PORTA = (d); WR_STROBE; } + #define read8inline() (RD_STROBE, PINA) + #define setWriteDirInline() DDRA = 0xff + #define setReadDirInline() DDRA = 0 + + #endif + + // All of the functions are inlined on the Arduino Mega. When using the + // breakout board, the macro versions aren't appreciably larger than the + // function equivalents, and they're super simple and fast. When using + // the shield, the macros become pretty complicated...but this board has + // so much code space, the macros are used anyway. If you need to free + // up program space, some macros can be removed, at a minor cost in speed. + #define write8 write8inline + #define read8 read8inline + #define setWriteDir setWriteDirInline + #define setReadDir setReadDirInline + #define writeRegister8 writeRegister8inline + #define writeRegister16 writeRegister16inline + #define writeRegisterPair writeRegisterPairInline + +#elif defined(__AVR_ATmega32U4__) + + // Arduino Leonardo + + #ifdef USE_ADAFRUIT_SHIELD_PINOUT + + #define RD_PORT PORTF + #define WR_PORT PORTF + #define CD_PORT PORTF + #define CS_PORT PORTF + #define RD_MASK B10000000 + #define WR_MASK B01000000 + #define CD_MASK B00100000 + #define CS_MASK B00010000 + + #define write8inline(d) { \ + PORTE = (PORTE & B10111111) | (((d) & B10000000)>>1); \ + PORTD = (PORTD & B01101111) | (((d) & B01000000)<<1) | ((d) & B00010000); \ + PORTC = (PORTC & B01111111) | (((d) & B00100000)<<2); \ + PORTB = (PORTB & B00001111) | (((d) & B00001111)<<4); \ + WR_STROBE; } + #define read8inline() (RD_STROBE, \ + (((PINE & B01000000) << 1) | ((PIND & B10000000) >> 1) | \ + ((PINC & B10000000) >> 2) | ((PINB & B11110000) >> 4) | \ + (PIND & B00010000))) + #define setWriteDirInline() { \ + DDRE |= B01000000; DDRD |= B10010000; \ + DDRC |= B10000000; DDRB |= B11110000; } + #define setReadDirInline() { \ + DDRE &= ~B01000000; DDRD &= ~B10010000; \ + DDRC &= ~B10000000; DDRB &= ~B11110000; } + + #else // Leonardo w/Breakout board + + #define write8inline(d) { \ + uint8_t dr1 = (d) >> 1, dl1 = (d) << 1; \ + PORTE = (PORTE & B10111111) | (dr1 & B01000000); \ + PORTD = (PORTD & B01101100) | (dl1 & B10000000) | (((d) & B00001000)>>3) | \ + (dr1 & B00000010) | ((d) & B00010000); \ + PORTC = (PORTC & B10111111) | (dl1 & B01000000); \ + PORTB = (PORTB & B11001111) |(((d) & B00000011)<<4); \ + WR_STROBE; } + + #define read8inline() (RD_STROBE, \ + (((PINE & B01000000) | (PIND & B00000010)) << 1) | \ + (((PINC & B01000000) | (PIND & B10000000)) >> 1) | \ + ((PIND & B00000001)<<3) | ((PINB & B00110000)>>4) | (PIND & B00010000)) + #define setWriteDirInline() { \ + DDRE |= B01000000; DDRD |= B10010011; \ + DDRC |= B01000000; DDRB |= B00110000; } + #define setReadDirInline() { \ + DDRE &= ~B01000000; DDRD &= ~B10010011; \ + DDRC &= ~B01000000; DDRB &= ~B00110000; } + + #endif + + // On the Leonardo, only the write8() macro is used -- though even that + // might be excessive given the code size and available program space + // on this board. You may need to disable this to get any sizable + // program to compile. + #define write8 write8inline + +#else + + #error "Board type unsupported / not recognized" + +#endif + +// Stuff common to all Arduino board types: + +#ifdef USE_ADAFRUIT_SHIELD_PINOUT + + // Control signals are ACTIVE LOW (idle is HIGH) + // Command/Data: LOW = command, HIGH = data + // These are single-instruction operations and always inline + #define RD_ACTIVE RD_PORT &= ~RD_MASK + #define RD_IDLE RD_PORT |= RD_MASK + #define WR_ACTIVE WR_PORT &= ~WR_MASK + #define WR_IDLE WR_PORT |= WR_MASK + #define CD_COMMAND CD_PORT &= ~CD_MASK + #define CD_DATA CD_PORT |= CD_MASK + #define CS_ACTIVE CS_PORT &= ~CS_MASK + #define CS_IDLE CS_PORT |= CS_MASK + +#else // Breakout board + + // When using the TFT breakout board, control pins are configurable. + #define RD_ACTIVE *rdPort &= rdPinUnset + #define RD_IDLE *rdPort |= rdPinSet + #define WR_ACTIVE *wrPort &= wrPinUnset + #define WR_IDLE *wrPort |= wrPinSet + #define CD_COMMAND *cdPort &= cdPinUnset + #define CD_DATA *cdPort |= cdPinSet + #define CS_ACTIVE *csPort &= csPinUnset + #define CS_IDLE *csPort |= csPinSet + +#endif + +// Data read and write strobes, ~2 instructions and always inline +#ifndef RD_STROBE + #define RD_STROBE RD_ACTIVE, RD_IDLE +#endif +#define WR_STROBE { WR_ACTIVE; WR_IDLE; } + +// These higher-level operations are usually functionalized, +// except on Mega where's there's gobs and gobs of program space. + +// Set value of TFT register: 8-bit address, 8-bit value +#define writeRegister8inline(a, d) { \ + CD_COMMAND; write8(a); CD_DATA; write8(d); } + +// Set value of TFT register: 16-bit address, 16-bit value +// See notes at top about macro expansion, hence hi & lo temp vars +#define writeRegister16inline(a, d) { \ + uint8_t hi, lo; \ + hi = (a) >> 8; lo = (a); CD_COMMAND; write8(hi); write8(lo); \ + hi = (d) >> 8; lo = (d); CD_DATA ; write8(hi); write8(lo); } + +// Set value of 2 TFT registers: Two 8-bit addresses (hi & lo), 16-bit value +#define writeRegisterPairInline(aH, aL, d) { \ + uint8_t hi = (d) >> 8, lo = (d); \ + CD_COMMAND; write8(aH); CD_DATA; write8(hi); \ + CD_COMMAND; write8(aL); CD_DATA; write8(lo); } + +#endif // _pin_magic_ diff --git a/lib/SdFat/MinimumSerial.cpp b/lib/SdFat/MinimumSerial.cpp new file mode 100644 index 0000000..1218566 --- /dev/null +++ b/lib/SdFat/MinimumSerial.cpp @@ -0,0 +1,58 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#include +#if defined(UDR0) || defined(DOXYGEN) +#include "MinimumSerial.h" +const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; +//------------------------------------------------------------------------------ +void MinimumSerial::begin(uint32_t baud) { + uint16_t baud_setting; + // don't worry, the compiler will squeeze out F_CPU != 16000000UL + if ((F_CPU != 16000000UL || baud != 57600) && baud > MIN_2X_BAUD) { + // Double the USART Transmission Speed + UCSR0A = 1 << U2X0; + baud_setting = (F_CPU / 4 / baud - 1) / 2; + } else { + // hardcoded exception for compatibility with the bootloader shipped + // with the Duemilanove and previous boards and the firmware on the 8U2 + // on the Uno and Mega 2560. + UCSR0A = 0; + baud_setting = (F_CPU / 8 / baud - 1) / 2; + } + // assign the baud_setting + UBRR0H = baud_setting >> 8; + UBRR0L = baud_setting; + // enable transmit and receive + UCSR0B |= (1 << TXEN0) | (1 << RXEN0); +} +//------------------------------------------------------------------------------ +int MinimumSerial::read() { + if (UCSR0A & (1 << RXC0)) { + return UDR0; + } + return -1; +} +//------------------------------------------------------------------------------ +size_t MinimumSerial::write(uint8_t b) { + while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} + UDR0 = b; + return 1; +} +#endif // defined(UDR0) || defined(DOXYGEN) diff --git a/lib/SdFat/MinimumSerial.h b/lib/SdFat/MinimumSerial.h new file mode 100644 index 0000000..667668f --- /dev/null +++ b/lib/SdFat/MinimumSerial.h @@ -0,0 +1,50 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#ifndef MinimumSerial_h +#define MinimumSerial_h +#include +//============================================================================== +/** + * \class MinimumSerial + * \brief mini serial class for the %SdFat library. + */ +class MinimumSerial : public Print { + public: + /** + * Set baud rate for serial port zero and enable in non interrupt mode. + * Do not call this function if you use another serial library. + * \param[in] baud rate + */ + void begin(uint32_t baud); + /** + * Unbuffered read + * \return -1 if no character is available or an available character. + */ + int read(); + /** + * Unbuffered write + * + * \param[in] b byte to write. + * \return 1 + */ + size_t write(uint8_t b); + using Print::write; +}; +#endif // MinimumSerial_h diff --git a/lib/SdFat/SdFat.h b/lib/SdFat/SdFat.h new file mode 100644 index 0000000..b6724a4 --- /dev/null +++ b/lib/SdFat/SdFat.h @@ -0,0 +1,341 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#ifndef SdFat_h +#define SdFat_h +/** + * \file + * \brief SdFat class + */ +#include "SdSpiCard.h" +#include "utility/FatLib.h" +//------------------------------------------------------------------------------ +/** SdFat version YYYYMMDD */ +#define SD_FAT_VERSION 20150324 +//============================================================================== +/** + * \class SdBaseFile + * \brief Class for backward compatibility. + */ +class SdBaseFile : public FatFile { + public: + SdBaseFile() {} + /** Create a file object and open it in the current working directory. + * + * \param[in] path A path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of open flags. see + * FatFile::open(FatFile*, const char*, uint8_t). + */ + SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} +}; +#if ENABLE_ARDUINO_FEATURES +/** + * \class SdFile + * \brief Class for backward compatibility. + */ + +class SdFile : public PrintFile { + public: + SdFile() {} + /** Create a file object and open it in the current working directory. + * + * \param[in] path A path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of open flags. see + * FatFile::open(FatFile*, const char*, uint8_t). + */ + SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} +}; +#endif // #if ENABLE_ARDUINO_FEATURES +/** + * \class SdFatBase + * \brief Virtual base class for %SdFat library. + */ +class SdFatBase : public FatFileSystem { + public: + /** Initialize SD card and file system. + * \param[in] spi SPI object for the card. + * \param[in] csPin SD card chip select pin. + * \param[in] divisor SPI divisor. + * \return true for success else false. + */ + bool begin(SdSpiCard::m_spi_t* spi, uint8_t csPin = SS, uint8_t divisor = 2) { + return m_sdCard.begin(spi, csPin, divisor) && + FatFileSystem::begin(); + } + /** \return Pointer to SD card object */ + SdSpiCard *card() { + return &m_sdCard; + } + /** %Print any SD error code to Serial and halt. */ + void errorHalt() { + errorHalt(&Serial); + } + /** %Print any SD error code and halt. + * + * \param[in] pr Print destination. + */ + void errorHalt(Print* pr); + /** %Print msg, any SD error code and halt. + * + * \param[in] msg Message to print. + */ + void errorHalt(char const* msg) { + errorHalt(&Serial, msg); + } + /** %Print msg, any SD error code, and halt. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void errorHalt(Print* pr, char const* msg); + /** %Print msg, any SD error code, and halt. + * + * \param[in] msg Message to print. + */ + void errorHalt(const __FlashStringHelper* msg) { + errorHalt(&Serial, msg); + } + /** %Print msg, any SD error code, and halt. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void errorHalt(Print* pr, const __FlashStringHelper* msg); + /** %Print any SD error code to Serial */ + void errorPrint() { + errorPrint(&Serial); + } + /** %Print any SD error code. + * \param[in] pr Print device. + */ + void errorPrint(Print* pr); + /** %Print msg, any SD error code. + * + * \param[in] msg Message to print. + */ + void errorPrint(const char* msg) { + errorPrint(&Serial, msg); + } + /** %Print msg, any SD error code. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void errorPrint(Print* pr, char const* msg); + /** %Print msg, any SD error code. + * + * \param[in] msg Message to print. + */ + void errorPrint(const __FlashStringHelper* msg) { + errorPrint(&Serial, msg); + } + /** %Print msg, any SD error code. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void errorPrint(Print* pr, const __FlashStringHelper* msg); + /** Diagnostic call to initialize FatFileSystem - use for + * diagnostic purposes only. + * \return true for success else false. + */ + bool fsBegin() { + return FatFileSystem::begin(); + } + /** %Print any SD error code and halt. */ + void initErrorHalt() { + initErrorHalt(&Serial); + } + /** %Print error details and halt after begin fails. + * + * \param[in] pr Print destination. + */ + void initErrorHalt(Print* pr); + /**Print message, error details, and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ + void initErrorHalt(char const *msg) { + initErrorHalt(&Serial, msg); + } + /**Print message, error details, and halt after SdFatBase::init() fails. + * \param[in] pr Print device. + * \param[in] msg Message to print. + */ + void initErrorHalt(Print* pr, char const *msg); + /**Print message, error details, and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ + void initErrorHalt(const __FlashStringHelper* msg) { + initErrorHalt(&Serial, msg); + } + /**Print message, error details, and halt after SdFatBase::init() fails. + * \param[in] pr Print device for message. + * \param[in] msg Message to print. + */ + void initErrorHalt(Print* pr, const __FlashStringHelper* msg); + /** Print error details after SdFat::init() fails. */ + void initErrorPrint() { + initErrorPrint(&Serial); + } + /** Print error details after SdFatBase::init() fails. + * + * \param[in] pr Print destination. + */ + void initErrorPrint(Print* pr); + /**Print message and error details and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ + void initErrorPrint(char const *msg) { + initErrorPrint(&Serial, msg); + } + /**Print message and error details and halt after SdFatBase::init() fails. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void initErrorPrint(Print* pr, char const *msg); + /**Print message and error details and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ + void initErrorPrint(const __FlashStringHelper* msg) { + initErrorPrint(&Serial, msg); + } + /**Print message and error details and halt after SdFatBase::init() fails. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void initErrorPrint(Print* pr, const __FlashStringHelper* msg); + + private: + uint8_t cardErrorCode() { + return m_sdCard.errorCode(); + } + uint8_t cardErrorData() { + return m_sdCard.errorData(); + } + bool readBlock(uint32_t block, uint8_t* dst) { + return m_sdCard.readBlock(block, dst); + } + bool writeBlock(uint32_t block, const uint8_t* src) { + return m_sdCard.writeBlock(block, src); + } + bool readBlocks(uint32_t block, uint8_t* dst, size_t n) { + return m_sdCard.readBlocks(block, dst, n); + } + bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) { + return m_sdCard.writeBlocks(block, src, n); + } + SdSpiCard m_sdCard; +}; +//============================================================================== +/** + * \class SdFat + * \brief Main file system class for %SdFat library. + */ +class SdFat : public SdFatBase { + public: + /** Initialize SD card and file system. + * + * \param[in] csPin SD card chip select pin. + * \param[in] divisor SPI divisor. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { + return SdFatBase::begin(&m_spi, csPin, divisor); + } + /** Diagnostic call to initialize SD card - use for diagnostic purposes only. + * \param[in] csPin SD card chip select pin. + * \param[in] divisor SPI divisor. + * \return true for success else false. + */ + bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { + return card()->begin(&m_spi, csPin, divisor); + } + private: + SpiDefault_t m_spi; +}; +//============================================================================== +#if SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) +/** + * \class SdFatLibSpi + * \brief SdFat class using the standard Arduino SPI library. + */ +class SdFatLibSpi: public SdFatBase { + public: + /** Initialize SD card and file system. + * + * \param[in] csPin SD card chip select pin. + * \param[in] divisor SPI divisor. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { + return SdFatBase::begin(&m_spi, csPin, divisor); + } + /** Diagnostic call to initialize SD card - use for diagnostic purposes only. + * \param[in] csPin SD card chip select pin. + * \param[in] divisor SPI divisor. + * \return true for success else false. + */ + bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { + return card()->begin(&m_spi, csPin, divisor); + } + + private: + SdSpiLib m_spi; +}; +//============================================================================== +/** + * \class SdFatSoftSpi + * \brief SdFat class using software SPI. + */ +template +class SdFatSoftSpi : public SdFatBase { + public: + /** Initialize SD card and file system. + * + * \param[in] csPin SD card chip select pin. + * \param[in] divisor SPI divisor. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, uint8_t divisor = 2) { + return SdFatBase::begin(&m_spi, csPin, divisor); + } + /** Diagnostic call to initialize SD card - use for diagnostic purposes only. + * \param[in] csPin SD card chip select pin. + * \param[in] divisor SPI divisor. + * \return true for success else false. + */ + bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) { + return card()->begin(&m_spi, csPin, divisor); + } + + private: + SdSpiSoft m_spi; +}; +#endif /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN) +#endif // SdFat_h diff --git a/lib/SdFat/SdFatBase.cpp b/lib/SdFat/SdFatBase.cpp new file mode 100644 index 0000000..1727b0a --- /dev/null +++ b/lib/SdFat/SdFatBase.cpp @@ -0,0 +1,99 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#include "SdFat.h" +//------------------------------------------------------------------------------ +void SdFatBase::errorHalt(Print* pr) { + errorPrint(pr); + while (1) {} +} +//------------------------------------------------------------------------------ +void SdFatBase::errorHalt(Print* pr, char const* msg) { + errorPrint(pr, msg); + while (1) {} +} +//------------------------------------------------------------------------------ +void SdFatBase::errorHalt(Print* pr, const __FlashStringHelper* msg) { + errorPrint(pr, msg); + while (1) {} +} +//------------------------------------------------------------------------------ +void SdFatBase::errorPrint(Print* pr) { + if (!cardErrorCode()) { + return; + } + pr->print(F("SD errorCode: 0X")); + pr->print(cardErrorCode(), HEX); + pr->print(F(",0X")); + pr->println(cardErrorData(), HEX); +} +//------------------------------------------------------------------------------ +void SdFatBase::errorPrint(Print* pr, char const* msg) { + pr->print(F("error: ")); + pr->println(msg); + errorPrint(pr); +} +//------------------------------------------------------------------------------ +void SdFatBase::errorPrint(Print* pr, const __FlashStringHelper* msg) { + pr->print(F("error: ")); + pr->println(msg); + errorPrint(pr); +} +//------------------------------------------------------------------------------ +void SdFatBase::initErrorHalt(Print* pr) { + initErrorPrint(pr); + while (1) {} +} +//------------------------------------------------------------------------------ +void SdFatBase::initErrorHalt(Print* pr, char const *msg) { + pr->println(msg); + initErrorHalt(pr); +} +//------------------------------------------------------------------------------ +void SdFatBase::initErrorHalt(Print* pr, const __FlashStringHelper* msg) { + pr->println(msg); + initErrorHalt(pr); +} +//------------------------------------------------------------------------------ +void SdFatBase::initErrorPrint(Print* pr) { + if (cardErrorCode()) { + pr->println(F("Can't access SD card. Do not reformat.")); + if (cardErrorCode() == SD_CARD_ERROR_CMD0) { + pr->println(F("No card, wrong chip select pin, or SPI problem?")); + } + errorPrint(pr); + } else if (vol()->fatType() == 0) { + pr->println(F("Invalid format, reformat SD.")); + } else if (!vwd()->isOpen()) { + pr->println(F("Can't open root directory.")); + } else { + pr->println(F("No error found.")); + } +} +//------------------------------------------------------------------------------ +void SdFatBase::initErrorPrint(Print* pr, char const *msg) { + pr->println(msg); + initErrorPrint(pr); +} +//------------------------------------------------------------------------------ +void SdFatBase::initErrorPrint(Print* pr, const __FlashStringHelper* msg) { + pr->println(msg); + initErrorPrint(pr); +} + diff --git a/lib/SdFat/SdFatConfig.h b/lib/SdFat/SdFatConfig.h new file mode 100644 index 0000000..431d493 --- /dev/null +++ b/lib/SdFat/SdFatConfig.h @@ -0,0 +1,188 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +/** + * \file + * \brief configuration definitions + */ +#ifndef SdFatConfig_h +#define SdFatConfig_h +#include +#ifdef __AVR__ +#include +#endif // __AVR__ +//------------------------------------------------------------------------------ +/** + * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN). + * Long File Name are limited to a maximum length of 255 characters. + * + * This implementation allows 7-bit characters in the range + * 0X20 to 0X7E except the following characters are not allowed: + * + * < (less than) + * > (greater than) + * : (colon) + * " (double quote) + * / (forward slash) + * \ (backslash) + * | (vertical bar or pipe) + * ? (question mark) + * * (asterisk) + * + */ +#define USE_LONG_FILE_NAMES 1 +//------------------------------------------------------------------------------ +/** + * Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class + * for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print + * will be used as the base class for the Arduino File class. + * + * You can save some flash if you do not use Stream input functions such as + * find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), + * parseInt(), and parseFloat(). + */ +#define ARDUINO_FILE_USES_STREAM 1 +//------------------------------------------------------------------------------ +/** + * The symbol SD_SPI_CONFIGURATION defines SPI access to the SD card. + * + * IF SD_SPI_CONFIGUTATION is define to be zero, only the SdFat class + * is define and SdFat uses a fast custom SPI implementation. + * + * If SD_SPI_CONFIGURATION is define to be one, only the SdFat class is + * define and SdFat uses the standard Arduino SPI.h library. + * + * If SD_SPI_CONFIGURATION is define to be two, only the SdFat class is + * define and SdFat uses software SPI on the pins defined below. + * + * If SD_SPI_CONFIGURATION is define to be three, the three classes, SdFat, + * SdFatLibSpi, and SdFatSoftSpi are defined. SdFat uses the fast + * custom SPI implementation. SdFatLibSpi uses the standard Arduino SPI + * library. SdFatSoftSpi is a template class that uses Software SPI. The + * template parameters define the software SPI pins. See the ThreeCard + * example for simultaneous use of all three classes. + */ +#define SD_SPI_CONFIGURATION 0 +//------------------------------------------------------------------------------ +/** + * If SD_SPI_CONFIGURATION is defined to be two, these definitions + * will define the pins used for software SPI. + * + * The default definition allows Uno shields to be used on other boards. + */ +/** Software SPI Master Out Slave In pin */ +uint8_t const SOFT_SPI_MOSI_PIN = 11; +/** Software SPI Master In Slave Out pin */ +uint8_t const SOFT_SPI_MISO_PIN = 12; +/** Software SPI Clock pin */ +uint8_t const SOFT_SPI_SCK_PIN = 13; +//------------------------------------------------------------------------------ +/** + * To enable SD card CRC checking set USE_SD_CRC nonzero. + * + * Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function. + * + * Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function. + */ +#define USE_SD_CRC 0 +//------------------------------------------------------------------------------ +/** + * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature + * of the standard Arduino SPI library. You must include SPI.h in your + * programs when ENABLE_SPI_TRANSACTION is nonzero. + */ +#define ENABLE_SPI_TRANSACTION 0 +//------------------------------------------------------------------------------ +/** + * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during + * SD card busy waits. + * + * This will allow interrupt routines to access the SPI bus if + * ENABLE_SPI_TRANSACTION is nonzero. + * + * Setting ENABLE_SPI_YIELD will introduce some extra overhead and will + * slightly slow transfer rates. A few older SD cards may fail when + * ENABLE_SPI_YIELD is nonzero. + */ +#define ENABLE_SPI_YIELD 0 +//------------------------------------------------------------------------------ +/** + * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes. + * FAT12 has not been well tested and requires additional flash. + */ +#define FAT12_SUPPORT 0 +//------------------------------------------------------------------------------ +/** + * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. + * + * Causes use of lots of heap in ARM. + */ +#define DESTRUCTOR_CLOSES_FILE 0 +//------------------------------------------------------------------------------ +/** + * Call flush for endl if ENDL_CALLS_FLUSH is nonzero + * + * The standard for iostreams is to call flush. This is very costly for + * SdFat. Each call to flush causes 2048 bytes of I/O to the SD. + * + * SdFat has a single 512 byte buffer for SD I/O so it must write the current + * data block to the SD, read the directory block from the SD, update the + * directory entry, write the directory block to the SD and read the data + * block back into the buffer. + * + * The SD flash memory controller is not designed for this many rewrites + * so performance may be reduced by more than a factor of 100. + * + * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force + * all data to be written to the SD. + */ +#define ENDL_CALLS_FLUSH 0 +//------------------------------------------------------------------------------ +/** + * SPI SCK divisor for SD initialization commands. + * or greater + */ +#ifdef __AVR__ +const uint8_t SPI_SCK_INIT_DIVISOR = 64; +#else +const uint8_t SPI_SCK_INIT_DIVISOR = 128; +#endif +//------------------------------------------------------------------------------ +/** + * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache + * for FAT table entries. This improves performance for large writes + * that are not a multiple of 512 bytes. + */ +#ifdef __arm__ +#define USE_SEPARATE_FAT_CACHE 1 +#else // __arm__ +#define USE_SEPARATE_FAT_CACHE 0 +#endif // __arm__ +//------------------------------------------------------------------------------ +/** + * Set USE_MULTI_BLOCK_IO nonzero to use multi-block SD read/write. + * + * Don't use mult-block read/write on small AVR boards. + */ +#if defined(RAMEND) && RAMEND < 3000 +#define USE_MULTI_BLOCK_IO 0 +#else // RAMEND +#define USE_MULTI_BLOCK_IO 1 +#endif // RAMEND +#endif // SdFatConfig_h diff --git a/lib/SdFat/SdFatUtil.cpp b/lib/SdFat/SdFatUtil.cpp new file mode 100644 index 0000000..eb29c00 --- /dev/null +++ b/lib/SdFat/SdFatUtil.cpp @@ -0,0 +1,51 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#include +#include "SdFat.h" +#include "SdFatUtil.h" +//------------------------------------------------------------------------------ +#ifdef __arm__ +extern "C" char* sbrk(int incr); +int SdFatUtil::FreeRam() { + char top; + return &top - reinterpret_cast(sbrk(0)); +} +#else // __arm__ +extern char *__brkval; +extern char __bss_end; +/** Amount of free RAM + * \return The number of free bytes. + */ +int SdFatUtil::FreeRam() { + char top; + return __brkval ? &top - __brkval : &top - &__bss_end; +} +#endif // __arm +//------------------------------------------------------------------------------ +void SdFatUtil::print_P(Print* pr, PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) { + pr->write(c); + } +} +//------------------------------------------------------------------------------ +void SdFatUtil::println_P(Print* pr, PGM_P str) { + print_P(pr, str); + pr->println(); +} diff --git a/lib/SdFat/SdFatUtil.h b/lib/SdFat/SdFatUtil.h new file mode 100644 index 0000000..1193218 --- /dev/null +++ b/lib/SdFat/SdFatUtil.h @@ -0,0 +1,67 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#ifndef SdFatUtil_h +#define SdFatUtil_h +/** + * \file + * \brief Useful utility functions. + */ +#include "SdFat.h" +/** 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)) + +namespace SdFatUtil { + /** Amount of free RAM + * \return The number of free bytes. + */ + int FreeRam(); + /** %Print a string in flash memory. + * + * \param[in] pr Print object for output. + * \param[in] str Pointer to string stored in flash memory. + */ + void print_P(Print* pr, PGM_P str); + /** %Print a string in flash memory followed by a CR/LF. + * + * \param[in] pr Print object for output. + * \param[in] str Pointer to string stored in flash memory. + */ + void println_P(Print* pr, PGM_P str); + //---------------------------------------------------------------------------- + /** %Print a string in flash memory to Serial. + * + * \param[in] str Pointer to string stored in flash memory. + */ + inline void SerialPrint_P(PGM_P str) { + print_P(&Serial, str); + } + //---------------------------------------------------------------------------- + /** %Print a string in flash memory to Serial followed by a CR/LF. + * + * \param[in] str Pointer to string stored in flash memory. + */ + inline void SerialPrintln_P(PGM_P str) { + println_P(&Serial, str); + } +} // namespace SdFatUtil +using namespace SdFatUtil; // NOLINT +#endif // #define SdFatUtil_h diff --git a/lib/SdFat/SdFatmainpage.h b/lib/SdFat/SdFatmainpage.h new file mode 100644 index 0000000..b758f33 --- /dev/null +++ b/lib/SdFat/SdFatmainpage.h @@ -0,0 +1,392 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ + +/** +\mainpage Arduino %SdFat Library +
Copyright © 2012, 2013, 2014, 2015 by William Greiman +
+ +\section Intro Introduction +The Arduino %SdFat Library is a minimal implementation of FAT16 and FAT32 +file systems on SD flash memory cards. Standard SD and high capacity SDHC +cards are supported. + +Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT +nonzero in SdFatConfig.h. + +The %SdFat library supports Long %File Names or short 8.3 names. +Edit the SdFatConfig.h file to select short or long file names. + +The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi, +SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream, +and \ref ofstream. + +The SdFat, SdFatLibSpi, and SdFatSoftSpi classes maintain a FAT volume, +a current working directory, and simplifies initialization of other classes. +The SdFat class uses a fast custom hardware SPI implementation. The +SdFatLibSpi class uses the standard Arduino SPI library. The SdFatSoftSpi +class uses software SPI. + +The SdBaseFile class provides basic file access functions such as open(), +binary read(), binary write(), close(), remove(), and sync(). SdBaseFile +is the smallest file class. + +The SdFile class has all the SdBaseFile class functions plus the Arduino +Print class functions. + +The File class has all the SdBaseFile functions plus the functions in +the Arduino SD.h File class. This provides compatibility with the +Arduino SD.h library. + +The StdioStream class implements functions similar to Linux/Unix standard +buffered input/output. + +The \ref fstream class implements C++ iostreams for both reading and writing +text files. + +The \ref ifstream class implements C++ iostreams for reading text files. + +The \ref ofstream class implements C++ iostreams for writing text files. + +The classes \ref ifstream, \ref ofstream, \ref istream, and \ref ostream +follow the C++ \ref iostream standard when possible. + +There are many tutorials and much documentation about using C++ iostreams +on the web. + +http://www.cplusplus.com/ is a good C++ site for learning iostreams. + +The classes \ref ibufstream and \ref obufstream format and parse character + strings in memory buffers. + +the classes ArduinoInStream and ArduinoOutStream provide iostream functions +for Serial, LiquidCrystal, and other devices. + +A number of example are provided in the %SdFat/examples folder. These were +developed to test %SdFat and illustrate its use. + +\section Install Installation + +You must manually install SdFat by copying the SdFat folder from the download +package to the Arduino libraries folder in you sketch folder. + +See the Manual installation section of this guide. + +http://arduino.cc/en/Guide/Libraries + +\section SDconfig SdFat Configuration + +Several configuration options may be changed by editing the SdFatConfig.h +file in the %SdFat folder. + +Set USE_LONG_FILE_NAMES nonzero to enable Long %File Names. By default, +Long %File Names are enabled. For the leanest fastest library disable +Long %File Names. Long %File names require extra flash but no extra RAM. +Opening Long %File Names can be slower than opening Short %File Names. +Data read and write performance is not changed by the type of %File Name. + +Set SD_SPI_CONFIGURATION to enable various SPI options. The SdFatSoftSpi +and SdFatLibSpi classes can be enabled. SdFatLibSpi uses the standard +Arduino SPI library and SdFatSoftSpi uses software SPI. + +To enable SD card CRC checking set USE_SD_CRC nonzero. + +Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. +FAT12 has not been well tested and requires additional flash. + +Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature +of the standard Arduino SPI library. You must include SPI.h in your +programs when ENABLE_SPI_TRANSACTION is nonzero. + +Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during +SD card busy waits. + +\section SDPath Paths and Working Directories + +Relative paths in SdFat are resolved in a manner similar to Windows. + +Each instance of SdFat has a current directory. In SdFat this directory +is called the volume working directory, vwd. Initially this directory is +the root directory for the volume. + +The volume working directory is changed by calling SdFat::chdir(path). + +The call sd.chdir("/2014") will change the volume working directory +for sd to "/2014", assuming "/2014" exists. + +Relative paths for SdFat member functions are resolved by starting at +the volume working directory. + +For example, the call sd.mkdir("April") will create the directory +"/2014/April" assuming the volume working directory is "/2014". + +SdFat has a current working directory, cwd, that is used to resolve paths +for file.open() calls. + +For a single SD card the current working directory is always the volume +working directory for that card. + +For multiple SD cards the current working directory is set to the volume +working directory of a card by calling the SdFat::chvol() member function. +The chvol() call is like the Windows \: command. + +The call sd2.chvol() will set the current working directory to the volume +working directory for sd2. + +If the volume working directory for sd2 is "/music" the call + +file.open("BigBand.wav", O_READ); + +will then open "/music/BigBand.wav" on sd2. + +The following functions are used to change or get current directories. +See the html documentation for more information. +@code +bool SdFat::chdir(bool set_cwd = false); +bool SdFat::chdir(const char* path, bool set_cwd = false); +void SdFat::chvol(); +SdBaseFile* SdFat::vwd(); +static SdBaseFile* SdBaseFile::cwd(); +@endcode + +\section SDcard SD\SDHC Cards + +Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and +most consumer devices use the 4-bit parallel SD protocol. A card that +functions well on A PC or Mac may not work well on the Arduino. + +Most cards have good SPI read performance but cards vary widely in SPI +write performance. Write performance is limited by how efficiently the +card manages internal erase/remapping operations. The Arduino cannot +optimize writes to reduce erase operations because of its limit RAM. + +SanDisk cards generally have good write performance. They seem to have +more internal RAM buffering than other cards and therefore can limit +the number of flash erase operations that the Arduino forces due to its +limited RAM. + +\section Hardware Hardware Configuration + +%SdFat was developed using an + Adafruit Industries +Data Logging Shield. + +The hardware interface to the SD card should not use a resistor based level +shifter. %SdFat sets the SPI bus frequency to 8 MHz which results in signal +rise times that are too slow for the edge detectors in many newer SD card +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. + +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 +card.init(SPI_HALF_SPEED) to initialize the SD card. + +A feature to use software SPI is available. Software SPI is slower +than hardware SPI but allows any digital pins to be used. See +SdFatConfig.h for software SPI definitions. + +\section comment Bugs and Comments + +If you wish to report bugs or have comments, send email to +fat16lib@sbcglobal.net. If possible, include a simple program that illustrates +the bug or problem. + +\section Trouble Troubleshooting + +The two example programs QuickStart, and SdInfo are useful for troubleshooting. + +A message like this from SdInfo with erorCode 0X1 indicates the SD card +is not seen by SdFat. This is often caused by a wiring error and reformatting +the card will not solve the problem. +
+cardBegin failed
+SD errorCode: 0X1
+SD errorData: 0XFF
+
+Here is a similar message from QuickStart: +
+SD initialization failed.
+Do not reformat the card!
+Is the card correctly inserted?
+Is chipSelect set to the correct value?
+Does another SPI device need to be disabled?
+Is there a wiring/soldering problem?
+
+errorCode: 0x1, errorData: 0xff
+
+Here is a message from QuickStart that indicates a formatting problem: +
+Card successfully initialized.
+Can't find a valid FAT16/FAT32 partition.
+Try reformatting the card.  For best results use
+the SdFormatter program in SdFat/examples or download
+and use SDFormatter from www.sdcard.org/downloads.
+
+ +The best source of recent information and help is the Arduino forum. + +http://arduino.cc/forum/ + +Also search the Adafruit forum. + +http://forums.adafruit.com/ + +If you are using a Teensy try. + +http://forum.pjrc.com/forum.php + +\section SdFatClass SdFat Usage + +SdFat supports Long File Names. Long names in SdFat are limited to 7-bit +ASCII characters in the range 0X20 - 0XFE The following are reserved characters: +
    +
  • < (less than) +
  • > (greater than) +
  • : (colon) +
  • " (double quote) +
  • / (forward slash) +
  • \ (backslash) +
  • | (vertical bar or pipe) +
  • ? (question mark) +
  • * (asterisk) +
+%SdFat uses a slightly restricted form of short names. +Short names are limited to 8 characters followed by an optional period (.) +and extension of up to 3 characters. The characters may be any combination +of letters and digits. The following special characters are also allowed: + +$ % ' - _ @ ~ ` ! ( ) { } ^ # & + +Short names are always converted to upper case and their original case +value is lost. Files that have a base-name where all characters have the +same case and an extension where all characters have the same case will +display properly. Examples this type name are UPPER.low, lower.TXT, +UPPER.TXT, and lower.txt. + +An application which writes to a file using print(), println() or +\link SdFile::write write() \endlink must close the file or call +\link SdFile::sync() sync() \endlink at the appropriate time to +force data and directory information to be written to the SD Card. + +Applications must use care calling \link SdFile::sync() sync() \endlink +since 2048 bytes of I/O is required to update file and +directory information. This includes writing the current data block, reading +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 a file object. +A file may be corrupted if data is written to the file by more than one +instance of a file object. + +\section HowTo How to format SD Cards as FAT Volumes + +The best way to restore an SD card's format on a PC or Mac is to use +SDFormatter which can be downloaded from: + +http://www.sdcard.org/downloads + +A formatter program, SdFormatter.ino, is included in the +%SdFat/examples/SdFormatter directory. This program attempts to +emulate SD Association's SDFormatter. + +SDFormatter aligns flash erase boundaries with file +system structures which reduces write latency and file system overhead. + +The PC/Mac SDFormatter does not have an option for FAT type so it may format +very small cards as FAT12. Use the SdFat formatter to force FAT16 +formatting of small cards. + +Do not format the SD card with an OS utility, OS utilities do not format SD +cards in conformance with the SD standard. + +You should use a freshly formatted SD card for best performance. FAT +file systems become slower if many files have been created and deleted. +This is because the directory entry for a deleted file is marked as deleted, +but is not deleted. When a new file is created, these entries must be scanned +before creating the file. Also files can become +fragmented which causes reads and writes to be slower. + +\section ExampleFilder Examples + +A number of examples are provided in the SdFat/examples folder. +See the html documentation for a list. + +To access these examples from the Arduino development environment +go to: %File -> Examples -> %SdFat -> \ + +Compile, upload to your Arduino and click on Serial Monitor to run +the example. + +Here is a list: + +AnalogBinLogger - Fast AVR ADC logger - see the AnalogBinLoggerExtras folder. + +bench - A read/write benchmark. + +cin_cout - Demo of ArduinoInStream and ArduinoOutStream. + +dataLogger - A simple modifiable data logger. + +directoryFunctions - Demo of chdir(), ls(), mkdir(), and rmdir(). + +fgets - Demo of the fgets read line/string function. + +formating - Print a table with various formatting options. + +getline - Example of getline from section 27.7.1.3 of the C++ standard. + +LongFileName - Example use of openNext, printName, and open by index. + +LowLatencyLogger - A modifiable data logger for higher data rates. + +OpenNext - Open all files in the root dir and print their filename. + +PrintBenchmark - A simple benchmark for printing to a text file. + +QuickStart - A program to quickly test your SD card and SD shield/module. + +RawWrite - A test of raw write functions for contiguous files. + +readCSV - Read a comma-separated value file using iostream extractors. + +ReadWriteSdFat - SdFat version of Arduino SD ReadWrite example. + +rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath). + +SdFormatter - This program will format an SD or SDHC card. + +SoftwareSpi - Simple demonstration of the SdFatSoftSpi template class. + +SdInfo - Initialize an SD card and analyze its structure for trouble shooting. + +StdioBench - Demo and test of stdio style stream. + +StreamParseInt - Demo of the SD.h API and the File class parseInt() function. + +ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi. + +Timestamp - Sets file create, modify, and access timestamps. + +TwoCards - Example using two SD cards. + */ diff --git a/lib/SdFat/SdInfo.h b/lib/SdFat/SdInfo.h new file mode 100644 index 0000000..d502c2f --- /dev/null +++ b/lib/SdFat/SdInfo.h @@ -0,0 +1,381 @@ +/* Arduino SdSpiCard Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdSpiCard Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpiCard Library. If not, see + * . + */ +#ifndef SdInfo_h +#define SdInfo_h +#include +// Based on the document: +// +// SD Specifications +// Part 1 +// Physical Layer +// Simplified Specification +// Version 3.01 +// May 18, 2010 +// +// http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs +//------------------------------------------------------------------------------ +// SD card errors +/** timeout error for command CMD0 (initialize card in SPI mode) */ +uint8_t const SD_CARD_ERROR_CMD0 = 0X1; +/** CMD8 was not accepted - not a valid SD card*/ +uint8_t const SD_CARD_ERROR_CMD8 = 0X2; +/** card returned an error response for CMD12 (stop multiblock read) */ +uint8_t const SD_CARD_ERROR_CMD12 = 0X3; +/** card returned an error response for CMD17 (read block) */ +uint8_t const SD_CARD_ERROR_CMD17 = 0X4; +/** card returned an error response for CMD18 (read multiple block) */ +uint8_t const SD_CARD_ERROR_CMD18 = 0X5; +/** card returned an error response for CMD24 (write block) */ +uint8_t const SD_CARD_ERROR_CMD24 = 0X6; +/** WRITE_MULTIPLE_BLOCKS command failed */ +uint8_t const SD_CARD_ERROR_CMD25 = 0X7; +/** card returned an error response for CMD58 (read OCR) */ +uint8_t const SD_CARD_ERROR_CMD58 = 0X8; +/** SET_WR_BLK_ERASE_COUNT failed */ +uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; +/** ACMD41 initialization process timeout */ +uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; +/** card returned a bad CSR version field */ +uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; +/** erase block group command failed */ +uint8_t const SD_CARD_ERROR_ERASE = 0XC; +/** card not capable of single block erase */ +uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; +/** Erase sequence timed out */ +uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; +/** card returned an error token instead of read data */ +uint8_t const SD_CARD_ERROR_READ = 0XF; +/** read CID or CSD failed */ +uint8_t const SD_CARD_ERROR_READ_REG = 0X10; +/** timeout while waiting for start of read data */ +uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; +/** card did not accept STOP_TRAN_TOKEN */ +uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; +/** card returned an error token as a response to a write operation */ +uint8_t const SD_CARD_ERROR_WRITE = 0X13; +/** attempt to write protected block zero */ +uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used +/** card did not go ready for a multiple block write */ +uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; +/** card returned an error to a CMD13 status check after a write */ +uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; +/** timeout occurred during write programming */ +uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; +/** incorrect rate selected */ +uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; +/** init() not called */ +uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; +/** card returned an error for CMD59 (CRC_ON_OFF) */ +uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; +/** invalid read CRC */ +uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; +/** SPI DMA error */ +uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C; +//------------------------------------------------------------------------------ +// card types +/** Standard capacity V1 SD card */ +uint8_t const SD_CARD_TYPE_SD1 = 1; +/** Standard capacity V2 SD card */ +uint8_t const SD_CARD_TYPE_SD2 = 2; +/** High Capacity SD card */ +uint8_t const SD_CARD_TYPE_SDHC = 3; +//------------------------------------------------------------------------------ +// SPI divisor constants +/** Set SCK to max rate of F_CPU/2. */ +uint8_t const SPI_FULL_SPEED = 2; +/** Set SCK rate to F_CPU/3 for Due */ +uint8_t const SPI_DIV3_SPEED = 3; +/** Set SCK rate to F_CPU/4. */ +uint8_t const SPI_HALF_SPEED = 4; +/** Set SCK rate to F_CPU/6 for Due */ +uint8_t const SPI_DIV6_SPEED = 6; +/** Set SCK rate to F_CPU/8. */ +uint8_t const SPI_QUARTER_SPEED = 8; +/** Set SCK rate to F_CPU/16. */ +uint8_t const SPI_EIGHTH_SPEED = 16; +/** Set SCK rate to F_CPU/32. */ +uint8_t const SPI_SIXTEENTH_SPEED = 32; +//------------------------------------------------------------------------------ +// SD operation timeouts +/** init timeout ms */ +uint16_t const SD_INIT_TIMEOUT = 2000; +/** erase timeout ms */ +uint16_t const SD_ERASE_TIMEOUT = 10000; +/** read timeout ms */ +uint16_t const SD_READ_TIMEOUT = 300; +/** write time out ms */ +uint16_t const SD_WRITE_TIMEOUT = 600; +//------------------------------------------------------------------------------ +// SD card commands +/** GO_IDLE_STATE - init card in spi mode if CS low */ +uint8_t const CMD0 = 0X00; +/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ +uint8_t const CMD8 = 0X08; +/** SEND_CSD - read the Card Specific Data (CSD register) */ +uint8_t const CMD9 = 0X09; +/** SEND_CID - read the card identification information (CID register) */ +uint8_t const CMD10 = 0X0A; +/** STOP_TRANSMISSION - end multiple block read sequence */ +uint8_t const CMD12 = 0X0C; +/** SEND_STATUS - read the card status register */ +uint8_t const CMD13 = 0X0D; +/** READ_SINGLE_BLOCK - read a single data block from the card */ +uint8_t const CMD17 = 0X11; +/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ +uint8_t const CMD18 = 0X12; +/** WRITE_BLOCK - write a single data block to the card */ +uint8_t const CMD24 = 0X18; +/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ +uint8_t const CMD25 = 0X19; +/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ +uint8_t const CMD32 = 0X20; +/** ERASE_WR_BLK_END - sets the address of the last block of the continuous + range to be erased*/ +uint8_t const CMD33 = 0X21; +/** ERASE - erase all previously selected blocks */ +uint8_t const CMD38 = 0X26; +/** APP_CMD - escape for application specific command */ +uint8_t const CMD55 = 0X37; +/** READ_OCR - read the OCR register of a card */ +uint8_t const CMD58 = 0X3A; +/** CRC_ON_OFF - enable or disable CRC checking */ +uint8_t const CMD59 = 0X3B; +/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be + pre-erased before writing */ +uint8_t const ACMD23 = 0X17; +/** SD_SEND_OP_COMD - Sends host capacity support information and + activates the card's initialization process */ +uint8_t const ACMD41 = 0X29; +//============================================================================== +/** status for card in the ready state */ +uint8_t const R1_READY_STATE = 0X00; +/** status for card in the idle state */ +uint8_t const R1_IDLE_STATE = 0X01; +/** status bit for illegal command */ +uint8_t const R1_ILLEGAL_COMMAND = 0X04; +/** start data token for read or write single block*/ +uint8_t const DATA_START_BLOCK = 0XFE; +/** stop token for write multiple blocks*/ +uint8_t const STOP_TRAN_TOKEN = 0XFD; +/** start data token for write multiple blocks*/ +uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; +/** mask for data response tokens after a write block operation */ +uint8_t const DATA_RES_MASK = 0X1F; +/** write data accepted token */ +uint8_t const DATA_RES_ACCEPTED = 0X05; +//============================================================================== +/** + * \class CID + * \brief Card IDentification (CID) register. + */ +typedef struct CID { + // byte 0 + /** Manufacturer ID */ + unsigned char mid; + // byte 1-2 + /** OEM/Application ID */ + char oid[2]; + // byte 3-7 + /** Product name */ + char pnm[5]; + // byte 8 + /** Product revision least significant digit */ + unsigned char prv_m : 4; + /** Product revision most significant digit */ + unsigned char prv_n : 4; + // byte 9-12 + /** Product serial number */ + uint32_t psn; + // byte 13 + /** Manufacturing date year low digit */ + unsigned char mdt_year_high : 4; + /** not used */ + unsigned char reserved : 4; + // byte 14 + /** Manufacturing date month */ + unsigned char mdt_month : 4; + /** Manufacturing date year low digit */ + unsigned char mdt_year_low : 4; + // byte 15 + /** not used always 1 */ + unsigned char always1 : 1; + /** CRC7 checksum */ + unsigned char crc : 7; +} __attribute__((packed)) cid_t; +//============================================================================== +/** + * \class CSDV1 + * \brief CSD register for version 1.00 cards . + */ +typedef struct CSDV1 { + // byte 0 + unsigned char reserved1 : 6; + unsigned char csd_ver : 2; + // byte 1 + unsigned char taac; + // byte 2 + unsigned char nsac; + // byte 3 + unsigned char tran_speed; + // byte 4 + unsigned char ccc_high; + // byte 5 + unsigned char read_bl_len : 4; + unsigned char ccc_low : 4; + // byte 6 + unsigned char c_size_high : 2; + unsigned char reserved2 : 2; + unsigned char dsr_imp : 1; + unsigned char read_blk_misalign : 1; + unsigned char write_blk_misalign : 1; + unsigned char read_bl_partial : 1; + // byte 7 + unsigned char c_size_mid; + // byte 8 + unsigned char vdd_r_curr_max : 3; + unsigned char vdd_r_curr_min : 3; + unsigned char c_size_low : 2; + // byte 9 + unsigned char c_size_mult_high : 2; + unsigned char vdd_w_cur_max : 3; + unsigned char vdd_w_curr_min : 3; + // byte 10 + unsigned char sector_size_high : 6; + unsigned char erase_blk_en : 1; + unsigned char c_size_mult_low : 1; + // byte 11 + unsigned char wp_grp_size : 7; + unsigned char sector_size_low : 1; + // byte 12 + unsigned char write_bl_len_high : 2; + unsigned char r2w_factor : 3; + unsigned char reserved3 : 2; + unsigned char wp_grp_enable : 1; + // byte 13 + unsigned char reserved4 : 5; + unsigned char write_partial : 1; + unsigned char write_bl_len_low : 2; + // byte 14 + unsigned char reserved5: 2; + unsigned char file_format : 2; + unsigned char tmp_write_protect : 1; + unsigned char perm_write_protect : 1; + unsigned char copy : 1; + /** Indicates the file format on the card */ + unsigned char file_format_grp : 1; + // byte 15 + unsigned char always1 : 1; + unsigned char crc : 7; +} __attribute__((packed)) csd1_t; +//============================================================================== +/** + * \class CSDV2 + * \brief CSD register for version 2.00 cards. + */ +typedef struct CSDV2 { + // byte 0 + unsigned char reserved1 : 6; + unsigned char csd_ver : 2; + // byte 1 + /** fixed to 0X0E */ + unsigned char taac; + // byte 2 + /** fixed to 0 */ + unsigned char nsac; + // byte 3 + unsigned char tran_speed; + // byte 4 + unsigned char ccc_high; + // byte 5 + /** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */ + unsigned char read_bl_len : 4; + unsigned char ccc_low : 4; + // byte 6 + /** not used */ + unsigned char reserved2 : 4; + unsigned char dsr_imp : 1; + /** fixed to 0 */ + unsigned char read_blk_misalign : 1; + /** fixed to 0 */ + unsigned char write_blk_misalign : 1; + /** fixed to 0 - no partial read */ + unsigned char read_bl_partial : 1; + // byte 7 + /** high part of card size */ + unsigned char c_size_high : 6; + /** not used */ + unsigned char reserved3 : 2; + // byte 8 + /** middle part of card size */ + unsigned char c_size_mid; + // byte 9 + /** low part of card size */ + unsigned char c_size_low; + // byte 10 + /** sector size is fixed at 64 KB */ + unsigned char sector_size_high : 6; + /** fixed to 1 - erase single is supported */ + unsigned char erase_blk_en : 1; + /** not used */ + unsigned char reserved4 : 1; + // byte 11 + unsigned char wp_grp_size : 7; + /** sector size is fixed at 64 KB */ + unsigned char sector_size_low : 1; + // byte 12 + /** write_bl_len fixed for 512 byte blocks */ + unsigned char write_bl_len_high : 2; + /** fixed value of 2 */ + unsigned char r2w_factor : 3; + /** not used */ + unsigned char reserved5 : 2; + /** fixed value of 0 - no write protect groups */ + unsigned char wp_grp_enable : 1; + // byte 13 + unsigned char reserved6 : 5; + /** always zero - no partial block read*/ + unsigned char write_partial : 1; + /** write_bl_len fixed for 512 byte blocks */ + unsigned char write_bl_len_low : 2; + // byte 14 + unsigned char reserved7: 2; + /** Do not use always 0 */ + unsigned char file_format : 2; + unsigned char tmp_write_protect : 1; + unsigned char perm_write_protect : 1; + unsigned char copy : 1; + /** Do not use always 0 */ + unsigned char file_format_grp : 1; + // byte 15 + /** not used always 1 */ + unsigned char always1 : 1; + /** checksum */ + unsigned char crc : 7; +} __attribute__((packed)) csd2_t; +//============================================================================== +/** + * \class csd_t + * \brief Union of old and new style CSD register. + */ +union csd_t { + csd1_t v1; + csd2_t v2; +}; +#endif // SdInfo_h diff --git a/lib/SdFat/SdSpi.h b/lib/SdFat/SdSpi.h new file mode 100644 index 0000000..b87cf1e --- /dev/null +++ b/lib/SdFat/SdSpi.h @@ -0,0 +1,370 @@ +/* Arduino SdSpi Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the Arduino SdSpi Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpi Library. If not, see + * . + */ +/** +* \file +* \brief SdSpi class for V2 SD/SDHC cards +*/ +#ifndef SdSpi_h +#define SdSpi_h +#include +#include "SdFatConfig.h" + +//------------------------------------------------------------------------------ +/** + * \class SdSpiBase + * \brief Virtual SPI class for access to SD and SDHC flash memory cards. + */ +class SdSpiBase { + public: + /** Initialize the SPI bus */ + virtual void begin() = 0; + /** Set SPI options for access to SD/SDHC cards. + * + * \param[in] divisor SCK clock divider relative to the system clock. + */ + virtual void init(uint8_t divisor); + /** Receive a byte. + * + * \return The byte. + */ + virtual uint8_t receive() = 0; + /** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] n Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + virtual uint8_t receive(uint8_t* buf, size_t n) = 0; + /** Send a byte. + * + * \param[in] data Byte to send + */ + virtual void send(uint8_t data) = 0; + /** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] n Number of bytes to send. + */ + virtual void send(const uint8_t* buf, size_t n) = 0; + /** \return true if hardware SPI else false */ + virtual bool useSpiTransactions() = 0; +}; +//------------------------------------------------------------------------------ +/** + * \class SdSpi + * \brief SPI class for access to SD and SDHC flash memory cards. + */ +#if SD_SPI_CONFIGURATION >= 3 +class SdSpi : public SdSpiBase { +#else // SD_SPI_CONFIGURATION >= 3 +class SdSpi { +#endif // SD_SPI_CONFIGURATION >= 3 + public: + /** Initialize the SPI bus */ + void begin(); + /** Set SPI options for access to SD/SDHC cards. + * + * \param[in] divisor SCK clock divider relative to the system clock. + */ + void init(uint8_t divisor); + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive(); + /** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] n Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + uint8_t receive(uint8_t* buf, size_t n); + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t data); + /** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] n Number of bytes to send. + */ + void send(const uint8_t* buf, size_t n); + /** \return true - uses SPI transactions */ + bool useSpiTransactions() { + return true; + } +}; +//------------------------------------------------------------------------------ +/** + * \class SdSpiLib + * \brief Arduino SPI library class for access to SD and SDHC flash + * memory cards. + */ +#if SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1 || defined(DOXYGEN) +#include +#if SD_SPI_CONFIGURATION >= 3 +class SdSpiLib : public SdSpiBase { +#else // SD_SPI_CONFIGURATION >= 3 +class SdSpiLib { +#endif // SD_SPI_CONFIGURATION >= 3 + public: + /** + * Initialize SPI pins. + */ + void begin() { + SPI.begin(); + } + /** Set SPI options for access to SD/SDHC cards. + * + * \param[in] divisor SCK clock divider relative to the system clock. + */ + void init(uint8_t divisor) { + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); +#ifndef SPI_CLOCK_DIV128 + SPI.setClockDivider(divisor); +#else // SPI_CLOCK_DIV128 + int v; + if (divisor <= 2) { + v = SPI_CLOCK_DIV2; + } else if (divisor <= 4) { + v = SPI_CLOCK_DIV4; + } else if (divisor <= 8) { + v = SPI_CLOCK_DIV8; + } else if (divisor <= 16) { + v = SPI_CLOCK_DIV16; + } else if (divisor <= 32) { + v = SPI_CLOCK_DIV32; + } else if (divisor <= 64) { + v = SPI_CLOCK_DIV64; + } else { + v = SPI_CLOCK_DIV128; + } + SPI.setClockDivider(v); +#endif // SPI_CLOCK_DIV128 + } + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive() { + return SPI.transfer(0XFF); + } + /** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] n Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + uint8_t receive(uint8_t* buf, size_t n) { + for (size_t i = 0; i < n; i++) { + buf[i] = SPI.transfer(0XFF); + } + return 0; + } + /** Send a byte. + * + * \param[in] b Byte to send + */ + void send(uint8_t b) { + SPI.transfer(b); + } + /** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] n Number of bytes to send. + */ + void send(const uint8_t* buf , size_t n) { + for (size_t i = 0; i < n; i++) { + SPI.transfer(buf[i]); + } + } + /** \return true - uses SPI transactions */ + bool useSpiTransactions() { + return true; + } +}; +#endif // SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1 +//------------------------------------------------------------------------------ +#if SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) +#include "utility/SoftSPI.h" +/** + * \class SdSpiSoft + * \brief Software SPI class for access to SD and SDHC flash memory cards. + */ +template +class SdSpiSoft : public SdSpiBase { + public: + /** + * initialize SPI pins + */ + void begin() { + m_spi.begin(); + } + /** + * Initialize hardware SPI - dummy for soft SPI + * \param[in] divisor SCK divisor - ignored. + */ + void init(uint8_t divisor) {} + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive() { + return m_spi.receive(); + } + /** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] n Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + uint8_t receive(uint8_t* buf, size_t n) { + for (size_t i = 0; i < n; i++) { + buf[i] = receive(); + } + return 0; + } + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t data) { + m_spi.send(data); + } + /** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] n Number of bytes to send. + */ + void send(const uint8_t* buf , size_t n) { + for (size_t i = 0; i < n; i++) { + send(buf[i]); + } + } + /** \return false - no SPI transactions */ + bool useSpiTransactions() { + return false; + } + + private: + SoftSPI m_spi; +}; +#endif // SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN) +//------------------------------------------------------------------------------ +#if SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3 +/** Default is custom fast SPI. */ +typedef SdSpi SpiDefault_t; +#elif SD_SPI_CONFIGURATION == 1 +/** Default is Arduino library SPI. */ +typedef SdSpiLib SpiDefault_t; +#elif SD_SPI_CONFIGURATION == 2 +/** Default is software SPI. */ +typedef SdSpiSoft +SpiDefault_t; +#else // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3 +#error bad SD_SPI_CONFIGURATION +#endif // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3 +//------------------------------------------------------------------------------ +// Use of in-line for AVR to save flash. +#ifdef __AVR__ +//------------------------------------------------------------------------------ +inline void SdSpi::begin() { +#ifdef __AVR_ATmega328P__ + // Save a few bytes for 328 CPU - gcc optimizes single bit '|' to sbi. + PORTB |= 1 << 2; // SS high + DDRB |= 1 << 2; // SS output mode + DDRB |= 1 << 3; // MOSI output mode + DDRB |= 1 << 5; // SCK output mode +#else // __AVR_ATmega328P__ + + // set SS high - may be chip select for another SPI device + digitalWrite(SS, HIGH); + + // SS must be in output mode even it is not chip select + pinMode(SS, OUTPUT); + pinMode(MOSI, OUTPUT); + pinMode(SCK, OUTPUT); +#endif // __AVR_ATmega328P__ +} +//------------------------------------------------------------------------------ +inline void SdSpi::init(uint8_t divisor) { + uint8_t b = 2; + uint8_t r = 0; + + // See AVR processor documentation. + for (; divisor > b && r < 7; b <<= 1, r += r < 5 ? 1 : 2) {} + SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1); + SPSR = r & 1 ? 0 : 1 << SPI2X; +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpi::receive() { + SPDR = 0XFF; + while (!(SPSR & (1 << SPIF))) {} + return SPDR; +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { + if (n-- == 0) { + return 0; + } + SPDR = 0XFF; + for (size_t i = 0; i < n; i++) { + while (!(SPSR & (1 << SPIF))) {} + uint8_t b = SPDR; + SPDR = 0XFF; + buf[i] = b; + } + while (!(SPSR & (1 << SPIF))) {} + buf[n] = SPDR; + return 0; +} +//------------------------------------------------------------------------------ +inline void SdSpi::send(uint8_t data) { + SPDR = data; + while (!(SPSR & (1 << SPIF))) {} +} +//------------------------------------------------------------------------------ +inline void SdSpi::send(const uint8_t* buf , size_t n) { + if (n == 0) { + return; + } + SPDR = buf[0]; + if (n > 1) { + uint8_t b = buf[1]; + size_t i = 2; + while (1) { + while (!(SPSR & (1 << SPIF))) {} + SPDR = b; + if (i == n) { + break; + } + b = buf[i++]; + } + } + while (!(SPSR & (1 << SPIF))) {} +} +#endif // __AVR__ +#endif // SdSpi_h diff --git a/lib/SdFat/SdSpiCard.cpp b/lib/SdFat/SdSpiCard.cpp new file mode 100644 index 0000000..1747484 --- /dev/null +++ b/lib/SdFat/SdSpiCard.cpp @@ -0,0 +1,636 @@ +/* Arduino SdSpiCard Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdSpiCard Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpiCard Library. If not, see + * . + */ +#include "SdSpiCard.h" +#include "SdSpi.h" +#if ENABLE_SPI_TRANSACTION +#include +#endif // ENABLE_SPI_TRANSACTION +// debug trace macro +#define SD_TRACE(m, b) +// #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); +//============================================================================== +#if USE_SD_CRC +// CRC functions +//------------------------------------------------------------------------------ +static uint8_t CRC7(const uint8_t* data, uint8_t n) { + uint8_t crc = 0; + for (uint8_t i = 0; i < n; i++) { + uint8_t d = data[i]; + for (uint8_t j = 0; j < 8; j++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) { + crc ^= 0x09; + } + d <<= 1; + } + } + return (crc << 1) | 1; +} +//------------------------------------------------------------------------------ +#if USE_SD_CRC == 1 +// slower CRC-CCITT +// uses the x^16,x^12,x^5,x^1 polynomial. +static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { + uint16_t crc = 0; + for (size_t i = 0; i < n; i++) { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= data[i]; + crc ^= (uint8_t)(crc & 0xff) >> 4; + crc ^= crc << 12; + crc ^= (crc & 0xff) << 5; + } + return crc; +} +#elif USE_SD_CRC > 1 // CRC_CCITT +//------------------------------------------------------------------------------ +// faster CRC-CCITT +// uses the x^16,x^12,x^5,x^1 polynomial. +#ifdef __AVR__ +static const uint16_t crctab[] PROGMEM = { +#else // __AVR__ +static const uint16_t crctab[] = { +#endif // __AVR__ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; +static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { + uint16_t crc = 0; + for (size_t i = 0; i < n; i++) { +#ifdef __AVR__ + crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0XFF]) ^ (crc << 8); +#else // __AVR__ + crc = crctab[(crc >> 8 ^ data[i]) & 0XFF] ^ (crc << 8); +#endif // __AVR__ + } + return crc; +} +#endif // CRC_CCITT +#endif // USE_SD_CRC +//============================================================================== +// SdSpiCard member functions +//------------------------------------------------------------------------------ +bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) { + m_errorCode = m_type = 0; + m_spi = spi; + m_chipSelectPin = chipSelectPin; + // 16-bit init start time allows over a minute + uint16_t t0 = (uint16_t)millis(); + uint32_t arg; + + pinMode(m_chipSelectPin, OUTPUT); + digitalWrite(m_chipSelectPin, HIGH); + spiBegin(); + + // set SCK rate for initialization commands + m_sckDivisor = SPI_SCK_INIT_DIVISOR; + spiInit(m_sckDivisor); + + // must supply min of 74 clock cycles with CS high. + for (uint8_t i = 0; i < 10; i++) { + spiSend(0XFF); + } + + // command to go idle in SPI mode + while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { + if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { + error(SD_CARD_ERROR_CMD0); + goto fail; + } + } +#if USE_SD_CRC + if (cardCommand(CMD59, 1) != R1_IDLE_STATE) { + error(SD_CARD_ERROR_CMD59); + goto fail; + } +#endif // USE_SD_CRC + // check SD version + while (1) { + if (cardCommand(CMD8, 0x1AA) == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE)) { + type(SD_CARD_TYPE_SD1); + break; + } + for (uint8_t i = 0; i < 4; i++) { + m_status = spiReceive(); + } + if (m_status == 0XAA) { + type(SD_CARD_TYPE_SD2); + break; + } + if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { + error(SD_CARD_ERROR_CMD8); + goto fail; + } + } + // initialize card and send host supports SDHC if SD2 + arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; + + while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { + // check for timeout + if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { + error(SD_CARD_ERROR_ACMD41); + goto fail; + } + } + // if SD2 read OCR register to check for SDHC card + if (type() == SD_CARD_TYPE_SD2) { + if (cardCommand(CMD58, 0)) { + error(SD_CARD_ERROR_CMD58); + goto fail; + } + if ((spiReceive() & 0XC0) == 0XC0) { + type(SD_CARD_TYPE_SDHC); + } + // Discard rest of ocr - contains allowed voltage range. + for (uint8_t i = 0; i < 3; i++) { + spiReceive(); + } + } + chipSelectHigh(); + m_sckDivisor = sckDivisor; + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +// send command and return error code. Return zero for OK +uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { + // select card + chipSelectLow(); + + // wait if busy + waitNotBusy(SD_WRITE_TIMEOUT); + + uint8_t *pa = reinterpret_cast(&arg); + +#if USE_SD_CRC + // form message + uint8_t d[6] = {cmd | 0X40, pa[3], pa[2], pa[1], pa[0]}; + + // add crc + d[5] = CRC7(d, 5); + + // send message + for (uint8_t k = 0; k < 6; k++) { + spiSend(d[k]); + } +#else // USE_SD_CRC + // send command + spiSend(cmd | 0x40); + + // send argument + for (int8_t i = 3; i >= 0; i--) { + spiSend(pa[i]); + } + + // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA + spiSend(cmd == CMD0 ? 0X95 : 0X87); +#endif // USE_SD_CRC + + // skip stuff byte for stop read + if (cmd == CMD12) { + spiReceive(); + } + + // wait for response + for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i != 0XFF; i++) { + } + return m_status; +} +//------------------------------------------------------------------------------ +uint32_t SdSpiCard::cardSize() { + csd_t csd; + if (!readCSD(&csd)) { + return 0; + } + if (csd.v1.csd_ver == 0) { + uint8_t read_bl_len = csd.v1.read_bl_len; + uint16_t c_size = (csd.v1.c_size_high << 10) + | (csd.v1.c_size_mid << 2) | csd.v1.c_size_low; + uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1) + | csd.v1.c_size_mult_low; + return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7); + } else if (csd.v2.csd_ver == 1) { + uint32_t c_size = 0X10000L * csd.v2.c_size_high + 0X100L + * (uint32_t)csd.v2.c_size_mid + csd.v2.c_size_low; + return (c_size + 1) << 10; + } else { + error(SD_CARD_ERROR_BAD_CSD); + return 0; + } +} +//------------------------------------------------------------------------------ +void SdSpiCard::spiYield() { +#if ENABLE_SPI_TRANSACTION && ENABLE_SPI_YIELD && defined(SPI_HAS_TRANSACTION) + chipSelectHigh(); + chipSelectLow(); +#endif // ENABLE_SPI_TRANSACTION && ENABLE_SPI_YIELD && SPI_HAS_TRANSACTION +} +//------------------------------------------------------------------------------ +void SdSpiCard::chipSelectHigh() { + digitalWrite(m_chipSelectPin, HIGH); + // insure MISO goes high impedance + spiSend(0XFF); +#if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) + if (useSpiTransactions()) { + SPI.endTransaction(); + } +#endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) +} +//------------------------------------------------------------------------------ +void SdSpiCard::chipSelectLow() { +#if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) + if (useSpiTransactions()) { + SPI.beginTransaction(SPISettings()); + } +#endif // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION) + spiInit(m_sckDivisor); + digitalWrite(m_chipSelectPin, LOW); +} +//------------------------------------------------------------------------------ +bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { + csd_t csd; + if (!readCSD(&csd)) { + goto fail; + } + // check for single block erase + if (!csd.v1.erase_blk_en) { + // erase size mask + uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; + if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { + // error card can't erase specified area + error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + goto fail; + } + } + if (m_type != SD_CARD_TYPE_SDHC) { + firstBlock <<= 9; + lastBlock <<= 9; + } + if (cardCommand(CMD32, firstBlock) + || cardCommand(CMD33, lastBlock) + || cardCommand(CMD38, 0)) { + error(SD_CARD_ERROR_ERASE); + goto fail; + } + if (!waitNotBusy(SD_ERASE_TIMEOUT)) { + error(SD_CARD_ERROR_ERASE_TIMEOUT); + goto fail; + } + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::eraseSingleBlockEnable() { + csd_t csd; + return readCSD(&csd) ? csd.v1.erase_blk_en : false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::isBusy() { + bool rtn; + chipSelectLow(); + for (uint8_t i = 0; i < 8; i++) { + rtn = spiReceive() != 0XFF; + if (!rtn) { + break; + } + } + chipSelectHigh(); + return rtn; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { + SD_TRACE("RB", blockNumber); + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) { + blockNumber <<= 9; + } + if (cardCommand(CMD17, blockNumber)) { + error(SD_CARD_ERROR_CMD17); + goto fail; + } + return readData(dst, 512); + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) { + if (!readStart(block)) { + return false; + } + for (uint16_t b = 0; b < count; b++, dst += 512) { + if (!readData(dst)) { + return false; + } + } + return readStop(); +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readData(uint8_t *dst) { + chipSelectLow(); + return readData(dst, 512); +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readData(uint8_t* dst, size_t count) { +#if USE_SD_CRC + uint16_t crc; +#endif // USE_SD_CRC + // wait for start block token + uint16_t t0 = millis(); + while ((m_status = spiReceive()) == 0XFF) { + if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) { + error(SD_CARD_ERROR_READ_TIMEOUT); + goto fail; + } + } + if (m_status != DATA_START_BLOCK) { + error(SD_CARD_ERROR_READ); + goto fail; + } + // transfer data + if ((m_status = spiReceive(dst, count))) { + error(SD_CARD_ERROR_SPI_DMA); + goto fail; + } + +#if USE_SD_CRC + // get crc + crc = (spiReceive() << 8) | spiReceive(); + if (crc != CRC_CCITT(dst, count)) { + error(SD_CARD_ERROR_READ_CRC); + goto fail; + } +#else + // discard crc + spiReceive(); + spiReceive(); +#endif // USE_SD_CRC + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readOCR(uint32_t* ocr) { + uint8_t *p = reinterpret_cast(ocr); + if (cardCommand(CMD58, 0)) { + error(SD_CARD_ERROR_CMD58); + goto fail; + } + for (uint8_t i = 0; i < 4; i++) { + p[3 - i] = spiReceive(); + } + + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** read CID or CSR register */ +bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { + uint8_t* dst = reinterpret_cast(buf); + if (cardCommand(cmd, 0)) { + error(SD_CARD_ERROR_READ_REG); + goto fail; + } + return readData(dst, 16); + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readStart(uint32_t blockNumber) { + SD_TRACE("RS", blockNumber); + if (type() != SD_CARD_TYPE_SDHC) { + blockNumber <<= 9; + } + if (cardCommand(CMD18, blockNumber)) { + error(SD_CARD_ERROR_CMD18); + goto fail; + } + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readStop() { + if (cardCommand(CMD12, 0)) { + error(SD_CARD_ERROR_CMD12); + goto fail; + } + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +// wait for card to go not busy +bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) { + uint16_t t0 = millis(); + while (spiReceive() != 0XFF) { + if (((uint16_t)millis() - t0) >= timeoutMillis) { + goto fail; + } + spiYield(); + } + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { + SD_TRACE("WB", blockNumber); + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) { + blockNumber <<= 9; + } + if (cardCommand(CMD24, blockNumber)) { + error(SD_CARD_ERROR_CMD24); + goto fail; + } + if (!writeData(DATA_START_BLOCK, src)) { + goto fail; + } + +#define CHECK_PROGRAMMING 0 +#if CHECK_PROGRAMMING + // wait for flash programming to complete + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + error(SD_CARD_ERROR_WRITE_TIMEOUT); + goto fail; + } + // response is r2 so get and check two bytes for nonzero + if (cardCommand(CMD13, 0) || spiReceive()) { + error(SD_CARD_ERROR_WRITE_PROGRAMMING); + goto fail; + } +#endif // CHECK_PROGRAMMING + + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { + if (!writeStart(block, count)) { + return false; + } + for (size_t b = 0; b < count; b++, src += 512) { + if (!writeData(src)) { + return false; + } + } + return writeStop(); +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeData(const uint8_t* src) { + chipSelectLow(); + // wait for previous write to finish + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + goto fail; + } + if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { + goto fail; + } + chipSelectHigh(); + return true; + +fail: + error(SD_CARD_ERROR_WRITE_MULTIPLE); + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +// send one block of data for write block or write multiple blocks +bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) { +#if USE_SD_CRC + uint16_t crc = CRC_CCITT(src, 512); +#else // USE_SD_CRC + uint16_t crc = 0XFFFF; +#endif // USE_SD_CRC + spiSend(token); + spiSend(src, 512); + spiSend(crc >> 8); + spiSend(crc & 0XFF); + + m_status = spiReceive(); + if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) { + error(SD_CARD_ERROR_WRITE); + goto fail; + } + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { + SD_TRACE("WS", blockNumber); + // send pre-erase count + if (cardAcmd(ACMD23, eraseCount)) { + error(SD_CARD_ERROR_ACMD23); + goto fail; + } + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) { + blockNumber <<= 9; + } + if (cardCommand(CMD25, blockNumber)) { + error(SD_CARD_ERROR_CMD25); + goto fail; + } + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeStop() { + chipSelectLow(); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + goto fail; + } + spiSend(STOP_TRAN_TOKEN); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + goto fail; + } + chipSelectHigh(); + return true; + +fail: + error(SD_CARD_ERROR_STOP_TRAN); + chipSelectHigh(); + return false; +} diff --git a/lib/SdFat/SdSpiCard.h b/lib/SdFat/SdSpiCard.h new file mode 100644 index 0000000..ab2d0a3 --- /dev/null +++ b/lib/SdFat/SdSpiCard.h @@ -0,0 +1,308 @@ +/* Arduino SdSpiCard Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdSpiCard Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpiCard Library. If not, see + * . + */ +#ifndef SpiCard_h +#define SpiCard_h +/** + * \file + * \brief SdSpiCard class for V2 SD/SDHC cards + */ +#include +#include +#include +#include +//============================================================================== +/** + * \class SdSpiCard + * \brief Raw access to SD and SDHC flash memory cards via SPI protocol. + */ +class SdSpiCard { + public: + /** typedef for SPI class. */ +#if SD_SPI_CONFIGURATION < 3 + typedef SpiDefault_t m_spi_t; +#else // SD_SPI_CONFIGURATION < 3 + typedef SdSpiBase m_spi_t; +#endif // SD_SPI_CONFIGURATION < 3 + /** Construct an instance of SdSpiCard. */ + SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} + /** Initialize the SD card. + * \param[in] spi SPI object. + * \param[in] chipSelectPin SD chip select pin. + * \param[in] sckDivisor SPI clock divisor. + * \return true for success else false. + */ + bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, + uint8_t sckDivisor = SPI_FULL_SPEED); + /** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte data blocks in the card + * or zero if an error occurs. + */ + uint32_t cardSize(); + /** Erase a range of blocks. + * + * \param[in] firstBlock The address of the first block in the range. + * \param[in] lastBlock The address of the last block in the range. + * + * \note This function requests the SD card to do a flash erase for a + * range of blocks. The data on the card after an erase operation is + * either 0 or 1, depends on the card vendor. The card must support + * single block erase. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool erase(uint32_t firstBlock, uint32_t lastBlock); + /** Determine if card supports single block erase. + * + * \return true is returned if single block erase is supported. + * false is returned if single block erase is not supported. + */ + bool eraseSingleBlockEnable(); + /** + * Set SD error code. + * \param[in] code value for error code. + */ + void error(uint8_t code) { + m_errorCode = code; + } + /** + * \return code for the last error. See SdSpiCard.h for a list of error codes. + */ + int errorCode() const { + return m_errorCode; + } + /** \return error data for last error. */ + int errorData() const { + return m_status; + } + /** + * Check for busy. MISO low indicates the card is busy. + * + * \return true if busy else false. + */ + bool isBusy(); + /** + * Read a 512 byte block from an SD card. + * + * \param[in] block Logical block to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool readBlock(uint32_t block, uint8_t* dst); + /** + * Read multiple 512 byte blocks from an SD card. + * + * \param[in] block Logical block to be read. + * \param[in] count Number of blocks to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool readBlocks(uint32_t block, uint8_t* dst, size_t count); + /** + * Read a card's CID register. The CID contains card identification + * information such as Manufacturer ID, Product name, Product serial + * number and Manufacturing date. + * + * \param[out] cid pointer to area for returned data. + * + * \return true for success or false for failure. + */ + bool readCID(cid_t* cid) { + return readRegister(CMD10, cid); + } + /** + * Read a card's CSD register. The CSD contains Card-Specific Data that + * provides information regarding access to the card's contents. + * + * \param[out] csd pointer to area for returned data. + * + * \return true for success or false for failure. + */ + bool readCSD(csd_t* csd) { + return readRegister(CMD9, csd); + } + /** Read one data block in a multiple block read sequence + * + * \param[out] dst Pointer to the location for the data to be read. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool readData(uint8_t *dst); + /** Read OCR register. + * + * \param[out] ocr Value of OCR register. + * \return true for success else false. + */ + bool readOCR(uint32_t* ocr); + /** Start a read multiple blocks sequence. + * + * \param[in] blockNumber Address of first block in sequence. + * + * \note This function is used with readData() and readStop() for optimized + * multiple block reads. SPI chipSelect must be low for the entire sequence. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool readStart(uint32_t blockNumber); + /** End a read multiple blocks sequence. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool readStop(); + /** Return SCK divisor. + * + * \return Requested SCK divisor. + */ + uint8_t sckDivisor() { + return m_sckDivisor; + } + /** Return the card type: SD V1, SD V2 or SDHC + * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. + */ + int type() const { + return m_type; + } + /** + * Writes a 512 byte block to an SD card. + * + * \param[in] blockNumber Logical block to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool writeBlock(uint32_t blockNumber, const uint8_t* src); + /** + * Write multiple 512 byte blocks to an SD card. + * + * \param[in] block Logical block to be written. + * \param[in] count Number of blocks to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool writeBlocks(uint32_t block, const uint8_t* src, size_t count); + /** Write one data block in a multiple block write sequence + * \param[in] src Pointer to the location of the data to be written. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool writeData(const uint8_t* src); + /** Start a write multiple blocks sequence. + * + * \param[in] blockNumber Address of first block in sequence. + * \param[in] eraseCount The number of blocks to be pre-erased. + * + * \note This function is used with writeData() and writeStop() + * for optimized multiple block writes. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool writeStart(uint32_t blockNumber, uint32_t eraseCount); + /** End a write multiple blocks sequence. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool writeStop(); + + private: + // 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); + bool readData(uint8_t* dst, size_t count); + bool readRegister(uint8_t cmd, void* buf); + void chipSelectHigh(); + void chipSelectLow(); + void spiYield(); + void type(uint8_t value) { + m_type = value; + } + bool waitNotBusy(uint16_t timeoutMillis); + bool writeData(uint8_t token, const uint8_t* src); + void spiBegin() { + m_spi->begin(); + } + void spiInit(uint8_t spiDivisor) { + m_spi->init(spiDivisor); + } + uint8_t spiReceive() { + return m_spi->receive(); + } + uint8_t spiReceive(uint8_t* buf, size_t n) { + return m_spi->receive(buf, n); + } + void spiSend(uint8_t data) { + m_spi->send(data); + } + void spiSend(const uint8_t* buf, size_t n) { + m_spi->send(buf, n); + } + bool useSpiTransactions() { + return m_spi->useSpiTransactions(); + } + m_spi_t* m_spi; + uint8_t m_chipSelectPin; + uint8_t m_errorCode; + uint8_t m_sckDivisor; + uint8_t m_status; + uint8_t m_type; +}; +//============================================================================== +/** + * \class Sd2Card + * \brief Raw access to SD and SDHC card using default SPI library. + */ +class Sd2Card : public SdSpiCard { + public: + /** Initialize the SD card. + * \param[in] chipSelectPin SD chip select pin. + * \param[in] sckDivisor SPI clock divisor. + * \return true for success else false. + */ + bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) { + return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor); + } + /** Initialize the SD card. Obsolete form. + * \param[in] chipSelectPin SD chip select pin. + * \param[in] sckDivisor SPI clock divisor. + * \return true for success else false. + */ + bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) { + return begin(chipSelectPin, sckDivisor); + } + private: + bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS, + uint8_t sckDivisor = SPI_FULL_SPEED) { + return false; + } + SpiDefault_t m_spi; +}; +#endif // SpiCard_h diff --git a/lib/SdFat/SdSpiSAM3X.cpp b/lib/SdFat/SdSpiSAM3X.cpp new file mode 100644 index 0000000..2298009 --- /dev/null +++ b/lib/SdFat/SdSpiSAM3X.cpp @@ -0,0 +1,218 @@ +/* Arduino SdSpi Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the Arduino SdSpi Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpi Library. If not, see + * . + */ +#include "SdSpi.h" +#if defined(__SAM3X8E__) || defined(__SAM3X8H__) +/** Use SAM3X DMAC if nonzero */ +#define USE_SAM3X_DMAC 1 +/** Use extra Bus Matrix arbitration fix if nonzero */ +#define USE_SAM3X_BUS_MATRIX_FIX 0 +/** Time in ms for DMA receive timeout */ +#define SAM3X_DMA_TIMEOUT 100 +/** chip select register number */ +#define SPI_CHIP_SEL 3 +/** DMAC receive channel */ +#define SPI_DMAC_RX_CH 1 +/** DMAC transmit channel */ +#define SPI_DMAC_TX_CH 0 +/** DMAC Channel HW Interface Number for SPI TX. */ +#define SPI_TX_IDX 1 +/** DMAC Channel HW Interface Number for SPI RX. */ +#define SPI_RX_IDX 2 +//------------------------------------------------------------------------------ +/** Disable DMA Controller. */ +static void dmac_disable() { + DMAC->DMAC_EN &= (~DMAC_EN_ENABLE); +} +/** Enable DMA Controller. */ +static void dmac_enable() { + DMAC->DMAC_EN = DMAC_EN_ENABLE; +} +/** Disable DMA Channel. */ +static void dmac_channel_disable(uint32_t ul_num) { + DMAC->DMAC_CHDR = DMAC_CHDR_DIS0 << ul_num; +} +/** Enable DMA Channel. */ +static void dmac_channel_enable(uint32_t ul_num) { + DMAC->DMAC_CHER = DMAC_CHER_ENA0 << ul_num; +} +/** Poll for transfer complete. */ +static bool dmac_channel_transfer_done(uint32_t ul_num) { + return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; +} +//------------------------------------------------------------------------------ +void SdSpi::begin() { + PIO_Configure( + g_APinDescription[PIN_SPI_MOSI].pPort, + g_APinDescription[PIN_SPI_MOSI].ulPinType, + g_APinDescription[PIN_SPI_MOSI].ulPin, + g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); + PIO_Configure( + g_APinDescription[PIN_SPI_MISO].pPort, + g_APinDescription[PIN_SPI_MISO].ulPinType, + g_APinDescription[PIN_SPI_MISO].ulPin, + g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); + PIO_Configure( + g_APinDescription[PIN_SPI_SCK].pPort, + g_APinDescription[PIN_SPI_SCK].ulPinType, + g_APinDescription[PIN_SPI_SCK].ulPin, + g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); + pmc_enable_periph_clk(ID_SPI0); +#if USE_SAM3X_DMAC + pmc_enable_periph_clk(ID_DMAC); + dmac_disable(); + DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED; + dmac_enable(); +#if USE_SAM3X_BUS_MATRIX_FIX + MATRIX->MATRIX_WPMR = 0x4d415400; + MATRIX->MATRIX_MCFG[1] = 1; + MATRIX->MATRIX_MCFG[2] = 1; + MATRIX->MATRIX_SCFG[0] = 0x01000010; + MATRIX->MATRIX_SCFG[1] = 0x01000010; + MATRIX->MATRIX_SCFG[7] = 0x01000010; +#endif // USE_SAM3X_BUS_MATRIX_FIX +#endif // USE_SAM3X_DMAC +} +//------------------------------------------------------------------------------ +// start RX DMA +static void spiDmaRX(uint8_t* dst, uint16_t count) { + dmac_channel_disable(SPI_DMAC_RX_CH); + DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_SADDR = (uint32_t)&SPI0->SPI_RDR; + DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst; + DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0; + DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count | + DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE; + DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | + DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC | + DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING; + DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) | + DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG; + dmac_channel_enable(SPI_DMAC_RX_CH); +} +//------------------------------------------------------------------------------ +// start TX DMA +static void spiDmaTX(const uint8_t* src, uint16_t count) { + static uint8_t ff = 0XFF; + uint32_t src_incr = DMAC_CTRLB_SRC_INCR_INCREMENTING; + if (!src) { + src = &ff; + src_incr = DMAC_CTRLB_SRC_INCR_FIXED; + } + dmac_channel_disable(SPI_DMAC_TX_CH); + DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)src; + DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR; + DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0; + DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count | + DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE; + + DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | + DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC | + src_incr | DMAC_CTRLB_DST_INCR_FIXED; + + DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) | + DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG; + + dmac_channel_enable(SPI_DMAC_TX_CH); +} +//------------------------------------------------------------------------------ +// initialize SPI controller +void SdSpi::init(uint8_t sckDivisor) { + uint8_t scbr = sckDivisor; + Spi* pSpi = SPI0; + // disable SPI + pSpi->SPI_CR = SPI_CR_SPIDIS; + // reset SPI + pSpi->SPI_CR = SPI_CR_SWRST; + // no mode fault detection, set master mode + pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; + // mode 0, 8-bit, + pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr) | SPI_CSR_NCPHA; + // enable SPI + pSpi->SPI_CR |= SPI_CR_SPIEN; +} +//------------------------------------------------------------------------------ +static inline uint8_t spiTransfer(uint8_t b) { + Spi* pSpi = SPI0; + + pSpi->SPI_TDR = b; + while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} + b = pSpi->SPI_RDR; + return b; +} +//------------------------------------------------------------------------------ +/** SPI receive a byte */ +uint8_t SdSpi::receive() { + return spiTransfer(0XFF); +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +uint8_t SdSpi::receive(uint8_t* buf, size_t n) { + Spi* pSpi = SPI0; + int rtn = 0; +#if USE_SAM3X_DMAC + // clear overrun error + uint32_t s = pSpi->SPI_SR; + + spiDmaRX(buf, n); + spiDmaTX(0, n); + + uint32_t m = millis(); + while (!dmac_channel_transfer_done(SPI_DMAC_RX_CH)) { + if ((millis() - m) > SAM3X_DMA_TIMEOUT) { + dmac_channel_disable(SPI_DMAC_RX_CH); + dmac_channel_disable(SPI_DMAC_TX_CH); + rtn = 2; + break; + } + } + if (pSpi->SPI_SR & SPI_SR_OVRES) { + rtn |= 1; + } +#else // USE_SAM3X_DMAC + for (size_t i = 0; i < n; i++) { + pSpi->SPI_TDR = 0XFF; + while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} + buf[i] = pSpi->SPI_RDR; + } +#endif // USE_SAM3X_DMAC + return rtn; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +void SdSpi::send(uint8_t b) { + spiTransfer(b); +} +//------------------------------------------------------------------------------ +void SdSpi::send(const uint8_t* buf , size_t n) { + Spi* pSpi = SPI0; +#if USE_SAM3X_DMAC + spiDmaTX(buf, n); + while (!dmac_channel_transfer_done(SPI_DMAC_TX_CH)) {} +#else // #if USE_SAM3X_DMAC + while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} + for (size_t i = 0; i < n; i++) { + pSpi->SPI_TDR = buf[i]; + while ((pSpi->SPI_SR & SPI_SR_TDRE) == 0) {} + } +#endif // #if USE_SAM3X_DMAC + while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} + // leave RDR empty + uint8_t b = pSpi->SPI_RDR; +} +#endif // defined(__SAM3X8E__) || defined(__SAM3X8H__) diff --git a/lib/SdFat/SdSpiSTM32F1.cpp b/lib/SdFat/SdSpiSTM32F1.cpp new file mode 100644 index 0000000..a36d5a1 --- /dev/null +++ b/lib/SdFat/SdSpiSTM32F1.cpp @@ -0,0 +1,171 @@ +/* Arduino SdSpi Library + * Copyright (C) 2013 by William Greiman + * + * STM32F1 code for Maple and Maple Mini support, 2015 by Victor Perez + * + * This file is part of the Arduino SdSpi Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpi Library. If not, see + * . + */ +#if defined(__STM32F1__) +#include "SdSpi.h" +#include +#include +/** Use STM32 DMAC if nonzero */ +#define USE_STM32F1_DMAC 1 +/** Time in ms for DMA receive timeout */ +#define STM32F1_DMA_TIMEOUT 100 +/** DMAC receive channel */ +#define SPI1_DMAC_RX_CH DMA_CH2 +/** DMAC transmit channel */ +#define SPI1_DMAC_TX_CH DMA_CH3 + +volatile bool SPI_DMA_TX_Active = false; +volatile bool SPI_DMA_RX_Active = false; + +/** ISR for DMA TX event. */ +inline void SPI_DMA_TX_Event() { + SPI_DMA_TX_Active = false; + dma_disable(DMA1, SPI_DMAC_TX_CH); +} + +/** ISR for DMA RX event. */ +inline void SPI_DMA_RX_Event() { + SPI_DMA_RX_Active = false; + dma_disable(DMA1, SPI1_DMAC_RX_CH); +} +//------------------------------------------------------------------------------ + +/** Disable DMA Channel. */ +static void dmac_channel_disable(dma_channel ul_num) { + dma_disable(DMA1, ul_num); +} +/** Enable DMA Channel. */ +static void dmac_channel_enable(dma_channel ul_num) { + dma_enable(DMA1, ul_num); +} +//------------------------------------------------------------------------------ +void SdSpi::begin() { + SPI.begin(); +} +//------------------------------------------------------------------------------ +// start RX DMA + +static void spiDmaRX(uint8_t* dst, uint16_t count) { +// spi_rx_dma_enable(SPI1); + if (count < 1) return; + dma_setup_transfer(DMA1, SPI1_DMAC_RX_CH, &SPI1->regs->DR, DMA_SIZE_8BITS, + dst, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT)); + dma_set_num_transfers(DMA1, SPI1_DMAC_RX_CH, count); // 2 bytes per pixel + SPI_DMA_RX_Active = true; + dma_enable(DMA1, SPI1_DMAC_RX_CH); +} +//------------------------------------------------------------------------------ +// start TX DMA +static void spiDmaTX(const uint8_t* src, uint16_t count) { + if (count < 1) return; + static uint8_t ff = 0XFF; + + if (!src) { + src = &ff; + dma_setup_transfer(DMA1, SPI1_DMAC_TX_CH, &SPI1->regs->DR, DMA_SIZE_8BITS, + const_cast(src), DMA_SIZE_8BITS, + (DMA_FROM_MEM | DMA_TRNS_CMPLT)); + } else { + dma_setup_transfer(DMA1, SPI1_DMAC_TX_CH, &SPI1->regs->DR, DMA_SIZE_8BITS, + const_cast(src), DMA_SIZE_8BITS, + (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT)); + } + dma_set_num_transfers(DMA1, SPI1_DMAC_TX_CH, count); // 2 bytes per pixel + SPI_DMA_TX_Active = true; + dma_enable(DMA1, SPI1_DMAC_TX_CH); +} +//------------------------------------------------------------------------------ +// initialize SPI controller STM32F1 +void SdSpi::init(uint8_t sckDivisor) { + if (sckDivisor < SPI_CLOCK_DIV2 || sckDivisor > SPI_CLOCK_DIV256) { + sckDivisor = SPI_CLOCK_DIV2; // may not be needed, testing. + } + SPI.setClockDivider(sckDivisor); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + +#if USE_STM32F1_DMAC + dma_init(DMA1); + dma_attach_interrupt(DMA1, SPI1_DMAC_TX_CH, SPI_DMA_TX_Event); + dma_attach_interrupt(DMA1, SPI1_DMAC_RX_CH, SPI_DMA_RX_Event); + spi_tx_dma_enable(SPI1); + spi_rx_dma_enable(SPI1); +#endif // USE_STM32F1_DMAC +} +//------------------------------------------------------------------------------ +// STM32 +static inline uint8_t spiTransfer(uint8_t b) { + return SPI.transfer(b); +} +//------------------------------------------------------------------------------ +// should be valid for STM32 +/** SPI receive a byte */ +uint8_t SdSpi::receive() { + return spiTransfer(0xFF); +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +// check and finish. + +uint8_t SdSpi::receive(uint8_t* buf, size_t n) { + int rtn = 0; + +#if USE_STM32F1_DMAC + + spiDmaRX(buf, n); + spiDmaTX(0, n); + + uint32_t m = millis(); + while (SPI_DMA_RX_Active) { + if ((millis() - m) > STM32F1_DMA_TIMEOUT) { + dmac_channel_disable(SPI_DMAC_RX_CH); + dmac_channel_disable(SPI_DMAC_TX_CH); + rtn = 2; + break; + } + } + +#else // USE_STM32F1_DMAC + for (size_t i = 0; i < n; i++) { + buf[i] = SPI.transfer(0xFF); + } +#endif // USE_STM32F1_DMAC + return rtn; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +void SdSpi::send(uint8_t b) { + spiTransfer(b); +} +//------------------------------------------------------------------------------ +void SdSpi::send(const uint8_t* buf , size_t n) { +#if USE_STM32F1_DMAC + spiDmaTX(buf, n); + while (SPI_DMA_TX_Active) {} + +#else // #if USE_STM32F1_DMAC + SPI.write(buf, n); +#endif // #if USE_STM32F1_DMAC + // leave RX register empty + // while (spi_is_rx_nonempty(SPI1)) + uint8_t b = spi_rx_reg(SPI1); +} +#endif // USE_NATIVE_STM32F1_SPI diff --git a/lib/SdFat/SdSpiTeensy3.cpp b/lib/SdFat/SdSpiTeensy3.cpp new file mode 100644 index 0000000..a9fe8b3 --- /dev/null +++ b/lib/SdFat/SdSpiTeensy3.cpp @@ -0,0 +1,301 @@ +/* Arduino SdSpi Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the Arduino SdSpi Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdSpi Library. If not, see + * . + */ +#include "SdSpi.h" +#if defined(__arm__) && defined(CORE_TEENSY) +// SPI definitions +#include "kinetis.h" +#ifdef KINETISK +// use 16-bit frame if SPI_USE_8BIT_FRAME is zero +#define SPI_USE_8BIT_FRAME 0 +// Limit initial fifo to three entries to avoid fifo overrun +#define SPI_INITIAL_FIFO_DEPTH 3 +// define some symbols that are not in mk20dx128.h +#ifndef SPI_SR_RXCTR +#define SPI_SR_RXCTR 0XF0 +#endif // SPI_SR_RXCTR +#ifndef SPI_PUSHR_CONT +#define SPI_PUSHR_CONT 0X80000000 +#endif // SPI_PUSHR_CONT +#ifndef SPI_PUSHR_CTAS +#define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) +#endif // SPI_PUSHR_CTAS +//------------------------------------------------------------------------------ +/** + * initialize SPI pins + */ +void SdSpi::begin() { + SIM_SCGC6 |= SIM_SCGC6_SPI0; +} +//------------------------------------------------------------------------------ +/** + * Initialize hardware SPI + * + */ +void SdSpi::init(uint8_t sckDivisor) { + uint32_t ctar, ctar0, ctar1; + + if (sckDivisor <= 2) { + // 1/2 speed + ctar = SPI_CTAR_DBR | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); + } else if (sckDivisor <= 4) { + // 1/4 speed + ctar = SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); + } else if (sckDivisor <= 8) { + // 1/8 speed + ctar = SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); + } else if (sckDivisor <= 12) { + // 1/12 speed + ctar = SPI_CTAR_BR(2) | SPI_CTAR_CSSCK(2); + } else if (sckDivisor <= 16) { + // 1/16 speed + ctar = SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(3); + } else if (sckDivisor <= 32) { + // 1/32 speed + ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(4); + } else if (sckDivisor <= 64) { + // 1/64 speed + ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5); + } else { + // 1/128 speed + ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(6); + } + // CTAR0 - 8 bit transfer + ctar0 = ctar | SPI_CTAR_FMSZ(7); + + // CTAR1 - 16 bit transfer + ctar1 = ctar | SPI_CTAR_FMSZ(15); + + if (SPI0_CTAR0 != ctar0 || SPI0_CTAR1 != ctar1) { + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); + SPI0_CTAR0 = ctar0; + SPI0_CTAR1 = ctar1; + } + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); + CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN12_CONFIG = PORT_PCR_MUX(2); + CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); +} +//------------------------------------------------------------------------------ +/** SPI receive a byte */ +uint8_t SdSpi::receive() { + SPI0_MCR |= SPI_MCR_CLR_RXF; + SPI0_SR = SPI_SR_TCF; + SPI0_PUSHR = 0xFF; + while (!(SPI0_SR & SPI_SR_TCF)) {} + return SPI0_POPR; +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +uint8_t SdSpi::receive(uint8_t* buf, size_t n) { + // clear any data in RX FIFO + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); +#if SPI_USE_8BIT_FRAME + // initial number of bytes to push into TX FIFO + int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; + for (int i = 0; i < nf; i++) { + SPI0_PUSHR = 0XFF; + } + // limit for pushing dummy data into TX FIFO + uint8_t* limit = buf + n - nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = 0XFF; + *buf++ = SPI0_POPR; + } + // limit for rest of RX data + limit += nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + *buf++ = SPI0_POPR; + } +#else // SPI_USE_8BIT_FRAME + // use 16 bit frame to avoid TD delay between frames + // get one byte if n is odd + if (n & 1) { + *buf++ = receive(); + n--; + } + // initial number of words to push into TX FIFO + int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; + for (int i = 0; i < nf; i++) { + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; + } + uint8_t* limit = buf + n - 2*nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; + uint16_t w = SPI0_POPR; + *buf++ = w >> 8; + *buf++ = w & 0XFF; + } + // limit for rest of RX data + limit += 2*nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + uint16_t w = SPI0_POPR; + *buf++ = w >> 8; + *buf++ = w & 0XFF; + } +#endif // SPI_USE_8BIT_FRAME + return 0; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +void SdSpi::send(uint8_t b) { + SPI0_MCR |= SPI_MCR_CLR_RXF; + SPI0_SR = SPI_SR_TCF; + SPI0_PUSHR = b; + while (!(SPI0_SR & SPI_SR_TCF)) {} +} +//------------------------------------------------------------------------------ +/** SPI send multiple bytes */ +void SdSpi::send(const uint8_t* buf , size_t n) { + // clear any data in RX FIFO + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); +#if SPI_USE_8BIT_FRAME + // initial number of bytes to push into TX FIFO + int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; + // limit for pushing data into TX fifo + const uint8_t* limit = buf + n; + for (int i = 0; i < nf; i++) { + SPI0_PUSHR = *buf++; + } + // write data to TX FIFO + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = *buf++; + SPI0_POPR; + } + // wait for data to be sent + while (nf) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_POPR; + nf--; + } +#else // SPI_USE_8BIT_FRAME + // use 16 bit frame to avoid TD delay between frames + // send one byte if n is odd + if (n & 1) { + send(*buf++); + n--; + } + // initial number of words to push into TX FIFO + int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; + // limit for pushing data into TX fifo + const uint8_t* limit = buf + n; + for (int i = 0; i < nf; i++) { + uint16_t w = (*buf++) << 8; + w |= *buf++; + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; + } + // write data to TX FIFO + while (buf < limit) { + uint16_t w = *buf++ << 8; + w |= *buf++; + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; + SPI0_POPR; + } + // wait for data to be sent + while (nf) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_POPR; + nf--; + } +#endif // SPI_USE_8BIT_FRAME +} +#else // KINETISK +//============================================================================== +// Use standard SPI library if not KINETISK +#include "SPI.h" +/** + * Initialize SPI pins. + */ +void SdSpi::begin() { + SPI.begin(); +} +/** Set SPI options for access to SD/SDHC cards. + * + * \param[in] divisor SCK clock divider relative to the system clock. + */ +void SdSpi::init(uint8_t divisor) { + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); +#ifndef SPI_CLOCK_DIV128 + SPI.setClockDivider(divisor); +#else // SPI_CLOCK_DIV128 + int v; + if (divisor <= 2) { + v = SPI_CLOCK_DIV2; + } else if (divisor <= 4) { + v = SPI_CLOCK_DIV4; + } else if (divisor <= 8) { + v = SPI_CLOCK_DIV8; + } else if (divisor <= 16) { + v = SPI_CLOCK_DIV16; + } else if (divisor <= 32) { + v = SPI_CLOCK_DIV32; + } else if (divisor <= 64) { + v = SPI_CLOCK_DIV64; + } else { + v = SPI_CLOCK_DIV128; + } + SPI.setClockDivider(v); +#endif // SPI_CLOCK_DIV128 +} +/** Receive a byte. + * + * \return The byte. + */ +uint8_t SdSpi::receive() { + return SPI.transfer(0XFF); +} +/** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] n Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ +uint8_t SdSpi::receive(uint8_t* buf, size_t n) { + for (size_t i = 0; i < n; i++) { + buf[i] = SPI.transfer(0XFF); + } + return 0; +} +/** Send a byte. + * + * \param[in] b Byte to send + */ +void SdSpi::send(uint8_t b) { + SPI.transfer(b); +} +/** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] n Number of bytes to send. + */ +void SdSpi::send(const uint8_t* buf , size_t n) { + for (size_t i = 0; i < n; i++) { + SPI.transfer(buf[i]); + } +} +#endif // KINETISK +#endif // defined(__arm__) && defined(CORE_TEENSY) diff --git a/lib/SdFat/SdVolume.h b/lib/SdFat/SdVolume.h new file mode 100644 index 0000000..f76db7a --- /dev/null +++ b/lib/SdFat/SdVolume.h @@ -0,0 +1,71 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#ifndef SdVolume_h +#include "SdSpiCard.h" +#include "utility/FatLib.h" +#define SdVolume_h +#ifndef USE_SD_VOLUME +#error SdVolume is deperacated. Remove this line to continue using this class. +#endif // USE_SD_VOLUME +//============================================================================== +/** + * \class SdVolume + * \brief SdVolume Soon to be removed. + */ +class SdVolume : public FatVolume { + public: + /** Initialize a FAT volume. Try partition one first then try super + * floppy format. + * + * \param[in] dev The Sd2Card where the volume is located. + * + * \return true for success else false. + */ + bool init(Sd2Card* dev) { + return init(dev, 1) ? true : init(dev, 0); + } + /** Initialize a FAT volume. + * + * \param[in] dev The Sd2Card where the volume is located. + * \param[in] part the partition to use. Zero for super floppy or 1-4. + * \return true for success else false. + */ + bool init(Sd2Card* dev, uint8_t part) { + m_sdCard = dev; + return FatVolume::init(part); + } + + private: +// friend class FatFile; + bool readBlock(uint32_t block, uint8_t* dst) { + return m_sdCard->readBlock(block, dst); + } + bool writeBlock(uint32_t block, const uint8_t* src) { + return m_sdCard->writeBlock(block, src); + } + bool readBlocks(uint32_t block, uint8_t* dst, size_t n) { + return m_sdCard->readBlocks(block, dst, n); + } + bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) { + return m_sdCard->writeBlocks(block, src, n); + } + Sd2Card* m_sdCard; // Sd2Card object for cache +}; +#endif // SdVolume_h diff --git a/lib/SdFat/utility/ArduinoFiles.h b/lib/SdFat/utility/ArduinoFiles.h new file mode 100644 index 0000000..da8e8cb --- /dev/null +++ b/lib/SdFat/utility/ArduinoFiles.h @@ -0,0 +1,248 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +/** + * \file + * \brief PrintFile class + */ +#ifndef ArduinoFiles_h +#define ArduinoFiles_h +#include "FatLibConfig.h" +#if ENABLE_ARDUINO_FEATURES +#include "FatFile.h" +#include +//------------------------------------------------------------------------------ +/** Arduino SD.h style flag for open for read. */ +#define FILE_READ O_READ +/** Arduino SD.h style flag for open at EOF for read/write with create. */ +#define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END) +//============================================================================== +/** + * \class PrintFile + * \brief FatFile with Print. + */ +class PrintFile : public FatFile, public Print { + public: + PrintFile() {} + /** Create a file object and open it in the current working directory. + * + * \param[in] path A path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of open flags. see + * FatFile::open(FatFile*, const char*, uint8_t). + */ + PrintFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} +#if DESTRUCTOR_CLOSES_FILE + ~PrintFile() {} +#endif // DESTRUCTOR_CLOSES_FILE + using FatFile::clearWriteError; + using FatFile::getWriteError; + using FatFile::read; + using FatFile::write; + /** \return number of bytes available from the current position to EOF + * or INT_MAX if more than INT_MAX bytes are available. + */ + int available() { + uint32_t n = FatFile::available(); + return n > INT_MAX ? INT_MAX : n; + } + /** Ensure that any bytes written to the file are saved to the SD card. */ + void flush() { + FatFile::sync(); + } + /** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ + int peek() { + return FatFile::peek(); + } + /** Read the next byte from a file. + * + * \return For success return the next byte in the file as an int. + * If an error occurs or end of file is reached return -1. + */ +// int read() { +// return FatFile::read(); +// } + /** Write a byte to a file. Required by the Arduino Print class. + * \param[in] b the byte to be written. + * Use getWriteError to check for errors. + * \return 1 for success and 0 for failure. + */ + size_t write(uint8_t b) { + return FatFile::write(b); + } + /** Write data to an open file. Form required by Print. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buf Pointer to the location of the data to be written. + * + * \param[in] size Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a nbyte. If an error occurs, write() returns -1. Possible errors + * include write() is called before a file has been opened, write is called + * for a read-only file, device is full, a corrupt file system or an + * I/O error. + */ + size_t write(const uint8_t *buf, size_t size) { + return FatFile::write(buf, size); + } +}; +//============================================================================== +/** + * \class File + * \brief Arduino SD.h style File API + */ +#if ARDUINO_FILE_USES_STREAM +class File : public FatFile, public Stream { +#else // ARDUINO_FILE_USES_STREAM +class File : public FatFile, public Print { +#endif // ARDUINO_FILE_USES_STREAM + public: + File() {} + /** Create a file object and open it in the current working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of open flags. see + * FatFile::open(FatFile*, const char*, uint8_t). + */ + File(const char* path, uint8_t oflag) { + open(path, oflag); + } + using FatFile::clearWriteError; + using FatFile::getWriteError; + using FatFile::read; + using FatFile::write; + /** The parenthesis operator. + * + * \return true if a file is open. + */ + operator bool() { + return isOpen(); + } + /** \return number of bytes available from the current position to EOF + * or INT_MAX if more than INT_MAX bytes are available. + */ + int available() { + uint32_t n = FatFile::available(); + return n > INT_MAX ? INT_MAX : n; + } + /** Ensure that any bytes written to the file are saved to the SD card. */ + void flush() { + FatFile::sync(); + } + /** This function reports if the current file is a directory or not. + * \return true if the file is a directory. + */ + bool isDirectory() { + return isDir(); + } + /** No longer implemented due to Long File Names. + * + * Use getName(char* name, size_t size). + * \return a pointer to replacement suggestion. + */ + const char* name() const { + return "use getName()"; + } + /** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ + int peek() { + return FatFile::peek(); + } + /** \return the current file position. */ + uint32_t position() { + return curPosition(); + } + /** Opens the next file or folder in a directory. + * + * \param[in] mode open mode flags. + * \return a File object. + */ + File openNextFile(uint8_t mode = O_READ) { + File tmpFile; + tmpFile.openNext(this, mode); + return tmpFile; + } + /** Read the next byte from a file. + * + * \return For success return the next byte in the file as an int. + * If an error occurs or end of file is reached return -1. + */ + int read() { + return FatFile::read(); + } + /** Rewind a file if it is a directory */ + void rewindDirectory() { + if (isDir()) { + rewind(); + } + } + /** + * Seek to a new position in the file, which must be between + * 0 and the size of the file (inclusive). + * + * \param[in] pos the new file position. + * \return true for success else false. + */ + bool seek(uint32_t pos) { + return seekSet(pos); + } + /** \return the file's size. */ + uint32_t size() { + return fileSize(); + } + /** Write a byte to a file. Required by the Arduino Print class. + * \param[in] b the byte to be written. + * Use getWriteError to check for errors. + * \return 1 for success and 0 for failure. + */ + size_t write(uint8_t b) { + return FatFile::write(b); + } + /** Write data to an open file. Form required by Print. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buf Pointer to the location of the data to be written. + * + * \param[in] size Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a nbyte. If an error occurs, write() returns -1. Possible errors + * include write() is called before a file has been opened, write is called + * for a read-only file, device is full, a corrupt file system or an + * I/O error. + */ + size_t write(const uint8_t *buf, size_t size) { + return FatFile::write(buf, size); + } +}; +#endif // ENABLE_ARDUINO_FEATURES +#endif // ArduinoFiles_h diff --git a/lib/SdFat/utility/ArduinoStream.h b/lib/SdFat/utility/ArduinoStream.h new file mode 100644 index 0000000..640af61 --- /dev/null +++ b/lib/SdFat/utility/ArduinoStream.h @@ -0,0 +1,141 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef ArduinoStream_h +#define ArduinoStream_h +/** + * \file + * \brief ArduinoInStream and ArduinoOutStream classes + */ +#include "FatLibConfig.h" +#if ENABLE_ARDUINO_FEATURES +#include +#include "bufstream.h" +//============================================================================== +/** + * \class ArduinoInStream + * \brief Input stream for Arduino Stream objects + */ +class ArduinoInStream : public ibufstream { + public: + /** + * Constructor + * \param[in] hws hardware stream + * \param[in] buf buffer for input line + * \param[in] size size of input buffer + */ + ArduinoInStream(Stream &hws, char* buf, size_t size) { + m_hw = &hws; + m_line = buf; + m_size = size; + } + /** read a line. */ + void readline() { + size_t i = 0; + uint32_t t; + m_line[0] = '\0'; + while (!m_hw->available()) {} + + while (1) { + t = millis(); + while (!m_hw->available()) { + if ((millis() - t) > 10) { + goto done; + } + } + if (i >= (m_size - 1)) { + setstate(failbit); + return; + } + m_line[i++] = m_hw->read(); + m_line[i] = '\0'; + } +done: + init(m_line); + } + + protected: + /** Internal - do not use. + * \param[in] off + * \param[in] way + * \return true/false. + */ + bool seekoff(off_type off, seekdir way) { + return false; + } + /** Internal - do not use. + * \param[in] pos + * \return true/false. + */ + bool seekpos(pos_type pos) { + return false; + } + + private: + char *m_line; + size_t m_size; + Stream* m_hw; +}; +//============================================================================== +/** + * \class ArduinoOutStream + * \brief Output stream for Arduino Print objects + */ +class ArduinoOutStream : public ostream { + public: + /** constructor + * + * \param[in] pr Print object for this ArduinoOutStream. + */ + explicit ArduinoOutStream(Print& pr) : m_pr(&pr) {} + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal do not use + * \param[in] c + */ + void putch(char c) { + if (c == '\n') { + m_pr->write('\r'); + } + m_pr->write(c); + } + void putstr(const char* str) { + m_pr->write(str); + } + bool seekoff(off_type off, seekdir way) { + return false; + } + bool seekpos(pos_type pos) { + return false; + } + bool sync() { + return true; + } + pos_type tellpos() { + return 0; + } + /// @endcond + private: + ArduinoOutStream() {} + Print* m_pr; +}; +#endif // ENABLE_ARDUINO_FEATURES +#endif // ArduinoStream_h diff --git a/lib/SdFat/utility/DigitalPin.h b/lib/SdFat/utility/DigitalPin.h new file mode 100644 index 0000000..b2cac38 --- /dev/null +++ b/lib/SdFat/utility/DigitalPin.h @@ -0,0 +1,702 @@ +/* Arduino DigitalIO Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the Arduino DigitalIO Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino DigitalIO Library. If not, see + * . + */ +/** + * @file + * @brief Fast Digital Pin functions + * + * @defgroup digitalPin Fast Pin I/O + * @details Fast Digital I/O functions and template class. + * @{ + */ +#ifndef DigitalPin_h +#define DigitalPin_h +#include +#ifdef __arm__ +#ifdef CORE_TEENSY +//------------------------------------------------------------------------------ +/** read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { + return *portInputRegister(pin); +} +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool value) { + if (value) { + *portSetRegister(pin) = 1; + } else { + *portClearRegister(pin) = 1; + } +} +#else // CORE_TEENSY +//------------------------------------------------------------------------------ +/** read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { + return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin; +} +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool value) { + if (value) { + g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin; + } else { + g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin; + } +} +#endif // CORE_TEENSY +//------------------------------------------------------------------------------ +inline void fastDigitalToggle(uint8_t pin) { + fastDigitalWrite(pin, !fastDigitalRead(pin)); +} +//------------------------------------------------------------------------------ +inline void fastPinMode(uint8_t pin, bool mode) { + pinMode(pin, mode); +} +#else // __arm__ +#include +#include +//------------------------------------------------------------------------------ +/** + * @class pin_map_t + * @brief struct for mapping digital pins + */ +struct pin_map_t { + volatile uint8_t* ddr; /**< address of DDR for this pin */ + volatile uint8_t* pin; /**< address of PIN for this pin */ + volatile uint8_t* port; /**< address of PORT for this pin */ + uint8_t bit; /**< bit number for this pin */ +}; +//------------------------------------------------------------------------------ +#if defined(__AVR_ATmega168__)\ +||defined(__AVR_ATmega168P__)\ +||defined(__AVR_ATmega328P__) +// 168 and 328 Arduinos +static const pin_map_t pinMap[] = { + {&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 +}; +//------------------------------------------------------------------------------ +#elif defined(__AVR_ATmega1280__)\ +|| defined(__AVR_ATmega2560__) +// Mega +static const pin_map_t pinMap[] = { + {&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_ATmega1284P__)\ +|| defined(__AVR_ATmega1284__)\ +|| defined(__AVR_ATmega644P__)\ +|| defined(__AVR_ATmega644__)\ +|| defined(__AVR_ATmega64__)\ +|| defined(__AVR_ATmega32__)\ +|| defined(__AVR_ATmega324__)\ +|| defined(__AVR_ATmega16__) + +#ifdef PORT_D0 +// Newer version of 1284P +#define DPM(x) {PORT_TO_MODE(PORT_D##x), PORT_TO_INPUT(PORT_D##x), \ + PORT_TO_OUTPUT(PORT_D##x), BIT_D##x} + +static const pin_map_t pinMap[] = { + DPM(0), + DPM(1), + DPM(2), + DPM(3), + DPM(4), + DPM(5), + DPM(6), + DPM(7), + DPM(8), + DPM(9), + DPM(10), + DPM(11), + DPM(12), + DPM(13), + DPM(14), + DPM(15), + DPM(16), + DPM(17), + DPM(18), + DPM(19), + DPM(20), + DPM(21), + DPM(22), + DPM(23), + DPM(24), + DPM(25), + DPM(26), + DPM(27), + DPM(28), + DPM(29), + DPM(30), + DPM(31) +}; + +#undef DPM + +#elif analogInputToDigitalPin(0) == 24 +// Mighty Layout +static const pin_map_t pinMap[] = { + {&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, 0}, // A0 24 + {&DDRA, &PINA, &PORTA, 1}, // A1 25 + {&DDRA, &PINA, &PORTA, 2}, // A2 26 + {&DDRA, &PINA, &PORTA, 3}, // A3 27 + {&DDRA, &PINA, &PORTA, 4}, // A4 28 + {&DDRA, &PINA, &PORTA, 5}, // A5 29 + {&DDRA, &PINA, &PORTA, 6}, // A6 30 + {&DDRA, &PINA, &PORTA, 7} // A7 31 +}; +#elif analogInputToDigitalPin(0) == 21 +// Bobuino Layout +static const pin_map_t pinMap[] = { + {&DDRD, &PIND, &PORTD, 0}, // D0 0 + {&DDRD, &PIND, &PORTD, 1}, // D1 1 + {&DDRD, &PIND, &PORTD, 2}, // D2 2 + {&DDRD, &PIND, &PORTD, 3}, // D3 3 + {&DDRB, &PINB, &PORTB, 0}, // B0 4 + {&DDRB, &PINB, &PORTB, 1}, // B1 5 + {&DDRB, &PINB, &PORTB, 2}, // B2 6 + {&DDRB, &PINB, &PORTB, 3}, // B3 7 + {&DDRD, &PIND, &PORTD, 5}, // D5 8 + {&DDRD, &PIND, &PORTD, 6}, // D6 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 + {&DDRA, &PINA, &PORTA, 7}, // A7 14 + {&DDRA, &PINA, &PORTA, 6}, // A6 15 + {&DDRA, &PINA, &PORTA, 5}, // A5 16 + {&DDRA, &PINA, &PORTA, 4}, // A4 17 + {&DDRA, &PINA, &PORTA, 3}, // A3 18 + {&DDRA, &PINA, &PORTA, 2}, // A2 19 + {&DDRA, &PINA, &PORTA, 1}, // A1 20 + {&DDRA, &PINA, &PORTA, 0}, // A0 21 + {&DDRC, &PINC, &PORTC, 0}, // C0 22 + {&DDRC, &PINC, &PORTC, 1}, // C1 23 + {&DDRC, &PINC, &PORTC, 2}, // C2 24 + {&DDRC, &PINC, &PORTC, 3}, // C3 25 + {&DDRC, &PINC, &PORTC, 4}, // C4 26 + {&DDRC, &PINC, &PORTC, 5}, // C5 27 + {&DDRC, &PINC, &PORTC, 6}, // C6 28 + {&DDRC, &PINC, &PORTC, 7}, // C7 29 + {&DDRD, &PIND, &PORTD, 4}, // D4 30 + {&DDRD, &PIND, &PORTD, 7} // D7 31 +}; +#elif analogInputToDigitalPin(0) == 31 +// Standard Layout +static const pin_map_t pinMap[] = { + {&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 +}; +#else // VARIANT_MIGHTY +#error Undefined variant 1284, 644, 324, 64, 32 +#endif // VARIANT_MIGHTY +//------------------------------------------------------------------------------ +#elif defined(__AVR_ATmega32U4__) +#ifdef CORE_TEENSY +// Teensy 2.0 +static const pin_map_t pinMap[] = { + {&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, 7}, // B7 4 + {&DDRD, &PIND, &PORTD, 0}, // D0 5 + {&DDRD, &PIND, &PORTD, 1}, // D1 6 + {&DDRD, &PIND, &PORTD, 2}, // D2 7 + {&DDRD, &PIND, &PORTD, 3}, // D3 8 + {&DDRC, &PINC, &PORTC, 6}, // C6 9 + {&DDRC, &PINC, &PORTC, 7}, // C7 10 + {&DDRD, &PIND, &PORTD, 6}, // D6 11 + {&DDRD, &PIND, &PORTD, 7}, // D7 12 + {&DDRB, &PINB, &PORTB, 4}, // B4 13 + {&DDRB, &PINB, &PORTB, 5}, // B5 14 + {&DDRB, &PINB, &PORTB, 6}, // B6 15 + {&DDRF, &PINF, &PORTF, 7}, // F7 16 + {&DDRF, &PINF, &PORTF, 6}, // F6 17 + {&DDRF, &PINF, &PORTF, 5}, // F5 18 + {&DDRF, &PINF, &PORTF, 4}, // F4 19 + {&DDRF, &PINF, &PORTF, 1}, // F1 20 + {&DDRF, &PINF, &PORTF, 0}, // F0 21 + {&DDRD, &PIND, &PORTD, 4}, // D4 22 + {&DDRD, &PIND, &PORTD, 5}, // D5 23 + {&DDRE, &PINE, &PORTE, 6} // E6 24 +}; +//------------------------------------------------------------------------------ +#else // CORE_TEENSY +// Leonardo +static const pin_map_t pinMap[] = { + {&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, 4}, // D4 24 + {&DDRD, &PIND, &PORTD, 7}, // D7 25 + {&DDRB, &PINB, &PORTB, 4}, // B4 26 + {&DDRB, &PINB, &PORTB, 5}, // B5 27 + {&DDRB, &PINB, &PORTB, 6}, // B6 28 + {&DDRD, &PIND, &PORTD, 6} // D6 29 +}; +#endif // CORE_TEENSY +//------------------------------------------------------------------------------ +#elif defined(__AVR_AT90USB646__)\ +|| defined(__AVR_AT90USB1286__) +// Teensy++ 1.0 & 2.0 +static const pin_map_t pinMap[] = { + {&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 // CPU type +#error unknown CPU type +#endif // CPU type +//------------------------------------------------------------------------------ +/** count of pins */ +static const uint8_t digitalPinCount = sizeof(pinMap)/sizeof(pin_map_t); +//============================================================================== +/** generate bad pin number error */ +void badPinNumber(void) +__attribute__((error("Pin number is too large or not a constant"))); +//------------------------------------------------------------------------------ +/** Check for valid pin number + * @param[in] pin Number of pin to be checked. + */ +static inline __attribute__((always_inline)) +void badPinCheck(uint8_t pin) { + if (!__builtin_constant_p(pin) || pin >= digitalPinCount) { + badPinNumber(); + } +} +//------------------------------------------------------------------------------ +/** fast write helper + * @param[in] address I/O register address + * @param[in] bit bit number to write + * @param[in] level value for bit + */ +static inline __attribute__((always_inline)) +void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) { + uint8_t oldSREG; + if (address > reinterpret_cast(0X5F)) { + oldSREG = SREG; + cli(); + } + if (level) { + *address |= 1 << bit; + } else { + *address &= ~(1 << bit); + } + if (address > reinterpret_cast(0X5F)) { + SREG = oldSREG; + } +} +//------------------------------------------------------------------------------ +/** read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { + badPinCheck(pin); + return (*pinMap[pin].pin >> pinMap[pin].bit) & 1; +} +//------------------------------------------------------------------------------ +/** toggle a pin + * @param[in] pin Arduino pin number + * + * If the pin is in output mode toggle the pin level. + * If the pin is in input mode toggle the state of the 20K pull-up. + */ +static inline __attribute__((always_inline)) +void fastDigitalToggle(uint8_t pin) { + badPinCheck(pin); + if (pinMap[pin].pin > reinterpret_cast(0X5F)) { + // must write bit to high address port + *pinMap[pin].pin = 1 << pinMap[pin].bit; + } else { + // will compile to sbi and PIN register will not be read. + *pinMap[pin].pin |= 1 << pinMap[pin].bit; + } +} +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool level) { + badPinCheck(pin); + fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, level); +} +//------------------------------------------------------------------------------ +/** set pin mode + * @param[in] pin Arduino pin number + * @param[in] mode if true set output mode else input mode + * + * fastPinMode does not enable or disable the 20K pull-up for input mode. + */ +static inline __attribute__((always_inline)) +void fastPinMode(uint8_t pin, bool mode) { + badPinCheck(pin); + fastBitWriteSafe(pinMap[pin].ddr, pinMap[pin].bit, mode); +} + +#endif // __arm__ +//------------------------------------------------------------------------------ +/** set pin configuration + * @param[in] pin Arduino pin number + * @param[in] mode If true set output mode else input mode + * @param[in] level If mode is output, set level high/low. + * If mode is input, enable or disable the pin's 20K pull-up. + */ +static inline __attribute__((always_inline)) +void fastPinConfig(uint8_t pin, bool mode, bool level) { + fastPinMode(pin, mode); + fastDigitalWrite(pin, level); +} +//============================================================================== +/** + * @class DigitalPin + * @brief Fast digital port I/O + */ +template +class DigitalPin { + public: + //---------------------------------------------------------------------------- + /** Constructor */ + DigitalPin() {} + //---------------------------------------------------------------------------- + /** Constructor + * @param[in] pinMode if true set output mode else input mode. + */ + explicit DigitalPin(bool pinMode) { + mode(pinMode); + } + //---------------------------------------------------------------------------- + /** Constructor + * @param[in] mode If true set output mode else input mode + * @param[in] level If mode is output, set level high/low. + * If mode is input, enable or disable the pin's 20K pull-up. + */ + DigitalPin(bool mode, bool level) { + config(mode, level); + } + //---------------------------------------------------------------------------- + /** Asignment operator + * @param[in] value If true set the pin's level high else set the + * pin's level low. + * + * @return This DigitalPin instance. + */ + inline DigitalPin & operator = (bool value) __attribute__((always_inline)) { + write(value); + return *this; + } + //---------------------------------------------------------------------------- + /** Parenthesis operator + * @return Pin's level + */ + inline operator bool() const __attribute__((always_inline)) { + return read(); + } + //---------------------------------------------------------------------------- + /** set pin configuration + * @param[in] mode If true set output mode else input mode + * @param[in] level If mode is output, set level high/low. If mode + * is input, enable or disable the pin's 20K pull-up. + */ + inline __attribute__((always_inline)) + void config(bool mode, bool level) { + fastPinConfig(PinNumber, mode, level); + } + //---------------------------------------------------------------------------- + /** + * Set pin level high if output mode or enable 20K pull-up if input mode. + */ + inline __attribute__((always_inline)) + void high() { + write(true); + } + //---------------------------------------------------------------------------- + /** + * Set pin level low if output mode or disable 20K pull-up if input mode. + */ + inline __attribute__((always_inline)) + void low() { + write(false); + } + //---------------------------------------------------------------------------- + /** + * Set pin mode + * @param[in] pinMode if true set output mode else input mode. + * + * mode() does not enable or disable the 20K pull-up for input mode. + */ + inline __attribute__((always_inline)) + void mode(bool pinMode) { + fastPinMode(PinNumber, pinMode); + } + //---------------------------------------------------------------------------- + /** @return Pin's level */ + inline __attribute__((always_inline)) + bool read() const { + return fastDigitalRead(PinNumber); + } + //---------------------------------------------------------------------------- + /** toggle a pin + * + * If the pin is in output mode toggle the pin's level. + * If the pin is in input mode toggle the state of the 20K pull-up. + */ + inline __attribute__((always_inline)) + void toggle() { + fastDigitalToggle(PinNumber); + } + //---------------------------------------------------------------------------- + /** Write the pin's level. + * @param[in] value If true set the pin's level high else set the + * pin's level low. + */ + inline __attribute__((always_inline)) + void write(bool value) { + fastDigitalWrite(PinNumber, value); + } +}; +#endif // DigitalPin_h +/** @} */ diff --git a/lib/SdFat/utility/FatApiConstants.h b/lib/SdFat/utility/FatApiConstants.h new file mode 100644 index 0000000..9bc5912 --- /dev/null +++ b/lib/SdFat/utility/FatApiConstants.h @@ -0,0 +1,67 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef FatApiConstants_h +#define FatApiConstants_h +//------------------------------------------------------------------------------ +// use the gnu style oflag in open() +/** open() oflag for reading */ +uint8_t const O_READ = 0X01; +/** open() oflag - same as O_IN */ +uint8_t const O_RDONLY = O_READ; +/** open() oflag for write */ +uint8_t const O_WRITE = 0X02; +/** open() oflag - same as O_WRITE */ +uint8_t const O_WRONLY = O_WRITE; +/** open() oflag for reading and writing */ +uint8_t const O_RDWR = (O_READ | O_WRITE); +/** open() oflag mask for access modes */ +uint8_t const O_ACCMODE = (O_READ | O_WRITE); +/** The file offset shall be set to the end of the file prior to each write. */ +uint8_t const O_APPEND = 0X04; +/** synchronous writes - call sync() after each write */ +uint8_t const O_SYNC = 0X08; +/** truncate the file to zero length */ +uint8_t const O_TRUNC = 0X10; +/** set the initial position at the end of the file */ +uint8_t const O_AT_END = 0X20; +/** create the file if nonexistent */ +uint8_t const O_CREAT = 0X40; +/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ +uint8_t const O_EXCL = 0X80; + +// FatFile class static and const definitions +// flags for ls() +/** ls() flag for list all files including hidden. */ +uint8_t const LS_A = 1; +/** ls() flag to print modify. date */ +uint8_t const LS_DATE = 2; +/** ls() flag to print file size. */ +uint8_t const LS_SIZE = 4; +/** ls() flag for recursive list of subdirectories */ +uint8_t const LS_R = 8; + +// flags for timestamp +/** set the file's last access date */ +uint8_t const T_ACCESS = 1; +/** set the file's creation date and time */ +uint8_t const T_CREATE = 2; +/** Set the file's write date and time */ +uint8_t const T_WRITE = 4; +#endif // FatApiConstants_h diff --git a/lib/SdFat/utility/FatFile.cpp b/lib/SdFat/utility/FatFile.cpp new file mode 100644 index 0000000..e013dd6 --- /dev/null +++ b/lib/SdFat/utility/FatFile.cpp @@ -0,0 +1,1494 @@ +/* FatLib Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include "FatFile.h" +#include "FatFileSystem.h" +//------------------------------------------------------------------------------ +// Pointer to cwd directory. +FatFile* FatFile::m_cwd = 0; +// Callback function for date/time. +void (*FatFile::m_dateTime)(uint16_t* date, uint16_t* time) = 0; +//------------------------------------------------------------------------------ +// Add a cluster to a file. +bool FatFile::addCluster() { + m_flags |= F_FILE_DIR_DIRTY; + return m_vol->allocateCluster(m_curCluster, &m_curCluster); +} +//------------------------------------------------------------------------------ +// Add a cluster to a directory file and zero the cluster. +// Return with first block of cluster in the cache. +bool FatFile::addDirCluster() { + uint32_t block; + cache_t* pc; + + if (isRootFixed()) { + DBG_FAIL_MACRO; + goto fail; + } + // max folder size + if (m_curPosition >= 512UL*4095) { + DBG_FAIL_MACRO; + goto fail; + } + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + block = m_vol->clusterStartBlock(m_curCluster); + pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + memset(pc, 0, 512); + // zero rest of clusters + for (uint8_t i = 1; i < m_vol->blocksPerCluster(); i++) { + if (!m_vol->writeBlock(block + i, pc->data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // Set position to EOF to avoid inconsistent curCluster/curPosition. + m_curPosition += 512UL*m_vol->blocksPerCluster(); + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +// cache a file's directory entry +// return pointer to cached entry or null for failure +dir_t* FatFile::cacheDirEntry(uint8_t action) { + cache_t* pc; + pc = m_vol->cacheFetchData(m_dirBlock, action); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + return pc->dir + (m_dirIndex & 0XF); + +fail: + return 0; +} +//------------------------------------------------------------------------------ +bool FatFile::close() { + bool rtn = sync(); + m_attr = FILE_ATTR_CLOSED; + return rtn; +} +//------------------------------------------------------------------------------ +bool FatFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { + // error if no blocks + if (m_firstCluster == 0) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint32_t c = m_firstCluster; ; c++) { + uint32_t next; + int8_t fg = m_vol->fatGet(c, &next); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + // check for contiguous + if (fg == 0 || next != (c + 1)) { + // error if not end of chain + if (fg) { + DBG_FAIL_MACRO; + goto fail; + } + *bgnBlock = m_vol->clusterStartBlock(m_firstCluster); + *endBlock = m_vol->clusterStartBlock(c) + + m_vol->blocksPerCluster() - 1; + return true; + } + } + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::createContiguous(FatFile* dirFile, + const char* path, uint32_t size) { + uint32_t count; + // don't allow zero length file + if (size == 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) { + DBG_FAIL_MACRO; + goto fail; + } + // calculate number of clusters needed + count = ((size - 1) >> (m_vol->clusterSizeShift() + 9)) + 1; + + // allocate clusters + if (!m_vol->allocContiguous(count, &m_firstCluster)) { + remove(); + DBG_FAIL_MACRO; + goto fail; + } + m_fileSize = size; + + // insure sync() will update dir entry + m_flags |= F_FILE_DIR_DIRTY; + + return sync(); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::dirEntry(dir_t* dst) { + dir_t* dir; + // Make sure fields on device are correct. + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // read entry + dir = cacheDirEntry(FatCache::CACHE_FOR_READ); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // copy to caller's struct + memcpy(dst, dir, sizeof(dir_t)); + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +uint8_t FatFile::dirName(const dir_t* dir, char* name) { + uint8_t j = 0; + uint8_t lcBit = DIR_NT_LC_BASE; + for (uint8_t i = 0; i < 11; i++) { + if (dir->name[i] == ' ') { + continue; + } + if (i == 8) { + // Position bit for extension. + lcBit = DIR_NT_LC_EXT; + name[j++] = '.'; + } + char c = dir->name[i]; + if ('A' <= c && c <= 'Z' && (lcBit & dir->reservedNT)) { + c += 'a' - 'A'; + } + name[j++] = c; + } + name[j] = 0; + return j; +} + +//------------------------------------------------------------------------------ +uint32_t FatFile::dirSize() { + int8_t fg; + if (!isDir()) { + return 0; + } + if (isRootFixed()) { + return 32*m_vol->rootDirEntryCount(); + } + uint16_t n = 0; + uint32_t c = isRoot32() ? m_vol->rootDirStart() : m_firstCluster; + do { + fg = m_vol->fatGet(c, &c); + if (fg < 0 || n > 4095) { + return 0; + } + n += m_vol->blocksPerCluster(); + } while (fg); + return 512UL*n; +} +//------------------------------------------------------------------------------ +int16_t FatFile::fgets(char* str, int16_t num, char* delim) { + char ch; + int16_t n = 0; + int16_t r = -1; + while ((n + 1) < num && (r = read(&ch, 1)) == 1) { + // delete CR + if (ch == '\r') { + continue; + } + str[n++] = ch; + if (!delim) { + if (ch == '\n') { + break; + } + } else { + if (strchr(delim, ch)) { + break; + } + } + } + if (r < 0) { + // read error + return -1; + } + str[n] = '\0'; + return n; +} +//------------------------------------------------------------------------------ +void FatFile::getpos(FatPos_t* pos) { + pos->position = m_curPosition; + pos->cluster = m_curCluster; +} +//------------------------------------------------------------------------------ +bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) { + fname_t fname; + FatFile tmpDir; + + if (isOpen() || !parent->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isDirSeparator(*path)) { + while (isDirSeparator(*path)) { + path++; + } + if (!tmpDir.openRoot(parent->m_vol)) { + DBG_FAIL_MACRO; + goto fail; + } + parent = &tmpDir; + } + while (1) { + if (!parsePathName(path, &fname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!*path) { + break; + } + if (!open(parent, &fname, O_READ)) { + if (!pFlag || !mkdir(parent, &fname)) { + DBG_FAIL_MACRO; + goto fail; + } + } + tmpDir = *this; + parent = &tmpDir; + close(); + } + return mkdir(parent, &fname); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::mkdir(FatFile* parent, fname_t* fname) { + uint32_t block; + dir_t dot; + dir_t* dir; + cache_t* pc; + + if (!parent->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // create a normal file + if (!open(parent, fname, O_CREAT | O_EXCL | O_RDWR)) { + DBG_FAIL_MACRO; + goto fail; + } + // convert file to directory + m_flags = O_READ; + m_attr = FILE_ATTR_SUBDIR; + + // allocate and zero first cluster + if (!addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + m_firstCluster = m_curCluster; + // Set to start of dir + rewind(); + // force entry to device + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // cache entry - should already be in cache due to sync() call + dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // change directory entry attribute + dir->attributes = DIR_ATT_DIRECTORY; + + // make entry for '.' + memcpy(&dot, dir, sizeof(dot)); + dot.name[0] = '.'; + for (uint8_t i = 1; i < 11; i++) { + dot.name[i] = ' '; + } + + // cache block for '.' and '..' + block = m_vol->clusterStartBlock(m_firstCluster); + pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + // copy '.' to block + memcpy(&pc->dir[0], &dot, sizeof(dot)); + // make entry for '..' + dot.name[1] = '.'; + dot.firstClusterLow = parent->m_firstCluster & 0XFFFF; + dot.firstClusterHigh = parent->m_firstCluster >> 16; + // copy '..' to block + memcpy(&pc->dir[1], &dot, sizeof(dot)); + // write first block + return m_vol->cacheSync(); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFileSystem* fs, const char* path, uint8_t oflag) { + return open(fs->vwd(), path, oflag); +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFile* dirFile, const char* path, uint8_t oflag) { + FatFile tmpDir; + fname_t fname; + + // error if already open + if (isOpen() || !dirFile->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isDirSeparator(*path)) { + while (isDirSeparator(*path)) { + path++; + } + if (*path == 0) { + return openRoot(dirFile->m_vol); + } + if (!tmpDir.openRoot(dirFile->m_vol)) { + DBG_FAIL_MACRO; + goto fail; + } + dirFile = &tmpDir; + } + while (1) { + if (!parsePathName(path, &fname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + if (*path == 0) { + break; + } + if (!open(dirFile, &fname, O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + tmpDir = *this; + dirFile = &tmpDir; + close(); + } + return open(dirFile, &fname, oflag); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFile* dirFile, uint16_t index, uint8_t oflag) { + uint8_t chksum = 0; + uint8_t lfnOrd = 0; + dir_t* dir; + ldir_t*ldir; + + // Error if already open. + if (isOpen() || !dirFile->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // Don't open existing file if O_EXCL - user call error. + if (oflag & O_EXCL) { + DBG_FAIL_MACRO; + goto fail; + } + if (index) { + // Check for LFN. + if (!dirFile->seekSet(32UL*(index -1))) { + DBG_FAIL_MACRO; + goto fail; + } + ldir = reinterpret_cast(dirFile->readDirCache()); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attr == DIR_ATT_LONG_NAME) { + if (1 == (ldir->ord & 0X1F)) { + chksum = ldir->chksum; + // Use largest possible number. + lfnOrd = index > 20 ? 20 : index; + } + } + } else { + dirFile->rewind(); + } + // read entry into cache + dir = dirFile->readDirCache(); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // error if empty slot or '.' or '..' + if (dir->name[0] == DIR_NAME_DELETED || + dir->name[0] == DIR_NAME_FREE || + dir->name[0] == '.') { + DBG_FAIL_MACRO; + goto fail; + } + if (lfnOrd && chksum != lfnChecksum(dir->name)) { + DBG_FAIL_MACRO; + goto fail; + } + // open cached entry + if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +// open a cached directory entry. + +bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex, + uint8_t oflag, uint8_t lfnOrd) { + uint32_t firstCluster; + memset(this, 0, sizeof(FatFile)); + // location of entry in cache + m_vol = dirFile->m_vol; + m_dirIndex = dirIndex; + m_dirCluster = dirFile->m_firstCluster; + dir_t* dir = &m_vol->cacheAddress()->dir[0XF & dirIndex]; + + // Must be file or subdirectory. + if (!DIR_IS_FILE_OR_SUBDIR(dir)) { + DBG_FAIL_MACRO; + goto fail; + } + m_attr = dir->attributes & FILE_ATTR_COPY; + if (DIR_IS_FILE(dir)) { + m_attr |= FILE_ATTR_FILE; + } + m_lfnOrd = lfnOrd; + // Write, truncate, or at end is an error for a directory or read-only file. + if (oflag & (O_WRITE | O_TRUNC | O_AT_END)) { + if (isSubDir() || isReadOnly()) { + DBG_FAIL_MACRO; + goto fail; + } + } + // save open flags for read/write + m_flags = oflag & F_OFLAG; + + m_dirBlock = m_vol->cacheBlockNumber(); + + // copy first cluster number for directory fields + firstCluster = ((uint32_t)dir->firstClusterHigh << 16) + | dir->firstClusterLow; + + if (oflag & O_TRUNC) { + if (firstCluster && !m_vol->freeChain(firstCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // need to update directory entry + m_flags |= F_FILE_DIR_DIRTY; + } else { + m_firstCluster = firstCluster; + m_fileSize = dir->fileSize; + } + if ((oflag & O_AT_END) && !seekSet(m_fileSize)) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + +fail: + m_attr = FILE_ATTR_CLOSED; + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::openNext(FatFile* dirFile, uint8_t oflag) { + uint8_t chksum = 0; + ldir_t* ldir; + uint8_t lfnOrd = 0; + uint16_t index; + + // Check for not open and valid directory.. + if (isOpen() || !dirFile->isDir() || (dirFile->curPosition() & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + while (1) { + // read entry into cache + index = dirFile->curPosition()/32; + dir_t* dir = dirFile->readDirCache(); + if (!dir) { + if (dirFile->getError()) { + DBG_FAIL_MACRO; + } + goto fail; + } + // done if last entry + if (dir->name[0] == DIR_NAME_FREE) { + goto fail; + } + // skip empty slot or '.' or '..' + if (dir->name[0] == '.' || dir->name[0] == DIR_NAME_DELETED) { + lfnOrd = 0; + } else if (DIR_IS_FILE_OR_SUBDIR(dir)) { + if (lfnOrd && chksum != lfnChecksum(dir->name)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + } else if (DIR_IS_LONG_NAME(dir)) { + ldir = reinterpret_cast(dir); + if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { + lfnOrd = ldir->ord & 0X1F; + chksum = ldir->chksum; + } + } else { + lfnOrd = 0; + } + } + +fail: + return false; +} +#ifndef DOXYGEN_SHOULD_SKIP_THIS +//------------------------------------------------------------------------------ +/** Open a file's parent directory. + * + * \param[in] file Parent of this directory will be opened. Must not be root. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ +bool FatFile::openParent(FatFile* dirFile) { + FatFile dotdot; + uint32_t lbn; + dir_t* dir; + uint32_t ddc; + cache_t* cb; + + if (isOpen() || !dirFile->isOpen()) { + goto fail; + } + if (dirFile->m_dirCluster == 0) { + return openRoot(dirFile->m_vol); + } + lbn = dirFile->m_vol->clusterStartBlock(dirFile->m_dirCluster); + cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ); + if (!cb) { + DBG_FAIL_MACRO; + goto fail; + } + // Point to dir entery for .. + dir = cb->dir + 1; + ddc = dir->firstClusterLow | ((uint32_t)dir->firstClusterHigh << 16); + if (ddc == 0) { + if (!dotdot.openRoot(dirFile->m_vol)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + memset(&dotdot, 0, sizeof(FatFile)); + dotdot.m_attr = FILE_ATTR_SUBDIR; + dotdot.m_flags = O_READ; + dotdot.m_vol = dirFile->m_vol; + dotdot.m_firstCluster = ddc; + } + uint32_t di; + do { + di = dotdot.curPosition()/32; + dir = dotdot.readDirCache(); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + ddc = dir->firstClusterLow | ((uint32_t)dir->firstClusterHigh << 16); + } while (ddc != dirFile->m_dirCluster); + return open(&dotdot, di, O_READ); + +fail: + return false; +} +#endif // DOXYGEN_SHOULD_SKIP_THIS +//------------------------------------------------------------------------------ +bool FatFile::openRoot(FatVolume* vol) { + // error if file is already open + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + memset(this, 0, sizeof(FatFile)); + + m_vol = vol; + switch (vol->fatType()) { +#if FAT12_SUPPORT + case 12: +#endif // FAT12_SUPPORT + case 16: + m_attr = FILE_ATTR_ROOT_FIXED; + break; + + case 32: + m_attr = FILE_ATTR_ROOT32; + break; + + default: + DBG_FAIL_MACRO; + goto fail; + } + // read only + m_flags = O_READ; + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +int FatFile::peek() { + FatPos_t pos; + getpos(&pos); + int c = read(); + if (c >= 0) { + setpos(&pos); + } + return c; +} +//------------------------------------------------------------------------------ +int FatFile::read(void* buf, size_t nbyte) { + int8_t fg; + uint8_t blockOfCluster = 0; + uint8_t* dst = reinterpret_cast(buf); + uint16_t offset; + size_t toRead; + uint32_t block; // raw device block number + cache_t* pc; + + // error if not open for read + if (!isOpen() || !(m_flags & O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + + if (isFile()) { + uint32_t tmp32 = m_fileSize - m_curPosition; + if (nbyte >= tmp32) { + nbyte = tmp32; + } + } else if (isRootFixed()) { + uint16_t tmp16 = 32*m_vol->m_rootDirEntryCount - (uint16_t)m_curPosition; + if (nbyte > tmp16) { + nbyte = tmp16; + } + } + toRead = nbyte; + while (toRead) { + size_t n; + offset = m_curPosition & 0X1FF; // offset in block + if (isRootFixed()) { + block = m_vol->rootDirStart() + (m_curPosition >> 9); + } else { + blockOfCluster = m_vol->blockOfCluster(m_curPosition); + if (offset == 0 && blockOfCluster == 0) { + // start of new cluster + if (m_curPosition == 0) { + // use first cluster in file + m_curCluster = isRoot32() ? m_vol->rootDirStart() : m_firstCluster; + } else { + // get next cluster from FAT + fg = m_vol->fatGet(m_curCluster, &m_curCluster); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg == 0) { + if (isDir()) { + break; + } + DBG_FAIL_MACRO; + goto fail; + } + } + } + block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; + } + if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { + // amount to be read from current block + n = 512 - offset; + if (n > toRead) { + n = toRead; + } + // read block to cache and copy data to caller + pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint8_t* src = pc->data + offset; + memcpy(dst, src, n); +#if USE_MULTI_BLOCK_IO + } else if (toRead >= 1024) { + uint8_t nb = toRead >> 9; + if (!isRootFixed()) { + uint8_t mb = m_vol->blocksPerCluster() - blockOfCluster; + if (mb < nb) { + nb = mb; + } + } + n = 512*nb; + if (m_vol->cacheBlockNumber() <= block + && block < (m_vol->cacheBlockNumber() + nb)) { + // flush cache if a block is in the cache + if (!m_vol->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (!m_vol->readBlocks(block, dst, nb)) { + DBG_FAIL_MACRO; + goto fail; + } +#endif // USE_MULTI_BLOCK_IO + } else { + // read single block + n = 512; + if (!m_vol->readBlock(block, dst)) { + DBG_FAIL_MACRO; + goto fail; + } + } + dst += n; + m_curPosition += n; + toRead -= n; + } + return nbyte - toRead; + +fail: + m_error |= READ_ERROR; + return -1; +} +//------------------------------------------------------------------------------ +int8_t FatFile::readDir(dir_t* dir) { + int16_t n; + // if not a directory file or miss-positioned return an error + if (!isDir() || (0X1F & m_curPosition)) { + return -1; + } + + while (1) { + n = read(dir, sizeof(dir_t)); + if (n != sizeof(dir_t)) { + return n == 0 ? 0 : -1; + } + // last entry if DIR_NAME_FREE + if (dir->name[0] == DIR_NAME_FREE) { + return 0; + } + // skip empty entries and entry for . and .. + if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + continue; + } + // return if normal file or subdirectory + if (DIR_IS_FILE_OR_SUBDIR(dir)) { + return n; + } + } +} +//------------------------------------------------------------------------------ +// Read next directory entry into the cache +// Assumes file is correctly positioned +dir_t* FatFile::readDirCache(bool skipReadOk) { +// uint8_t b; + uint8_t i = (m_curPosition >> 5) & 0XF; + + if (i == 0 || !skipReadOk) { + int8_t n = read(&n, 1); + if (n != 1) { + if (n != 0) { + DBG_FAIL_MACRO; + } + goto fail; + } +// if (read(&b, 1) != 1) { +// DBG_FAIL_MACRO; +// goto fail; +// } + m_curPosition += 31; + } else { + m_curPosition += 32; + } + // return pointer to entry + return m_vol->cacheAddress()->dir + i; + +fail: + return 0; +} +//------------------------------------------------------------------------------ +bool FatFile::remove(FatFile* dirFile, const char* path) { + FatFile file; + if (!file.open(dirFile, path, O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + return file.remove(); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::rename(FatFile* dirFile, const char* newPath) { + dir_t entry; + uint32_t dirCluster = 0; + FatFile file; + FatFile oldFile; + cache_t* pc; + dir_t* dir; + + // Must be an open file or subdirectory. + if (!(isFile() || isSubDir())) { + DBG_FAIL_MACRO; + goto fail; + } + // Can't rename LFN in 8.3 mode. + if (!USE_LONG_FILE_NAMES && isLFN()) { + DBG_FAIL_MACRO; + goto fail; + } + // Can't move file to new volume. + if (m_vol != dirFile->m_vol) { + DBG_FAIL_MACRO; + goto fail; + } + // sync() and cache directory entry + sync(); + oldFile = *this; + dir = cacheDirEntry(FatCache::CACHE_FOR_READ); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // save directory entry + memcpy(&entry, dir, sizeof(entry)); + // make directory entry for new path + if (isFile()) { + if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // don't create missing path prefix components + if (!file.mkdir(dirFile, newPath, false)) { + DBG_FAIL_MACRO; + goto fail; + } + // save cluster containing new dot dot + dirCluster = file.m_firstCluster; + } + // change to new directory entry + + m_dirBlock = file.m_dirBlock; + m_dirIndex = file.m_dirIndex; + m_lfnOrd = file.m_lfnOrd; + m_dirCluster = file.m_dirCluster; + // mark closed to avoid possible destructor close call + file.m_attr = FILE_ATTR_CLOSED; + + // cache new directory entry + dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // copy all but name and name flags to new directory entry + memcpy(&dir->creationTimeTenths, &entry.creationTimeTenths, + sizeof(entry) - sizeof(dir->name) - 2); + dir->attributes = entry.attributes; + + // update dot dot if directory + if (dirCluster) { + // get new dot dot + uint32_t block = m_vol->clusterStartBlock(dirCluster); + pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + memcpy(&entry, &pc->dir[1], sizeof(entry)); + + // free unused cluster + if (!m_vol->freeChain(dirCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // store new dot dot + block = m_vol->clusterStartBlock(m_firstCluster); + pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + memcpy(&pc->dir[1], &entry, sizeof(entry)); + } + // Remove old directory entry; + oldFile.m_firstCluster = 0; + oldFile.m_flags = O_WRITE; + oldFile.m_attr = FILE_ATTR_FILE; + if (!oldFile.remove()) { + DBG_FAIL_MACRO; + goto fail; + } + return m_vol->cacheSync(); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::rmdir() { + // must be open subdirectory + if (!isSubDir() || (!USE_LONG_FILE_NAMES && isLFN())) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + + // make sure directory is empty + while (1) { + dir_t* dir = readDirCache(true); + if (!dir) { + // EOF if no error. + if (!getError()) { + break; + } + DBG_FAIL_MACRO; + goto fail; + } + // done if past last used entry + if (dir->name[0] == DIR_NAME_FREE) { + break; + } + // skip empty slot, '.' or '..' + if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + continue; + } + // error not empty + if (DIR_IS_FILE_OR_SUBDIR(dir)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // convert empty directory to normal file for remove + m_attr = FILE_ATTR_FILE; + m_flags |= O_WRITE; + return remove(); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::rmRfStar() { + uint16_t index; + FatFile f; + if (!isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + while (1) { + // remember position + index = m_curPosition/32; + + dir_t* dir = readDirCache(); + if (!dir) { + // At EOF if no error. + if (!getError()) { + break; + } + DBG_FAIL_MACRO; + goto fail; + } + // done if past last entry + if (dir->name[0] == DIR_NAME_FREE) { + break; + } + + // skip empty slot or '.' or '..' + if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + continue; + } + + // skip if part of long file name or volume label in root + if (!DIR_IS_FILE_OR_SUBDIR(dir)) { + continue; + } + + if (!f.open(this, index, O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + if (f.isSubDir()) { + // recursively delete + if (!f.rmRfStar()) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // ignore read-only + f.m_flags |= O_WRITE; + if (!f.remove()) { + DBG_FAIL_MACRO; + goto fail; + } + } + // position to next entry if required + if (m_curPosition != (32UL*(index + 1))) { + if (!seekSet(32UL*(index + 1))) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + // don't try to delete root + if (!isRoot()) { + if (!rmdir()) { + DBG_FAIL_MACRO; + goto fail; + } + } + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::seekSet(uint32_t pos) { + uint32_t nCur; + uint32_t nNew; + uint32_t tmp = m_curCluster; + // error if file not open + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (pos == 0) { + // set position to start of file + m_curCluster = 0; + goto done; + } + if (isFile()) { + if (pos > m_fileSize) { + DBG_FAIL_MACRO; + goto fail; + } + } else if (isRootFixed()) { + if (pos <= 32*m_vol->rootDirEntryCount()) { + goto done; + } + DBG_FAIL_MACRO; + goto fail; + } + // calculate cluster index for cur and new position + nCur = (m_curPosition - 1) >> (m_vol->clusterSizeShift() + 9); + nNew = (pos - 1) >> (m_vol->clusterSizeShift() + 9); + + if (nNew < nCur || m_curPosition == 0) { + // must follow chain from first cluster + m_curCluster = isRoot32() ? m_vol->rootDirStart() : m_firstCluster; + } else { + // advance from curPosition + nNew -= nCur; + } + while (nNew--) { + if (m_vol->fatGet(m_curCluster, &m_curCluster) <= 0) { + DBG_FAIL_MACRO; + goto fail; + } + } + +done: + m_curPosition = pos; + return true; + +fail: + m_curCluster = tmp; + return false; +} +//------------------------------------------------------------------------------ +void FatFile::setpos(FatPos_t* pos) { + m_curPosition = pos->position; + m_curCluster = pos->cluster; +} +//------------------------------------------------------------------------------ +bool FatFile::sync() { + if (!isOpen()) { + return true; + } + + if (m_flags & F_FILE_DIR_DIRTY) { + dir_t* dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + // check for deleted by another open file object + if (!dir || dir->name[0] == DIR_NAME_DELETED) { + DBG_FAIL_MACRO; + goto fail; + } + // do not set filesize for dir files + if (isFile()) { + dir->fileSize = m_fileSize; + } + + // update first cluster fields + dir->firstClusterLow = m_firstCluster & 0XFFFF; + dir->firstClusterHigh = m_firstCluster >> 16; + + // set modify time if user supplied a callback date/time function + if (m_dateTime) { + m_dateTime(&dir->lastWriteDate, &dir->lastWriteTime); + dir->lastAccessDate = dir->lastWriteDate; + } + // clear directory dirty + m_flags &= ~F_FILE_DIR_DIRTY; + } + if (m_vol->cacheSync()) { + return true; + } + DBG_FAIL_MACRO; + +fail: + m_error |= WRITE_ERROR; + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::timestamp(FatFile* file) { + dir_t* dir; + dir_t srcDir; + + // most be files get timestamps + if (!isFile() || !file->isFile() || !file->dirEntry(&srcDir)) { + DBG_FAIL_MACRO; + goto fail; + } + // update directory fields + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // copy timestamps + dir->lastAccessDate = srcDir.lastAccessDate; + dir->creationDate = srcDir.creationDate; + dir->creationTime = srcDir.creationTime; + dir->creationTimeTenths = srcDir.creationTimeTenths; + dir->lastWriteDate = srcDir.lastWriteDate; + dir->lastWriteTime = srcDir.lastWriteTime; + + // write back entry + return m_vol->cacheSync(); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, + uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { + uint16_t dirDate; + uint16_t dirTime; + dir_t* dir; + + if (!isFile() + || year < 1980 + || year > 2107 + || month < 1 + || month > 12 + || day < 1 + || day > 31 + || hour > 23 + || minute > 59 + || second > 59) { + DBG_FAIL_MACRO; + goto fail; + } + // update directory entry + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + dirDate = FAT_DATE(year, month, day); + dirTime = FAT_TIME(hour, minute, second); + if (flags & T_ACCESS) { + dir->lastAccessDate = dirDate; + } + if (flags & T_CREATE) { + dir->creationDate = dirDate; + dir->creationTime = dirTime; + // seems to be units of 1/100 second not 1/10 as Microsoft states + dir->creationTimeTenths = second & 1 ? 100 : 0; + } + if (flags & T_WRITE) { + dir->lastWriteDate = dirDate; + dir->lastWriteTime = dirTime; + } + return m_vol->cacheSync(); + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::truncate(uint32_t length) { + uint32_t newPos; + // error if not a normal file or read-only + if (!isFile() || !(m_flags & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // error if length is greater than current size + if (length > m_fileSize) { + DBG_FAIL_MACRO; + goto fail; + } + // fileSize and length are zero - nothing to do + if (m_fileSize == 0) { + return true; + } + + // remember position for seek after truncation + newPos = m_curPosition > length ? length : m_curPosition; + + // position to last cluster in truncated file + if (!seekSet(length)) { + DBG_FAIL_MACRO; + goto fail; + } + if (length == 0) { + // free all clusters + if (!m_vol->freeChain(m_firstCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + m_firstCluster = 0; + } else { + uint32_t toFree; + int8_t fg = m_vol->fatGet(m_curCluster, &toFree); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg) { + // free extra clusters + if (!m_vol->freeChain(toFree)) { + DBG_FAIL_MACRO; + goto fail; + } + // current cluster is end of chain + if (!m_vol->fatPutEOC(m_curCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + m_fileSize = length; + + // need to update directory entry + m_flags |= F_FILE_DIR_DIRTY; + + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // set file to correct position + return seekSet(newPos); + +fail: + return false; +} +//------------------------------------------------------------------------------ +int FatFile::write(const void* buf, size_t nbyte) { + // convert void* to uint8_t* - must be before goto statements + const uint8_t* src = reinterpret_cast(buf); + cache_t* pc; + uint8_t cacheOption; + // number of bytes left to write - must be before goto statements + size_t nToWrite = nbyte; + size_t n; + // error if not a normal file or is read-only + if (!isFile() || !(m_flags & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // seek to end of file if append flag + if ((m_flags & O_APPEND)) { + if (!seekSet(m_fileSize)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // Don't exceed max fileSize. + if (nbyte > (0XFFFFFFFF - m_curPosition)) { + DBG_FAIL_MACRO; + goto fail; + } + while (nToWrite) { + uint8_t blockOfCluster = m_vol->blockOfCluster(m_curPosition); + uint16_t blockOffset = m_curPosition & 0X1FF; + if (blockOfCluster == 0 && blockOffset == 0) { + // start of new cluster + if (m_curCluster != 0) { + int8_t fg = m_vol->fatGet(m_curCluster, &m_curCluster); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg == 0) { + // add cluster if at end of chain + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + } + } else { + if (m_firstCluster == 0) { + // allocate first cluster of file + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + m_firstCluster = m_curCluster; + } else { + m_curCluster = m_firstCluster; + } + } + } + // block for data write + uint32_t block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster; + + if (blockOffset != 0 || nToWrite < 512) { + // partial block - must use cache + // max space in block + n = 512 - blockOffset; + // lesser of space and amount to write + if (n > nToWrite) { + n = nToWrite; + } + + if (blockOffset == 0 && m_curPosition >= m_fileSize) { + // start of new block don't need to read into cache + cacheOption = FatCache::CACHE_RESERVE_FOR_WRITE; + } else { + // rewrite part of block + cacheOption = FatCache::CACHE_FOR_WRITE; + } + pc = m_vol->cacheFetchData(block, cacheOption); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint8_t* dst = pc->data + blockOffset; + memcpy(dst, src, n); + if (512 == (n + blockOffset)) { + // Force write if block is full - improves large writes. + if (!m_vol->cacheSyncData()) { + DBG_FAIL_MACRO; + goto fail; + } + } +#if USE_MULTI_BLOCK_IO + } else if (nToWrite >= 1024) { + // use multiple block write command + uint8_t maxBlocks = m_vol->blocksPerCluster() - blockOfCluster; + uint8_t nBlock = nToWrite >> 9; + if (nBlock > maxBlocks) { + nBlock = maxBlocks; + } + n = 512*nBlock; + if (m_vol->cacheBlockNumber() <= block + && block < (m_vol->cacheBlockNumber() + nBlock)) { + // invalidate cache if block is in cache + m_vol->cacheInvalidate(); + } + if (!m_vol->writeBlocks(block, src, nBlock)) { + DBG_FAIL_MACRO; + goto fail; + } +#endif // USE_MULTI_BLOCK_IO + } else { + // use single block write command + n = 512; + if (m_vol->cacheBlockNumber() == block) { + m_vol->cacheInvalidate(); + } + if (!m_vol->writeBlock(block, src)) { + DBG_FAIL_MACRO; + goto fail; + } + } + m_curPosition += n; + src += n; + nToWrite -= n; + } + if (m_curPosition > m_fileSize) { + // update fileSize and insure sync will update dir entry + m_fileSize = m_curPosition; + m_flags |= F_FILE_DIR_DIRTY; + } else if (m_dateTime) { + // insure sync will update modified date and time + m_flags |= F_FILE_DIR_DIRTY; + } + + if (m_flags & O_SYNC) { + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + return nbyte; + +fail: + // return for write error + m_error |= WRITE_ERROR; + return -1; +} diff --git a/lib/SdFat/utility/FatFile.h b/lib/SdFat/utility/FatFile.h new file mode 100644 index 0000000..1e93926 --- /dev/null +++ b/lib/SdFat/utility/FatFile.h @@ -0,0 +1,990 @@ +/* FatLib Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef FatFile_h +#define FatFile_h +/** + * \file + * \brief FatFile class + */ +// #include +#include +#include +#include +#include "FatLibConfig.h" +#include "FatApiConstants.h" +#include "FatStructs.h" +#include "FatVolume.h" +class FatFileSystem; +//------------------------------------------------------------------------------ +// Stuff to store strings in AVR flash. +#ifdef __AVR__ +#include +#else // __AVR__ +#ifndef PGM_P +/** pointer to flash for ARM */ +#define PGM_P const char* +#endif // PGM_P +#ifndef PSTR +/** store literal string in flash for ARM */ +#define PSTR(x) (x) +#endif // PSTR +#ifndef pgm_read_byte +/** read 8-bits from flash for ARM */ +#define pgm_read_byte(addr) (*(const unsigned char*)(addr)) +#endif // pgm_read_byte +#ifndef pgm_read_word +/** read 16-bits from flash for ARM */ +#define pgm_read_word(addr) (*(const uint16_t*)(addr)) +#endif // pgm_read_word +#ifndef PROGMEM +/** store in flash for ARM */ +#define PROGMEM const +#endif // PROGMEM +#endif // __AVR__ +//------------------------------------------------------------------------------ +/** + * \struct FatPos_t + * \brief Internal type for file position - do not use in user apps. + */ +struct FatPos_t { + /** stream position */ + uint32_t position; + /** cluster for position */ + uint32_t cluster; + FatPos_t() : position(0), cluster(0) {} +}; +//------------------------------------------------------------------------------ +/** Expression for path name separator. */ +#define isDirSeparator(c) ((c) == '/') +//------------------------------------------------------------------------------ +/** + * \struct fname_t + * \brief Internal type for Short File Name - do not use in user apps. + */ +struct fname_t { + /** Flags for base and extension character case and LFN. */ + uint8_t flags; + /** length of Long File Name */ + size_t len; + /** Long File Name start. */ + const char* lfn; + /** position for sequence number */ + uint8_t seqPos; + /** Short File Name */ + uint8_t sfn[11]; +}; +/** Derived from a LFN with loss or conversion of characters. */ +const uint8_t FNAME_FLAG_LOST_CHARS = 0X01; +/** Base-name or extension has mixed case. */ +const uint8_t FNAME_FLAG_MIXED_CASE = 0X02; +/** LFN entries are required for file name. */ +const uint8_t FNAME_FLAG_NEED_LFN = + FNAME_FLAG_LOST_CHARS | FNAME_FLAG_MIXED_CASE; +/** Filename base-name is all lower case */ +const uint8_t FNAME_FLAG_LC_BASE = DIR_NT_LC_BASE; +/** Filename extension is all lower case. */ +const uint8_t FNAME_FLAG_LC_EXT = DIR_NT_LC_EXT; +//============================================================================== +/** + * \class FatFile + * \brief Basic file class. + */ +class FatFile { + public: + /** Create an instance. */ + FatFile() : m_attr(FILE_ATTR_CLOSED), m_error(0) {} + /** Create a file object and open it in the current working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t). + */ + FatFile(const char* path, uint8_t oflag) { + m_attr = FILE_ATTR_CLOSED; + m_error = 0; + open(path, oflag); + } +#if DESTRUCTOR_CLOSES_FILE + ~FatFile() { + if (isOpen()) { + close(); + } + } +#endif // DESTRUCTOR_CLOSES_FILE + +#if ENABLE_ARDUINO_FEATURES + /** List directory contents. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ + void ls(uint8_t flags = 0) { + ls(&Serial, flags); + } + /** %Print a directory date field. + * + * Format is yyyy-mm-dd. + * + * \param[in] fatDate The date field from a directory entry. + */ + static void printFatDate(uint16_t fatDate) { + printFatDate(&Serial, fatDate); + } + /** %Print a directory time field. + * + * Format is hh:mm:ss. + * + * \param[in] fatTime The time field from a directory entry. + */ + static void printFatTime(uint16_t fatTime) { + printFatTime(&Serial, fatTime); + } + /** Print a file's name. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + size_t printName() { + return FatFile::printName(&Serial); + } +#endif // ENABLE_ARDUINO_FEATURES + + /** \return value of writeError */ + bool getWriteError() { + return m_error & WRITE_ERROR; + } + /** Set writeError to zero */ + void clearWriteError() { + m_error &= ~WRITE_ERROR; + } + /** Clear all error bits. */ + void clearError() { + m_error = 0; + } + /** \return All error bits. */ + uint8_t getError() { + return m_error; + } + /** get position for streams + * \param[out] pos struct to receive position + */ + void getpos(FatPos_t* pos); + /** set position for streams + * \param[out] pos struct with value for new position + */ + void setpos(FatPos_t* pos); + /** \return The number of bytes available from the current position + * to EOF for normal files. Zero is returned for directory files. + */ + uint32_t available() { + return isFile() ? fileSize() - curPosition() : 0; + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool close(); + /** Check for contiguous file and return its raw block range. + * + * \param[out] bgnBlock the first block address for the file. + * \param[out] endBlock the last block address for the file. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + /** Create and open a new contiguous file of a specified size. + * + * \note This function only supports short DOS 8.3 names. + * See open() for more information. + * + * \param[in] dirFile The directory where the file will be created. + * \param[in] path A path with a valid DOS 8.3 file name. + * \param[in] size The desired file size. + * + * \return The value true is returned for success and + * the value false, is returned for failure. + */ + bool createContiguous(FatFile* dirFile, + const char* path, uint32_t size); + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster() const { + return m_curCluster; + } + /** \return The current position for a file or directory. */ + uint32_t curPosition() const { + return m_curPosition; + } + /** \return Current working directory */ + static FatFile* cwd() { + return m_cwd; + } + /** 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)) { + m_dateTime = dateTime; + } + /** Cancel the date/time callback function. */ + static void dateTimeCallbackCancel() { + m_dateTime = 0; + } + /** Return a file's directory entry. + * + * \param[out] dir Location for return of the file's directory entry. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool dirEntry(dir_t* dir); + /** + * \return The index of this file in it's directory. + */ + uint16_t dirIndex() { + return m_dirIndex; + } + /** Format the name field of \a dir into the 13 byte array + * \a name in standard 8.3 short name format. + * + * \param[in] dir The directory structure containing the name. + * \param[out] name A 13 byte char array for the formatted name. + * \return length of the name. + */ + static uint8_t dirName(const dir_t* dir, char* name); + /** \return The number of bytes allocated to a directory or zero + * if an error occurs. + */ + uint32_t dirSize(); + /** Dump file in Hex + * \param[in] pr Print stream for list. + * \param[in] pos Start position in file. + * \param[in] n number of locations to dump. + */ + void dmpFile(print_t* pr, uint32_t pos, size_t n); + /** Test for the existence of a file in a directory + * + * \param[in] path Path of the file to be tested for. + * + * The calling instance must be an open directory file. + * + * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory + * dirFile. + * + * \return true if the file exists else false. + */ + bool exists(const char* path) { + FatFile file; + return file.open(this, path, O_READ); + } + /** + * Get a string from a file. + * + * fgets() reads bytes from a file into the array pointed to by \a str, until + * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str, + * or end-of-file is encountered. The string is then terminated + * with a null byte. + * + * fgets() deletes CR, '\\r', from the string. This insures only a '\\n' + * terminates the string for Windows text files which use CRLF for newline. + * + * \param[out] str Pointer to the array where the string is stored. + * \param[in] num Maximum number of characters to be read + * (including the final null byte). Usually the length + * of the array \a str is used. + * \param[in] delim Optional set of delimiters. The default is "\n". + * + * \return For success fgets() returns the length of the string in \a str. + * If no data is read, fgets() returns zero for EOF or -1 if an error occurred. + */ + int16_t fgets(char* str, int16_t num, char* delim = 0); + /** \return The total number of bytes in a file. */ + uint32_t fileSize() const { + return m_fileSize; + } + /** \return The first cluster number for a file or directory. */ + uint32_t firstCluster() const { + return m_firstCluster; + } + /** + * Get a file's name followed by a zero byte. + * + * \param[out] name An array of characters for the file's name. + * \param[in] size The size of the array in bytes. The array + * must be at least 13 bytes long. The file's name will be + * truncated if the file's name is too long. + * \return The value true, is returned for success and + * the value false, is returned for failure. + */ + bool getName(char* name, size_t size); + /** + * Get a file's Short File Name followed by a zero byte. + * + * \param[out] name An array of characters for the file's name. + * The array must be at least 13 bytes long. + * \return The value true, is returned for success and + * the value false, is returned for failure. + */ + bool getSFN(char* name); + /** \return True if this is a directory else false. */ + bool isDir() const { + return m_attr & FILE_ATTR_DIR; + } + /** \return True if this is a normal file else false. */ + bool isFile() const { + return m_attr & FILE_ATTR_FILE; + } + /** \return True if this is a hidden file else false. */ + bool isHidden() const { + return m_attr & FILE_ATTR_HIDDEN; + } + /** \return true if this file has a Long File Name. */ + bool isLFN() const { + return m_lfnOrd; + } + /** \return True if this is an open file/directory else false. */ + bool isOpen() const { + return m_attr; + } + /** \return True if this is the root directory. */ + bool isRoot() const { + return m_attr & FILE_ATTR_ROOT; + } + /** \return True if this is the FAT32 root directory. */ + bool isRoot32() const { + return m_attr & FILE_ATTR_ROOT32; + } + /** \return True if this is the FAT12 of FAT16 root directory. */ + bool isRootFixed() const { + return m_attr & FILE_ATTR_ROOT_FIXED; + } + /** \return True if file is read-only */ + bool isReadOnly() const { + return m_attr & FILE_ATTR_READ_ONLY; + } + /** \return True if this is a subdirectory else false. */ + bool isSubDir() const { + return m_attr & FILE_ATTR_SUBDIR; + } + /** \return True if this is a system file else false. */ + bool isSystem() const { + return m_attr & FILE_ATTR_SYSTEM; + } + /** Check for a legal 8.3 character. + * \param[in] c Character to be checked. + * \return true for a legal 8.3 character else false. + */ + static bool legal83Char(uint8_t c) { + if (c == '"' || c == '|') { + return false; + } + // *+,./ + if (0X2A <= c && c <= 0X2F && c != 0X2D) { + return false; + } + // :;<=>? + if (0X3A <= c && c <= 0X3F) { + return false; + } + // [\] + if (0X5B <= c && c <= 0X5D) { + return false; + } + return 0X20 < c && c < 0X7F; + } + /** List directory contents. + * + * \param[in] pr Print stream for list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \param[in] indent Amount of space before file name. Used for recursive + * list to indicate subdirectory level. + */ + void ls(print_t* pr, uint8_t flags = 0, uint8_t indent = 0); + /** Make a new directory. + * + * \param[in] dir An open FatFile instance for the directory that will + * contain the new directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the new directory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool mkdir(FatFile* dir, const char* path, bool pFlag = true); + /** Open a file in the volume working directory of a FatFileSystem. + * + * \param[in] fs File System where the file is located. + * + * \param[in] path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag bitwise-inclusive OR of open mode flags. + * See see FatFile::open(FatFile*, const char*, uint8_t). + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool open(FatFileSystem* fs, const char* path, uint8_t oflag); + /** Open a file by index. + * + * \param[in] dirFile An open FatFile instance for the directory. + * + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * + * \param[in] oflag bitwise-inclusive OR of open mode flags. + * See see FatFile::open(FatFile*, const char*, uint8_t). + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ + bool open(FatFile* dirFile, uint16_t index, uint8_t oflag); + /** Open a file or directory by name. + * + * \param[in] dirFile An open FatFile instance for the directory containing + * the file to be opened. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of flags from the following list + * + * O_READ - Open for reading. + * + * O_RDONLY - Same as O_READ. + * + * O_WRITE - Open for writing. + * + * O_WRONLY - Same as O_WRITE. + * + * O_RDWR - Open for reading and writing. + * + * O_APPEND - If set, the file offset shall be set to the end of the + * file prior to each write. + * + * O_AT_END - Set the initial position at the end of the file. + * + * O_CREAT - If the file exists, this flag has no effect except as noted + * under O_EXCL below. Otherwise, the file shall be created + * + * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. + * + * O_SYNC - Call sync() after each write. This flag should not be used with + * write(uint8_t) or any functions do character at a time writes since sync() + * will be called after each byte. + * + * O_TRUNC - If the file exists and is a regular file, and the file is + * successfully opened and is not read only, its length shall be truncated to 0. + * + * WARNING: A given file must not be opened by more than one FatFile object + * or file corruption may occur. + * + * \note Directory files must be opened read only. Write and truncation is + * not allowed for directory files. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool open(FatFile* dirFile, const char* path, uint8_t oflag); + /** Open a file in the current working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag bitwise-inclusive OR of open mode flags. + * See see FatFile::open(FatFile*, const char*, uint8_t). + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool open(const char* path, uint8_t oflag = O_READ) { + return open(m_cwd, path, oflag); + } + /** Open the next file or subdirectory in a directory. + * + * \param[in] dirFile An open FatFile instance for the directory + * containing the file to be opened. + * + * \param[in] oflag bitwise-inclusive OR of open mode flags. + * See see FatFile::open(FatFile*, const char*, uint8_t). + * + * \return true for success or false for failure. + */ + bool openNext(FatFile* dirFile, uint8_t oflag = O_READ); + /** Open a volume's root directory. + * + * \param[in] vol The FAT volume containing the root directory to be opened. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool openRoot(FatVolume* vol); + /** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ + int peek(); + /** Print a file's creation date and time + * + * \param[in] pr Print stream for output. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool printCreateDateTime(print_t* pr); + /** %Print a directory date field. + * + * Format is yyyy-mm-dd. + * + * \param[in] pr Print stream for output. + * \param[in] fatDate The date field from a directory entry. + */ + static void printFatDate(print_t* pr, uint16_t fatDate); + /** %Print a directory time field. + * + * Format is hh:mm:ss. + * + * \param[in] pr Print stream for output. + * \param[in] fatTime The time field from a directory entry. + */ + static void printFatTime(print_t* pr, uint16_t fatTime); + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(float value, char term, uint8_t prec = 2); + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(int16_t value, char term); + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(uint16_t value, char term); + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(int32_t value, char term); + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(uint32_t value, char term); + /** Print a file's modify date and time + * + * \param[in] pr Print stream for output. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool printModifyDateTime(print_t* pr); + /** Print a file's name + * + * \param[in] pr Print stream for output. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + size_t printName(print_t* pr); + /** Print a file's size. + * + * \param[in] pr Print stream for output. + * + * \return The number of characters printed is returned + * for success and zero is returned for failure. + */ + size_t printFileSize(print_t* pr); + /** Print a file's Short File Name. + * + * \param[in] pr Print stream for output. + * + * \return The number of characters printed is returned + * for success and zero is returned for failure. + */ + size_t printSFN(print_t* pr); + /** 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. + */ + int read() { + uint8_t b; + return read(&b, 1) == 1 ? b : -1; + } + /** Read data from a file starting at the current position. + * + * \param[out] buf Pointer to the location that will receive the data. + * + * \param[in] nbyte Maximum number of bytes to read. + * + * \return For success read() returns the number of bytes read. + * A value less than \a nbyte, including zero, will be returned + * if end of file is reached. + * If an error occurs, read() returns -1. Possible errors include + * read() called before a file has been opened, corrupt file system + * or an I/O error occurred. + */ + int read(void* buf, size_t nbyte); + /** Read the next directory entry from a directory file. + * + * \param[out] dir The dir_t struct that will receive the data. + * + * \return For success readDir() returns the number of bytes read. + * A value of zero will be returned if end of file is reached. + * If an error occurs, readDir() returns -1. Possible errors include + * readDir() called before a directory has been opened, this is not + * a directory file or an I/O error occurred. + */ + int8_t readDir(dir_t* dir); + /** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool remove(); + /** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \param[in] dirFile The directory that contains the file. + * \param[in] path Path for the file to be removed. + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + static bool remove(FatFile* dirFile, const char* path); + /** Set the file's current position to zero. */ + void rewind() { + seekSet(0); + } + /** Rename a file or subdirectory. + * + * \param[in] dirFile Directory for the new path. + * \param[in] newPath New path name for the file/directory. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool rename(FatFile* dirFile, const char* newPath); + /** Remove a directory file. + * + * The directory file will be removed only if it is empty and is not the + * root directory. rmdir() follows DOS and Windows and ignores the + * read-only attribute for the directory. + * + * \note This function should not be used to delete the 8.3 version of a + * directory that has a long name. For example if a directory has the + * long name "New folder" you should not delete the 8.3 name "NEWFOL~1". + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool rmdir(); + /** Recursively delete a directory and all contained files. + * + * This is like the Unix/Linux 'rm -rf *' if called with the root directory + * hence the name. + * + * Warning - This will remove all contents of the directory including + * subdirectories. The directory will then be removed if it is not root. + * The read-only attribute for files will be ignored. + * + * \note This function should not be used to delete the 8.3 version of + * a directory that has a long name. See remove() and rmdir(). + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool rmRfStar(); + /** Set the files position to current position + \a pos. See seekSet(). + * \param[in] offset The new position in bytes from the current position. + * \return true for success or false for failure. + */ + bool seekCur(int32_t offset) { + return seekSet(m_curPosition + offset); + } + /** Set the files position to end-of-file + \a offset. See seekSet(). + * Can't be used for directory files since file size is not defined. + * \param[in] offset The new position in bytes from end-of-file. + * \return true for success or false for failure. + */ + bool seekEnd(int32_t offset = 0) { + return isFile() ? seekSet(m_fileSize + offset) : false; + } + /** Sets a file's position. + * + * \param[in] pos The new position in bytes from the beginning of the file. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool seekSet(uint32_t pos); + /** Set the current working directory. + * + * \param[in] dir New current working directory. + * + * \return true for success else false. + */ + static bool setCwd(FatFile* dir) { + if (!dir->isDir()) { + return false; + } + m_cwd = dir; + return true; + } + /** The sync() call causes all modified data and directory fields + * to be written to the storage device. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool sync(); + /** Copy a file's timestamps + * + * \param[in] file File to copy timestamps from. + * + * \note + * Modify and access timestamps may be overwritten if a date time callback + * function has been set by dateTimeCallback(). + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool timestamp(FatFile* file); + /** Set a file's timestamps in its directory entry. + * + * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive + * OR of flags from the following list + * + * T_ACCESS - Set the file's last access date. + * + * T_CREATE - Set the file's creation date and time. + * + * T_WRITE - Set the file's last write/modification date and time. + * + * \param[in] year Valid range 1980 - 2107 inclusive. + * + * \param[in] month Valid range 1 - 12 inclusive. + * + * \param[in] day Valid range 1 - 31 inclusive. + * + * \param[in] hour Valid range 0 - 23 inclusive. + * + * \param[in] minute Valid range 0 - 59 inclusive. + * + * \param[in] second Valid range 0 - 59 inclusive + * + * \note It is possible to set an invalid date since there is no check for + * the number of days in a month. + * + * \note + * Modify and access timestamps may be overwritten if a date time callback + * function has been set by dateTimeCallback(). + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second); + /** Type of file. You should use isFile() or isDir() instead of fileType() + * if possible. + * + * \return The file or directory type. + */ + uint8_t fileAttr() const { + return m_attr; + } + /** Truncate a file to a specified length. The current file position + * will be maintained if it is less than or equal to \a length otherwise + * it will be set to end of file. + * + * \param[in] length The desired length for the file. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool truncate(uint32_t length); + /** \return FatVolume that contains this file. */ + FatVolume* volume() const { + return m_vol; + } + /** Write a string to a file. Used by the Arduino Print class. + * \param[in] str Pointer to the string. + * Use getWriteError to check for errors. + * \return count of characters written for success or -1 for failure. + */ + int write(const char* str) { + return write(str, strlen(str)); + } + /** Write a single byte. + * \param[in] b The byte to be written. + * \return +1 for success or -1 for failure. + */ + int write(uint8_t b) { + return write(&b, 1); + } + /** Write data to an open file. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buf Pointer to the location of the data to be written. + * + * \param[in] nbyte Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a nbyte. If an error occurs, write() returns -1. Possible errors + * include write() is called before a file has been opened, write is called + * for a read-only file, device is full, a corrupt file system or an I/O error. + * + */ + int write(const void* buf, size_t nbyte); +//------------------------------------------------------------------------------ + private: + /** This file has not been opened. */ + static const uint8_t FILE_ATTR_CLOSED = 0; + /** File is read-only. */ + static const uint8_t FILE_ATTR_READ_ONLY = DIR_ATT_READ_ONLY; + /** File should be hidden in directory listings. */ + static const uint8_t FILE_ATTR_HIDDEN = DIR_ATT_HIDDEN; + /** Entry is for a system file. */ + static const uint8_t FILE_ATTR_SYSTEM = DIR_ATT_SYSTEM; + /** Entry for normal data file */ + static const uint8_t FILE_ATTR_FILE = 0X08; + /** Entry is for a subdirectory */ + static const uint8_t FILE_ATTR_SUBDIR = DIR_ATT_DIRECTORY; + /** A FAT12 or FAT16 root directory */ + static const uint8_t FILE_ATTR_ROOT_FIXED = 0X20; + /** A FAT32 root directory */ + static const uint8_t FILE_ATTR_ROOT32 = 0X40; + /** Entry is for root. */ + static const uint8_t FILE_ATTR_ROOT = FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32; + /** Directory type bits */ + static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT; + /** Attributes to copy from directory entry */ + static const uint8_t FILE_ATTR_COPY = DIR_ATT_READ_ONLY | DIR_ATT_HIDDEN | + DIR_ATT_SYSTEM | DIR_ATT_DIRECTORY; + + /** experimental don't use */ + + bool openParent(FatFile* dir); + + // private functions + bool addCluster(); + bool addDirCluster(); + dir_t* cacheDirEntry(uint8_t action); + static uint8_t lfnChecksum(uint8_t* name); + bool lfnUniqueSfn(fname_t* fname); + bool openCluster(FatFile* file); + static bool parsePathName(const char* str, fname_t* fname, const char** ptr); + bool mkdir(FatFile* parent, fname_t* fname); + bool open(FatFile* dirFile, fname_t* fname, uint8_t oflag); + bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, uint8_t oflag, + uint8_t lfnOrd); + bool readLBN(uint32_t* lbn); + dir_t* readDirCache(bool skipReadOk = false); + bool setDirSize(); + + // bits defined in m_flags + // should be 0X0F + static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); + // sync of directory entry required + static uint8_t const F_FILE_DIR_DIRTY = 0X80; + + // global pointer to cwd dir + static FatFile* m_cwd; + // data time callback function + static void (*m_dateTime)(uint16_t* date, uint16_t* time); + // private data + static const uint8_t WRITE_ERROR = 0X1; + static const uint8_t READ_ERROR = 0X2; + uint8_t m_attr; // File attributes + uint8_t m_error; // Error bits. + uint8_t m_flags; // See above for definition of m_flags bits + uint8_t m_lfnOrd; + uint16_t m_dirIndex; // index of directory entry in dir file + FatVolume* m_vol; // volume where file is located + uint32_t m_dirCluster; + uint32_t m_curCluster; // cluster for current file position + uint32_t m_curPosition; // current file position + uint32_t m_dirBlock; // block for this files directory entry + uint32_t m_fileSize; // file size in bytes + uint32_t m_firstCluster; // first cluster of file +}; +#endif // FatFile_h diff --git a/lib/SdFat/utility/FatFileLFN.cpp b/lib/SdFat/utility/FatFileLFN.cpp new file mode 100644 index 0000000..5ab9f45 --- /dev/null +++ b/lib/SdFat/utility/FatFileLFN.cpp @@ -0,0 +1,683 @@ +/* FatLib Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include "FatFile.h" +//------------------------------------------------------------------------------ +// +uint8_t FatFile::lfnChecksum(uint8_t* name) { + uint8_t sum = 0; + for (uint8_t i = 0; i < 11; i++) { + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + name[i]; + } + return sum; +} +#if USE_LONG_FILE_NAMES +//------------------------------------------------------------------------------ +// Saves about 90 bytes of flash on 328 over tolower(). +inline char lfnToLower(char c) { + return 'A' <= c && c <= 'Z' ? c + 'a' - 'A' : c; +} +//------------------------------------------------------------------------------ +// Daniel Bernstein University of Illinois at Chicago. +// Original had + instead of ^ +static uint16_t Bernstein(uint16_t hash, const char *str, size_t len) { + for (size_t i = 0; i < len; i++) { + // hash = hash * 33 ^ str[i]; + hash = ((hash << 5) + hash) ^ str[i]; + } + return hash; +} +//------------------------------------------------------------------------------ +/** + * Fetch a 16-bit long file name character. + * + * \param[in] ldir Pointer to long file name directory entry. + * \param[in] i Index of character. + * \return The 16-bit character. + */ +static uint16_t lfnGetChar(ldir_t *ldir, uint8_t i) { + if (i < LDIR_NAME1_DIM) { + return ldir->name1[i]; + } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) { + return ldir->name2[i - LDIR_NAME1_DIM]; + } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) { + return ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM]; + } + return 0; +} +//------------------------------------------------------------------------------ +static bool lfnGetName(ldir_t *ldir, char* name, size_t n) { + uint8_t i; + size_t k = 13*((ldir->ord & 0X1F) - 1); + for (i = 0; i < 13; i++) { + uint16_t c = lfnGetChar(ldir, i); + if (c == 0 || k >= n) { + break; + } + name[k++] = c >= 0X7F ? '?' : c; + } + // Terminate with zero byte if name fits. + if (k < n && (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY)) { + name[k] = 0; + } + // Truncate if name is too long. + name[n - 1] = 0; + return true; +} +//------------------------------------------------------------------------------ +inline bool lfnLegalChar(char c) { + if (c == '/' || c == '\\' || c == '"' || c == '*' || + c == ':' || c == '<' || c == '>' || c == '?' || c == '|') { + return false; + } + return 0X1F < c && c < 0X7F; +} +//------------------------------------------------------------------------------ +/** + * Store a 16-bit long file name character. + * + * \param[in] ldir Pointer to long file name directory entry. + * \param[in] i Index of character. + * \param[in] c The 16-bit character. + */ +static void lfnPutChar(ldir_t *ldir, uint8_t i, uint16_t c) { + if (i < LDIR_NAME1_DIM) { + ldir->name1[i] = c; + } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) { + ldir->name2[i - LDIR_NAME1_DIM] = c; + } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) { + ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM] = c; + } +} +//------------------------------------------------------------------------------ +static void lfnPutName(ldir_t *ldir, const char* name, size_t n) { + size_t k = 13*((ldir->ord & 0X1F) - 1); + for (uint8_t i = 0; i < 13; i++, k++) { + uint16_t c = k < n ? name[k] : k == n ? 0 : 0XFFFF; + lfnPutChar(ldir, i, c); + } +} +//============================================================================== +bool FatFile::getName(char* name, size_t size) { + FatFile dirFile; + ldir_t* ldir; + if (!isOpen() || size < 13) { + DBG_FAIL_MACRO; + goto fail; + } + if (!isLFN()) { + return getSFN(name); + } + if (!dirFile.openCluster(this)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) { + if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) { + DBG_FAIL_MACRO; + goto fail; + } + ldir = reinterpret_cast(dirFile.readDirCache()); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attr != DIR_ATT_LONG_NAME) { + DBG_FAIL_MACRO; + goto fail; + } + if (ord != (ldir->ord & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!lfnGetName(ldir, name, size)) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { + return true; + } + } + // Fall into fail. + DBG_FAIL_MACRO; + +fail: + name[0] = 0; + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::openCluster(FatFile* file) { + if (file->m_dirCluster == 0) { + return openRoot(file->m_vol); + } + memset(this, 0, sizeof(FatFile)); + m_attr = FILE_ATTR_SUBDIR; + m_flags = O_READ; + m_vol = file->m_vol; + m_firstCluster = file->m_dirCluster; + return true; +} +//------------------------------------------------------------------------------ +bool FatFile::parsePathName(const char* path, + fname_t* fname, const char** ptr) { + char c; + bool is83; + uint8_t bit = DIR_NT_LC_BASE; + uint8_t lc = 0; + uint8_t uc = 0; + uint8_t i = 0; + uint8_t in = 7; + int end; + int len = 0; + int si; + int dot; + + // Skip leading spaces. + while (*path == ' ') { + path++; + } + fname->lfn = path; + + for (len = 0; ; len++) { + c = path[len]; + if (c == 0 || isDirSeparator(c)) { + break; + } + if (!lfnLegalChar(c)) { + return false; + } + } + // Advance to next path component. + for (end = len; path[end] == ' ' || isDirSeparator(path[end]); end++) {} + *ptr = &path[end]; + + // Back over spaces and dots. + while (len) { + c = path[len - 1]; + if (c != '.' && c != ' ') { + break; + } + len--; + } + // Max length of LFN is 255. + if (len > 255) { + return false; + } + fname->len = len; + // Blank file short name. + for (uint8_t k = 0; k < 11; k++) { + fname->sfn[k] = ' '; + } + // skip leading spaces and dots. + for (si = 0; path[si] == '.' || path[si] == ' '; si++) {} + // Not 8.3 if leading dot or space. + is83 = !si; + + // find last dot. + for (dot = len - 1; dot >= 0 && path[dot] != '.'; dot--) {} + for (; si < len; si++) { + c = path[si]; + if (c == ' ' || (c == '.' && dot != si)) { + is83 = false; + continue; + } + if (!legal83Char(c) && si != dot) { + is83 = false; + c = '_'; + } + if (si == dot || i > in) { + if (in == 10) { + // Done - extension longer than three characters. + is83 = false; + break; + } + if (si != dot) { + is83 = false; + } + // Break if no dot and base-name is longer than eight characters. + if (si > dot) { + break; + } + si = dot; + in = 10; // Max index for full 8.3 name. + i = 8; // Place for extension. + bit = DIR_NT_LC_EXT; // bit for extension. + } else { + if ('a' <= c && c <= 'z') { + c += 'A' - 'a'; + lc |= bit; + } else if ('A' <= c && c <= 'Z') { + uc |= bit; + } + fname->sfn[i++] = c; + if (i < 7) { + fname->seqPos = i; + } + } + } + if (fname->sfn[0] == ' ') { + return false; + } + + if (is83) { + fname->flags = lc & uc ? FNAME_FLAG_MIXED_CASE : lc; + } else { + fname->flags = FNAME_FLAG_LOST_CHARS; + fname->sfn[fname->seqPos] = '~'; + fname->sfn[fname->seqPos + 1] = '1'; + } + return true; +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { + bool fnameFound = false; + uint8_t lfnOrd = 0; + uint8_t freeNeed; + uint8_t freeFound = 0; + uint8_t ord = 0; + uint8_t chksum = 0; + uint16_t freeIndex = 0; + uint16_t curIndex; + dir_t* dir; + ldir_t* ldir; + size_t len = fname->len; + + if (!dirFile->isDir() || isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + // Number of directory entries needed. + freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1; + + dirFile->rewind(); + while (1) { + curIndex = dirFile->m_curPosition/32; + dir = dirFile->readDirCache(true); + if (!dir) { + if (dirFile->getError()) { + DBG_FAIL_MACRO; + goto fail; + } + // At EOF + goto create; + } + if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == DIR_NAME_FREE) { + if (freeFound == 0) { + freeIndex = curIndex; + } + if (freeFound < freeNeed) { + freeFound++; + } + if (dir->name[0] == DIR_NAME_FREE) { + goto create; + } + } else { + if (freeFound < freeNeed) { + freeFound = 0; + } + } + // skip empty slot or '.' or '..' + if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + lfnOrd = 0; + } else if (DIR_IS_LONG_NAME(dir)) { + ldir_t *ldir = reinterpret_cast(dir); + if (!lfnOrd) { + if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) { + continue; + } + lfnOrd = ord = ldir->ord & 0X1F; + chksum = ldir->chksum; + } else if (ldir->ord != --ord || chksum != ldir->chksum) { + lfnOrd = 0; + continue; + } + size_t k = 13*(ord - 1); + if (k >= len) { + // Not found. + lfnOrd = 0; + continue; + } + for (uint8_t i = 0; i < 13; i++) { + uint16_t u = lfnGetChar(ldir, i); + if (k == len) { + if (u != 0) { + // Not found. + lfnOrd = 0; + } + break; + } + if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) { + // Not found. + lfnOrd = 0; + break; + } + } + } else if (DIR_IS_FILE_OR_SUBDIR(dir)) { + if (lfnOrd) { + if (1 == ord && lfnChecksum(dir->name) == chksum) { + goto found; + } + DBG_FAIL_MACRO; + goto fail; + } + if (!memcmp(dir->name, fname->sfn, sizeof(fname->sfn))) { + if (!(fname->flags & FNAME_FLAG_LOST_CHARS)) { + goto found; + } + fnameFound = true; + } + } else { + lfnOrd = 0; + } + } + +found: + // Don't open if create only. + if (oflag & O_EXCL) { + DBG_FAIL_MACRO; + goto fail; + } + goto open; + +create: + // don't create unless O_CREAT and O_WRITE + if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // If at EOF start in next cluster. + if (freeFound == 0) { + freeIndex = curIndex; + } + + while (freeFound < freeNeed) { + dir = dirFile->readDirCache(); + if (!dir) { + if (dirFile->getError()) { + DBG_FAIL_MACRO; + goto fail; + } + // EOF if no error. + break; + } + freeFound++; + } + while (freeFound < freeNeed) { + // Will fail if FAT16 root. + if (!dirFile->addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + // Done if more than one block per cluster. Max freeNeed is 21. + if (dirFile->m_vol->blocksPerCluster() > 1) { + break; + } + freeFound += 16; + } + if (fnameFound) { + if (!dirFile->lfnUniqueSfn(fname)) { + goto fail; + } + } + if (!dirFile->seekSet(32UL*freeIndex)) { + DBG_FAIL_MACRO; + goto fail; + } + lfnOrd = freeNeed - 1; + for (uint8_t ord = lfnOrd ; ord ; ord--) { + ldir = reinterpret_cast(dirFile->readDirCache()); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + dirFile->m_vol->cacheDirty(); + ldir->ord = ord == lfnOrd ? LDIR_ORD_LAST_LONG_ENTRY | ord : ord; + ldir->attr = DIR_ATT_LONG_NAME; + ldir->type = 0; + ldir->chksum = lfnChecksum(fname->sfn); + ldir->mustBeZero = 0; + lfnPutName(ldir, fname->lfn, len); + } + curIndex = dirFile->m_curPosition/32; + dir = dirFile->readDirCache(); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // initialize as empty file + memset(dir, 0, sizeof(dir_t)); + memcpy(dir->name, fname->sfn, 11); + + // Set base-name and extension lower case bits. + dir->reservedNT = (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags; + + // set timestamps + if (m_dateTime) { + // call user date/time function + m_dateTime(&dir->creationDate, &dir->creationTime); + } else { + // use default date/time + dir->creationDate = FAT_DEFAULT_DATE; + dir->creationTime = FAT_DEFAULT_TIME; + } + dir->lastAccessDate = dir->creationDate; + dir->lastWriteDate = dir->creationDate; + dir->lastWriteTime = dir->creationTime; + + // Force write of entry to device. + dirFile->m_vol->cacheDirty(); + +open: + // open entry in cache. + if (!openCachedEntry(dirFile, curIndex, oflag, lfnOrd)) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +size_t FatFile::printName(print_t* pr) { + FatFile dirFile; + uint16_t u; + size_t n = 0; + ldir_t* ldir; + + if (!isLFN()) { + return printSFN(pr); + } + if (!dirFile.openCluster(this)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) { + if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) { + DBG_FAIL_MACRO; + goto fail; + } + ldir = reinterpret_cast(dirFile.readDirCache()); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attr != DIR_ATT_LONG_NAME || + ord != (ldir->ord & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t i = 0; i < 13; i++) { + u = lfnGetChar(ldir, i); + if (u == 0) { + // End of name. + break; + } + if (u > 0X7E) { + u = '?'; + } + pr->write(static_cast(u)); + n++; + } + if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { + return n; + } + } + // Fall into fail; + DBG_FAIL_MACRO; + +fail: + return 0; +} +//------------------------------------------------------------------------------ +bool FatFile::remove() { + bool last; + uint8_t chksum; + uint8_t ord; + FatFile dirFile; + dir_t* dir; + ldir_t* ldir; + + // Cant' remove not open for write. + if (!isFile() || !(m_flags & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // Free any clusters. + if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // Cache directory entry. + dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + chksum = lfnChecksum(dir->name); + + // Mark entry deleted. + dir->name[0] = DIR_NAME_DELETED; + + // Set this file closed. + m_attr = FILE_ATTR_CLOSED; + + // Write entry to device. + if (!m_vol->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!isLFN()) { + // Done, no LFN entries. + return true; + } + if (!dirFile.openCluster(this)) { + DBG_FAIL_MACRO; + goto fail; + } + for (ord = 1; ord <= m_lfnOrd; ord++) { + if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) { + DBG_FAIL_MACRO; + goto fail; + } + ldir = reinterpret_cast(dirFile.readDirCache()); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attr != DIR_ATT_LONG_NAME || + ord != (ldir->ord & 0X1F) || + chksum != ldir->chksum) { + DBG_FAIL_MACRO; + goto fail; + } + last = ldir->ord & LDIR_ORD_LAST_LONG_ENTRY; + ldir->ord = DIR_NAME_DELETED; + m_vol->cacheDirty(); + if (last) { + if (!m_vol->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + } + } + // Fall into fail. + DBG_FAIL_MACRO; + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::lfnUniqueSfn(fname_t* fname) { + const uint8_t FIRST_HASH_SEQ = 2; // min value is 2 + uint8_t pos = fname->seqPos;; + dir_t *dir; + uint16_t hex; + + DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS)); + DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1'); + + for (uint8_t seq = 2; seq < 100; seq++) { + if (seq < FIRST_HASH_SEQ) { + fname->sfn[pos + 1] = '0' + seq; + } else { + DBG_PRINT_IF(seq > FIRST_HASH_SEQ); + hex = Bernstein(seq + fname->len, fname->lfn, fname->len); + if (pos > 3) { + // Make space in name for ~HHHH. + pos = 3; + } + for (uint8_t i = pos + 4 ; i > pos; i--) { + uint8_t h = hex & 0XF; + fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10; + hex >>= 4; + } + } + fname->sfn[pos] = '~'; + rewind(); + while (1) { + dir = readDirCache(true); + if (!dir) { + if (!getError()) { + // At EOF and name not found if no error. + goto done; + } + DBG_FAIL_MACRO; + goto fail; + } + if (dir->name[0] == DIR_NAME_FREE) { + goto done; + } + if (DIR_IS_FILE_OR_SUBDIR(dir) && !memcmp(fname->sfn, dir->name, 11)) { + // Name found - try another. + break; + } + } + } + // fall inti fail - too many tries. + DBG_FAIL_MACRO; + +fail: + return false; + +done: + return true; +} +#endif // #if USE_LONG_FILE_NAMES diff --git a/lib/SdFat/utility/FatFilePrint.cpp b/lib/SdFat/utility/FatFilePrint.cpp new file mode 100644 index 0000000..c6a0661 --- /dev/null +++ b/lib/SdFat/utility/FatFilePrint.cpp @@ -0,0 +1,248 @@ +/* FatLib Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include +#include "FatFile.h" +#include "FmtNumber.h" +//------------------------------------------------------------------------------ +// print uint8_t with width 2 +static void print2u(print_t* pr, uint8_t v) { + char c0 = '?'; + char c1 = '?'; + if (v < 100) { + c1 = v/10; + c0 = v - 10*c1 + '0'; + c1 += '0'; + } + pr->write(c1); + pr->write(c0); +} +//------------------------------------------------------------------------------ +static void printU32(print_t* pr, uint32_t v) { + char buf[11]; + char* ptr = buf + sizeof(buf); + *--ptr = 0; + pr->write(fmtDec(v, ptr)); +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint8_t w, uint16_t h) { + char buf[5]; + char* ptr = buf + sizeof(buf); + *--ptr = 0; + for (uint8_t i = 0; i < w; i++) { + char c = h & 0XF; + *--ptr = c < 10 ? c + '0' : c + 'A' - 10; + h >>= 4; + } + pr->write(ptr); +} +//------------------------------------------------------------------------------ +void FatFile::dmpFile(print_t* pr, uint32_t pos, size_t n) { + char text[17]; + text[16] = 0; + if (n >= 0XFFF0) { + n = 0XFFF0; + } + if (!seekSet(pos)) { + return; + } + for (size_t i = 0; i <= n; i++) { + if ((i & 15) == 0) { + if (i) { + pr->write(' '); + pr->write(text); + if (i == n) { + break; + } + } + pr->write('\r'); + pr->write('\n'); + if (i >= n) { + break; + } + printHex(pr, 4, i); + pr->write(' '); + } + int16_t h = read(); + if (h < 0) { + break; + } + pr->write(' '); + printHex(pr, 2, h); + text[i&15] = ' ' <= h && h < 0X7F ? h : '.'; + } + pr->write('\r'); + pr->write('\n'); +} +//------------------------------------------------------------------------------ +void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { + FatFile file; + rewind(); + while (file.openNext(this, O_READ)) { + // indent for dir level + if (!file.isHidden() || (flags & LS_A)) { + for (uint8_t i = 0; i < indent; i++) { + pr->write(' '); + } + if (flags & LS_DATE) { + file.printModifyDateTime(pr); + pr->write(' '); + } + if (flags & LS_SIZE) { + file.printFileSize(pr); + pr->write(' '); + } + file.printName(pr); + if (file.isDir()) { + pr->write('/'); + } + pr->write('\r'); + pr->write('\n'); + if ((flags & LS_R) && file.isDir()) { + file.ls(pr, flags, indent + 2); + } + } + file.close(); + } +} +//------------------------------------------------------------------------------ +bool FatFile::printCreateDateTime(print_t* pr) { + dir_t dir; + if (!dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + printFatDate(pr, dir.creationDate); + pr->write(' '); + printFatTime(pr, dir.creationTime); + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +void FatFile::printFatDate(print_t* pr, uint16_t fatDate) { + printU32(pr, FAT_YEAR(fatDate)); + pr->write('-'); + print2u(pr, FAT_MONTH(fatDate)); + pr->write('-'); + print2u(pr, FAT_DAY(fatDate)); +} +//------------------------------------------------------------------------------ +void FatFile::printFatTime(print_t* pr, uint16_t fatTime) { + print2u(pr, FAT_HOUR(fatTime)); + pr->write(':'); + print2u(pr, FAT_MINUTE(fatTime)); + pr->write(':'); + print2u(pr, FAT_SECOND(fatTime)); +} +//------------------------------------------------------------------------------ +/** Template for FatFile::printField() */ +template +static int printFieldT(FatFile* file, char sign, Type value, char term) { + char buf[3*sizeof(Type) + 3]; + char* str = &buf[sizeof(buf)]; + + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } +#ifdef OLD_FMT + do { + Type m = value; + value /= 10; + *--str = '0' + m - 10*value; + } while (value); +#else // OLD_FMT + str = fmtDec(value, str); +#endif // OLD_FMT + if (sign) { + *--str = sign; + } + return file->write(str, &buf[sizeof(buf)] - str); +} +//------------------------------------------------------------------------------ + +int FatFile::printField(float value, char term, uint8_t prec) { + char buf[24]; + char* str = &buf[sizeof(buf)]; + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + str = fmtFloat(value, str, prec); + return write(str, buf + sizeof(buf) - str); +} +//------------------------------------------------------------------------------ +int FatFile::printField(uint16_t value, char term) { + return printFieldT(this, 0, value, term); +} +//------------------------------------------------------------------------------ +int FatFile::printField(int16_t value, char term) { + char sign = 0; + if (value < 0) { + sign = '-'; + value = -value; + } + return printFieldT(this, sign, (uint16_t)value, term); +} +//------------------------------------------------------------------------------ +int FatFile::printField(uint32_t value, char term) { + return printFieldT(this, 0, value, term); +} +//------------------------------------------------------------------------------ +int FatFile::printField(int32_t value, char term) { + char sign = 0; + if (value < 0) { + sign = '-'; + value = -value; + } + return printFieldT(this, sign, (uint32_t)value, term); +} +//------------------------------------------------------------------------------ +bool FatFile::printModifyDateTime(print_t* pr) { + dir_t dir; + if (!dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + printFatDate(pr, dir.lastWriteDate); + pr->write(' '); + printFatTime(pr, dir.lastWriteTime); + return true; + +fail: + return false; +} + +//------------------------------------------------------------------------------ +size_t FatFile::printFileSize(print_t* pr) { + char buf[11]; + char *ptr = buf + sizeof(buf); + *--ptr = 0; + ptr = fmtDec(fileSize(), ptr); + while (ptr > buf) { + *--ptr = ' '; + } + return pr->write(buf); +} diff --git a/lib/SdFat/utility/FatFileSFN.cpp b/lib/SdFat/utility/FatFileSFN.cpp new file mode 100644 index 0000000..4b89f09 --- /dev/null +++ b/lib/SdFat/utility/FatFileSFN.cpp @@ -0,0 +1,273 @@ +/* FatLib Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include "FatFile.h" +#include "FatFileSystem.h" +//------------------------------------------------------------------------------ +bool FatFile::getSFN(char* name) { + dir_t* dir; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isRoot()) { + name[0] = '/'; + name[1] = '\0'; + return true; + } + // cache entry + dir = cacheDirEntry(FatCache::CACHE_FOR_READ); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // format name + dirName(dir, name); + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +size_t FatFile::printSFN(print_t* pr) { + char name[13]; + if (!getSFN(name)) { + DBG_FAIL_MACRO; + goto fail; + } + return pr->write(name); + +fail: + return 0; +} +#if !USE_LONG_FILE_NAMES +//------------------------------------------------------------------------------ +bool FatFile::getName(char* name, size_t size) { + return size < 13 ? 0 : getSFN(name); +} +//------------------------------------------------------------------------------ +// format directory name field from a 8.3 name string +bool FatFile::parsePathName(const char* path, fname_t* fname, + const char** ptr) { + uint8_t uc = 0; + uint8_t lc = 0; + uint8_t bit = FNAME_FLAG_LC_BASE; + // blank fill name and extension + for (uint8_t i = 0; i < 11; i++) { + fname->sfn[i] = ' '; + } + + for (uint8_t i = 0, n = 7;; path++) { + uint8_t c = *path; + if (c == 0 || isDirSeparator(c)) { + // Done. + break; + } + if (c == '.' && n == 7) { + n = 10; // max index for full 8.3 name + i = 8; // place for extension + + // bit for extension. + bit = FNAME_FLAG_LC_EXT; + } else { + if (!legal83Char(c) || i > n) { + DBG_FAIL_MACRO; + goto fail; + } + if ('a' <= c && c <= 'z') { + c += 'A' - 'a'; + lc |= bit; + } else if ('A' <= c && c <= 'Z') { + uc |= bit; + } + fname->sfn[i++] = c; + } + } + // must have a file name, extension is optional + if (fname->sfn[0] == ' ') { + DBG_FAIL_MACRO; + goto fail; + } + // Set base-name and extension bits. + fname->flags = lc & uc ? 0 : lc; + while (isDirSeparator(*path)) { + path++; + } + *ptr = path; + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +// open with filename in fname +#define SFN_OPEN_USES_CHKSUM 0 +bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { + bool emptyFound = false; +#if SFN_OPEN_USES_CHKSUM + uint8_t chksum; +#endif + uint8_t lfnOrd = 0; + uint16_t emptyIndex; + uint16_t index = 0; + dir_t* dir; + ldir_t* ldir; + + dirFile->rewind(); + while (1) { + if (!emptyFound) { + emptyIndex = index; + } + dir = dirFile->readDirCache(true); + if (!dir) { + if (dirFile->getError()) { + DBG_FAIL_MACRO; + goto fail; + } + // At EOF if no error. + break; + } + if (dir->name[0] == DIR_NAME_FREE) { + emptyFound = true; + break; + } + if (dir->name[0] == DIR_NAME_DELETED) { + lfnOrd = 0; + emptyFound = true; + } else if (DIR_IS_FILE_OR_SUBDIR(dir)) { + if (!memcmp(fname->sfn, dir->name, 11)) { + // don't open existing file if O_EXCL + if (oflag & O_EXCL) { + DBG_FAIL_MACRO; + goto fail; + } +#if SFN_OPEN_USES_CHKSUM + if (lfnOrd && chksum != lfnChecksum(dir->name)) { + DBG_FAIL_MACRO; + goto fail; + } +#endif // SFN_OPEN_USES_CHKSUM + if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + } else { + lfnOrd = 0; + } + } else if (DIR_IS_LONG_NAME(dir)) { + ldir = reinterpret_cast(dir); + if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { + lfnOrd = ldir->ord & 0X1F; +#if SFN_OPEN_USES_CHKSUM + chksum = ldir->chksum; +#endif // SFN_OPEN_USES_CHKSUM + } + } else { + lfnOrd = 0; + } + index++; + } + // don't create unless O_CREAT and O_WRITE + if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + if (emptyFound) { + index = emptyIndex; + } else { + if (!dirFile->addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (!dirFile->seekSet(32UL*index)) { + DBG_FAIL_MACRO; + goto fail; + } + dir = dirFile->readDirCache(); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // initialize as empty file + memset(dir, 0, sizeof(dir_t)); + memcpy(dir->name, fname->sfn, 11); + + // Set base-name and extension lower case bits. + dir->reservedNT = (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags; + + // set timestamps + if (m_dateTime) { + // call user date/time function + m_dateTime(&dir->creationDate, &dir->creationTime); + } else { + // use default date/time + dir->creationDate = FAT_DEFAULT_DATE; + dir->creationTime = FAT_DEFAULT_TIME; + } + dir->lastAccessDate = dir->creationDate; + dir->lastWriteDate = dir->creationDate; + dir->lastWriteTime = dir->creationTime; + + // Force write of entry to device. + dirFile->m_vol->cacheDirty(); + + // open entry in cache. + return openCachedEntry(dirFile, index, oflag, 0); + +fail: + return false; +} +//------------------------------------------------------------------------------ +size_t FatFile::printName(print_t* pr) { + return printSFN(pr); +} +//------------------------------------------------------------------------------ +bool FatFile::remove() { + dir_t* dir; + // Can't remove if LFN or not open for write. + if (!isFile() || isLFN() || !(m_flags & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // Free any clusters. + if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // Cache directory entry. + dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + // Mark entry deleted. + dir->name[0] = DIR_NAME_DELETED; + + // Set this file closed. + m_attr = FILE_ATTR_CLOSED; + + // Write entry to device. + return m_vol->cacheSync(); + +fail: + return false; +} +#endif // !USE_LONG_FILE_NAMES diff --git a/lib/SdFat/utility/FatFileSystem.h b/lib/SdFat/utility/FatFileSystem.h new file mode 100644 index 0000000..88955a4 --- /dev/null +++ b/lib/SdFat/utility/FatFileSystem.h @@ -0,0 +1,310 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef FatFileSystem_h +#define FatFileSystem_h +#include "FatVolume.h" +#include "FatFile.h" +#include "ArduinoFiles.h" +/** + * \file + * \brief FatFileSystem class + */ +//------------------------------------------------------------------------------ +/** + * \class FatFileSystem + * \brief Integration class for the FatLib library. + */ +class FatFileSystem : public FatVolume { + public: + /** + * Initialize an FatFileSystem object. + * \param[in] part partition to initialize. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool begin(uint8_t part = 0) { + vwd()->close(); + return (part ? init(part) : init(1) || init(0)) + && vwd()->openRoot(this) && FatFile::setCwd(vwd()); + } +#if ENABLE_ARDUINO_FEATURES + /** List the directory contents of the volume working directory to Serial. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ + void ls(uint8_t flags = 0) { + ls(&Serial, flags); + } + /** List the directory contents of a directory to Serial. + * + * \param[in] path directory to list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ + void ls(const char* path, uint8_t flags = 0) { + ls(&Serial, path, flags); + } + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] mode open mode flags. + * \return a File object. + */ + File open(const char *path, uint8_t mode = FILE_READ) { + File tmpFile; + tmpFile.open(vwd(), path, mode); + return tmpFile; + } +#endif // ENABLE_ARDUINO_FEATURES + /** Change a volume's working directory to root + * + * Changes the volume's working directory to the SD's root directory. + * Optionally set the current working directory to the volume's + * working directory. + * + * \param[in] set_cwd Set the current working directory to this volume's + * working directory if true. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool chdir(bool set_cwd = false) { + vwd()->close(); + return vwd()->openRoot(this) && (set_cwd ? FatFile::setCwd(vwd()) : true); + } + /** Change a volume's working directory + * + * Changes the volume working directory to the \a path subdirectory. + * Optionally set the current working directory to the volume's + * working directory. + * + * Example: If the volume's working directory is "/DIR", chdir("SUB") + * will change the volume's working directory from "/DIR" to "/DIR/SUB". + * + * If path is "/", the volume's working directory will be changed to the + * root directory + * + * \param[in] path The name of the subdirectory. + * + * \param[in] set_cwd Set the current working directory to this volume's + * working directory if true. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + //---------------------------------------------------------------------------- + bool chdir(const char *path, bool set_cwd = false) { + FatFile dir; + if (path[0] == '/' && path[1] == '\0') { + return chdir(set_cwd); + } + if (!dir.open(vwd(), path, O_READ)) { + goto fail; + } + if (!dir.isDir()) { + goto fail; + } +// *m_vwd = dir; + m_vwd = dir; + if (set_cwd) { + FatFile::setCwd(vwd()); + } + return true; + +fail: + return false; + } + //---------------------------------------------------------------------------- + /** Set the current working directory to a volume's working directory. + * + * This is useful with multiple SD cards. + * + * The current working directory is changed to this + * volume's working directory. + * + * This is like the Windows/DOS \: command. + */ + void chvol() { + FatFile::setCwd(vwd()); + } + //---------------------------------------------------------------------------- + /** + * Test for the existence of a file. + * + * \param[in] path Path of the file to be tested for. + * + * \return true if the file exists else false. + */ + bool exists(const char* path) { + return vwd()->exists(path); + } + //---------------------------------------------------------------------------- + /** List the directory contents of the volume working directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ + void ls(print_t* pr, uint8_t flags) { + vwd()->ls(pr, flags); + } + //---------------------------------------------------------------------------- + /** List the directory contents of a directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] path directory to list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ + void ls(print_t* pr, const char* path, uint8_t flags) { + FatFile dir; + dir.open(vwd(), path, O_READ); + dir.ls(pr, flags); + } + //---------------------------------------------------------------------------- + /** Make a subdirectory in the volume working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool mkdir(const char* path, bool pFlag = true) { + FatFile sub; + return sub.mkdir(vwd(), path, pFlag); + } + //---------------------------------------------------------------------------- + /** Remove a file from the volume working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the file. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool remove(const char* path) { + return FatFile::remove(vwd(), path); + } + //---------------------------------------------------------------------------- + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool rename(const char *oldPath, const char *newPath) { + FatFile file; + if (!file.open(vwd(), oldPath, O_READ)) { + return false; + } + return file.rename(vwd(), newPath); + } + //---------------------------------------------------------------------------- + /** Remove a subdirectory from the volume's working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool rmdir(const char* path) { + FatFile sub; + if (!sub.open(vwd(), path, O_READ)) { + return false; + } + return sub.rmdir(); + } + //---------------------------------------------------------------------------- + /** Truncate a file to a specified length. The current file position + * will be maintained if it is less than or equal to \a length otherwise + * it will be set to end of file. + * + * \param[in] path A path with a valid 8.3 DOS name for the file. + * \param[in] length The desired length for the file. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool truncate(const char* path, uint32_t length) { + FatFile file; + if (!file.open(vwd(), path, O_WRITE)) { + return false; + } + return file.truncate(length); + } + /** \return a pointer to the FatVolume object. */ + FatVolume* vol() { + return this; + } + /** \return a pointer to the volume working directory. */ + FatFile* vwd() { + return &m_vwd; + } + /** Wipe all data from the volume. You must reinitialize the volume before + * accessing it again. + * \param[in] pr print stream for status dots. + * \return true for success else false. + */ + bool wipe(print_t* pr = 0) { + vwd()->close(); + return FatVolume::wipe(pr); + } + + private: + FatFile m_vwd; +}; +#endif // FatFileSystem_h diff --git a/lib/SdFat/utility/FatLib.h b/lib/SdFat/utility/FatLib.h new file mode 100644 index 0000000..425a92b --- /dev/null +++ b/lib/SdFat/utility/FatLib.h @@ -0,0 +1,33 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef FatLib_h +#define FatLib_h +#include "ArduinoFiles.h" +#include "ArduinoStream.h" +#include "FatFileSystem.h" +#include "FatLibConfig.h" +#include "FatVolume.h" +#include "FatFile.h" +#include "StdioStream.h" +#include "fstream.h" +//------------------------------------------------------------------------------ +/** FatFileSystem version YYYYMMDD */ +#define FAT_LIB_VERSION 20150131 +#endif // FatLib_h diff --git a/lib/SdFat/utility/FatLibConfig.h b/lib/SdFat/utility/FatLibConfig.h new file mode 100644 index 0000000..923e7a0 --- /dev/null +++ b/lib/SdFat/utility/FatLibConfig.h @@ -0,0 +1,143 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +/** + * \file + * \brief configuration definitions + */ +#ifndef FatLibConfig_h +#define FatLibConfig_h +#include +// Allow this file to override defaults. +#include "../SdFatConfig.h" + +#ifdef __AVR__ +#include +#endif // __AVR__ +//------------------------------------------------------------------------------ +/** + * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN). + * Long File Name are limited to a maximum length of 255 characters. + * + * This implementation allows 7-bit characters in the range + * 0X20 to 0X7E. The following characters are not allowed: + * + * < (less than) + * > (greater than) + * : (colon) + * " (double quote) + * / (forward slash) + * \ (backslash) + * | (vertical bar or pipe) + * ? (question mark) + * * (asterisk) + * + */ +#ifndef USE_LONG_FILE_NAMES +#define USE_LONG_FILE_NAMES 1 +#endif // USE_LONG_FILE_NAMES +//------------------------------------------------------------------------------ +/** + * Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class + * for the Arduino File class. If ARDUINO_FILE_USES_STREAM is zero, Print + * will be used as the base class for the Arduino File class. + * + * You can save some flash if you do not use Stream input functions such as + * find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), + * parseInt(), and parsefloat(). + */ +#ifndef ARDUINO_FILE_USES_STREAM +#define ARDUINO_FILE_USES_STREAM 1 +#endif // ARDUINO_FILE_USES_STREAM +//------------------------------------------------------------------------------ +/** + * Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache + * for FAT table entries. Improves performance for large writes that + * are not a multiple of 512 bytes. + */ +#ifndef USE_SEPARATE_FAT_CACHE +#ifdef __arm__ +#define USE_SEPARATE_FAT_CACHE 1 +#else // __arm__ +#define USE_SEPARATE_FAT_CACHE 0 +#endif // __arm__ +#endif // USE_SEPARATE_FAT_CACHE +//------------------------------------------------------------------------------ +/** + * Set USE_MULTI_BLOCK_IO non-zero to use multi-block SD read/write. + * + * Don't use mult-block read/write on small AVR boards. + */ +#ifndef USE_MULTI_BLOCK_IO +#if defined(RAMEND) && RAMEND < 3000 +#define USE_MULTI_BLOCK_IO 0 +#else // RAMEND +#define USE_MULTI_BLOCK_IO 1 +#endif // RAMEND +#endif // USE_MULTI_BLOCK_IO +//------------------------------------------------------------------------------ +/** + * Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. + * + * Causes use of lots of heap in ARM. + */ +#ifndef DESTRUCTOR_CLOSES_FILE +#define DESTRUCTOR_CLOSES_FILE 0 +#endif // DESTRUCTOR_CLOSES_FILE +//------------------------------------------------------------------------------ +/** + * Call flush for endl if ENDL_CALLS_FLUSH is non-zero + * + * The standard for iostreams is to call flush. This is very costly for + * SdFat. Each call to flush causes 2048 bytes of I/O to the SD. + * + * SdFat has a single 512 byte buffer for I/O so it must write the current + * data block to the SD, read the directory block from the SD, update the + * directory entry, write the directory block to the SD and read the data + * block back into the buffer. + * + * The SD flash memory controller is not designed for this many rewrites + * so performance may be reduced by more than a factor of 100. + * + * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force + * all data to be written to the SD. + */ +#ifndef ENDL_CALLS_FLUSH +#define ENDL_CALLS_FLUSH 0 +#endif // ENDL_CALLS_FLUSH +//------------------------------------------------------------------------------ +/** + * Allow FAT12 volumes if FAT12_SUPPORT is non-zero. + * FAT12 has not been well tested. + */ +#ifndef FAT12_SUPPORT +#define FAT12_SUPPORT 0 +#endif // FAT12_SUPPORT +//------------------------------------------------------------------------------ +/** + * Enable Extra features for Arduino. + */ +#ifndef ENABLE_ARDUINO_FEATURES +#if defined(ARDUINO) || defined(DOXYGEN) +#define ENABLE_ARDUINO_FEATURES 1 +#else // #if defined(ARDUINO) || defined(DOXYGEN) +#define ENABLE_ARDUINO_FEATURES 0 +#endif // defined(ARDUINO) || defined(DOXYGEN) +#endif // ENABLE_ARDUINO_FEATURES +#endif // FatLibConfig_h diff --git a/lib/SdFat/utility/FatStructs.h b/lib/SdFat/utility/FatStructs.h new file mode 100644 index 0000000..6aae34b --- /dev/null +++ b/lib/SdFat/utility/FatStructs.h @@ -0,0 +1,755 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef FatStructs_h +#define FatStructs_h +/** + * \file + * \brief FAT file structures + */ +/* + * mostly from Microsoft document fatgen103.doc + * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + */ +//------------------------------------------------------------------------------ +/** Value for byte 510 of boot block or MBR */ +uint8_t const BOOTSIG0 = 0X55; +/** Value for byte 511 of boot block or MBR */ +uint8_t const BOOTSIG1 = 0XAA; +/** Value for bootSignature field int FAT/FAT32 boot sector */ +uint8_t const EXTENDED_BOOT_SIG = 0X29; +//------------------------------------------------------------------------------ +/** + * \struct partitionTable + * \brief MBR partition table entry + * + * A partition table entry for a MBR formatted storage device. + * The MBR partition table has four entries. + */ +struct partitionTable { + /** + * Boot Indicator . Indicates whether the volume is the active + * partition. Legal values include: 0X00. Do not use for booting. + * 0X80 Active partition. + */ + uint8_t boot; + /** + * Head part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t beginHead; + /** + * Sector part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned beginSector : 6; + /** High bits cylinder for first block in partition. */ + unsigned beginCylinderHigh : 2; + /** + * Combine beginCylinderLow with beginCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t beginCylinderLow; + /** + * Partition type. See defines that begin with PART_TYPE_ for + * some Microsoft partition types. + */ + uint8_t type; + /** + * head part of cylinder-head-sector address of the last sector in the + * partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t endHead; + /** + * Sector part of cylinder-head-sector address of the last sector in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned endSector : 6; + /** High bits of end cylinder */ + unsigned endCylinderHigh : 2; + /** + * Combine endCylinderLow with endCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t endCylinderLow; + /** Logical block address of the first block in the partition. */ + uint32_t firstSector; + /** Length of the partition, in blocks. */ + uint32_t totalSectors; +} __attribute__((packed)); +/** Type name for partitionTable */ +typedef struct partitionTable part_t; +//------------------------------------------------------------------------------ +/** + * \struct masterBootRecord + * + * \brief Master Boot Record + * + * The first block of a storage device that is formatted with a MBR. + */ +struct masterBootRecord { + /** Code Area for master boot program. */ + uint8_t codeArea[440]; + /** Optional Windows NT disk signature. May contain boot code. */ + uint32_t diskSignature; + /** Usually zero but may be more boot code. */ + uint16_t usuallyZero; + /** Partition tables. */ + part_t part[4]; + /** First MBR signature byte. Must be 0X55 */ + uint8_t mbrSig0; + /** Second MBR signature byte. Must be 0XAA */ + uint8_t mbrSig1; +} __attribute__((packed)); +/** Type name for masterBootRecord */ +typedef struct masterBootRecord mbr_t; +//------------------------------------------------------------------------------ +/** + * \struct fat_boot + * + * \brief Boot sector for a FAT12/FAT16 volume. + * + */ +struct fat_boot { + /** + * The first three bytes of the boot sector must be valid, + * executable x 86-based CPU instructions. This includes a + * jump instruction that skips the next non-executable bytes. + */ + uint8_t jump[3]; + /** + * This is typically a string of characters that identifies + * the operating system that formatted the volume. + */ + char oemId[8]; + /** + * The size of a hardware sector. Valid decimal values for this + * field are 512, 1024, 2048, and 4096. For most disks used in + * the United States, the value of this field is 512. + */ + uint16_t bytesPerSector; + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. + */ + uint8_t sectorsPerCluster; + /** + * The number of sectors preceding the start of the first FAT, + * including the boot sector. The value of this field is always 1. + */ + uint16_t reservedSectorCount; + /** + * The number of copies of the FAT on the volume. + * The value of this field is always 2. + */ + uint8_t fatCount; + /** + * For FAT12 and FAT16 volumes, this field contains the count of + * 32-byte directory entries in the root directory. For FAT32 volumes, + * this field must be set to 0. For FAT12 and FAT16 volumes, this + * value should always specify a count that when multiplied by 32 + * results in a multiple of bytesPerSector. FAT16 volumes should + * use the value 512. + */ + uint16_t rootDirEntryCount; + /** + * This field is the old 16-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then totalSectors32 + * must be non-zero. For FAT32 volumes, this field must be 0. For + * FAT12 and FAT16 volumes, this field contains the sector count, and + * totalSectors32 is 0 if the total sector count fits + * (is less than 0x10000). + */ + uint16_t totalSectors16; + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (non-removable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ + uint8_t mediaType; + /** + * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrack; + /** Number of heads for interrupt 0x13. Not used otherwise. */ + uint16_t headCount; + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ + uint32_t hidddenSectors; + /** + * This field is the new 32-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then + * totalSectors16 must be non-zero. + */ + uint32_t totalSectors32; + /** + * Related to the BIOS physical drive number. Floppy drives are + * identified as 0x00 and physical hard disks are identified as + * 0x80, regardless of the number of physical disk drives. + * Typically, this value is set prior to issuing an INT 13h BIOS + * call to specify the device to access. The value is only + * relevant if the device is a boot device. + */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 0X29 if next three fields are valid */ + uint8_t bootSignature; + /** + * A random serial number created when formatting a disk, + * which helps to distinguish between disks. + * Usually generated by combining date and time. + */ + uint32_t volumeSerialNumber; + /** + * A field once used to store the volume label. The volume label + * is now stored as a special file in the root directory. + */ + char volumeLabel[11]; + /** + * A field with a value of either FAT, FAT12 or FAT16, + * depending on the disk format. + */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[448]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +} __attribute__((packed)); +/** Type name for FAT Boot Sector */ +typedef struct fat_boot fat_boot_t; +//------------------------------------------------------------------------------ +/** + * \struct fat32_boot + * + * \brief Boot sector for a FAT32 volume. + * + */ +struct fat32_boot { + /** + * The first three bytes of the boot sector must be valid, + * executable x 86-based CPU instructions. This includes a + * jump instruction that skips the next non-executable bytes. + */ + uint8_t jump[3]; + /** + * This is typically a string of characters that identifies + * the operating system that formatted the volume. + */ + char oemId[8]; + /** + * The size of a hardware sector. Valid decimal values for this + * field are 512, 1024, 2048, and 4096. For most disks used in + * the United States, the value of this field is 512. + */ + uint16_t bytesPerSector; + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. + */ + uint8_t sectorsPerCluster; + /** + * The number of sectors preceding the start of the first FAT, + * including the boot sector. Must not be zero + */ + uint16_t reservedSectorCount; + /** + * The number of copies of the FAT on the volume. + * The value of this field is always 2. + */ + uint8_t fatCount; + /** + * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0. + */ + uint16_t rootDirEntryCount; + /** + * For FAT32 volumes, this field must be 0. + */ + uint16_t totalSectors16; + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (non-removable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ + uint8_t mediaType; + /** + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrack; + /** Number of heads for interrupt 0x13. Not used otherwise. */ + uint16_t headCount; + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ + uint32_t hidddenSectors; + /** + * Contains the total number of sectors in the FAT32 volume. + */ + uint32_t totalSectors32; + /** + * Count of sectors occupied by one FAT on FAT32 volumes. + */ + uint32_t sectorsPerFat32; + /** + * This field is only defined for FAT32 media and does not exist on + * FAT12 and FAT16 media. + * Bits 0-3 -- Zero-based number of active FAT. + * Only valid if mirroring is disabled. + * Bits 4-6 -- Reserved. + * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. + * -- 1 means only one FAT is active; it is the one referenced + * in bits 0-3. + * Bits 8-15 -- Reserved. + */ + uint16_t fat32Flags; + /** + * FAT32 version. High byte is major revision number. + * Low byte is minor revision number. Only 0.0 define. + */ + uint16_t fat32Version; + /** + * Cluster number of the first cluster of the root directory for FAT32. + * This usually 2 but not required to be 2. + */ + uint32_t fat32RootCluster; + /** + * Sector number of FSINFO structure in the reserved area of the + * FAT32 volume. Usually 1. + */ + uint16_t fat32FSInfo; + /** + * If non-zero, indicates the sector number in the reserved area + * of the volume of a copy of the boot record. Usually 6. + * No value other than 6 is recommended. + */ + uint16_t fat32BackBootBlock; + /** + * Reserved for future expansion. Code that formats FAT32 volumes + * should always set all of the bytes of this field to 0. + */ + uint8_t fat32Reserved[12]; + /** + * Related to the BIOS physical drive number. Floppy drives are + * identified as 0x00 and physical hard disks are identified as + * 0x80, regardless of the number of physical disk drives. + * Typically, this value is set prior to issuing an INT 13h BIOS + * call to specify the device to access. The value is only + * relevant if the device is a boot device. + */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 0X29 if next three fields are valid */ + uint8_t bootSignature; + /** + * A random serial number created when formatting a disk, + * which helps to distinguish between disks. + * Usually generated by combining date and time. + */ + uint32_t volumeSerialNumber; + /** + * A field once used to store the volume label. The volume label + * is now stored as a special file in the root directory. + */ + char volumeLabel[11]; + /** + * A text field with a value of FAT32. + */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[420]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +} __attribute__((packed)); +/** Type name for FAT32 Boot Sector */ +typedef struct fat32_boot fat32_boot_t; +//------------------------------------------------------------------------------ +/** Lead signature for a FSINFO sector */ +uint32_t const FSINFO_LEAD_SIG = 0x41615252; +/** Struct signature for a FSINFO sector */ +uint32_t const FSINFO_STRUCT_SIG = 0x61417272; +/** + * \struct fat32_fsinfo + * + * \brief FSINFO sector for a FAT32 volume. + * + */ +struct fat32_fsinfo { + /** must be 0X52, 0X52, 0X61, 0X41 */ + uint32_t leadSignature; + /** must be zero */ + uint8_t reserved1[480]; + /** must be 0X72, 0X72, 0X41, 0X61 */ + uint32_t structSignature; + /** + * Contains the last known free cluster count on the volume. + * If the value is 0xFFFFFFFF, then the free count is unknown + * and must be computed. Any other value can be used, but is + * not necessarily correct. It should be range checked at least + * to make sure it is <= volume cluster count. + */ + uint32_t freeCount; + /** + * This is a hint for the FAT driver. It indicates the cluster + * number at which the driver should start looking for free clusters. + * If the value is 0xFFFFFFFF, then there is no hint and the driver + * should start looking at cluster 2. + */ + uint32_t nextFree; + /** must be zero */ + uint8_t reserved2[12]; + /** must be 0X00, 0X00, 0X55, 0XAA */ + uint8_t tailSignature[4]; +} __attribute__((packed)); +/** Type name for FAT32 FSINFO Sector */ +typedef struct fat32_fsinfo fat32_fsinfo_t; +//------------------------------------------------------------------------------ +// End Of Chain values for FAT entries +/** FAT12 end of chain value used by Microsoft. */ +uint16_t const FAT12EOC = 0XFFF; +/** Minimum value for FAT12 EOC. Use to test for EOC. */ +uint16_t const FAT12EOC_MIN = 0XFF8; +/** FAT16 end of chain value used by Microsoft. */ +uint16_t const FAT16EOC = 0XFFFF; +/** Minimum value for FAT16 EOC. Use to test for EOC. */ +uint16_t const FAT16EOC_MIN = 0XFFF8; +/** FAT32 end of chain value used by Microsoft. */ +uint32_t const FAT32EOC = 0X0FFFFFFF; +/** Minimum value for FAT32 EOC. Use to test for EOC. */ +uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; +/** Mask a for FAT32 entry. Entries are 28 bits. */ +uint32_t const FAT32MASK = 0X0FFFFFFF; +//------------------------------------------------------------------------------ +/** + * \struct directoryEntry + * \brief FAT short directory entry + * + * Short means short 8.3 name, not the entry size. + * + * Date Format. A FAT directory entry date stamp is a 16-bit field that is + * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the + * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the + * 16-bit word): + * + * Bits 9-15: Count of years from 1980, valid value range 0-127 + * inclusive (1980-2107). + * + * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. + * + * Bits 0-4: Day of month, valid value range 1-31 inclusive. + * + * Time Format. A FAT directory entry time stamp is a 16-bit field that has + * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the + * 16-bit word, bit 15 is the MSB of the 16-bit word). + * + * Bits 11-15: Hours, valid value range 0-23 inclusive. + * + * Bits 5-10: Minutes, valid value range 0-59 inclusive. + * + * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). + * + * The valid time range is from Midnight 00:00:00 to 23:59:58. + */ +struct directoryEntry { + /** Short 8.3 name. + * + * The first eight bytes contain the file name with blank fill. + * The last three bytes contain the file extension with blank fill. + */ + uint8_t name[11]; + /** Entry attributes. + * + * The upper two bits of the attribute byte are reserved and should + * always be set to 0 when a file is created and never modified or + * looked at after that. See defines that begin with DIR_ATT_. + */ + uint8_t attributes; + /** + * Reserved for use by Windows NT. Set value to 0 when a file is + * created and never modify or look at it after that. + */ + uint8_t reservedNT; + /** + * The granularity of the seconds part of creationTime is 2 seconds + * so this field is a count of tenths of a second and its valid + * value range is 0-199 inclusive. (WHG note - seems to be hundredths) + */ + uint8_t creationTimeTenths; + /** Time file was created. */ + uint16_t creationTime; + /** Date file was created. */ + uint16_t creationDate; + /** + * Last access date. Note that there is no last access time, only + * a date. This is the date of last read or write. In the case of + * a write, this should be set to the same date as lastWriteDate. + */ + uint16_t lastAccessDate; + /** + * High word of this entry's first cluster number (always 0 for a + * FAT12 or FAT16 volume). + */ + uint16_t firstClusterHigh; + /** Time of last write. File creation is considered a write. */ + uint16_t lastWriteTime; + /** Date of last write. File creation is considered a write. */ + uint16_t lastWriteDate; + /** Low word of this entry's first cluster number. */ + uint16_t firstClusterLow; + /** 32-bit unsigned holding this file's size in bytes. */ + uint32_t fileSize; +} __attribute__((packed)); +/** Type name for directoryEntry */ +typedef struct directoryEntry dir_t; +//------------------------------------------------------------------------------ +// Definitions for directory entries +// +/** escape for name[0] = 0XE5 */ +uint8_t const DIR_NAME_0XE5 = 0X05; +/** name[0] value for entry that is free after being "deleted" */ +uint8_t const DIR_NAME_DELETED = 0XE5; +/** name[0] value for entry that is free and no allocated entries follow */ +uint8_t const DIR_NAME_FREE = 0X00; +/** file is read-only */ +uint8_t const DIR_ATT_READ_ONLY = 0X01; +/** File should e hidden in directory listings */ +uint8_t const DIR_ATT_HIDDEN = 0X02; +/** Entry is for a system file */ +uint8_t const DIR_ATT_SYSTEM = 0X04; +/** Directory entry contains the volume label */ +uint8_t const DIR_ATT_VOLUME_ID = 0X08; +/** Entry is for a directory */ +uint8_t const DIR_ATT_DIRECTORY = 0X10; +/** Old DOS archive bit for backup support */ +uint8_t const DIR_ATT_ARCHIVE = 0X20; +/** Test value for long name entry. Test is + (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ +uint8_t const DIR_ATT_LONG_NAME = 0X0F; +/** Test mask for long name entry */ +uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; +/** defined attribute bits */ +uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; + +/** Mask for file/subdirectory tests */ +uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); + +/** Filename base-name is all lower case */ +const uint8_t DIR_NT_LC_BASE = 0X08; +/** Filename extension is all lower case.*/ +const uint8_t DIR_NT_LC_EXT = 0X10; + + +/** Directory entry is for a file + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for a normal file else false. + */ +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 file or subdirectory + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for a normal file or subdirectory else false. + */ +static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { + return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; +} +/** Directory entry is part of a long name + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for part of a long name else false. + */ +static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { + return dir->attributes == DIR_ATT_LONG_NAME; +} +/** Directory entry is hidden + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is hidden else false. + */ +static inline uint8_t DIR_IS_HIDDEN(const dir_t* dir) { + return dir->attributes & DIR_ATT_HIDDEN; +} +/** Directory entry is for a subdirectory + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for a subdirectory else false. + */ +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 system type + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is system else false. + */ +static inline uint8_t DIR_IS_SYSTEM(const dir_t* dir) { + return dir->attributes & DIR_ATT_SYSTEM; +} +/** date field for FAT directory entry + * \param[in] year [1980,2107] + * \param[in] month [1,12] + * \param[in] day [1,31] + * + * \return Packed date for dir_t entry. + */ +static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { + return (year - 1980) << 9 | month << 5 | day; +} +/** year part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted year [1980,2107] + */ +static inline uint16_t FAT_YEAR(uint16_t fatDate) { + return 1980 + (fatDate >> 9); +} +/** month part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted month [1,12] + */ +static inline uint8_t FAT_MONTH(uint16_t fatDate) { + return (fatDate >> 5) & 0XF; +} +/** day part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted day [1,31] + */ +static inline uint8_t FAT_DAY(uint16_t fatDate) { + return fatDate & 0X1F; +} +/** time field for FAT directory entry + * \param[in] hour [0,23] + * \param[in] minute [0,59] + * \param[in] second [0,59] + * + * \return Packed time for dir_t 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 + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted hour [0,23] + */ +static inline uint8_t FAT_HOUR(uint16_t fatTime) { + return fatTime >> 11; +} +/** minute part of FAT directory time field + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted minute [0,59] + */ +static inline uint8_t FAT_MINUTE(uint16_t fatTime) { + return (fatTime >> 5) & 0X3F; +} +/** second part of FAT directory time field + * Note second/2 is stored in packed time. + * + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted second [0,58] + */ +static inline uint8_t FAT_SECOND(uint16_t fatTime) { + return 2*(fatTime & 0X1F); +} +/** Default date for file timestamps is 1 Jan 2000 */ +uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; +/** Default time for file timestamp is 1 am */ +uint16_t const FAT_DEFAULT_TIME = (1 << 11); +//------------------------------------------------------------------------------ +/** Dimension of first name field in long directory entry */ +const uint8_t LDIR_NAME1_DIM = 5; +/** Dimension of first name field in long directory entry */ +const uint8_t LDIR_NAME2_DIM = 6; +/** Dimension of first name field in long directory entry */ +const uint8_t LDIR_NAME3_DIM = 2; +/** + * \struct longDirectoryEntry + * \brief FAT long directory entry + */ +struct longDirectoryEntry { + /** + * The order of this entry in the sequence of long dir entries + * associated with the short dir entry at the end of the long dir set. + * + * If masked with 0X40 (LAST_LONG_ENTRY), this indicates the + * entry is the last long dir entry in a set of long dir entries. + * All valid sets of long dir entries must begin with an entry having + * this mask. + */ + uint8_t ord; + /** Characters 1-5 of the long-name sub-component in this entry. */ + uint16_t name1[LDIR_NAME1_DIM]; + /** Attributes - must be ATTR_LONG_NAME */ + uint8_t attr; + /** + * If zero, indicates a directory entry that is a sub-component of a + * long name. NOTE: Other values reserved for future extensions. + * + * Non-zero implies other directory entry types. + */ + uint8_t type; + /** + * Checksum of name in the short dir entry at the end of the + * long dir set. + */ + uint8_t chksum; + /** Characters 6-11 of the long-name sub-component in this entry. */ + uint16_t name2[LDIR_NAME2_DIM]; + /** Must be ZERO. This is an artifact of the FAT "first cluster" */ + uint16_t mustBeZero; + /** Characters 12 and 13 of the long-name sub-component in this entry. */ + uint16_t name3[LDIR_NAME3_DIM]; +} __attribute__((packed)); +/** Type name for longDirectoryEntry */ +typedef struct longDirectoryEntry ldir_t; +/** + * Ord mast that indicates the entry is the last long dir entry in a + * set of long dir entries. All valid sets of long dir entries must + * begin with an entry having this mask. + */ +const uint8_t LDIR_ORD_LAST_LONG_ENTRY = 0X40; +#endif // FatStructs_h diff --git a/lib/SdFat/utility/FatVolume.cpp b/lib/SdFat/utility/FatVolume.cpp new file mode 100644 index 0000000..dcb0a4d --- /dev/null +++ b/lib/SdFat/utility/FatVolume.cpp @@ -0,0 +1,585 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include +#include "FatVolume.h" +//------------------------------------------------------------------------------ +cache_t* FatCache::read(uint32_t lbn, uint8_t option) { + if (m_lbn != lbn) { + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!(option & CACHE_OPTION_NO_READ)) { + if (!m_vol->readBlock(lbn, m_block.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + m_status = 0; + m_lbn = lbn; + } + m_status |= option & CACHE_STATUS_MASK; + return &m_block; + +fail: + return 0; +} +//------------------------------------------------------------------------------ +bool FatCache::sync() { + if (m_status & CACHE_STATUS_DIRTY) { + if (!m_vol->writeBlock(m_lbn, m_block.data)) { + DBG_FAIL_MACRO; + goto fail; + } + // mirror second FAT + if (m_status & CACHE_STATUS_MIRROR_FAT) { + uint32_t lbn = m_lbn + m_vol->blocksPerFat(); + if (!m_vol->writeBlock(lbn, m_block.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + m_status &= ~CACHE_STATUS_DIRTY; + } + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatVolume::allocateCluster(uint32_t current, uint32_t* next) { + uint32_t find = current ? current : m_allocSearchStart; + uint32_t start = find; + while (1) { + find++; + // If at end of FAT go to beginning of FAT. + if (find > m_lastCluster) { + find = 2; + } + uint32_t f; + int8_t fg = fatGet(find, &f); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg && f == 0) { + break; + } + if (find == start) { + // Can't find space checked all clusters. + DBG_FAIL_MACRO; + goto fail; + } + } + // mark end of chain + if (!fatPutEOC(find)) { + DBG_FAIL_MACRO; + goto fail; + } + if (current) { + // link clusters + if (!fatPut(current, find)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // Remember place for search start. + m_allocSearchStart = find; + } + *next = find; + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +// find a contiguous group of clusters +bool FatVolume::allocContiguous(uint32_t count, uint32_t* firstCluster) { + // flag to save place to start next search + bool setStart = true; + // start of group + uint32_t bgnCluster; + // end of group + uint32_t endCluster; + // Start at cluster after last allocated cluster. + uint32_t startCluster = m_allocSearchStart; + endCluster = bgnCluster = startCluster + 1; + + // search the FAT for free clusters + while (1) { + // If past end - start from beginning of FAT. + if (endCluster > m_lastCluster) { + bgnCluster = endCluster = 2; + } + uint32_t f; + int8_t fg = fatGet(endCluster, &f); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (f || fg == 0) { + // cluster in use try next cluster as bgnCluster + bgnCluster = endCluster + 1; + + // don't update search start if unallocated clusters before endCluster. + if (bgnCluster != endCluster) { + setStart = false; + } + } else if ((endCluster - bgnCluster + 1) == count) { + // done - found space + break; + } + // Can't find space if all clusters checked. + if (startCluster == endCluster) { + DBG_FAIL_MACRO; + goto fail; + } + endCluster++; + } + // remember possible next free cluster + if (setStart) { + m_allocSearchStart = endCluster + 1; + } + + // mark end of chain + if (!fatPutEOC(endCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // link clusters + while (endCluster > bgnCluster) { + if (!fatPut(endCluster - 1, endCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + endCluster--; + } + // return first cluster number to caller + *firstCluster = bgnCluster; + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +uint32_t FatVolume::clusterStartBlock(uint32_t cluster) const { + return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); +} +//------------------------------------------------------------------------------ +// Fetch a FAT entry - return -1 error, 0 EOC, else 1. +int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { + uint32_t lba; + uint32_t next; + cache_t* pc; + + // error if reserved cluster of beyond FAT + DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); + + if (m_fatType == 32) { + lba = m_fatStartBlock + (cluster >> 7); + pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + next = pc->fat32[cluster & 0X7F] & FAT32MASK; + goto done; + } + + if (m_fatType == 16) { + lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); + pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + next = pc->fat16[cluster & 0XFF]; + goto done; + } + if (FAT12_SUPPORT && m_fatType == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = m_fatStartBlock + (index >> 9); + pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + index &= 0X1FF; + uint16_t tmp = pc->data[index]; + index++; + if (index == 512) { + pc = cacheFetchFat(lba + 1, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + index = 0; + } + tmp |= pc->data[index] << 8; + next = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; + goto done; + } else { + DBG_FAIL_MACRO; + goto fail; + } +done: + if (isEOC(next)) { + return 0; + } + *value = next; + return 1; + +fail: + return -1; +} +//------------------------------------------------------------------------------ +// Store a FAT entry +bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { + uint32_t lba; + cache_t* pc; + + // error if reserved cluster of beyond FAT + DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster); + + if (m_fatType == 32) { + lba = m_fatStartBlock + (cluster >> 7); + pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + pc->fat32[cluster & 0X7F] = value; + return true; + } + + if (m_fatType == 16) { + lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); + pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + pc->fat16[cluster & 0XFF] = value; + return true; + } + + if (FAT12_SUPPORT && m_fatType == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = m_fatStartBlock + (index >> 9); + pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + index &= 0X1FF; + uint8_t tmp = value; + if (cluster & 1) { + tmp = (pc->data[index] & 0XF) | tmp << 4; + } + pc->data[index] = tmp; + + index++; + if (index == 512) { + lba++; + index = 0; + pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + } + tmp = value >> 4; + if (!(cluster & 1)) { + tmp = ((pc->data[index] & 0XF0)) | tmp >> 4; + } + pc->data[index] = tmp; + return true; + } else { + DBG_FAIL_MACRO; + goto fail; + } + +fail: + return false; +} +//------------------------------------------------------------------------------ +// free a cluster chain +bool FatVolume::freeChain(uint32_t cluster) { + uint32_t next; + int8_t fg; + do { + fg = fatGet(cluster, &next); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + // free cluster + if (!fatPut(cluster, 0)) { + DBG_FAIL_MACRO; + goto fail; + } + if (cluster < m_allocSearchStart) { + m_allocSearchStart = cluster; + } + cluster = next; + } while (fg); + + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +int32_t FatVolume::freeClusterCount() { + uint32_t free = 0; + uint32_t lba; + uint32_t todo = m_lastCluster + 1; + uint16_t n; + + if (FAT12_SUPPORT && m_fatType == 12) { + for (unsigned i = 2; i < todo; i++) { + uint32_t c; + int8_t fg = fatGet(i, &c); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg && c == 0) { + free++; + } + } + } else if (m_fatType == 16 || m_fatType == 32) { + lba = m_fatStartBlock; + while (todo) { + cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + n = m_fatType == 16 ? 256 : 128; + if (todo < n) { + n = todo; + } + if (m_fatType == 16) { + for (uint16_t i = 0; i < n; i++) { + if (pc->fat16[i] == 0) { + free++; + } + } + } else { + for (uint16_t i = 0; i < n; i++) { + if (pc->fat32[i] == 0) { + free++; + } + } + } + todo -= n; + } + } else { + // invalid FAT type + DBG_FAIL_MACRO; + goto fail; + } + return free; + +fail: + return -1; +} +//------------------------------------------------------------------------------ +bool FatVolume::init(uint8_t part) { + uint32_t clusterCount; + uint32_t totalBlocks; + uint32_t volumeStartBlock = 0; + fat32_boot_t* fbs; + cache_t* pc; + uint8_t tmp; + m_fatType = 0; + m_allocSearchStart = 1; + + m_cache.init(this); +#if USE_SEPARATE_FAT_CACHE + m_fatCache.init(this); +#endif // USE_SEPARATE_FAT_CACHE + + // if part == 0 assume super floppy with FAT boot sector in block zero + // if part > 0 assume mbr volume with partition table + if (part) { + if (part > 4) { + DBG_FAIL_MACRO; + goto fail; + } + pc = cacheFetchData(0, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + part_t* p = &pc->mbr.part[part - 1]; + if ((p->boot & 0X7F) != 0 || p->firstSector == 0) { + // not a valid partition + DBG_FAIL_MACRO; + goto fail; + } + volumeStartBlock = p->firstSector; + } + pc = cacheFetchData(volumeStartBlock, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + fbs = &(pc->fbs32); + if (fbs->bytesPerSector != 512 || + fbs->fatCount != 2 || + fbs->reservedSectorCount == 0) { + // not valid FAT volume + DBG_FAIL_MACRO; + goto fail; + } + m_blocksPerCluster = fbs->sectorsPerCluster; + m_clusterBlockMask = m_blocksPerCluster - 1; + + // determine shift that is same as multiply by m_blocksPerCluster + m_clusterSizeShift = 0; + for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { + if (tmp == 0) { + DBG_FAIL_MACRO; + goto fail; + } + } + + m_blocksPerFat = fbs->sectorsPerFat16 ? + fbs->sectorsPerFat16 : fbs->sectorsPerFat32; + + m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount; + + // count for FAT16 zero for FAT32 + m_rootDirEntryCount = fbs->rootDirEntryCount; + + // directory start for FAT16 dataStart for FAT32 + m_rootDirStart = m_fatStartBlock + 2 * m_blocksPerFat; + // data start for FAT16 and FAT32 + m_dataStartBlock = m_rootDirStart + ((32 * fbs->rootDirEntryCount + 511)/512); + + // total blocks for FAT16 or FAT32 + totalBlocks = fbs->totalSectors16 ? + fbs->totalSectors16 : fbs->totalSectors32; + // total data blocks + clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock); + + // divide by cluster size to get cluster count + clusterCount >>= m_clusterSizeShift; + m_lastCluster = clusterCount + 1; + + // FAT type is determined by cluster count + if (clusterCount < 4085) { + m_fatType = 12; + if (!FAT12_SUPPORT) { + DBG_FAIL_MACRO; + goto fail; + } + } else if (clusterCount < 65525) { + m_fatType = 16; + } else { + m_rootDirStart = fbs->fat32RootCluster; + m_fatType = 32; + } + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatVolume::wipe(print_t* pr) { + cache_t* cache; + uint16_t count; + uint32_t lbn; + if (!m_fatType) { + DBG_FAIL_MACRO; + goto fail; + } + cache = cacheClear(); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + memset(cache->data, 0, 512); + // Zero root. + if (m_fatType == 32) { + lbn = clusterStartBlock(m_rootDirStart); + count = m_blocksPerCluster; + } else { + lbn = m_rootDirStart; + count = m_rootDirEntryCount/16; + } + for (uint32_t n = 0; n < count; n++) { + if (!writeBlock(lbn + n, cache->data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // Clear FATs. + count = 2*m_blocksPerFat; + lbn = m_fatStartBlock; + for (uint32_t nb = 0; nb < count; nb++) { + if (pr && (nb & 0XFF) == 0) { + pr->write('.'); + } + if (!writeBlock(lbn + nb, cache->data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // Reserve first two clusters. + if (m_fatType == 32) { + cache->fat32[0] = 0x0FFFFFF8; + cache->fat32[1] = 0x0FFFFFFF; + } else if (m_fatType == 16) { + cache->fat16[0] = 0XFFF8; + cache->fat16[1] = 0XFFFF; + } else if (FAT12_SUPPORT && m_fatType == 12) { + cache->fat32[0] = 0XFFFFF8; + } else { + DBG_FAIL_MACRO; + goto fail; + } + if (!writeBlock(m_fatStartBlock, cache->data) || + !writeBlock(m_fatStartBlock + m_blocksPerFat, cache->data)) { + DBG_FAIL_MACRO; + goto fail; + } + if (m_fatType == 32) { + // Reserve root cluster. + if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (pr) { + pr->write('\r'); + pr->write('\n'); + } + m_fatType = 0; + return true; + +fail: + m_fatType = 0; + return false; +} diff --git a/lib/SdFat/utility/FatVolume.h b/lib/SdFat/utility/FatVolume.h new file mode 100644 index 0000000..b558101 --- /dev/null +++ b/lib/SdFat/utility/FatVolume.h @@ -0,0 +1,338 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef FatVolume_h +#define FatVolume_h +/** + * \file + * \brief FatVolume class + */ +#include +#include "FatLibConfig.h" +#include "FatStructs.h" +//------------------------------------------------------------------------------ +#ifndef DOXYGEN_SHOULD_SKIP_THIS +/** Macro for debug. */ +#define DEBUG_MODE 0 +#if DEBUG_MODE +#include +#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__) +#define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;} +#define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\ + DBG_FAIL_MACRO; while (1);} +#else // DEBUG_MODE +#define DBG_FAIL_MACRO +#define DBG_PRINT_IF(b) +#define DBG_HALT_IF(b) +#endif // DEBUG_MODE +#endif // DOXYGEN_SHOULD_SKIP_THIS +//------------------------------------------------------------------------------ +#if ENABLE_ARDUINO_FEATURES +#include +/** Use Print on Arduino */ +typedef Print print_t; +#else // ENABLE_ARDUINO_FEATURES +// Arduino type for flash string. +class __FlashStringHelper; +/** + * \class CharWriter + * \brief Character output - often serial port. + */ +class CharWriter { + public: + virtual size_t write(char c) = 0; + virtual size_t write(const char* s) = 0; +}; +typedef CharWriter print_t; +#endif // ENABLE_ARDUINO_FEATURES +//------------------------------------------------------------------------------ +// Forward declaration of FatVolume. +class FatVolume; +//------------------------------------------------------------------------------ +/** + * \brief Cache for an raw data block. + */ +union cache_t { + /** Used to access cached file data blocks. */ + 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]; + /** Used to access a cached Master Boot Record. */ + mbr_t mbr; + /** Used to access to a cached FAT boot sector. */ + fat_boot_t fbs; + /** Used to access to a cached FAT32 boot sector. */ + fat32_boot_t fbs32; + /** Used to access to a cached FAT32 FSINFO sector. */ + fat32_fsinfo_t fsinfo; +}; +//============================================================================== +/** + * \class FatCache + * \brief Block cache. + */ +class FatCache { + public: + /** Cached block is dirty */ + static const uint8_t CACHE_STATUS_DIRTY = 1; + /** Cashed block is FAT entry and must be mirrored in second FAT. */ + static const uint8_t CACHE_STATUS_MIRROR_FAT = 2; + /** Cache block status bits */ + static const uint8_t CACHE_STATUS_MASK + = CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT; + /** Sync existing block but do not read new block. */ + static const uint8_t CACHE_OPTION_NO_READ = 4; + /** Cache block for read. */ + static uint8_t const CACHE_FOR_READ = 0; + /** Cache block for write. */ + static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; + /** Reserve cache block for write - do not read from block device. */ + static uint8_t const CACHE_RESERVE_FOR_WRITE + = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; + /** \return Cache block address. */ + cache_t* block() { + return &m_block; + } + /** Set current block dirty. */ + void dirty() { + m_status |= CACHE_STATUS_DIRTY; + } + /** Initialize the cache. + * \param[in] vol FatVolume that owns this FatCache. + */ + void init(FatVolume *vol) { + m_vol = vol; + invalidate(); + } + /** Invalidate current cache block. */ + void invalidate() { + m_status = 0; + m_lbn = 0XFFFFFFFF; + } + /** \return Logical block number for cached block. */ + uint32_t lbn() { + return m_lbn; + } + /** Read a block into the cache. + * \param[in] lbn Block to read. + * \param[in] option mode for cached block. + * \return Address of cached block. */ + cache_t* read(uint32_t lbn, uint8_t option); + /** Write current block if dirty. + * \return true for success else false. + */ + bool sync(); + + private: + uint8_t m_status; + FatVolume* m_vol; + uint32_t m_lbn; + cache_t m_block; +}; +//============================================================================== +/** + * \class FatVolume + * \brief Access FAT16 and FAT32 volumes on raw file devices. + */ +class FatVolume { + public: + /** Create an instance of FatVolume + */ + FatVolume() : m_fatType(0) {} + + /** \return The volume's cluster size in blocks. */ + uint8_t blocksPerCluster() const { + return m_blocksPerCluster; + } + /** \return The number of blocks in one FAT. */ + uint32_t blocksPerFat() const { + return m_blocksPerFat; + } + /** Clear the cache and returns a pointer to the cache. Not for normal apps. + * \return A pointer to the cache buffer or zero if an error occurs. + */ + cache_t* cacheClear() { + if (!cacheSync()) { + return 0; + } + m_cache.invalidate(); + return m_cache.block(); + } + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount() const { + return m_lastCluster - 1; + } + /** \return The shift count required to multiply by blocksPerCluster. */ + uint8_t clusterSizeShift() const { + return m_clusterSizeShift; + } + /** \return The logical block number for the start of file data. */ + uint32_t dataStartBlock() const { + return m_dataStartBlock; + } + /** \return The number of File Allocation Tables. */ + uint8_t fatCount() { + return 2; + } + /** \return The logical block number for the start of the first FAT. */ + uint32_t fatStartBlock() const { + return m_fatStartBlock; + } + /** \return The FAT type of the volume. Values are 12, 16 or 32. */ + uint8_t fatType() const { + return m_fatType; + } + /** Volume free space in clusters. + * + * \return Count of free clusters for success or -1 if an error occurs. + */ + int32_t freeClusterCount(); + /** Initialize a FAT volume. Try partition one first then try super + * floppy format. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool init() { + return init(1) || init(0); + } + /** Initialize a FAT volume. + + * \param[in] part The partition to be used. Legal values for \a part are + * 1-4 to use the corresponding partition on a device formatted with + * a MBR, Master Boot Record, or zero if the device is formatted as + * a super floppy with the FAT boot sector in block zero. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool init(uint8_t part); + /** \return The number of entries in the root directory for FAT16 volumes. */ + uint16_t rootDirEntryCount() const { + return m_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() const { + return m_rootDirStart; + } + /** \return The number of blocks in the volume */ + uint32_t volumeBlockCount() const { + return blocksPerCluster()*clusterCount(); + } + /** Wipe all data from the volume. + * \param[in] pr print stream for status dots. + * \return true for success else false. + */ + bool wipe(print_t* pr = 0); + /** Debug access to FAT table + * + * \param[in] n cluster number. + * \param[out] v value of entry + * \return true for success or false for failure + */ + int8_t dbgFat(uint32_t n, uint32_t* v) { + return fatGet(n, v); + } +//------------------------------------------------------------------------------ + private: + // Allow FatFile and FatCache access to FatVolume private functions. + friend class FatCache; + friend class FatFile; +//------------------------------------------------------------------------------ + uint8_t m_blocksPerCluster; // Cluster size in blocks. + uint8_t m_clusterBlockMask; // Mask to extract block of cluster. + uint8_t m_clusterSizeShift; // Cluster count to block count shift. + uint8_t m_fatType; // Volume type (12, 16, OR 32). + uint16_t m_rootDirEntryCount; // Number of entries in FAT16 root dir. + uint32_t m_allocSearchStart; // Start cluster for alloc search. + uint32_t m_blocksPerFat; // FAT size in blocks + uint32_t m_dataStartBlock; // First data block number. + uint32_t m_fatStartBlock; // Start block for first FAT. + uint32_t m_lastCluster; // Last cluster number in FAT. + uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. +//------------------------------------------------------------------------------ +// block caches + FatCache m_cache; +#if USE_SEPARATE_FAT_CACHE + FatCache m_fatCache; + cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { + return m_fatCache.read(blockNumber, + options | FatCache::CACHE_STATUS_MIRROR_FAT); + } + bool cacheSync() { + return m_cache.sync() && m_fatCache.sync(); + } +#else // + cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { + return cacheFetchData(blockNumber, + options | FatCache::CACHE_STATUS_MIRROR_FAT); + } + bool cacheSync() { + return m_cache.sync(); + } +#endif // USE_SEPARATE_FAT_CACHE + cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { + return m_cache.read(blockNumber, options); + } + void cacheInvalidate() { + m_cache.invalidate(); + } + bool cacheSyncData() { + return m_cache.sync(); + } + cache_t *cacheAddress() { + return m_cache.block(); + } + uint32_t cacheBlockNumber() { + return m_cache.lbn(); + } + void cacheDirty() { + m_cache.dirty(); + } +//------------------------------------------------------------------------------ + bool allocateCluster(uint32_t current, uint32_t* next); + bool allocContiguous(uint32_t count, uint32_t* firstCluster); + uint8_t blockOfCluster(uint32_t position) const { + return (position >> 9) & m_clusterBlockMask; + } + uint32_t clusterStartBlock(uint32_t cluster) const; + int8_t fatGet(uint32_t cluster, uint32_t* value); + bool fatPut(uint32_t cluster, uint32_t value); + bool fatPutEOC(uint32_t cluster) { + return fatPut(cluster, 0x0FFFFFFF); + } + bool freeChain(uint32_t cluster); + bool isEOC(uint32_t cluster) const { + return cluster > m_lastCluster; + } + //---------------------------------------------------------------------------- + // Virtual block I/O functions. + virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; + virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; +#if USE_MULTI_BLOCK_IO + virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; + virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; +#endif // USE_MULTI_BLOCK_IO +}; +#endif // FatVolume diff --git a/lib/SdFat/utility/FmtNumber.cpp b/lib/SdFat/utility/FmtNumber.cpp new file mode 100644 index 0000000..3aa5bec --- /dev/null +++ b/lib/SdFat/utility/FmtNumber.cpp @@ -0,0 +1,455 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include "FmtNumber.h" +// Use Stimmer div/mod 10 on avr +#ifdef __AVR__ +#include +#define USE_STIMMER +#endif // __AVR__ +//------------------------------------------------------------------------------ +// Stimmer div/mod 10 for AVR +// this code fragment works out i/10 and i%10 by calculating +// i*(51/256)*(256/255)/2 == i*51/510 == i/10 +// by "j.k" I mean 32.8 fixed point, j is integer part, k is fractional part +// j.k = ((j+1.0)*51.0)/256.0 +// (we add 1 because we will be using the floor of the result later) +// divmod10_asm16 and divmod10_asm32 are public domain code by Stimmer. +// http://forum.arduino.cc/index.php?topic=167414.msg1293679#msg1293679 +#define divmod10_asm16(in32, mod8, tmp8) \ +asm volatile( \ + " ldi %2,51 \n\t" \ + " mul %A0,%2 \n\t" \ + " clr %A0 \n\t" \ + " add r0,%2 \n\t" \ + " adc %A0,r1 \n\t" \ + " mov %1,r0 \n\t" \ + " mul %B0,%2 \n\t" \ + " clr %B0 \n\t" \ + " add %A0,r0 \n\t" \ + " adc %B0,r1 \n\t" \ + " clr r1 \n\t" \ + " add %1,%A0 \n\t" \ + " adc %A0,%B0 \n\t" \ + " adc %B0,r1 \n\t" \ + " add %1,%B0 \n\t" \ + " adc %A0,r1 \n\t" \ + " adc %B0,r1 \n\t" \ + " lsr %B0 \n\t" \ + " ror %A0 \n\t" \ + " ror %1 \n\t" \ + " ldi %2,10 \n\t" \ + " mul %1,%2 \n\t" \ + " mov %1,r1 \n\t" \ + " clr r1 \n\t" \ + :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0") + +#define divmod10_asm32(in32, mod8, tmp8) \ +asm volatile( \ + " ldi %2,51 \n\t" \ + " mul %A0,%2 \n\t" \ + " clr %A0 \n\t" \ + " add r0,%2 \n\t" \ + " adc %A0,r1 \n\t" \ + " mov %1,r0 \n\t" \ + " mul %B0,%2 \n\t" \ + " clr %B0 \n\t" \ + " add %A0,r0 \n\t" \ + " adc %B0,r1 \n\t" \ + " mul %C0,%2 \n\t" \ + " clr %C0 \n\t" \ + " add %B0,r0 \n\t" \ + " adc %C0,r1 \n\t" \ + " mul %D0,%2 \n\t" \ + " clr %D0 \n\t" \ + " add %C0,r0 \n\t" \ + " adc %D0,r1 \n\t" \ + " clr r1 \n\t" \ + " add %1,%A0 \n\t" \ + " adc %A0,%B0 \n\t" \ + " adc %B0,%C0 \n\t" \ + " adc %C0,%D0 \n\t" \ + " adc %D0,r1 \n\t" \ + " add %1,%B0 \n\t" \ + " adc %A0,%C0 \n\t" \ + " adc %B0,%D0 \n\t" \ + " adc %C0,r1 \n\t" \ + " adc %D0,r1 \n\t" \ + " add %1,%D0 \n\t" \ + " adc %A0,r1 \n\t" \ + " adc %B0,r1 \n\t" \ + " adc %C0,r1 \n\t" \ + " adc %D0,r1 \n\t" \ + " lsr %D0 \n\t" \ + " ror %C0 \n\t" \ + " ror %B0 \n\t" \ + " ror %A0 \n\t" \ + " ror %1 \n\t" \ + " ldi %2,10 \n\t" \ + " mul %1,%2 \n\t" \ + " mov %1,r1 \n\t" \ + " clr r1 \n\t" \ + :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0") +//------------------------------------------------------------------------------ +/* +// C++ code is based on this version of divmod10 by robtillaart. +// http://forum.arduino.cc/index.php?topic=167414.msg1246851#msg1246851 +// from robtillaart post: +// The code is based upon the divu10() code from the book Hackers Delight1. +// My insight was that the error formula in divu10() was in fact modulo 10 +// but not always. Sometimes it was 10 more. +void divmod10(uint32_t in, uint32_t &div, uint32_t &mod) +{ + // q = in * 0.8; + uint32_t q = (in >> 1) + (in >> 2); + q = q + (q >> 4); + q = q + (q >> 8); + q = q + (q >> 16); // not needed for 16 bit version + + // q = q / 8; ==> q = in *0.1; + q = q >> 3; + + // determine error + uint32_t r = in - ((q << 3) + (q << 1)); // r = in - q*10; + div = q + (r > 9); + if (r > 9) mod = r - 10; + else mod = r; +} +// Hackers delight function is here: +// http://www.hackersdelight.org/hdcodetxt/divuc.c.txt +// Code below uses 8/10 = 0.1100 1100 1100 1100 1100 1100 1100 1100. +// 15 ops including the multiply, or 17 elementary ops. +unsigned divu10(unsigned n) { + unsigned q, r; + + q = (n >> 1) + (n >> 2); + q = q + (q >> 4); + q = q + (q >> 8); + q = q + (q >> 16); + q = q >> 3; + r = n - q*10; + return q + ((r + 6) >> 4); +// return q + (r > 9); +} +*/ +//------------------------------------------------------------------------------ +#ifndef DOXYGEN_SHOULD_SKIP_THIS +#ifdef __AVR__ +static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; +static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; +#else // __AVR__ +static const float m[] = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; +static const float p[] = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; +#endif // __AVR__ +#endif // DOXYGEN_SHOULD_SKIP_THIS +// scale float v by power of ten. return v*10^n +float scale10(float v, int8_t n) { + const float *s; + if (n < 0) { + n = -n; + s = m; + } else { + s = p; + } + n &= 63; + for (uint8_t i = 0; n; n >>= 1, i++) { +#ifdef __AVR__ + if (n & 1) { + v *= pgm_read_float(&s[i]); + } +#else // __AVR__ + if (n & 1) { + v *= s[i]; + } +#endif // __AVR__ + } + return v; +} +//------------------------------------------------------------------------------ +// Format 16-bit unsigned +char* fmtDec(uint16_t n, char* p) { + while (n > 9) { +#ifdef USE_STIMMER + uint8_t tmp8, r; + divmod10_asm16(n, r, tmp8); +#else // USE_STIMMER + uint16_t t = n; + n = (n >> 1) + (n >> 2); + n = n + (n >> 4); + n = n + (n >> 8); + // n = n + (n >> 16); // no code for 16-bit n + n = n >> 3; + uint8_t r = t - (((n << 2) + n) << 1); + if (r > 9) { + n++; + r -= 10; + } +#endif // USE_STIMMER + *--p = r + '0'; + } + *--p = n + '0'; + return p; +} +//------------------------------------------------------------------------------ +// format 32-bit unsigned +char* fmtDec(uint32_t n, char* p) { + while (n >> 16) { +#ifdef USE_STIMMER + uint8_t tmp8, r; + divmod10_asm32(n, r, tmp8); +#else // USE_STIMMER + uint32_t t = n; + n = (n >> 1) + (n >> 2); + n = n + (n >> 4); + n = n + (n >> 8); + n = n + (n >> 16); + n = n >> 3; + uint8_t r = t - (((n << 2) + n) << 1); + if (r > 9) { + n++; + r -= 10; + } +#endif // USE_STIMMER + *--p = r + '0'; + } + return fmtDec((uint16_t)n, p); +} +//------------------------------------------------------------------------------ +char* fmtFloat(float value, char* p, uint8_t prec) { + char sign = value < 0 ? '-' : 0; + if (sign) { + value = -value; + } + + if (isnan(value)) { + *--p = 'n'; + *--p = 'a'; + *--p = 'n'; + return p; + } + if (isinf(value)) { + *--p = 'f'; + *--p = 'n'; + *--p = 'i'; + return p; + } + if (value > 4294967040.0) { + *--p = 'f'; + *--p = 'v'; + *--p = 'o'; + return p; + } + if (prec > 9) { + prec = 9; + } + value += scale10(0.5, -prec); + + uint32_t whole = value; + if (prec) { + char* tmp = p - prec; + uint32_t fraction = scale10(value - whole, prec); + p = fmtDec(fraction, p); + while (p > tmp) { + *--p = '0'; + } + *--p = '.'; + } + p = fmtDec(whole, p); + if (sign) { + *--p = sign; + } + return p; +} +//------------------------------------------------------------------------------ +/** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] ptr Pointer to last char in buffer. + * \param[in] prec Number of digits after decimal point. + * \param[in] expChar Use exp format if non zero. + * \return Pointer to first character of result. + */ +char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { + bool neg = value < 0; + if (neg) { + value = -value; + } + + // check for nan inf ovf + if (isnan(value)) { + *--ptr = 'n'; + *--ptr = 'a'; + *--ptr = 'n'; + return ptr; + } + if (isinf(value)) { + *--ptr = 'f'; + *--ptr = 'n'; + *--ptr = 'i'; + return ptr; + } + if (!expChar && value > 4294967040.0) { + *--ptr = 'f'; + *--ptr = 'v'; + *--ptr = 'o'; + return ptr; + } + if (prec > 9) { + prec = 9; + } + float round = scale10(0.5, -prec); + if (expChar) { + int8_t exp = 0; + bool expNeg = false; + if (value) { + while (value > 10.0) { + value *= 0.1; + exp++; + } + while (value < 1.0) { + value *= 10.0; + exp--; + } + value += round; + if (value > 10.0) { + value *= 0.1; + exp++; + } + expNeg = exp < 0; + if (expNeg) { + exp = -exp; + } + } + ptr = fmtDec((uint16_t)exp, ptr); + if (exp < 10) { + *--ptr = '0'; + } + *--ptr = expNeg ? '-' : '+'; + *--ptr = expChar; + } else { + // round value + value += round; + } + uint32_t whole = value; + if (prec) { + char* tmp = ptr - prec; + uint32_t fraction = scale10(value - whole, prec); + ptr = fmtDec(fraction, ptr); + while (ptr > tmp) { + *--ptr = '0'; + } + *--ptr = '.'; + } + ptr = fmtDec(whole, ptr); + if (neg) { + *--ptr = '-'; + } + return ptr; +} +//------------------------------------------------------------------------------ +char* fmtHex(uint32_t n, char* p) { + do { + uint8_t h = n & 0XF; + *--p = h + (h < 10 ? '0' : 'A' - 10); + n >>= 4; + } while (n); + return p; +} +//------------------------------------------------------------------------------ +float scanFloat(const char* str, char** ptr) { + int16_t const EXP_LIMIT = 100; + bool digit = false; + bool dot = false; + uint32_t fract = 0; + int fracExp = 0; + uint8_t nd = 0; + bool neg; + int c; + float v; + const char* successPtr = str; + + if (ptr) { + *ptr = const_cast(str); + } + + while (isSpace((c = *str++))) {} + neg = c == '-'; + if (c == '-' || c == '+') { + c = *str++; + } + // Skip leading zeros + while (c == '0') { + c = *str++; + digit = true; + } + for (;;) { + if (isDigit(c)) { + digit = true; + if (nd < 9) { + fract = 10*fract + c - '0'; + nd++; + if (dot) { + fracExp--; + } + } else { + if (!dot) { + fracExp++; + } + } + } else if (c == '.') { + if (dot) { + goto fail; + } + dot = true; + } else { + if (!digit) { + goto fail; + } + break; + } + successPtr = str; + c = *str++; + } + if (c == 'e' || c == 'E') { + int exp = 0; + c = *str++; + bool expNeg = c == '-'; + if (c == '-' || c == '+') { + c = *str++; + } + while (isDigit(c)) { + if (exp > EXP_LIMIT) { + goto fail; + } + exp = 10*exp + c - '0'; + successPtr = str; + c = *str++; + } + fracExp += expNeg ? -exp : exp; + } + if (ptr) { + *ptr = const_cast(successPtr); + } + v = scale10(static_cast(fract), fracExp); + return neg ? -v : v; + +fail: + return 0; +} + + diff --git a/lib/SdFat/utility/FmtNumber.h b/lib/SdFat/utility/FmtNumber.h new file mode 100644 index 0000000..2bb97dc --- /dev/null +++ b/lib/SdFat/utility/FmtNumber.h @@ -0,0 +1,38 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef FmtNumber_h +#define FmtNumber_h +// #include +inline bool isDigit(char c) { + return '0' <= c && c <= '9'; +} +inline bool isSpace(char c) { + return c == ' ' || (0X9 <= c && c <= 0XD); +} +#include +#include +char* fmtDec(uint16_t n, char* p); +char* fmtDec(uint32_t n, char* p); +char* fmtFloat(float value, char* p, uint8_t prec); +char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar); +char* fmtHex(uint32_t n, char* p); +float scale10(float v, int8_t n); +float scanFloat(const char* str, char** ptr); +#endif // FmtNumber_h diff --git a/lib/SdFat/utility/SoftSPI.h b/lib/SdFat/utility/SoftSPI.h new file mode 100644 index 0000000..b8cbe9e --- /dev/null +++ b/lib/SdFat/utility/SoftSPI.h @@ -0,0 +1,170 @@ +/* Arduino DigitalIO Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the Arduino DigitalIO Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino DigitalIO Library. If not, see + * . + */ +/** + * @file + * @brief Software SPI. + * + * @defgroup softSPI Software SPI + * @details Software SPI Template Class. + * @{ + */ + +#ifndef SoftSPI_h +#define SoftSPI_h +#include "DigitalPin.h" +//------------------------------------------------------------------------------ +/** Nop for timing. */ +#define nop asm volatile ("nop\n\t") +//------------------------------------------------------------------------------ +/** Pin Mode for MISO is input.*/ +const bool MISO_MODE = false; +/** Pullups disabled for MISO are disabled. */ +const bool MISO_LEVEL = false; +/** Pin Mode for MOSI is output.*/ +const bool MOSI_MODE = true; +/** Pin Mode for SCK is output. */ +const bool SCK_MODE = true; +//------------------------------------------------------------------------------ +/** + * @class SoftSPI + * @brief Fast software SPI. + */ +template +class SoftSPI { + public: + //---------------------------------------------------------------------------- + /** Initialize SoftSPI pins. */ + void begin() { + fastPinConfig(MisoPin, MISO_MODE, MISO_LEVEL); + fastPinConfig(MosiPin, MOSI_MODE, !MODE_CPHA(Mode)); + fastPinConfig(SckPin, SCK_MODE, MODE_CPOL(Mode)); + } + //---------------------------------------------------------------------------- + /** Soft SPI receive byte. + * @return Data byte received. + */ + inline __attribute__((always_inline)) + uint8_t receive() { + uint8_t data = 0; + receiveBit(7, &data); + receiveBit(6, &data); + receiveBit(5, &data); + receiveBit(4, &data); + receiveBit(3, &data); + receiveBit(2, &data); + receiveBit(1, &data); + receiveBit(0, &data); + return data; + } + //---------------------------------------------------------------------------- + /** Soft SPI send byte. + * @param[in] data Data byte to send. + */ + inline __attribute__((always_inline)) + void send(uint8_t data) { + sendBit(7, data); + sendBit(6, data); + sendBit(5, data); + sendBit(4, data); + sendBit(3, data); + sendBit(2, data); + sendBit(1, data); + sendBit(0, data); + } + //---------------------------------------------------------------------------- + /** Soft SPI transfer byte. + * @param[in] txData Data byte to send. + * @return Data byte received. + */ + inline __attribute__((always_inline)) + uint8_t transfer(uint8_t txData) { + uint8_t rxData = 0; + transferBit(7, &rxData, txData); + transferBit(6, &rxData, txData); + transferBit(5, &rxData, txData); + transferBit(4, &rxData, txData); + transferBit(3, &rxData, txData); + transferBit(2, &rxData, txData); + transferBit(1, &rxData, txData); + transferBit(0, &rxData, txData); + return rxData; + } + + private: + //---------------------------------------------------------------------------- + inline __attribute__((always_inline)) + bool MODE_CPHA(uint8_t mode) { + return (mode & 1) != 0; + } + inline __attribute__((always_inline)) + bool MODE_CPOL(uint8_t mode) { + return (mode & 2) != 0; + } + inline __attribute__((always_inline)) + void receiveBit(uint8_t bit, uint8_t* data) { + if (MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); + } + nop; + nop; + fastDigitalWrite(SckPin, + MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); + if (fastDigitalRead(MisoPin)) { + *data |= 1 << bit; + } + if (!MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, MODE_CPOL(Mode)); + } + } + //---------------------------------------------------------------------------- + inline __attribute__((always_inline)) + void sendBit(uint8_t bit, uint8_t data) { + if (MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); + } + fastDigitalWrite(MosiPin, data & (1 << bit)); + fastDigitalWrite(SckPin, + MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); + nop; + nop; + if (!MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, MODE_CPOL(Mode)); + } + } + //---------------------------------------------------------------------------- + inline __attribute__((always_inline)) + void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData) { + if (MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); + } + fastDigitalWrite(MosiPin, txData & (1 << bit)); + fastDigitalWrite(SckPin, + MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); + if (fastDigitalRead(MisoPin)) { + *rxData |= 1 << bit; + } + if (!MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, MODE_CPOL(Mode)); + } + } + //---------------------------------------------------------------------------- +}; +#endif // SoftSPI_h +/** @} */ diff --git a/lib/SdFat/utility/StdioStream.cpp b/lib/SdFat/utility/StdioStream.cpp new file mode 100644 index 0000000..0f61fa0 --- /dev/null +++ b/lib/SdFat/utility/StdioStream.cpp @@ -0,0 +1,606 @@ +/* Arduino RamDisk Library + * Copyright (C) 2014 by William Greiman + * + * This file is part of the Arduino RamDisk Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino RamDisk Library. If not, see + * . + */ +#include "StdioStream.h" +#include "FmtNumber.h" +//------------------------------------------------------------------------------ +int StdioStream::fclose() { + int rtn = 0; + if (!m_flags) { + return EOF; + } + if (m_flags & F_SWR) { + if (!flushBuf()) { + rtn = EOF; + } + } + if (!FatFile::close()) { + rtn = EOF; + } + m_r = 0; + m_w = 0; + m_flags = 0; + return rtn; +} +//------------------------------------------------------------------------------ +int StdioStream::fflush() { + if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) { + if (flushBuf() && FatFile::sync()) { + return 0; + } + } + return EOF; +} +//------------------------------------------------------------------------------ +char* StdioStream::fgets(char* str, size_t num, size_t* len) { + char* s = str; + size_t n; + if (num-- == 0) { + return 0; + } + while (num) { + if ((n = m_r) == 0) { + if (!fillBuf()) { + if (s == str) { + return 0; + } + break; + } + n = m_r; + } + if (n > num) { + n = num; + } + uint8_t* end = reinterpret_cast(memchr(m_p, '\n', n)); + if (end != 0) { + n = ++end - m_p; + memcpy(s, m_p, n); + m_r -= n; + m_p = end; + s += n; + break; + } + memcpy(s, m_p, n); + m_r -= n; + m_p += n; + s += n; + num -= n; + } + *s = 0; + if (len) { + *len = s - str; + } + return str; +} +//------------------------------------------------------------------------------ +bool StdioStream::fopen(const char* path, const char* mode) { + uint8_t oflag; + switch (*mode++) { + case 'a': + m_flags = F_SWR; + oflag = O_WRITE | O_CREAT | O_APPEND | O_AT_END; + break; + + case 'r': + m_flags = F_SRD; + oflag = O_READ; + break; + + case 'w': + m_flags = F_SWR; + oflag = O_WRITE | O_CREAT | O_TRUNC; + break; + + default: + goto fail; + } + while (*mode) { + switch (*mode++) { + case '+': + m_flags |= F_SRW; + oflag |= O_RDWR; + break; + + case 'b': + break; + + case 'x': + oflag |= O_EXCL; + break; + + default: + goto fail; + } + } + if ((oflag & O_EXCL) && !(oflag & O_WRITE)) { + goto fail; + } + if (!FatFile::open(path, oflag)) { + goto fail; + } + m_r = 0; + m_w = 0; + m_p = m_buf; + return true; + +fail: + m_flags = 0; + return false; +} +//------------------------------------------------------------------------------ +int StdioStream::fputs(const char* str) { + size_t len = strlen(str); + return fwrite(str, 1, len) == len ? len : EOF; +} +//------------------------------------------------------------------------------ +int StdioStream::fputs_P(PGM_P str) { + PGM_P bgn = str; + for (char c; (c = pgm_read_byte(str)); str++) { + if (putc(c) < 0) { + return EOF; + } + } + return str - bgn; +} +//------------------------------------------------------------------------------ +size_t StdioStream::fread(void* ptr, size_t size, size_t count) { + uint8_t* dst = reinterpret_cast(ptr); + size_t total = size*count; + if (total == 0) { + return 0; + } + size_t need = total; + while (need > m_r) { + memcpy(dst, m_p, m_r); + dst += m_r; + m_p += m_r; + need -= m_r; + if (!fillBuf()) { + return (total - need)/size; + } + } + memcpy(dst, m_p, need); + m_r -= need; + m_p += need; + return count; +} +//------------------------------------------------------------------------------ +int StdioStream::fseek(int32_t offset, int origin) { + int32_t pos; + if (m_flags & F_SWR) { + if (!flushBuf()) { + goto fail; + } + } + switch (origin) { + case SEEK_CUR: + pos = ftell(); + if (pos < 0) { + goto fail; + } + pos += offset; + if (!FatFile::seekCur(pos)) { + goto fail; + } + break; + + case SEEK_SET: + if (!FatFile::seekSet(offset)) { + goto fail; + } + break; + + case SEEK_END: + if (!FatFile::seekEnd(offset)) { + goto fail; + } + break; + + default: + goto fail; + } + m_r = 0; + m_p = m_buf; + return 0; + +fail: + return EOF; +} +//------------------------------------------------------------------------------ +int32_t StdioStream::ftell() { + uint32_t pos = FatFile::curPosition(); + if (m_flags & F_SRD) { + if (m_r > pos) { + return -1L; + } + pos -= m_r; + } else if (m_flags & F_SWR) { + pos += m_p - m_buf; + } + return pos; +} +//------------------------------------------------------------------------------ +size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) { + return write(ptr, count*size) < 0 ? EOF : count; +#if 0 //////////////////////////////////////////////////////////////////////////////////// + const uint8_t* src = static_cast(ptr); + size_t total = count*size; + if (total == 0) { + return 0; + } + size_t todo = total; + + while (todo > m_w) { + memcpy(m_p, src, m_w); + m_p += m_w; + src += m_w; + todo -= m_w; + if (!flushBuf()) { + return (total - todo)/size; + } + } + memcpy(m_p, src, todo); + m_p += todo; + m_w -= todo; + return count; +#endif ////////////////////////////////////////////////////////////////////////////////// +} +//------------------------------------------------------------------------------ +int StdioStream::write(const void* buf, size_t count) { + const uint8_t* src = static_cast(buf); + size_t todo = count; + + while (todo > m_w) { + memcpy(m_p, src, m_w); + m_p += m_w; + src += m_w; + todo -= m_w; + if (!flushBuf()) { + return EOF; + } + } + memcpy(m_p, src, todo); + m_p += todo; + m_w -= todo; + return count; +} +//------------------------------------------------------------------------------ +size_t StdioStream::print(const __FlashStringHelper *str) { + const char *p = (const char PROGMEM *)str; + uint8_t c; + while ((c = pgm_read_byte(p))) { + if (putc(c) < 0) { + return 0; + } + p++; + } + return p - (const char PROGMEM *)str; +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(float value, uint8_t prec) { +#define FLOAT_NEW_WAY +#ifdef FLOAT_NEW_WAY + char buf[24]; + char *ptr = fmtFloat(value, buf + sizeof(buf), prec); + // return fputs(ptr); + // uint8_t len = buf + sizeof(buf) - ptr; + return write(ptr, buf + sizeof(buf) - ptr); +#else + char* ptr; + uint8_t rtn = 0; + uint8_t sign = 0; + if (value < 0) { + value = -value; + sign = '-'; + } + // check for NaN INF OVF + if (isnan(value)) { + if (fputs_P(PSTR("nan")) < 0) { + return -1; + } + rtn += 3; + } else if (isinf(value)) { + if (fputs_P(PSTR("inf")) < 0) { + return -1; + } + rtn += 3; + } else if (value > 4294967040.0) { + if (fputs_P(PSTR("ovf")) < 0) { + return -1; + } + rtn += 3; + } else { + if (sign) { + if (putc(sign) < 0) { + return -1; + } + rtn++; + } + if (prec > 9) { + prec = 9; + } + + /* + uint32_t s = 1; + for (uint8_t i = 0; i < prec; i++) { + // s *= 10; + s = ((s << 2) + s) << 1; + } + // round value + value += 0.5/s; + */ + value += scale10(0.5, -prec); + uint32_t whole = value; + int np; + if ((np = printDec(whole)) < 0) { + return -1; + } + rtn += np; + if (prec) { + if (putc('.') < 0) { + return -1; + } + char* str = fmtSpace(prec); + if (!str) { + return -1; + } + char* tmp = str - prec; + + // uint32_t fraction = s*(value - whole); + uint32_t fraction = scale10(value - whole, prec); + ptr = fmtDec(fraction, str); + while (ptr > tmp) { + *--ptr = '0'; + } + rtn += prec + 1; + } + } + return rtn; +#endif +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(signed char n) { + if (n < 0) { + if (fputc('-') < 0) { + return -1; + } + n = -n; + } + return printDec((unsigned char)n); +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(int16_t n) { + int s; + uint8_t rtn = 0; + if (n < 0) { + if (fputc('-') < 0) { + return -1; + } + n = -n; + rtn++; + } + if ((s = printDec((uint16_t)n)) < 0) { + return s; + } + return rtn; +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(uint16_t n) { +#define NEW_WAY +#ifdef NEW_WAY + char buf[5]; + char *ptr = fmtDec(n, buf + sizeof(buf)); + uint8_t len = buf + sizeof(buf) - ptr; + return write(ptr, len); +#else + uint8_t len; + if (n < 100) { + len = n < 10 ? 1 : 2; + } else { + len = n < 1000 ? 3 : n < 10000 ? 4 : 5; + } + char* str = fmtSpace(len); + if (!str) { + return -1; + } + fmtDec(n, str); + return len; +#endif +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(int32_t n) { + uint8_t s = 0; + if (n < 0) { + if (fputc('-') < 0) { + return -1; + } + n = -n; + s = 1; + } + int rtn = printDec((uint32_t)n); + return rtn > 0 ? rtn + s : -1; +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(uint32_t n) { +#ifdef NEW_WAY + char buf[10]; + char *ptr = fmtDec(n, buf + sizeof(buf)); + uint8_t len = buf + sizeof(buf) - ptr; + return write(ptr, len); +#else + uint8_t len; + if (n < 0X10000) { + return printDec((uint16_t)n); + } + if (n < 10000000) { + len = n < 100000 ? 5 : n < 1000000 ? 6 : 7; + } else { + len = n < 100000000 ? 8 : n < 1000000000 ? 9 : 10; + } + + char* str = fmtSpace(len); + if (!str) { + return -1; + } + fmtDec(n, str); + return len; +#endif +} +//------------------------------------------------------------------------------ +int StdioStream::printHex(uint32_t n) { +#ifdef NEW_WAY + char buf[8]; + char *ptr = fmtHex(n, buf + sizeof(buf)); + uint8_t len = buf + sizeof(buf) - ptr; + return write(ptr, len); +#else + size_t len; + if (n < 0X10000) { + len = n < 0X10 ? 1 : n < 0X100 ? 2 : n < 0X1000 ? 3 : 4; + } else { + len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8; + } + char* str = fmtSpace(len); + if (!str) { + return -1; + } + + do { + uint8_t h = n & 0XF; + *str-- = h + (h < 10 ? '0' : 'A' - 10); + n >>= 4; + } while (n); + return len; +#endif +} +//------------------------------------------------------------------------------ +bool StdioStream::rewind() { + if (m_flags & F_SWR) { + if (!flushBuf()) { + return false; + } + } + FatFile::seekSet(0); + m_r = 0; + return true; +} +//------------------------------------------------------------------------------ +int StdioStream::ungetc(int c) { + // error if EOF. + if (c == EOF) { + return EOF; + } + // error if not reading. + if ((m_flags & F_SRD) == 0) { + return EOF; + } + // error if no space. + if (m_p == m_buf) { + return EOF; + } + m_r++; + m_flags &= ~F_EOF; + return *--m_p = (uint8_t)c; +} +//============================================================================== +// private +//------------------------------------------------------------------------------ +int StdioStream::fillGet() { + if (!fillBuf()) { + return EOF; + } + m_r--; + return *m_p++; +} +//------------------------------------------------------------------------------ +// private +bool StdioStream::fillBuf() { + if (!(m_flags & + F_SRD)) { /////////////check for F_ERR and F_EOF ??///////////////// + if (!(m_flags & F_SRW)) { + m_flags |= F_ERR; + return false; + } + if (m_flags & F_SWR) { + if (!flushBuf()) { + return false; + } + m_flags &= ~F_SWR; + m_flags |= F_SRD; + m_w = 0; + } + } + m_p = m_buf + UNGETC_BUF_SIZE; + int nr = FatFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE); + if (nr <= 0) { + m_flags |= nr < 0 ? F_ERR : F_EOF; + m_r = 0; + return false; + } + m_r = nr; + return true; +} +//------------------------------------------------------------------------------ +// private +bool StdioStream::flushBuf() { + if (!(m_flags & + F_SWR)) { /////////////////check for F_ERR ??//////////////////////// + if (!(m_flags & F_SRW)) { + m_flags |= F_ERR; + return false; + } + m_flags &= ~F_SRD; + m_flags |= F_SWR; + m_r = 0; + m_w = sizeof(m_buf); + m_p = m_buf; + return true; + } + uint8_t n = m_p - m_buf; + m_p = m_buf; + m_w = sizeof(m_buf); + if (FatFile::write(m_buf, n) == n) { + return true; + } + m_flags |= F_ERR; + return false; +} +//------------------------------------------------------------------------------ +int StdioStream::flushPut(uint8_t c) { + if (!flushBuf()) { + return EOF; + } + m_w--; + return *m_p++ = c; +} +//------------------------------------------------------------------------------ +char* StdioStream::fmtSpace(uint8_t len) { + if (m_w < len) { + if (!flushBuf() || m_w < len) { + return 0; + } + } + if (len > m_w) { + return 0; + } + m_p += len; + m_w -= len; + return reinterpret_cast(m_p); +} + diff --git a/lib/SdFat/utility/StdioStream.h b/lib/SdFat/utility/StdioStream.h new file mode 100644 index 0000000..b8b7d71 --- /dev/null +++ b/lib/SdFat/utility/StdioStream.h @@ -0,0 +1,670 @@ +/* Arduino RamDisk Library + * Copyright (C) 2014 by William Greiman + * + * This file is part of the Arduino RamDisk Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino RamDisk Library. If not, see + * . + */ +#ifndef StdioStream_h +#define StdioStream_h +/** + * \file + * \brief StdioStream class + */ +#include +#include "FatFile.h" +//------------------------------------------------------------------------------ +/** Total size of stream buffer. The entire buffer is used for output. + * During input UNGETC_BUF_SIZE of this space is reserved for ungetc. + */ +const uint8_t STREAM_BUF_SIZE = 64; +/** Amount of buffer allocated for ungetc during input. */ +const uint8_t UNGETC_BUF_SIZE = 2; +//------------------------------------------------------------------------------ +// Get rid of any macros defined in . +#include +#undef clearerr +#undef fclose +#undef feof +#undef ferror +#undef fflush +#undef fgetc +#undef fgetpos +#undef fgets +#undef fopen +#undef fprintf +#undef fputc +#undef fputs +#undef fread +#undef freopen +#undef fscanf +#undef fseek +#undef fsetpos +#undef ftell +#undef fwrite +#undef getc +#undef getchar +#undef gets +#undef perror +#undef printf +#undef putc +#undef putchar +#undef puts +#undef remove +#undef rename +#undef rewind +#undef scanf +#undef setbuf +#undef setvbuf +//#undef sprintf // NOLINT +#undef sscanf +#undef tmpfile +#undef tmpnam +#undef ungetc +#undef vfprintf +#undef vprintf +#undef vsprintf + +// make sure needed macros are defined +#ifndef EOF +/** End-of-file return value. */ +#define EOF (-1) +#endif // EOF +#ifndef NULL +/** Null pointer */ +#define NULL 0 +#endif // NULL +#ifndef SEEK_CUR +/** Seek relative to current position. */ +#define SEEK_CUR 1 +#endif // SEEK_CUR +#ifndef SEEK_END +/** Seek relative to end-of-file. */ +#define SEEK_END 2 +#endif // SEEK_END +#ifndef SEEK_SET +/** Seek relative to start-of-file. */ +#define SEEK_SET 0 +#endif // SEEK_SET +//------------------------------------------------------------------------------ +/** \class StdioStream + * \brief StdioStream implements a minimal stdio stream. + * + * StdioStream does not support subdirectories or long file names. + */ +class StdioStream : private FatFile { + public: + /** Constructor + * + */ + StdioStream() { + m_w = m_r = 0; + m_p = m_buf; + m_flags = 0; + } + //---------------------------------------------------------------------------- + /** Clear the stream's end-of-file and error indicators. */ + void clearerr() { + m_flags &= ~(F_ERR | F_EOF); + } + //---------------------------------------------------------------------------- + /** Close a stream. + * + * A successful call to the fclose function causes the stream to be + * flushed and the associated file to be closed. Any unwritten buffered + * data is written to the file; any unread buffered data is discarded. + * Whether or not the call succeeds, the stream is disassociated from + * the file. + * + * \return zero if the stream was successfully closed, or EOF if any any + * errors are detected. + */ + int fclose(); + //---------------------------------------------------------------------------- + /** Test the stream's end-of-file indicator. + * \return non-zero if and only if the end-of-file indicator is set. + */ + int feof() { + return (m_flags & F_EOF) != 0; + } + //---------------------------------------------------------------------------- + /** Test the stream's error indicator. + * \return return non-zero if and only if the error indicator is set. + */ + int ferror() { + return (m_flags & F_ERR) != 0; + } + //---------------------------------------------------------------------------- + /** Flush the stream. + * + * If stream is an output stream or an update stream in which the most + * recent operation was not input, any unwritten data is written to the + * file; otherwise the call is an error since any buffered input data + * would be lost. + * + * \return sets the error indicator for the stream and returns EOF if an + * error occurs, otherwise it returns zero. + */ + int fflush(); + //---------------------------------------------------------------------------- + /** Get a byte from the stream. + * + * \return If the end-of-file indicator for the stream is set, or if the + * stream is at end-of-file, the end-of-file indicator for the stream is + * set and the fgetc function returns EOF. Otherwise, the fgetc function + * returns the next character from the input stream. + */ + int fgetc() { + return m_r-- == 0 ? fillGet() : *m_p++; + } + //---------------------------------------------------------------------------- + /** Get a string from a stream. + * + * The fgets function reads at most one less than the number of + * characters specified by num from the stream into the array pointed + * to by str. No additional characters are read after a new-line + * character (which is retained) or after end-of-file. A null character + * is written immediately after the last character read into the array. + * + * \param[out] str Pointer to an array of where the string is copied. + * + * \param[in] num Maximum number of characters including the null + * character. + * + * \param[out] len If len is not null and fgets is successful, the + * length of the string is returned. + * + * \return str if successful. If end-of-file is encountered and no + * characters have been read into the array, the contents of the array + * remain unchanged and a null pointer is returned. If a read error + * occurs during the operation, the array contents are indeterminate + * and a null pointer is returned. + */ + char* fgets(char* str, size_t num, size_t* len = 0); + //---------------------------------------------------------------------------- + /** Open a stream. + * + * Open a file and associates the stream with it. + * + * \param[in] path file to be opened. + * + * \param[in] mode a string that indicates the open mode. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
"r" or "rb"Open a file for reading. The file must exist.
"w" or "wb"Truncate an existing to zero length or create an empty file + * for writing.
"wx" or "wbx"Create a file for writing. Fails if the file already exists.
"a" or "ab"Append; open or create file for writing at end-of-file.
"r+" or "rb+" or "r+b"Open a file for update (reading and writing).
"w+" or "w+b" or "wb+"Truncate an existing to zero length or create a file for update.
"w+x" or "w+bx" or "wb+x"Create a file for update. Fails if the file already exists.
"a+" or "a+b" or "ab+"Append; open or create a file for update, writing at end-of-file.
+ * The character 'b' shall have no effect, but is allowed for ISO C + * standard conformance. + * + * Opening a file with append mode causes all subsequent writes to the + * file to be forced to the then current end-of-file, regardless of + * intervening calls to the fseek function. + * + * When a file is opened with update mode, both input and output may be + * performed on the associated stream. However, output shall not be + * directly followed by input without an intervening call to the fflush + * function or to a file positioning function (fseek, or rewind), and + * input shall not be directly followed by output without an intervening + * call to a file positioning function, unless the input operation + * encounters end-of-file. + * + * \return true for success or false for failure. + */ + bool fopen(const char* path, const char * mode); + //---------------------------------------------------------------------------- + /** Write a byte to a stream. + * + * \param[in] c the byte to be written (converted to an unsigned char). + * + * \return Upon successful completion, fputc() returns the value it + * has written. Otherwise, it returns EOF and sets the error indicator for + * the stream. + */ + int fputc(int c) { + return m_w-- == 0 ? flushPut(c) : *m_p++ = c; + } + //---------------------------------------------------------------------------- + /** Write a string to a stream. + * + * \param[in] str a pointer to the string to be written. + * + * \return for success, fputs() returns a non-negative + * number. Otherwise, it returns EOF and sets the error indicator for + * the stream. + */ + int fputs(const char* str); + //---------------------------------------------------------------------------- + /** Write a string stored in flash. + * + * \param[in] str string to be written. + * + * \return for success, fputs() returns a non-negative + * number. Otherwise, it returns EOF and sets the error indicator for + * the stream. + */ + int fputs_P(PGM_P str); + //---------------------------------------------------------------------------- + /** Binary input. + * + * Reads an array of up to count elements, each one with a size of size + * bytes. + * \param[out] ptr pointer to area of at least (size*count) bytes where + * the data will be stored. + * + * \param[in] size the size, in bytes, of each element to be read. + * + * \param[in] count the number of elements to be read. + * + * \return number of elements successfully read, which may be less than + * count if a read error or end-of-file is encountered. If size or count + * is zero, fread returns zero and the contents of the array and the + * state of the stream remain unchanged. + */ + size_t fread(void* ptr, size_t size, size_t count); + //---------------------------------------------------------------------------- + /** Set the file position for the stream. + * + * \param[in] offset number of offset from the origin. + * + * \param[in] origin position used as reference for the offset. It is + * specified by one of the following constants. + * + * SEEK_SET - Beginning of file. + * + * SEEK_CUR - Current position of the file pointer. + * + * SEEK_END - End of file. + * + * \return zero for success. Otherwise, it returns non-zero and sets the + * error indicator for the stream. + */ + int fseek(int32_t offset, int origin); + //---------------------------------------------------------------------------- + /** Get the current position in a stream. + * + * \return If successful, ftell return the current value of the position + * indicator. On failure, ftell returns −1L. + */ + int32_t ftell(); + //---------------------------------------------------------------------------- + /** Binary output. + * + * Writes an array of up to count elements, each one with a size of size + * bytes. + * \param[in] ptr pointer to (size*count) bytes of data to be written. + * + * \param[in] size the size, in bytes, of each element to be written. + * + * \param[in] count the number of elements to be written. + * + * \return number of elements successfully written. if this number is + * less than count, an error has occurred. If size or count is zero, + * fwrite returns zero. + */ + size_t fwrite(const void * ptr, size_t size, size_t count); + //---------------------------------------------------------------------------- + /** Get a byte from the stream. + * + * getc and fgetc are equivalent but getc is in-line so it is faster but + * require more flash memory. + * + * \return If the end-of-file indicator for the stream is set, or if the + * stream is at end-of-file, the end-of-file indicator for the stream is + * set and the fgetc function returns EOF. Otherwise, the fgetc function + * returns the next character from the input stream. + */ + inline __attribute__((always_inline)) + int getc() { + return m_r-- == 0 ? fillGet() : *m_p++; + } + //---------------------------------------------------------------------------- + /** Write a byte to a stream. + * + * putc and fputc are equivalent but putc is in-line so it is faster but + * require more flash memory. + * + * \param[in] c the byte to be written (converted to an unsigned char). + * + * \return Upon successful completion, fputc() returns the value it + * has written. Otherwise, it returns EOF and sets the error indicator for + * the stream. + */ + inline __attribute__((always_inline)) + int putc(int c) { + return m_w-- == 0 ? flushPut(c) : *m_p++ = c; + } + //---------------------------------------------------------------------------- + /** Write a CR/LF. + * + * \return two, the number of bytes written, for success or -1 for failure. + */ + inline __attribute__((always_inline)) + int putCRLF() { + if (m_w < 2) { + if (!flushBuf()) { + return -1; + } + } + *m_p++ = '\r'; + *m_p++ = '\n'; + m_w -= 2; + return 2; + } + //---------------------------------------------------------------------------- + /** Write a character. + * \param[in] c the character to write. + * \return the number of bytes written. + */ + size_t print(char c) { + return putc(c) < 0 ? 0 : 1; + } + //---------------------------------------------------------------------------- + /** Write a string. + * + * \param[in] str the string to be written. + * + * \return the number of bytes written. + */ + size_t print(const char* str) { + int n = fputs(str); + return n < 0 ? 0 : n; + } + //---------------------------------------------------------------------------- + /** Print a string stored in flash memory. + * + * \param[in] str the string to print. + * + * \return the number of bytes written. + */ + size_t print(const __FlashStringHelper *str); + //---------------------------------------------------------------------------- + /** Print a floating point number. + * + * \param[in] prec Number of digits after decimal point. + * + * \param[in] val the number to be printed. + * + * \return the number of bytes written. + */ + size_t print(double val, uint8_t prec = 2) { + return print(static_cast(val), prec); + } + //---------------------------------------------------------------------------- + /** Print a floating point number. + * + * \param[in] prec Number of digits after decimal point. + * + * \param[in] val the number to be printed. + * + * \return the number of bytes written. + */ + size_t print(float val, uint8_t prec = 2) { + int n = printDec(val, prec); + return n > 0 ? n : 0; + } + //---------------------------------------------------------------------------- + /** Print a number. + * + * \param[in] val the number to be printed. + * + * \return the number of bytes written. + */ + template + size_t print(T val) { + int n = printDec(val); + return n > 0 ? n : 0; + } + //---------------------------------------------------------------------------- + /** Write a CR/LF. + * + * \return two, the number of bytes written, for success or zero for failure. + */ + size_t println() { + return putCRLF() > 0 ? 2 : 0; + } + //---------------------------------------------------------------------------- + /** Print a floating point number followed by CR/LF. + * + * \param[in] val the number to be printed. + * + * \param[in] prec Number of digits after decimal point. + * + * \return the number of bytes written. + */ + size_t println(double val, uint8_t prec = 2) { + return println(static_cast(val), prec); + } + //---------------------------------------------------------------------------- + /** Print a floating point number followed by CR/LF. + * + * \param[in] val the number to be printed. + * + * \param[in] prec Number of digits after decimal point. + * + * \return the number of bytes written. + */ + size_t println(float val, uint8_t prec = 2) { + int n = printDec(val, prec); + return n > 0 && putCRLF() > 0 ? n + 2 : 0; + } + //---------------------------------------------------------------------------- + /** Print an item followed by CR/LF + * + * \param[in] val the item to be printed. + * + * \return the number of bytes written. + */ + template + size_t println(T val) { + int n = print(val); + return putCRLF() > 0 ? n + 2 : 0; + } + //---------------------------------------------------------------------------- + /** Print a char as a number. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(char n) { + if (CHAR_MIN == 0) { + return printDec((unsigned char)n); + } else { + return printDec((signed char)n); + } + } + //---------------------------------------------------------------------------- + /** print a signed 8-bit integer + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(signed char n); + //---------------------------------------------------------------------------- + /** Print an unsigned 8-bit number. + * \param[in] n number to be print. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(unsigned char n) { + return printDec((uint16_t)n); + } + //---------------------------------------------------------------------------- + /** Print a int16_t + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(int16_t n); + //---------------------------------------------------------------------------- + /** print a uint16_t. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(uint16_t n); + //---------------------------------------------------------------------------- + /** Print a signed 32-bit integer. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(int32_t n); + //---------------------------------------------------------------------------- + /** Write an unsigned 32-bit number. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(uint32_t n); + //---------------------------------------------------------------------------- + /** Print a double. + * \param[in] value The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(double value, uint8_t prec) { + return printDec(static_cast(value), prec); + } + //---------------------------------------------------------------------------- + /** Print a float. + * \param[in] value The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(float value, uint8_t prec); + //---------------------------------------------------------------------------- + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(double value, char term, uint8_t prec = 2) { + return printField(static_cast(value), term, prec) > 0; + } + //---------------------------------------------------------------------------- + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(float value, char term, uint8_t prec = 2) { + int rtn = printDec(value, prec); + return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; + } + //---------------------------------------------------------------------------- + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. + * \return The number of bytes written or -1 if an error occurs. + */ + template + int printField(T value, char term) { + int rtn = printDec(value); + return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; + } + //---------------------------------------------------------------------------- + /** Print HEX + * \param[in] n number to be printed as HEX. + * + * \return The number of bytes written or -1 if an error occurs. + */ + int printHex(uint32_t n); + //---------------------------------------------------------------------------- + /** Print HEX with CRLF + * \param[in] n number to be printed as HEX. + * + * \return The number of bytes written or -1 if an error occurs. + */ + int printHexln(uint32_t n) { + int rtn = printHex(n); + return rtn < 0 || putCRLF() != 2 ? -1 : rtn + 2; + } + //---------------------------------------------------------------------------- + /** Set position of a stream to the beginning. + * + * The rewind function sets the file position to the beginning of the + * file. It is equivalent to fseek(0L, SEEK_SET) except that the error + * indicator for the stream is also cleared. + * + * \return true for success or false for failure. + */ + bool rewind(); + //---------------------------------------------------------------------------- + /** Push a byte back into an input stream. + * + * \param[in] c the byte (converted to an unsigned char) to be pushed back. + * + * One character of push-back is guaranteed. If the ungetc function is + * called too many times without an intervening read or file positioning + * operation on that stream, the operation may fail. + * + * A successful intervening call to a file positioning function (fseek, + * fsetpos, or rewind) discards any pushed-back characters for the stream. + * + * \return Upon successful completion, ungetc() returns the byte pushed + * back after conversion. Otherwise it returns EOF. + */ + int ungetc(int c); + //============================================================================ + private: + bool fillBuf(); + int fillGet(); + bool flushBuf(); + int flushPut(uint8_t c); + char* fmtSpace(uint8_t len); + int write(const void* buf, size_t count); + //---------------------------------------------------------------------------- + // F_SRD and F_WR are never simultaneously asserted + static const uint8_t F_SRD = 0x01; // OK to read + static const uint8_t F_SWR = 0x02; // OK to write + static const uint8_t F_SRW = 0x04; // open for reading & writing + static const uint8_t F_EOF = 0x10; // found EOF + static const uint8_t F_ERR = 0x20; // found error + //---------------------------------------------------------------------------- + uint8_t m_flags; + uint8_t* m_p; + uint8_t m_r; + uint8_t m_w; + uint8_t m_buf[STREAM_BUF_SIZE]; +}; +//------------------------------------------------------------------------------ +#endif // StdioStream_h diff --git a/lib/SdFat/utility/bufstream.h b/lib/SdFat/utility/bufstream.h new file mode 100644 index 0000000..0253e37 --- /dev/null +++ b/lib/SdFat/utility/bufstream.h @@ -0,0 +1,163 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef bufstream_h +#define bufstream_h +/** + * \file + * \brief \ref ibufstream and \ref obufstream classes + */ +#include +#include "iostream.h" +//============================================================================== +/** + * \class ibufstream + * \brief parse a char string + */ +class ibufstream : public istream { + public: + /** Constructor */ + ibufstream() : m_buf(0), m_len(0) {} + /** Constructor + * \param[in] str pointer to string to be parsed + * Warning: The string will not be copied so must stay in scope. + */ + explicit ibufstream(const char* str) { + init(str); + } + /** Initialize an ibufstream + * \param[in] str pointer to string to be parsed + * Warning: The string will not be copied so must stay in scope. + */ + void init(const char* str) { + m_buf = str; + m_len = strlen(m_buf); + m_pos = 0; + clear(); + } + + protected: + /// @cond SHOW_PROTECTED + int16_t getch() { + if (m_pos < m_len) { + return m_buf[m_pos++]; + } + setstate(eofbit); + return -1; + } + void getpos(FatPos_t *pos) { + pos->position = m_pos; + } + bool seekoff(off_type off, seekdir way) { + return false; + } + bool seekpos(pos_type pos) { + if (pos < m_len) { + m_pos = pos; + return true; + } + return false; + } + void setpos(FatPos_t *pos) { + m_pos = pos->position; + } + pos_type tellpos() { + return m_pos; + } + /// @endcond + private: + const char* m_buf; + size_t m_len; + size_t m_pos; +}; +//============================================================================== +/** + * \class obufstream + * \brief format a char string + */ +class obufstream : public ostream { + public: + /** constructor */ + obufstream() : m_in(0) {} + /** Constructor + * \param[in] buf buffer for formatted string + * \param[in] size buffer size + */ + obufstream(char *buf, size_t size) { + init(buf, size); + } + /** Initialize an obufstream + * \param[in] buf buffer for formatted string + * \param[in] size buffer size + */ + void init(char *buf, size_t size) { + m_buf = buf; + buf[0] = '\0'; + m_size = size; + m_in = 0; + } + /** \return a pointer to the buffer */ + char* buf() { + return m_buf; + } + /** \return the length of the formatted string */ + size_t length() { + return m_in; + } + + protected: + /// @cond SHOW_PROTECTED + void putch(char c) { + if (m_in >= (m_size - 1)) { + setstate(badbit); + return; + } + m_buf[m_in++] = c; + m_buf[m_in] = '\0'; + } + void putstr(const char *str) { + while (*str) { + putch(*str++); + } + } + bool seekoff(off_type off, seekdir way) { + return false; + } + bool seekpos(pos_type pos) { + if (pos > m_in) { + return false; + } + m_in = pos; + m_buf[m_in] = '\0'; + return true; + } + bool sync() { + return true; + } + + pos_type tellpos() { + return m_in; + } + /// @endcond + private: + char *m_buf; + size_t m_size; + size_t m_in; +}; +#endif // bufstream_h diff --git a/lib/SdFat/utility/fstream.cpp b/lib/SdFat/utility/fstream.cpp new file mode 100644 index 0000000..505f083 --- /dev/null +++ b/lib/SdFat/utility/fstream.cpp @@ -0,0 +1,167 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include "fstream.h" +//============================================================================== +/// @cond SHOW_PROTECTED +int16_t FatStreamBase::getch() { + uint8_t c; + int8_t s = read(&c, 1); + if (s != 1) { + if (s < 0) { + setstate(badbit); + } else { + setstate(eofbit); + } + return -1; + } + if (c != '\r' || (getmode() & ios::binary)) { + return c; + } + s = read(&c, 1); + if (s == 1 && c == '\n') { + return c; + } + if (s == 1) { + seekCur(-1); + } + return '\r'; +} +//------------------------------------------------------------------------------ +void FatStreamBase::open(const char* path, ios::openmode mode) { + uint8_t flags; + switch (mode & (app | in | out | trunc)) { + case app | in: + case app | in | out: + flags = O_RDWR | O_APPEND | O_CREAT; + break; + + case app: + case app | out: + flags = O_WRITE | O_APPEND | O_CREAT; + break; + + case in: + flags = O_READ; + break; + + case in | out: + flags = O_RDWR; + break; + + case in | out | trunc: + flags = O_RDWR | O_TRUNC | O_CREAT; + break; + + case out: + case out | trunc: + flags = O_WRITE | O_TRUNC | O_CREAT; + break; + + default: + goto fail; + } + if (mode & ios::ate) { + flags |= O_AT_END; + } + if (!FatFile::open(path, flags)) { + goto fail; + } + setmode(mode); + clear(); + return; + +fail: + FatFile::close(); + setstate(failbit); + return; +} +//------------------------------------------------------------------------------ +void FatStreamBase::putch(char c) { + if (c == '\n' && !(getmode() & ios::binary)) { + write('\r'); + } + write(c); + if (getWriteError()) { + setstate(badbit); + } +} +//------------------------------------------------------------------------------ +void FatStreamBase::putstr(const char* str) { + size_t n = 0; + while (1) { + char c = str[n]; + if (c == '\0' || (c == '\n' && !(getmode() & ios::binary))) { + if (n > 0) { + write(str, n); + } + if (c == '\0') { + break; + } + write('\r'); + str += n; + n = 0; + } + n++; + } + if (getWriteError()) { + setstate(badbit); + } +} +//------------------------------------------------------------------------------ +/** Internal do not use + * \param[in] off + * \param[in] way + */ +bool FatStreamBase::seekoff(off_type off, seekdir way) { + pos_type pos; + switch (way) { + case beg: + pos = off; + break; + + case cur: + pos = curPosition() + off; + break; + + case end: + pos = fileSize() + off; + break; + + default: + return false; + } + return seekpos(pos); +} +//------------------------------------------------------------------------------ +/** Internal do not use + * \param[in] pos + */ +bool FatStreamBase::seekpos(pos_type pos) { + return seekSet(pos); +} +//------------------------------------------------------------------------------ +int FatStreamBase::write(const void* buf, size_t n) { + return FatFile::write(buf, n); +} +//------------------------------------------------------------------------------ +void FatStreamBase::write(char c) { + write(&c, 1); +} +/// @endcond diff --git a/lib/SdFat/utility/fstream.h b/lib/SdFat/utility/fstream.h new file mode 100644 index 0000000..5410c38 --- /dev/null +++ b/lib/SdFat/utility/fstream.h @@ -0,0 +1,315 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef fstream_h +#define fstream_h +/** + * \file + * \brief \ref fstream, \ref ifstream, and \ref ofstream classes + */ +#include "FatFile.h" +#include "iostream.h" +//============================================================================== +/** + * \class FatStreamBase + * \brief Base class for C++ style streams + */ +class FatStreamBase : protected FatFile, virtual public ios { + protected: + /// @cond SHOW_PROTECTED + int16_t getch(); + void putch(char c); + void putstr(const char *str); + void open(const char* path, ios::openmode mode); + /** Internal do not use + * \return mode + */ + ios::openmode getmode() { + return m_mode; + } + /** Internal do not use + * \param[in] mode + */ + void setmode(ios::openmode mode) { + m_mode = mode; + } + bool seekoff(off_type off, seekdir way); + bool seekpos(pos_type pos); + int write(const void* buf, size_t n); + void write(char c); + /// @endcond + private: + ios::openmode m_mode; +}; +//============================================================================== +/** + * \class fstream + * \brief file input/output stream. + */ +class fstream : public iostream, FatStreamBase { + public: + using iostream::peek; + fstream() {} + /** Constructor with open + * + * \param[in] path path to open + * \param[in] mode open mode + */ + explicit fstream(const char* path, openmode mode = in | out) { + open(path, mode); + } +#if DESTRUCTOR_CLOSES_FILE + ~fstream() {} +#endif // DESTRUCTOR_CLOSES_FILE + /** Clear state and writeError + * \param[in] state new state for stream + */ + void clear(iostate state = goodbit) { + ios::clear(state); + FatFile::clearWriteError(); + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() { + FatFile::close(); + } + /** Open a fstream + * \param[in] path file to open + * \param[in] mode open mode + * + * Valid open modes are (at end, ios::ate, and/or ios::binary may be added): + * + * ios::in - Open file for reading. + * + * ios::out or ios::out | ios::trunc - Truncate to 0 length, if existent, + * or create a file for writing only. + * + * ios::app or ios::out | ios::app - Append; open or create file for + * writing at end-of-file. + * + * ios::in | ios::out - Open file for update (reading and writing). + * + * ios::in | ios::out | ios::trunc - Truncate to zero length, if existent, + * or create file for update. + * + * ios::in | ios::app or ios::in | ios::out | ios::app - Append; open or + * create text file for update, writing at end of file. + */ + void open(const char* path, openmode mode = in | out) { + FatStreamBase::open(path, mode); + } + /** \return True if stream is open else false. */ + bool is_open() { + return FatFile::isOpen(); + } + + protected: + /// @cond SHOW_PROTECTED + /** Internal - do not use + * \return + */ + int16_t getch() { + return FatStreamBase::getch(); + } + /** Internal - do not use + * \param[out] pos + */ + void getpos(FatPos_t* pos) { + FatFile::getpos(pos); + } + /** Internal - do not use + * \param[in] c + */ + void putch(char c) { + FatStreamBase::putch(c); + } + /** Internal - do not use + * \param[in] str + */ + void putstr(const char *str) { + FatStreamBase::putstr(str); + } + /** Internal - do not use + * \param[in] pos + */ + bool seekoff(off_type off, seekdir way) { + return FatStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) { + return FatStreamBase::seekpos(pos); + } + void setpos(FatPos_t* pos) { + FatFile::setpos(pos); + } + bool sync() { + return FatStreamBase::sync(); + } + pos_type tellpos() { + return FatStreamBase::curPosition(); + } + /// @endcond +}; +//============================================================================== +/** + * \class ifstream + * \brief file input stream. + */ +class ifstream : public istream, FatStreamBase { + public: + using istream::peek; + ifstream() {} + /** Constructor with open + * \param[in] path file to open + * \param[in] mode open mode + */ + explicit ifstream(const char* path, openmode mode = in) { + open(path, mode); + } +#if DESTRUCTOR_CLOSES_FILE + ~ifstream() {} +#endif // DESTRUCTOR_CLOSES_FILE + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() { + FatFile::close(); + } + /** \return True if stream is open else false. */ + bool is_open() { + return FatFile::isOpen(); + } + /** Open an ifstream + * \param[in] path file to open + * \param[in] mode open mode + * + * \a mode See fstream::open() for valid modes. + */ + void open(const char* path, openmode mode = in) { + FatStreamBase::open(path, mode | in); + } + + protected: + /// @cond SHOW_PROTECTED + /** Internal - do not use + * \return + */ + int16_t getch() { + return FatStreamBase::getch(); + } + /** Internal - do not use + * \param[out] pos + */ + void getpos(FatPos_t* pos) { + FatFile::getpos(pos); + } + /** Internal - do not use + * \param[in] pos + */ + bool seekoff(off_type off, seekdir way) { + return FatStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) { + return FatStreamBase::seekpos(pos); + } + void setpos(FatPos_t* pos) { + FatFile::setpos(pos); + } + pos_type tellpos() { + return FatStreamBase::curPosition(); + } + /// @endcond +}; +//============================================================================== +/** + * \class ofstream + * \brief file output stream. + */ +class ofstream : public ostream, FatStreamBase { + public: + ofstream() {} + /** Constructor with open + * \param[in] path file to open + * \param[in] mode open mode + */ + explicit ofstream(const char* path, ios::openmode mode = out) { + open(path, mode); + } +#if DESTRUCTOR_CLOSES_FILE + ~ofstream() {} +#endif // DESTRUCTOR_CLOSES_FILE + /** Clear state and writeError + * \param[in] state new state for stream + */ + void clear(iostate state = goodbit) { + ios::clear(state); + FatFile::clearWriteError(); + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() { + FatFile::close(); + } + /** Open an ofstream + * \param[in] path file to open + * \param[in] mode open mode + * + * \a mode See fstream::open() for valid modes. + */ + void open(const char* path, openmode mode = out) { + FatStreamBase::open(path, mode | out); + } + /** \return True if stream is open else false. */ + bool is_open() { + return FatFile::isOpen(); + } + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal do not use + * \param[in] c + */ + void putch(char c) { + FatStreamBase::putch(c); + } + void putstr(const char* str) { + FatStreamBase::putstr(str); + } + bool seekoff(off_type off, seekdir way) { + return FatStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) { + return FatStreamBase::seekpos(pos); + } + /** + * Internal do not use + * \param[in] b + */ + bool sync() { + return FatStreamBase::sync(); + } + pos_type tellpos() { + return FatStreamBase::curPosition(); + } + /// @endcond +}; +//------------------------------------------------------------------------------ +#endif // fstream_h diff --git a/lib/SdFat/utility/ios.h b/lib/SdFat/utility/ios.h new file mode 100644 index 0000000..d89d96c --- /dev/null +++ b/lib/SdFat/utility/ios.h @@ -0,0 +1,418 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef ios_h +#define ios_h +#include "FatFile.h" +/** + * \file + * \brief \ref ios_base and \ref ios classes + */ +//============================================================================== +/** + * \class ios_base + * \brief Base class for all streams + */ +class ios_base { + public: + /** typedef for iostate bitmask */ + typedef unsigned char iostate; + // State flags. + /** iostate for no flags */ + static const iostate goodbit = 0x00; + /** iostate bad bit for a nonrecoverable error. */ + static const iostate badbit = 0X01; + /** iostate bit for end of file reached */ + static const iostate eofbit = 0x02; + /** iostate fail bit for nonfatal error */ + static const iostate failbit = 0X04; + /** + * unsigned size that can represent maximum file size. + * (violates spec - should be signed) + */ + typedef uint32_t streamsize; + /** type for absolute seek position */ + typedef uint32_t pos_type; + /** type for relative seek offset */ + typedef int32_t off_type; + + /** enumerated type for the direction of relative seeks */ + enum seekdir { + /** seek relative to the beginning of the stream */ + beg, + /** seek relative to the current stream position */ + cur, + /** seek relative to the end of the stream */ + end + }; + /** type for format flags */ + typedef unsigned int fmtflags; + /** left adjust fields */ + static const fmtflags left = 0x0001; + /** right adjust fields */ + static const fmtflags right = 0x0002; + /** fill between sign/base prefix and number */ + static const fmtflags internal = 0x0004; + /** base 10 flag*/ + static const fmtflags dec = 0x0008; + /** base 16 flag */ + static const fmtflags hex = 0x0010; + /** base 8 flag */ + static const fmtflags oct = 0x0020; + // static const fmtflags fixed = 0x0040; + // static const fmtflags scientific = 0x0080; + /** use strings true/false for bool */ + static const fmtflags boolalpha = 0x0100; + /** use prefix 0X for hex and 0 for oct */ + static const fmtflags showbase = 0x0200; + /** always show '.' for floating numbers */ + static const fmtflags showpoint = 0x0400; + /** show + sign for nonnegative numbers */ + static const fmtflags showpos = 0x0800; + /** skip initial white space */ + static const fmtflags skipws = 0x1000; + // static const fmtflags unitbuf = 0x2000; + /** use uppercase letters in number representations */ + static const fmtflags uppercase = 0x4000; + /** mask for adjustfield */ + static const fmtflags adjustfield = left | right | internal; + /** mask for basefield */ + static const fmtflags basefield = dec | hex | oct; + // static const fmtflags floatfield = scientific | fixed; + //---------------------------------------------------------------------------- + /** typedef for iostream open mode */ + typedef uint8_t openmode; + + // Openmode flags. + /** seek to end before each write */ + static const openmode app = 0X4; + /** open and seek to end immediately after opening */ + static const openmode ate = 0X8; + /** perform input and output in binary mode (as opposed to text mode) */ + static const openmode binary = 0X10; + /** open for input */ + static const openmode in = 0X20; + /** open for output */ + static const openmode out = 0X40; + /** truncate an existing stream when opening */ + static const openmode trunc = 0X80; + //---------------------------------------------------------------------------- + ios_base() : m_fill(' '), m_fmtflags(dec | right | skipws) + , m_precision(2), m_width(0) {} + /** \return fill character */ + char fill() { + return m_fill; + } + /** Set fill character + * \param[in] c new fill character + * \return old fill character + */ + char fill(char c) { + char r = m_fill; + m_fill = c; + return r; + } + /** \return format flags */ + fmtflags flags() const { + return m_fmtflags; + } + /** set format flags + * \param[in] fl new flag + * \return old flags + */ + fmtflags flags(fmtflags fl) { + fmtflags tmp = m_fmtflags; + m_fmtflags = fl; + return tmp; + } + /** \return precision */ + int precision() const { + return m_precision; + } + /** set precision + * \param[in] n new precision + * \return old precision + */ + int precision(unsigned int n) { + int r = m_precision; + m_precision = n; + return r; + } + /** set format flags + * \param[in] fl new flags to be or'ed in + * \return old flags + */ + fmtflags setf(fmtflags fl) { + fmtflags r = m_fmtflags; + m_fmtflags |= fl; + return r; + } + /** modify format flags + * \param[in] mask flags to be removed + * \param[in] fl flags to be set after mask bits have been cleared + * \return old flags + */ + fmtflags setf(fmtflags fl, fmtflags mask) { + fmtflags r = m_fmtflags; + m_fmtflags &= ~mask; + m_fmtflags |= fl; + return r; + } + /** clear format flags + * \param[in] fl flags to be cleared + * \return old flags + */ + void unsetf(fmtflags fl) { + m_fmtflags &= ~fl; + } + /** \return width */ + unsigned width() { + return m_width; + } + /** set width + * \param[in] n new width + * \return old width + */ + unsigned width(unsigned n) { + unsigned r = m_width; + m_width = n; + return r; + } + + protected: + /** \return current number base */ + uint8_t flagsToBase() { + uint8_t f = flags() & basefield; + return f == oct ? 8 : f != hex ? 10 : 16; + } + + private: + char m_fill; + fmtflags m_fmtflags; + unsigned char m_precision; + unsigned int m_width; +}; +//------------------------------------------------------------------------------ +/** function for boolalpha manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& boolalpha(ios_base& str) { + str.setf(ios_base::boolalpha); + return str; +} +/** function for dec manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& dec(ios_base& str) { + str.setf(ios_base::dec, ios_base::basefield); + return str; +} +/** function for hex manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& hex(ios_base& str) { + str.setf(ios_base::hex, ios_base::basefield); + return str; +} +/** function for internal manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& internal(ios_base& str) { + str.setf(ios_base::internal, ios_base::adjustfield); + return str; +} +/** function for left manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& left(ios_base& str) { + str.setf(ios_base::left, ios_base::adjustfield); + return str; +} +/** function for noboolalpha manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noboolalpha(ios_base& str) { + str.unsetf(ios_base::boolalpha); + return str; +} +/** function for noshowbase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noshowbase(ios_base& str) { + str.unsetf(ios_base::showbase); + return str; +} +/** function for noshowpoint manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noshowpoint(ios_base& str) { + str.unsetf(ios_base::showpoint); + return str; +} +/** function for noshowpos manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noshowpos(ios_base& str) { + str.unsetf(ios_base::showpos); + return str; +} +/** function for noskipws manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noskipws(ios_base& str) { + str.unsetf(ios_base::skipws); + return str; +} +/** function for nouppercase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& nouppercase(ios_base& str) { + str.unsetf(ios_base::uppercase); + return str; +} +/** function for oct manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& oct(ios_base& str) { + str.setf(ios_base::oct, ios_base::basefield); + return str; +} +/** function for right manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& right(ios_base& str) { + str.setf(ios_base::right, ios_base::adjustfield); + return str; +} +/** function for showbase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& showbase(ios_base& str) { + str.setf(ios_base::showbase); + return str; +} +/** function for showpos manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& showpos(ios_base& str) { + str.setf(ios_base::showpos); + return str; +} +/** function for showpoint manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& showpoint(ios_base& str) { + str.setf(ios_base::showpoint); + return str; +} +/** function for skipws manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& skipws(ios_base& str) { + str.setf(ios_base::skipws); + return str; +} +/** function for uppercase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& uppercase(ios_base& str) { + str.setf(ios_base::uppercase); + return str; +} +//============================================================================== +/** + * \class ios + * \brief Error and state information for all streams + */ +class ios : public ios_base { + public: + /** Create ios with no error flags set */ + ios() : m_iostate(0) {} + + /** \return null pointer if fail() is true. */ + operator const void*() const { + return !fail() ? reinterpret_cast(this) : 0; + } + /** \return true if fail() else false. */ + bool operator!() const { + return fail(); + } + /** \return The iostate flags for this file. */ + iostate rdstate() const { + return m_iostate; + } + /** \return True if no iostate flags are set else false. */ + bool good() const { + return m_iostate == goodbit; + } + /** \return true if end of file has been reached else false. + * + * Warning: An empty file returns false before the first read. + * + * Moral: eof() is only useful in combination with fail(), to find out + * whether EOF was the cause for failure + */ + bool eof() const { + return m_iostate & eofbit; + } + /** \return true if any iostate bit other than eof are set else false. */ + bool fail() const { + return m_iostate & (failbit | badbit); + } + /** \return true if bad bit is set else false. */ + bool bad() const { + return m_iostate & badbit; + } + /** Clear iostate bits. + * + * \param[in] state The flags you want to set after clearing all flags. + **/ + void clear(iostate state = goodbit) { + m_iostate = state; + } + /** Set iostate bits. + * + * \param[in] state Bitts to set. + **/ + void setstate(iostate state) { + m_iostate |= state; + } + + private: + iostate m_iostate; +}; +#endif // ios_h diff --git a/lib/SdFat/utility/iostream.h b/lib/SdFat/utility/iostream.h new file mode 100644 index 0000000..deeae6e --- /dev/null +++ b/lib/SdFat/utility/iostream.h @@ -0,0 +1,153 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef iostream_h +#define iostream_h +/** + * \file + * \brief \ref iostream class + */ +#include "istream.h" +#include "ostream.h" +/** Skip white space + * \param[in] is the Stream + * \return The stream + */ +inline istream& ws(istream& is) { + is.skipWhite(); + return is; +} +/** insert endline + * \param[in] os The Stream + * \return The stream + */ +inline ostream& endl(ostream& os) { + os.put('\n'); +#if ENDL_CALLS_FLUSH + os.flush(); +#endif // ENDL_CALLS_FLUSH + return os; +} +/** flush manipulator + * \param[in] os The stream + * \return The stream + */ +inline ostream& flush(ostream& os) { + os.flush(); + return os; +} +/** + * \struct setfill + * \brief type for setfill manipulator + */ +struct setfill { + /** fill character */ + char c; + /** constructor + * + * \param[in] arg new fill character + */ + explicit setfill(char arg) : c(arg) {} +}; +/** setfill manipulator + * \param[in] os the stream + * \param[in] arg set setfill object + * \return the stream + */ +inline ostream &operator<< (ostream &os, const setfill &arg) { + os.fill(arg.c); + return os; +} +/** setfill manipulator + * \param[in] obj the stream + * \param[in] arg set setfill object + * \return the stream + */ +inline istream &operator>>(istream &obj, const setfill &arg) { + obj.fill(arg.c); + return obj; +} +//------------------------------------------------------------------------------ +/** \struct setprecision + * \brief type for setprecision manipulator + */ +struct setprecision { + /** precision */ + unsigned int p; + /** constructor + * \param[in] arg new precision + */ + explicit setprecision(unsigned int arg) : p(arg) {} +}; +/** setprecision manipulator + * \param[in] os the stream + * \param[in] arg set setprecision object + * \return the stream + */ +inline ostream &operator<< (ostream &os, const setprecision &arg) { + os.precision(arg.p); + return os; +} +/** setprecision manipulator + * \param[in] is the stream + * \param[in] arg set setprecision object + * \return the stream + */ +inline istream &operator>>(istream &is, const setprecision &arg) { + is.precision(arg.p); + return is; +} +//------------------------------------------------------------------------------ +/** \struct setw + * \brief type for setw manipulator + */ +struct setw { + /** width */ + unsigned w; + /** constructor + * \param[in] arg new width + */ + explicit setw(unsigned arg) : w(arg) {} +}; +/** setw manipulator + * \param[in] os the stream + * \param[in] arg set setw object + * \return the stream + */ +inline ostream &operator<< (ostream &os, const setw &arg) { + os.width(arg.w); + return os; +} +/** setw manipulator + * \param[in] is the stream + * \param[in] arg set setw object + * \return the stream + */ +inline istream &operator>>(istream &is, const setw &arg) { + is.width(arg.w); + return is; +} +//============================================================================== +/** + * \class iostream + * \brief Input/Output stream + */ +class iostream : public istream, public ostream { +}; +#endif // iostream_h diff --git a/lib/SdFat/utility/istream.cpp b/lib/SdFat/utility/istream.cpp new file mode 100644 index 0000000..6a4e9f3 --- /dev/null +++ b/lib/SdFat/utility/istream.cpp @@ -0,0 +1,381 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +// #include +#include +#include +#include "istream.h" +//------------------------------------------------------------------------------ +int istream::get() { + int c; + m_gcount = 0; + c = getch(); + if (c < 0) { + setstate(failbit); + } else { + m_gcount = 1; + } + return c; +} +//------------------------------------------------------------------------------ +istream& istream::get(char& c) { + int tmp = get(); + if (tmp >= 0) { + c = tmp; + } + return *this; +} +//------------------------------------------------------------------------------ +istream& istream::get(char *str, streamsize n, char delim) { + int c; + FatPos_t pos; + m_gcount = 0; + while ((m_gcount + 1) < n) { + c = getch(&pos); + if (c < 0) { + break; + } + if (c == delim) { + setpos(&pos); + break; + } + str[m_gcount++] = c; + } + if (n > 0) { + str[m_gcount] = '\0'; + } + if (m_gcount == 0) { + setstate(failbit); + } + return *this; +} +//------------------------------------------------------------------------------ +void istream::getBool(bool *b) { + if ((flags() & boolalpha) == 0) { + getNumber(b); + return; + } + PGM_P truePtr = PSTR("true"); + PGM_P falsePtr = PSTR("false"); + const uint8_t true_len = 4; + const uint8_t false_len = 5; + bool trueOk = true; + bool falseOk = true; + uint8_t i = 0; + int c = readSkip(); + while (1) { + falseOk = falseOk && c == pgm_read_byte(falsePtr + i); + trueOk = trueOk && c == pgm_read_byte(truePtr + i); + if (trueOk == false && falseOk == false) { + break; + } + i++; + if (trueOk && i == true_len) { + *b = true; + return; + } + if (falseOk && i == false_len) { + *b = false; + return; + } + c = getch(); + } + setstate(failbit); +} +//------------------------------------------------------------------------------ +void istream::getChar(char* ch) { + int16_t c = readSkip(); + if (c < 0) { + setstate(failbit); + } else { + *ch = c; + } +} +//------------------------------------------------------------------------------ +// +// http://www.exploringbinary.com/category/numbers-in-computers/ +// +int16_t const EXP_LIMIT = 100; +static const uint32_t uint32_max = (uint32_t)-1; +bool istream::getDouble(double* value) { + bool got_digit = false; + bool got_dot = false; + bool neg; + int16_t c; + bool expNeg = false; + int16_t exp = 0; + int16_t fracExp = 0; + uint32_t frac = 0; + FatPos_t endPos; + double pow10; + double v; + + getpos(&endPos); + c = readSkip(); + neg = c == '-'; + if (c == '-' || c == '+') { + c = getch(); + } + while (1) { + if (isdigit(c)) { + got_digit = true; + if (frac < uint32_max/10) { + frac = frac * 10 + (c - '0'); + if (got_dot) { + fracExp--; + } + } else { + if (!got_dot) { + fracExp++; + } + } + } else if (!got_dot && c == '.') { + got_dot = true; + } else { + break; + } + if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) { + goto fail; + } + c = getch(&endPos); + } + if (!got_digit) { + goto fail; + } + if (c == 'e' || c == 'E') { + c = getch(); + expNeg = c == '-'; + if (c == '-' || c == '+') { + c = getch(); + } + while (isdigit(c)) { + if (exp > EXP_LIMIT) { + goto fail; + } + exp = exp * 10 + (c - '0'); + c = getch(&endPos); + } + } + v = static_cast(frac); + exp = expNeg ? fracExp - exp : fracExp + exp; + expNeg = exp < 0; + if (expNeg) { + exp = -exp; + } + pow10 = 10.0; + while (exp) { + if (exp & 1) { + if (expNeg) { + // check for underflow + if (v < FLT_MIN * pow10 && frac != 0) { + goto fail; + } + v /= pow10; + } else { + // check for overflow + if (v > FLT_MAX / pow10) { + goto fail; + } + v *= pow10; + } + } + pow10 *= pow10; + exp >>= 1; + } + setpos(&endPos); + *value = neg ? -v : v; + return true; + +fail: + // error restore position to last good place + setpos(&endPos); + setstate(failbit); + return false; +} +//------------------------------------------------------------------------------ + +istream& istream::getline(char *str, streamsize n, char delim) { + FatPos_t pos; + int c; + m_gcount = 0; + if (n > 0) { + str[0] = '\0'; + } + while (1) { + c = getch(&pos); + if (c < 0) { + break; + } + if (c == delim) { + m_gcount++; + break; + } + if ((m_gcount + 1) >= n) { + setpos(&pos); + setstate(failbit); + break; + } + str[m_gcount++] = c; + str[m_gcount] = '\0'; + } + if (m_gcount == 0) { + setstate(failbit); + } + return *this; +} +//------------------------------------------------------------------------------ +bool istream::getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num) { + int16_t c; + int8_t any = 0; + int8_t have_zero = 0; + uint8_t neg; + uint32_t val = 0; + uint32_t cutoff; + uint8_t cutlim; + FatPos_t endPos; + uint8_t f = flags() & basefield; + uint8_t base = f == oct ? 8 : f != hex ? 10 : 16; + getpos(&endPos); + c = readSkip(); + + neg = c == '-' ? 1 : 0; + if (c == '-' || c == '+') { + c = getch(); + } + + if (base == 16 && c == '0') { // TESTSUITE + c = getch(&endPos); + if (c == 'X' || c == 'x') { + c = getch(); + // remember zero in case no hex digits follow x/X + have_zero = 1; + } else { + any = 1; + } + } + // set values for overflow test + cutoff = neg ? negMax : posMax; + cutlim = cutoff % base; + cutoff /= base; + + while (1) { + if (isdigit(c)) { + c -= '0'; + } else if (isalpha(c)) { + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + } else { + break; + } + if (c >= base) { + break; + } + if (val > cutoff || (val == cutoff && c > cutlim)) { + // indicate overflow error + any = -1; + break; + } + val = val * base + c; + c = getch(&endPos); + any = 1; + } + setpos(&endPos); + if (any > 0 || (have_zero && any >= 0)) { + *num = neg ? -val : val; + return true; + } + setstate(failbit); + return false; +} +//------------------------------------------------------------------------------ +void istream::getStr(char *str) { + FatPos_t pos; + uint16_t i = 0; + uint16_t m = width() ? width() - 1 : 0XFFFE; + if (m != 0) { + getpos(&pos); + int c = readSkip(); + + while (i < m) { + if (c < 0) { + break; + } + if (isspace(c)) { + setpos(&pos); + break; + } + str[i++] = c; + c = getch(&pos); + } + } + str[i] = '\0'; + if (i == 0) { + setstate(failbit); + } + width(0); +} +//------------------------------------------------------------------------------ +istream& istream::ignore(streamsize n, int delim) { + int c; + m_gcount = 0; + while (m_gcount < n) { + c = getch(); + if (c < 0) { + break; + } + m_gcount++; + if (c == delim) { + break; + } + } + return *this; +} +//------------------------------------------------------------------------------ +int istream::peek() { + int16_t c; + FatPos_t pos; + m_gcount = 0; + getpos(&pos); + c = getch(); + if (c < 0) { + if (!bad()) { + setstate(eofbit); + } + } else { + setpos(&pos); + } + return c; +} +//------------------------------------------------------------------------------ +int16_t istream::readSkip() { + int16_t c; + do { + c = getch(); + } while (isspace(c) && (flags() & skipws)); + return c; +} +//------------------------------------------------------------------------------ +/** used to implement ws() */ +void istream::skipWhite() { + int c; + FatPos_t pos; + do { + c = getch(&pos); + } while (isspace(c)); + setpos(&pos); +} diff --git a/lib/SdFat/utility/istream.h b/lib/SdFat/utility/istream.h new file mode 100644 index 0000000..4075507 --- /dev/null +++ b/lib/SdFat/utility/istream.h @@ -0,0 +1,379 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef istream_h +#define istream_h +/** + * \file + * \brief \ref istream class + */ +#include "ios.h" + +/** + * \class istream + * \brief Input Stream + */ +class istream : public virtual ios { + public: + istream() {} + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + istream& operator>>(istream& (*pf)(istream& str)) { + return pf(*this); + } + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + istream& operator>>(ios_base& (*pf)(ios_base& str)) { + pf(*this); + return *this; + } + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + istream& operator>>(ios& (*pf)(ios& str)) { + pf(*this); + return *this; + } + /** + * Extract a character string + * \param[out] str location to store the string. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(char *str) { + getStr(str); + return *this; + } + /** + * Extract a character + * \param[out] ch location to store the character. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(char& ch) { + getChar(&ch); + return *this; + } + /** + * Extract a character string + * \param[out] str location to store the string. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(signed char *str) { + getStr(reinterpret_cast(str)); + return *this; + } + /** + * Extract a character + * \param[out] ch location to store the character. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(signed char& ch) { + getChar(reinterpret_cast(&ch)); + return *this; + } + /** + * Extract a character string + * \param[out] str location to store the string. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(unsigned char *str) { + getStr(reinterpret_cast(str)); + return *this; + } + /** + * Extract a character + * \param[out] ch location to store the character. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(unsigned char& ch) { + getChar(reinterpret_cast(&ch)); + return *this; + } + /** + * Extract a value of type bool. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(bool& arg) { + getBool(&arg); + return *this; + } + /** + * Extract a value of type short. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(short& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type unsigned short. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(unsigned short& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type int. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(int& arg) { + getNumber(&arg); + return *this; + } + /** + * Extract a value of type unsigned int. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(unsigned int& arg) { + getNumber(&arg); + return *this; + } + /** + * Extract a value of type long. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(long& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type unsigned long. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(unsigned long& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type double. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>> (double& arg) { + getDouble(&arg); + return *this; + } + /** + * Extract a value of type float. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>> (float& arg) { + double v; + getDouble(&v); + arg = v; + return *this; + } + /** + * Extract a value of type void*. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>> (void*& arg) { + uint32_t val; + getNumber(&val); + arg = reinterpret_cast(val); + return *this; + } + /** + * \return The number of characters extracted by the last unformatted + * input function. + */ + streamsize gcount() const { + return m_gcount; + } + /** + * Extract a character if one is available. + * + * \return The character or -1 if a failure occurs. A failure is indicated + * by the stream state. + */ + int get(); + /** + * Extract a character if one is available. + * + * \param[out] ch location to receive the extracted character. + * + * \return always returns *this. A failure is indicated by the stream state. + */ + istream& get(char& ch); + /** + * Extract characters. + * + * \param[out] str Location to receive extracted characters. + * \param[in] n Size of str. + * \param[in] delim Delimiter + * + * Characters are extracted until extraction fails, n is less than 1, + * n-1 characters are extracted, or the next character equals + * \a delim (delim is not extracted). If no characters are extracted + * failbit is set. If end-of-file occurs the eofbit is set. + * + * \return always returns *this. A failure is indicated by the stream state. + */ + istream& get(char *str, streamsize n, char delim = '\n'); + /** + * Extract characters + * + * \param[out] str Location to receive extracted characters. + * \param[in] n Size of str. + * \param[in] delim Delimiter + * + * Characters are extracted until extraction fails, + * the next character equals \a delim (delim is extracted), or n-1 + * characters are extracted. + * + * The failbit is set if no characters are extracted or n-1 characters + * are extracted. If end-of-file occurs the eofbit is set. + * + * \return always returns *this. A failure is indicated by the stream state. + */ + istream& getline(char *str, streamsize n, char delim = '\n'); + /** + * Extract characters and discard them. + * + * \param[in] n maximum number of characters to ignore. + * \param[in] delim Delimiter. + * + * Characters are extracted until extraction fails, \a n characters + * are extracted, or the next input character equals \a delim + * (the delimiter is extracted). If end-of-file occurs the eofbit is set. + * + * Failures are indicated by the state of the stream. + * + * \return *this + * + */ + istream& ignore(streamsize n = 1, int delim = -1); + /** + * Return the next available character without consuming it. + * + * \return The character if the stream state is good else -1; + * + */ + int peek(); +// istream& read(char *str, streamsize count); +// streamsize readsome(char *str, streamsize count); + /** + * \return the stream position + */ + pos_type tellg() { + return tellpos(); + } + /** + * Set the stream position + * \param[in] pos The absolute position in which to move the read pointer. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& seekg(pos_type pos) { + if (!seekpos(pos)) { + setstate(failbit); + } + return *this; + } + /** + * Set the stream position. + * + * \param[in] off An offset to move the read pointer relative to way. + * \a off is a signed 32-bit int so the offset is limited to +- 2GB. + * \param[in] way One of ios::beg, ios::cur, or ios::end. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& seekg(off_type off, seekdir way) { + if (!seekoff(off, way)) { + setstate(failbit); + } + return *this; + } + void skipWhite(); + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal - do not use + * \return + */ + virtual int16_t getch() = 0; + /** + * Internal - do not use + * \param[out] pos + * \return + */ + int16_t getch(FatPos_t* pos) { + getpos(pos); + return getch(); + } + /** + * Internal - do not use + * \param[out] pos + */ + virtual void getpos(FatPos_t* pos) = 0; + /** + * Internal - do not use + * \param[in] pos + */ + virtual bool seekoff(off_type off, seekdir way) = 0; + virtual bool seekpos(pos_type pos) = 0; + virtual void setpos(FatPos_t* pos) = 0; + virtual pos_type tellpos() = 0; + + /// @endcond + private: + void getBool(bool *b); + void getChar(char* ch); + bool getDouble(double* value); + template void getNumber(T* value); + bool getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num); + void getStr(char *str); + int16_t readSkip(); + + size_t m_gcount; +}; +//------------------------------------------------------------------------------ +template +void istream::getNumber(T* value) { + uint32_t tmp; + if ((T)-1 < 0) { + // number is signed, max positive value + uint32_t const m = ((uint32_t)-1) >> (33 - sizeof(T) * 8); + // max absolute value of negative number is m + 1. + if (getNumber(m, m + 1, &tmp)) { + *value = (T)tmp; + } + } else { + // max unsigned value for T + uint32_t const m = (T)-1; + if (getNumber(m, m, &tmp)) { + *value = (T)tmp; + } + } +} +#endif // istream_h diff --git a/lib/SdFat/utility/ostream.cpp b/lib/SdFat/utility/ostream.cpp new file mode 100644 index 0000000..230cda4 --- /dev/null +++ b/lib/SdFat/utility/ostream.cpp @@ -0,0 +1,191 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#include +#include "ostream.h" +#ifndef PSTR +#define PSTR(x) x +#endif +//------------------------------------------------------------------------------ +void ostream::do_fill(unsigned len) { + for (; len < width(); len++) { + putch(fill()); + } + width(0); +} +//------------------------------------------------------------------------------ +void ostream::fill_not_left(unsigned len) { + if ((flags() & adjustfield) != left) { + do_fill(len); + } +} +//------------------------------------------------------------------------------ +char* ostream::fmtNum(uint32_t n, char *ptr, uint8_t base) { + char a = flags() & uppercase ? 'A' - 10 : 'a' - 10; + do { + uint32_t m = n; + n /= base; + char c = m - base * n; + *--ptr = c < 10 ? c + '0' : c + a; + } while (n); + return ptr; +} +//------------------------------------------------------------------------------ +void ostream::putBool(bool b) { + if (flags() & boolalpha) { + if (b) { + putPgm(PSTR("true")); + } else { + putPgm(PSTR("false")); + } + } else { + putChar(b ? '1' : '0'); + } +} +//------------------------------------------------------------------------------ +void ostream::putChar(char c) { + fill_not_left(1); + putch(c); + do_fill(1); +} +//------------------------------------------------------------------------------ +void ostream::putDouble(double n) { + uint8_t nd = precision(); + double round = 0.5; + char sign; + char buf[13]; // room for sign, 10 digits, '.', and zero byte + char *end = buf + sizeof(buf) - 1; + char *str = end; + // terminate string + *end = '\0'; + + // get sign and make nonnegative + if (n < 0.0) { + sign = '-'; + n = -n; + } else { + sign = flags() & showpos ? '+' : '\0'; + } + // check for larger than uint32_t + if (n > 4.0E9) { + putPgm(PSTR("BIG FLT")); + return; + } + // round up and separate int and fraction parts + for (uint8_t i = 0; i < nd; ++i) { + round *= 0.1; + } + n += round; + uint32_t intPart = n; + double fractionPart = n - intPart; + + // format intPart and decimal point + if (nd || (flags() & showpoint)) { + *--str = '.'; + } + str = fmtNum(intPart, str, 10); + + // calculate length for fill + uint8_t len = sign ? 1 : 0; + len += nd + end - str; + + // extract adjust field + fmtflags adj = flags() & adjustfield; + if (adj == internal) { + if (sign) { + putch(sign); + } + do_fill(len); + } else { + // do fill for internal or right + fill_not_left(len); + if (sign) { + *--str = sign; + } + } + putstr(str); + // output fraction + while (nd-- > 0) { + fractionPart *= 10.0; + int digit = static_cast(fractionPart); + putch(digit + '0'); + fractionPart -= digit; + } + // do fill if not done above + do_fill(len); +} +//------------------------------------------------------------------------------ +void ostream::putNum(int32_t n) { + bool neg = n < 0 && flagsToBase() == 10; + if (neg) { + n = -n; + } + putNum(n, neg); +} +//------------------------------------------------------------------------------ +void ostream::putNum(uint32_t n, bool neg) { + char buf[13]; + char* end = buf + sizeof(buf) - 1; + char* num; + char* str; + uint8_t base = flagsToBase(); + *end = '\0'; + str = num = fmtNum(n, end, base); + if (base == 10) { + if (neg) { + *--str = '-'; + } else if (flags() & showpos) { + *--str = '+'; + } + } else if (flags() & showbase) { + if (flags() & hex) { + *--str = flags() & uppercase ? 'X' : 'x'; + } + *--str = '0'; + } + uint8_t len = end - str; + fmtflags adj = flags() & adjustfield; + if (adj == internal) { + while (str < num) { + putch(*str++); + } + } + if (adj != left) { + do_fill(len); + } + putstr(str); + do_fill(len); +} +//------------------------------------------------------------------------------ +void ostream::putPgm(const char* str) { + int n; + for (n = 0; pgm_read_byte(&str[n]); n++) {} + fill_not_left(n); + for (uint8_t c; (c = pgm_read_byte(str)); str++) { + putch(c); + } + do_fill(n); +} +//------------------------------------------------------------------------------ +void ostream::putStr(const char *str) { + unsigned n = strlen(str); + fill_not_left(n); + putstr(str); + do_fill(n); +} diff --git a/lib/SdFat/utility/ostream.h b/lib/SdFat/utility/ostream.h new file mode 100644 index 0000000..3a232da --- /dev/null +++ b/lib/SdFat/utility/ostream.h @@ -0,0 +1,295 @@ +/* FatLib Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the FatLib Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the FatLib Library. If not, see + * . + */ +#ifndef ostream_h +#define ostream_h +/** + * \file + * \brief \ref ostream class + */ +#include "ios.h" +//------------------------------------------------------------------------------ +/** macro for flash inserter */ +#define pstr(str) pgm(PSTR(str)) +/** \struct pgm + * \brief type for string in flash + */ +struct pgm { + /** Pointer to flash string */ + char *ptr; + /** constructor + * \param[in] str initializer for pointer. + */ + explicit pgm(char* str) : ptr(str) {} + /** constructor + * \param[in] str initializer for pointer. + */ + explicit pgm(const char *str) : ptr(const_cast(str)) {} +}; +//============================================================================== +/** + * \class ostream + * \brief Output Stream + */ +class ostream : public virtual ios { + public: + ostream() {} + + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + ostream& operator<< (ostream& (*pf)(ostream& str)) { + return pf(*this); + } + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + ostream& operator<< (ios_base& (*pf)(ios_base& str)) { + pf(*this); + return *this; + } + /** Output bool + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (bool arg) { + putBool(arg); + return *this; + } + /** Output string + * \param[in] arg string to output + * \return the stream + */ + ostream &operator<< (const char *arg) { + putStr(arg); + return *this; + } + /** Output string + * \param[in] arg string to output + * \return the stream + */ + ostream &operator<< (const signed char *arg) { + putStr((const char*)arg); + return *this; + } + /** Output string + * \param[in] arg string to output + * \return the stream + */ + ostream &operator<< (const unsigned char *arg) { + putStr((const char*)arg); + return *this; + } + /** Output character + * \param[in] arg character to output + * \return the stream + */ + ostream &operator<< (char arg) { + putChar(arg); + return *this; + } + /** Output character + * \param[in] arg character to output + * \return the stream + */ + ostream &operator<< (signed char arg) { + putChar(static_cast(arg)); + return *this; + } + /** Output character + * \param[in] arg character to output + * \return the stream + */ + ostream &operator<< (unsigned char arg) { + putChar(static_cast(arg)); + return *this; + } + /** Output double + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (double arg) { + putDouble(arg); + return *this; + } + /** Output float + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (float arg) { + putDouble(arg); + return *this; + } + /** Output signed short + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (short arg) { // NOLINT + putNum((int32_t)arg); + return *this; + } + /** Output unsigned short + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned short arg) { // NOLINT + putNum((uint32_t)arg); + return *this; + } + /** Output signed int + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (int arg) { + putNum((int32_t)arg); + return *this; + } + /** Output unsigned int + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned int arg) { + putNum((uint32_t)arg); + return *this; + } + /** Output signed long + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (long arg) { // NOLINT + putNum((int32_t)arg); + return *this; + } + /** Output unsigned long + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned long arg) { // NOLINT + putNum((uint32_t)arg); + return *this; + } + /** Output pointer + * \param[in] arg value to output + * \return the stream + */ + ostream& operator<< (const void* arg) { + putNum(reinterpret_cast(arg)); + return *this; + } + /** Output a string from flash using the pstr() macro + * \param[in] arg pgm struct pointing to string + * \return the stream + */ + ostream &operator<< (pgm arg) { + putPgm(arg.ptr); + return *this; + } + /** Output a string from flash using the Arduino F() macro. + * \param[in] arg pointing to flash string + * \return the stream + */ + ostream &operator<< (const __FlashStringHelper *arg) { + putPgm(reinterpret_cast(arg)); + return *this; + } + /** + * Puts a character in a stream. + * + * The unformatted output function inserts the element \a ch. + * It returns *this. + * + * \param[in] ch The character + * \return A reference to the ostream object. + */ + ostream& put(char ch) { + putch(ch); + return *this; + } +// ostream& write(char *str, streamsize count); + /** + * Flushes the buffer associated with this stream. The flush function + * calls the sync function of the associated file. + * \return A reference to the ostream object. + */ + ostream& flush() { + if (!sync()) { + setstate(badbit); + } + return *this; + } + /** + * \return the stream position + */ + pos_type tellp() { + return tellpos(); + } + /** + * Set the stream position + * \param[in] pos The absolute position in which to move the write pointer. + * \return Is always *this. Failure is indicated by the state of *this. + */ + ostream& seekp(pos_type pos) { + if (!seekpos(pos)) { + setstate(failbit); + } + return *this; + } + /** + * Set the stream position. + * + * \param[in] off An offset to move the write pointer relative to way. + * \a off is a signed 32-bit int so the offset is limited to +- 2GB. + * \param[in] way One of ios::beg, ios::cur, or ios::end. + * \return Is always *this. Failure is indicated by the state of *this. + */ + ostream& seekp(off_type off, seekdir way) { + if (!seekoff(off, way)) { + setstate(failbit); + } + return *this; + } + + protected: + /// @cond SHOW_PROTECTED + /** Put character with binary/text conversion + * \param[in] ch character to write + */ + virtual void putch(char ch) = 0; + virtual void putstr(const char *str) = 0; + virtual bool seekoff(off_type pos, seekdir way) = 0; + virtual bool seekpos(pos_type pos) = 0; + virtual bool sync() = 0; + + virtual pos_type tellpos() = 0; + /// @endcond + private: + void do_fill(unsigned len); + void fill_not_left(unsigned len); + char* fmtNum(uint32_t n, char *ptr, uint8_t base); + void putBool(bool b); + void putChar(char c); + void putDouble(double n); + void putNum(uint32_t n, bool neg = false); + void putNum(int32_t n); + void putPgm(const char* str); + void putStr(const char* str); +}; +#endif // ostream_h diff --git a/lib/TouchScreen/TouchScreen.cpp b/lib/TouchScreen/TouchScreen.cpp new file mode 100644 index 0000000..2c33921 --- /dev/null +++ b/lib/TouchScreen/TouchScreen.cpp @@ -0,0 +1,248 @@ +// Touch screen library with X Y and Z (pressure) readings as well +// as oversampling to avoid 'bouncing' +// (c) ladyada / adafruit +// Code under MIT License + +#include "pins_arduino.h" +#include "wiring_private.h" +#include +#include "TouchScreen.h" + +// increase or decrease the touchscreen oversampling. This is a little different than you make think: +// 1 is no oversampling, whatever data we get is immediately returned +// 2 is double-sampling and we only return valid data if both points are the same +// 3+ uses insert sort to get the median value. +// We found 2 is precise yet not too slow so we suggest sticking with it! + +#define NUMSAMPLES 7 + +Point::Point(void) { + x = y = 0; +} + +Point::Point(int16_t x0, int16_t y0, int16_t z0) { + x = x0; + y = y0; + z = z0; +} + +bool Point::operator==(Point p1) { + return ((p1.x == x) && (p1.y == y) && (p1.z == z)); +} + +bool Point::operator!=(Point p1) { + return ((p1.x != x) || (p1.y != y) || (p1.z != z)); +} + +#if (NUMSAMPLES > 2) +static void insert_sort(int array[], uint8_t size) { + uint8_t j; + int save; + + for (int i = 1; i < size; i++) { + save = array[i]; + for (j = i; j >= 1 && save < array[j - 1]; j--) + array[j] = array[j - 1]; + array[j] = save; + } +} +#endif + +Point TouchScreen::getPoint(void) { + int x, y, z; + int samples[NUMSAMPLES]; + uint8_t i, valid; + + + uint8_t xp_port = digitalPinToPort(_xp); + uint8_t yp_port = digitalPinToPort(_yp); + uint8_t xm_port = digitalPinToPort(_xm); + uint8_t ym_port = digitalPinToPort(_ym); + + uint8_t xp_pin = digitalPinToBitMask(_xp); + uint8_t yp_pin = digitalPinToBitMask(_yp); + uint8_t xm_pin = digitalPinToBitMask(_xm); + uint8_t ym_pin = digitalPinToBitMask(_ym); + + + valid = 1; + + pinMode(_yp, INPUT); + pinMode(_ym, INPUT); + + *portOutputRegister(yp_port) &= ~yp_pin; + *portOutputRegister(ym_port) &= ~ym_pin; + //digitalWrite(_yp, LOW); + //digitalWrite(_ym, LOW); + + pinMode(_xp, OUTPUT); + pinMode(_xm, OUTPUT); + //digitalWrite(_xp, HIGH); + //digitalWrite(_xm, LOW); + *portOutputRegister(xp_port) |= xp_pin; + *portOutputRegister(xm_port) &= ~xm_pin; + + delay(10); + + for (i=0; i 2 + insert_sort(samples, NUMSAMPLES); +#endif +#if NUMSAMPLES == 2 + if (samples[0] != samples[1]) { valid = 0; } +#endif + x = (1023-samples[NUMSAMPLES/2]); + + pinMode(_xp, INPUT); + pinMode(_xm, INPUT); + *portOutputRegister(xp_port) &= ~xp_pin; + //digitalWrite(_xp, LOW); + + pinMode(_yp, OUTPUT); + *portOutputRegister(yp_port) |= yp_pin; + //digitalWrite(_yp, HIGH); + pinMode(_ym, OUTPUT); + + delay(10); + + for (i=0; i 2 + insert_sort(samples, NUMSAMPLES); +#endif +#if NUMSAMPLES == 2 + if (samples[0] != samples[1]) { valid = 0; } +#endif + + y = (1023-samples[NUMSAMPLES/2]); + + // Set X+ to ground + pinMode(_xp, OUTPUT); + *portOutputRegister(xp_port) &= ~xp_pin; + //digitalWrite(_xp, LOW); + + // Set Y- to VCC + *portOutputRegister(ym_port) |= ym_pin; + //digitalWrite(_ym, HIGH); + + // Hi-Z X- and Y+ + *portOutputRegister(yp_port) &= ~yp_pin; + //digitalWrite(_yp, LOW); + pinMode(_yp, INPUT); + + + delay(10); + + int z1 = analogRead(_xm); + int z2 = analogRead(_yp); + + if (_rxplate != 0) { + // now read the x + float rtouch; + rtouch = z2; + rtouch /= z1; + rtouch -= 1; + rtouch *= x; + rtouch *= _rxplate; + rtouch /= 1024; + + z = 1023-rtouch; + } else { + z = (1023-(z2-z1)); + } + + if (! valid) { + z = 0; + } + + return Point(x, y, z); +} + +TouchScreen::TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym) { + _yp = yp; + _xm = xm; + _ym = ym; + _xp = xp; + _rxplate = 0; + pressureThreshhold = 10; +} + + +TouchScreen::TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym, + uint16_t rxplate) { + _yp = yp; + _xm = xm; + _ym = ym; + _xp = xp; + _rxplate = rxplate; + + pressureThreshhold = 10; +} + +int TouchScreen::readTouchX(void) { + pinMode(_yp, INPUT); + pinMode(_ym, INPUT); + digitalWrite(_yp, LOW); + digitalWrite(_ym, LOW); + + pinMode(_xp, OUTPUT); + digitalWrite(_xp, HIGH); + pinMode(_xm, OUTPUT); + digitalWrite(_xm, LOW); + + return (1023-analogRead(_yp)); +} + + +int TouchScreen::readTouchY(void) { + pinMode(_xp, INPUT); + pinMode(_xm, INPUT); + digitalWrite(_xp, LOW); + digitalWrite(_xm, LOW); + + pinMode(_yp, OUTPUT); + digitalWrite(_yp, HIGH); + pinMode(_ym, OUTPUT); + digitalWrite(_ym, LOW); + + return (1023-analogRead(_xm)); +} + + +uint16_t TouchScreen::pressure(void) { + // Set X+ to ground + pinMode(_xp, OUTPUT); + digitalWrite(_xp, LOW); + + // Set Y- to VCC + pinMode(_ym, OUTPUT); + digitalWrite(_ym, HIGH); + + // Hi-Z X- and Y+ + digitalWrite(_xm, LOW); + pinMode(_xm, INPUT); + digitalWrite(_yp, LOW); + pinMode(_yp, INPUT); + + int z1 = analogRead(_xm); + int z2 = analogRead(_yp); + + if (_rxplate != 0) { + // now read the x + float rtouch; + rtouch = z2; + rtouch /= z1; + rtouch -= 1; + rtouch *= readTouchX(); + rtouch *= _rxplate; + rtouch /= 1024; + + return rtouch; + } else { + return (1023-(z2-z1)); + } +} diff --git a/lib/TouchScreen/TouchScreen.h b/lib/TouchScreen/TouchScreen.h new file mode 100644 index 0000000..dde8c97 --- /dev/null +++ b/lib/TouchScreen/TouchScreen.h @@ -0,0 +1,39 @@ +// Touch screen library with X Y and Z (pressure) readings as well +// as oversampling to avoid 'bouncing' +// (c) ladyada / adafruit +// Code under MIT License + +#ifndef _ADAFRUIT_TOUCHSCREEN_H_ +#define _ADAFRUIT_TOUCHSCREEN_H_ + +#include + +class Point { + public: + Point(void); + Point(int16_t x, int16_t y, int16_t z); + + bool operator==(Point); + bool operator!=(Point); + + int16_t x, y, z; +}; + +class TouchScreen { + public: + TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym); + TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym, uint16_t rx); + + bool isTouching(void); + uint16_t pressure(void); + int readTouchY(); + int readTouchX(); + Point getPoint(); + int16_t pressureThreshhold; + +private: + uint8_t _yp, _ym, _xm, _xp; + uint16_t _rxplate; +}; + +#endif diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..92651f0 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,25 @@ +# +# Project Configuration File +# +# A detailed documentation with the EXAMPLES is located here: +# http://docs.platformio.org/en/latest/projectconf.html +# + +# A sign `#` at the beginning of the line indicates a comment +# Comment lines are ignored. + +# Simple and base environment +# [env:mybaseenv] +# platform = %INSTALLED_PLATFORM_NAME_HERE% +# framework = +# board = +# +# Automatic targets - enable auto-uploading +# targets = upload + +[env:uno] +platform = atmelavr +framework = arduino +board = megaatmega2560 +#targets = upload + diff --git a/src/GfxArrowButton.cpp b/src/GfxArrowButton.cpp new file mode 100644 index 0000000..9a8b5b8 --- /dev/null +++ b/src/GfxArrowButton.cpp @@ -0,0 +1,46 @@ + +#include "GfxArrowButton.h" + + +// Constructor +GfxArrowButton::GfxArrowButton() { + + x1 = 0,y1 = 0,x2 = 0,y2 = 0,x3 = 0,y3 = 0; +} + +void GfxArrowButton::create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, byte _dir, unsigned int _fgColour, unsigned int _bgColour) { + + GfxButton::create(_tft,_x,_y,_w,_h,_fgColour,_bgColour); + + if (_dir == GFXARROW_DOWN) + { + x1 = x; + y1 = y; + x2 = x+w; + y2 = y; + x3 = x+(w/2); + y3 = y+h; + } + + if (_dir == GFXARROW_UP) + { + x1 = x; + y1 = y+h; + x2 = x+(w/2); + y2 = y; + x3 = x+w; + y3 = y+h; + } +} + +void GfxArrowButton::draw() +{ + tft->fillTriangle(x1, y1, x2 , y2, x3 ,y3, fgColour); + tft->drawTriangle(x1, y1, x2 , y2, x3 ,y3, bgColour); +} + + +void GfxArrowButton::drawPressed() +{ + tft->fillTriangle(x1, y1, x2 , y2, x3 ,y3, bgColour); +} diff --git a/src/GfxArrowButton.h b/src/GfxArrowButton.h new file mode 100644 index 0000000..ca03af5 --- /dev/null +++ b/src/GfxArrowButton.h @@ -0,0 +1,27 @@ + +#ifndef _GFXARROWBUTTON_H_ +#define _GFXARROWBUTTON_H_ + +#include "GfxButton.h" + +#define GFXARROW_UP 0 +#define GFXARROW_DOWN 1 +#define GFXARROW_LEFT 2 +#define GFXARROW_RIGHT 3 + +class GfxArrowButton : public GfxButton { + + +public: + + GfxArrowButton(); + virtual ~GfxArrowButton() {}; + void create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, byte _dir, unsigned int _fgColour, unsigned int _bgColour); + void draw(); + void drawPressed(); + +private: + int x1,y1,x2,y2,x3,y3; +}; + +#endif diff --git a/src/GfxButton.cpp b/src/GfxButton.cpp new file mode 100644 index 0000000..475aefe --- /dev/null +++ b/src/GfxButton.cpp @@ -0,0 +1,72 @@ +#include "debug.h" + +#include "GfxButton.h" + + +// Constructor +GfxButton::GfxButton() { + tft = NULL; + x = 0, y = 0, w = 0, h = 0, fgColour = 0, bgColour = 0, armed = false; +} + +void GfxButton::create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, unsigned int _fgColour, unsigned int _bgColour) { + tft = _tft; + x = _x; + y = _y; + w = _w; + h = _h; + fgColour = _fgColour; + bgColour = _bgColour; +} + +void GfxButton::swapColours() +{ + int c = fgColour; + fgColour = bgColour; + bgColour = c; +} + +void GfxButton::draw() +{ + tft->fillRoundRect(x, y, w, h, 4, fgColour); + tft->drawRoundRect(x, y, w, h, 4, bgColour); +} + +void GfxButton::drawPressed() +{ + tft->fillRoundRect(x, y, w, h, 4, bgColour); +} + +bool GfxButton::isPressed(Point p) +{ + // TouchScreen doesn't follow TFT orientation nor dimensions + int p_x = p.x; + p.x = map(p.y, TS_MINY, TS_MAXY, 0, tft->width()); + p.y = map(p_x, TS_MINX, TS_MAXX, tft->height(), 0); + + if (armed && p.z < MINPRESSURE) + { + armed = false; + delay(100); + draw(); + return true; + } + + if (p.z < MINPRESSURE) return false; + + D(debugSerial->printf(F("%d, %d, %d\r\n"),p.x,p.y,p.z);) + if (p.x > x && p.x < x+w && p.y > y && p.y < y+h) + { + if (p.z > MINPRESSURE && p.z < MAXPRESSURE && !armed) + { + armed = true; + drawPressed(); + } + } + else if (armed) + { + armed = false; + draw(); + } + return false; +} diff --git a/src/GfxButton.h b/src/GfxButton.h new file mode 100644 index 0000000..314a2ef --- /dev/null +++ b/src/GfxButton.h @@ -0,0 +1,47 @@ + +#ifndef _GFXBUTTON_H_ +#define _GFXBUTTON_H_ + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "Adafruit_TFTLCD.h" +#include "TouchScreen.h" +#include + +#define MINPRESSURE 400 +#define MAXPRESSURE 1000 +#define TS_MINX 150 +#define TS_MINY 120 +#define TS_MAXX 920 +#define TS_MAXY 940 + +class GfxButton { + + +public: + + GfxButton(); + virtual ~GfxButton() {}; + virtual void create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, unsigned int _fgColour, unsigned int _bgColour); + virtual void draw(); + virtual void drawPressed(); + virtual bool isPressed(Point p); +// virtual void setDebug(SoftwareSerial* _debug); + virtual void swapColours(); + +protected: +// SoftwareSerial* debug; + Adafruit_TFTLCD* tft; + int16_t x; + int16_t y; + int16_t w; + int16_t h; + unsigned int fgColour; + unsigned int bgColour; + bool armed; +}; + +#endif diff --git a/src/GfxDataField.cpp b/src/GfxDataField.cpp new file mode 100644 index 0000000..9536b2a --- /dev/null +++ b/src/GfxDataField.cpp @@ -0,0 +1,42 @@ + +#include "GfxDataField.h" + +// Constructor +GfxDataField::GfxDataField() {} + +void GfxDataField::create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, char _fntsize, char _place, unsigned int _bgColour, unsigned int _fgColour) { + + GfxLabel::create(_tft, _bgColour, _fgColour); + x = _x; + y = _y; + fntsize = _fntsize; + place = _place; + setValue(0); +} + +void GfxDataField::setValue(int value) +{ + prepDrawText(x,y,fntsize); + checkShrinkage((char)tft->print(value)); +} + +void GfxDataField::setValue(float value) +{ + prepDrawText(x,y,fntsize); + checkShrinkage((char)tft->print(value,place)); +} + +void GfxDataField::setUnknown() +{ + prepDrawText(x,y,fntsize); + checkShrinkage((char)tft->print(F("?"))); +} + +void GfxDataField::checkShrinkage(char newLength) +{ + if (newLength < lastLength) + { + tft->fillRect(x+(newLength*fntsize*6), y, (lastLength-newLength)*fntsize*6, fntsize*8, bgColour); + } + lastLength = newLength; +} diff --git a/src/GfxDataField.h b/src/GfxDataField.h new file mode 100644 index 0000000..aae77b1 --- /dev/null +++ b/src/GfxDataField.h @@ -0,0 +1,32 @@ + +#ifndef _GFXDATAFIELD_H_ +#define _GFXDATAFIELD_H_ + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "Adafruit_TFTLCD.h" +#include "GfxLabel.h" + +class GfxDataField : public GfxLabel { + +public: + + GfxDataField(); + void create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, char _fntsize, char _place, unsigned int _bgColour, unsigned int _fgColour); + void setValue(int value); + void setValue(float value); + void setUnknown(); + +private: + unsigned int x; + unsigned int y; + char fntsize; + char place; + char lastLength; + void checkShrinkage(char newLength); +}; + +#endif diff --git a/src/GfxIndicator.cpp b/src/GfxIndicator.cpp new file mode 100644 index 0000000..fd3f3cb --- /dev/null +++ b/src/GfxIndicator.cpp @@ -0,0 +1,32 @@ + +#include "GfxIndicator.h" + +// Constructor +GfxIndicator::GfxIndicator() {} + +void GfxIndicator::create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, byte _fntsize) { + tft = _tft; + x = _x; + y = _y; + w = _w; + h = _h; + fntsize = _fntsize; + state = -1; +} + +void GfxIndicator::setState(byte newstate, unsigned int fgColour, unsigned int bgColour, const __FlashStringHelper *ifsh) +{ + String s = String(ifsh); + if (newstate != state) + { + const char* text = s.c_str(); + tft->fillRect(x, y,w,h, bgColour); + unsigned int x1 = x+(w-(strlen(text)*6*fntsize))/2; + unsigned int y1 = y+(h-8*fntsize)/2; + tft->setCursor(x1,y1); + tft->setTextSize(fntsize); + tft->setTextColor(fgColour, bgColour); + tft->print(text); + state = newstate; + } +} diff --git a/src/GfxIndicator.h b/src/GfxIndicator.h new file mode 100644 index 0000000..311df05 --- /dev/null +++ b/src/GfxIndicator.h @@ -0,0 +1,30 @@ + +#ifndef _GFXINDICATOR_H_ +#define _GFXINDICATOR_H_ + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "Adafruit_TFTLCD.h" + +class GfxIndicator { + +public: + + GfxIndicator(); + void create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, byte _fntsize); + void setState(byte newstate, unsigned int fgColour, unsigned int bgColour, const __FlashStringHelper *ifsh); + +private: + Adafruit_TFTLCD* tft; + unsigned int x; + unsigned int y; + unsigned int w; + unsigned int h; + byte fntsize; + byte state; +}; + +#endif diff --git a/src/GfxLabel.cpp b/src/GfxLabel.cpp new file mode 100644 index 0000000..436c065 --- /dev/null +++ b/src/GfxLabel.cpp @@ -0,0 +1,28 @@ + +#include "GfxLabel.h" + +// Constructor +GfxLabel::GfxLabel() { + tft = NULL; + fgColour = 0, bgColour = 0; +} + +void GfxLabel::create(Adafruit_TFTLCD* _tft,unsigned int _bgColour, unsigned int _fgColour) { + tft = _tft; + bgColour = _bgColour; + fgColour = _fgColour; +} + +void GfxLabel::prepDrawText(unsigned int _x, unsigned int _y, char _fntsize) +{ + tft->setCursor(_x,_y); + tft->setTextSize(_fntsize); + tft->setTextColor(fgColour, bgColour); +} + +void GfxLabel::drawLabel(unsigned int _x, unsigned int _y, char _fntsize, const __FlashStringHelper *ifsh) +{ + prepDrawText(_x,_y,_fntsize); + tft->print(ifsh); +} + diff --git a/src/GfxLabel.h b/src/GfxLabel.h new file mode 100644 index 0000000..73df229 --- /dev/null +++ b/src/GfxLabel.h @@ -0,0 +1,27 @@ +#ifndef _GFXLABEL_H_ +#define _GFXLABEL_H_ + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "Adafruit_TFTLCD.h" + +class GfxLabel { + +public: + + GfxLabel(); + void create(Adafruit_TFTLCD* _tft, unsigned int _bgColour, unsigned int _fgColour); + void drawLabel(unsigned int _x, unsigned int _y, char _fntsize, const __FlashStringHelper *ifsh); + +protected: + void prepDrawText(unsigned int _x, unsigned int _y, char _fntsize); + Adafruit_TFTLCD* tft; + unsigned int bgColour; + unsigned int fgColour; +}; + +#endif + diff --git a/src/GfxTextButton.cpp b/src/GfxTextButton.cpp new file mode 100644 index 0000000..0a23c2f --- /dev/null +++ b/src/GfxTextButton.cpp @@ -0,0 +1,38 @@ + +#include "GfxTextButton.h" + + +// Constructor +GfxTextButton::GfxTextButton() {} + +void GfxTextButton::create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, byte _fntsize, const __FlashStringHelper *_ifsh, unsigned int _fgColour, unsigned int _bgColour) { + + GfxButton::create(_tft,_x,_y,_w,_h,_fgColour,_bgColour); + fntsize = _fntsize; + ifsh = _ifsh; +} + +void GfxTextButton::drawText(unsigned int col) +{ + String s = String(ifsh); + const char* text = s.c_str(); + unsigned int x1 = x+(w-(strlen(text)*6*fntsize))/2; + unsigned int y1 = y+(h-8*fntsize)/2; + tft->setCursor(x1,y1); + tft->setTextSize(fntsize); + tft->setTextColor(col); + tft->print(text); +} + +void GfxTextButton::draw() +{ + GfxButton::draw(); + drawText(bgColour); +} + + +void GfxTextButton::drawPressed() +{ + GfxButton::drawPressed(); + drawText(fgColour); +} diff --git a/src/GfxTextButton.h b/src/GfxTextButton.h new file mode 100644 index 0000000..d1194f1 --- /dev/null +++ b/src/GfxTextButton.h @@ -0,0 +1,23 @@ + +#ifndef _GFXTEXTBUTTON_H_ +#define _GFXTEXTBUTTON_H_ + +#include "GfxButton.h" + +class GfxTextButton : public GfxButton { + + +public: + + GfxTextButton(); + void create(Adafruit_TFTLCD* _tft,unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h, byte _fntsize, const __FlashStringHelper *ifsh, unsigned int _fgColour, unsigned int _bgColour); + void draw(); + void drawPressed(); + +private: + void drawText(unsigned int col); + byte fntsize; + const __FlashStringHelper *ifsh; +}; + +#endif diff --git a/src/MegaSquirt.cpp b/src/MegaSquirt.cpp new file mode 100644 index 0000000..dc71a2f --- /dev/null +++ b/src/MegaSquirt.cpp @@ -0,0 +1,114 @@ +#include "debug.h" + +#include "MegaSquirt.h" + +#define offset 3 // new serial protocol is offset by 3 + +// Constructor +MegaSquirt::MegaSquirt() { +} + +byte MegaSquirt::getEngine() { + return data[offset + 11]; +} + +unsigned int MegaSquirt::getRpm() { + return ((data[offset + 6] << 8) | data[offset + 7]); +} + +float MegaSquirt::getMap() { + return ((data[offset + 18] << 8) | data[offset + 19]) * 0.1f; +} + +float MegaSquirt::getMat(byte celsius) { + if (celsius) { + return (((data[offset + 20] << 8) | data[offset + 21]) - 320) * 0.05555f; + } + return ((data[offset + 20] << 8) | data[offset + 21]) * 0.1f; +} + +float MegaSquirt::getClt(byte celsius) { + if (celsius) { + return (((data[offset + 22] << 8) | data[offset + 23]) - 320) * 0.05555f; + } + return ((data[offset + 22] << 8) | data[offset + 23]) * 0.1f; +} + +float MegaSquirt::getTps() { + return ((data[offset + 24] << 8) | data[offset + 25]) * 0.1f; +} + +float MegaSquirt::getAfr() { + return ((data[offset + 28] << 8) | data[offset + 29]) * 0.1f; +} + +float MegaSquirt::getTaf() { + return data[offset + 12] * 0.1f; +} + +float MegaSquirt::getAdv() { + return ((data[offset + 8] << 8) | data[offset + 9]) * 0.1f; +} + +float MegaSquirt::getPwp() { + return ((data[offset + 2] << 8) | data[offset + 3]) * 0.000666f; +} + +int MegaSquirt::requestData() { + return runCommand((byte*) (const byte[] ) { 0x0, 0x1, 0x41, 0xD3, 0xD9, + 0x9E, 0x8B }, 7, data, 170); +// return runCommand((byte*)(const byte []){0x41},1,data,170); + +} + +void MegaSquirt::requestSignature() { + runCommand((byte*) (const byte[] ) { 'S' }, 1, data, 59); +} + +//private +int MegaSquirt::runCommand(byte cmd[], byte cmdLength, byte data[], + byte dataLength) { + // Flush any leftover data from the last command. + // Read any leftover dat on the line. + Serial.flush(); + while (Serial.available()) + Serial.read(); + + // Send the specified command to the controller. + byte i; + for (i = 0; i < cmdLength; i++) { + Serial.write(cmd[i]); + } + + unsigned long timeOut; + int counter; + + // The Serial buffer is 64 bytes, this is less than the largest payload + // from the controller, as such don't wait, start reading the data right + // away and don't stop until either the requested number of bytes has + // been read or the timeout is reached. + // + counter = 0; + timeOut = millis() + 300; // MS WAIT TIMEOUT + while (counter < dataLength && millis() < timeOut) { + while (counter < dataLength && Serial.available()) { + data[counter] = Serial.read(); + ++counter; + } + } + + // If there is still data pending to be read, raise OVERFLOW error. + if (Serial.available() > 0 && counter >= dataLength) { + D(debugSerial->println(F("MS_ERR_COMM_OVERFLOW"));) + return counter; //MS_ERR_COMM_OVERFLOW + } + + // If the wrong amount of data was read, return timeout. + if (counter != dataLength) { + D(debugSerial->println(F("MS_ERR_COMM_TIMEOUT"));) + return counter; //MS_ERR_COMM_TIMEOUT + } + + // Otherwise return success. + return 1; //MS_COMM_SUCCESS +} diff --git a/src/MegaSquirt.h b/src/MegaSquirt.h new file mode 100644 index 0000000..994f118 --- /dev/null +++ b/src/MegaSquirt.h @@ -0,0 +1,42 @@ +#ifndef megasquirt_h +#define megasquirt_h + +#define MS_CELSIUS 1 +#define MS_FAHRENHEIT 0 +#define MS_ENGINE_READY 1 +#define MS_ENGINE_CRANKING 2 +#define MS_ENGINE_STARTW 4 +#define MS_ENGINE_WARMUP 8 +#define MS_ENGINE_TPSACCAEN 16 +#define MS_ENGINE_TPSACCDEN 32 +#define MS_ENGINE_MAPACCAEN 64 +#define MS_ENGINE_MAPACCDEN 128 + +#include "Arduino.h" + +class MegaSquirt { + +public: + + MegaSquirt(); + byte getEngine(); + unsigned int getRpm(); + float getMap(); + float getMat(byte celsius); + float getClt(byte celsius); + float getTps(); + float getAfr(); + float getTaf(); + float getAdv(); + float getPwp(); + int requestData(); + void requestSignature(); + +private: + byte data[400]; + int runCommand(byte cmd[], byte cmdLength, byte data[], byte dataLength); +}; + + +#endif + diff --git a/src/TempSensor.cpp b/src/TempSensor.cpp new file mode 100644 index 0000000..361f887 --- /dev/null +++ b/src/TempSensor.cpp @@ -0,0 +1,123 @@ +#include "TempSensor.h" + + +// Constructor +TempSensor::TempSensor(void) { + digitalWrite(REF_PIN, LOW); + pinMode(REF_PIN, INPUT); // sets the digital pin as input (logic 1) +} + +void TempSensor::OneWireReset (int Pin) // reset. Should improve to act as a presence pulse +{ + digitalWrite (Pin, LOW); + pinMode (Pin, OUTPUT); // bring low for 500 us + delayMicroseconds (500); + pinMode (Pin, INPUT); + delayMicroseconds (500); +} + +void TempSensor::OneWireOutByte (int Pin, byte d) // output byte d (least sig bit first). +{ + byte n; + + for (n=8; n!=0; n--) + { + if ((d & 0x01) == 1) // test least sig bit + { + digitalWrite (Pin, LOW); + pinMode (Pin, OUTPUT); + delayMicroseconds (5); + pinMode (Pin, INPUT); + delayMicroseconds (60); + } + else + { + digitalWrite (Pin, LOW); + pinMode (Pin, OUTPUT); + delayMicroseconds (60); + pinMode (Pin, INPUT); + } + + d = d>>1; // now the next bit is in the least sig bit position. + } +} + + +byte TempSensor::OneWireInByte (int Pin) // read byte, least sig byte first +{ + byte d =0, n, b; + + for (n=0; n<8; n++) + { + digitalWrite (Pin, LOW); + pinMode (Pin, OUTPUT); + delayMicroseconds (5); + pinMode (Pin, INPUT); + delayMicroseconds (5); + b = digitalRead (Pin); + delayMicroseconds (50); + d = (d >> 1) | (b<<7); // shift d to right and insert b in most sig bit position + } + return (d); +} + + +int TempSensor::getTempInt () +{ + return getTempInt100() / 100; + +} + +float TempSensor::getTempFloat () +{ + return getTempInt100() / 100.0; +} + +int TempSensor::getTempInt100 () +{ + int HighByte, LowByte, TReading, Tc_100, sign; + + OneWireReset (REF_PIN); + OneWireOutByte (REF_PIN, 0xcc); + OneWireOutByte (REF_PIN, 0x44); // perform temperature conversion, strong pullup for one sec + + OneWireReset (REF_PIN); + OneWireOutByte (REF_PIN, 0xcc); + OneWireOutByte (REF_PIN, 0xbe); + + LowByte = OneWireInByte (REF_PIN); + HighByte = OneWireInByte (REF_PIN); + TReading = (HighByte << 8) + LowByte; + sign = TReading & 0x8000; // test most sig bit + if (sign) // negative + { + TReading = (TReading ^ 0xffff) + 1; // 2's comp + } + Tc_100 = (6 * TReading) + TReading / 4; // multiply by (100 * 0.0625) or 6.25 + if (sign) Tc_100 *=-1; + + return Tc_100; + +// int whole = Tc_100 / 100; // separate off the whole and fractional portions +// int fract = Tc_100 % 100; +// +// if (sign) { +// temp[0] = '-'; +// } else { +// temp[0] = '+'; +// } +// +// if (whole/100 == 0) { +// temp[1] = ' '; +// } else { +// temp[1] = whole/100+'0'; +// } +// +// temp[2] = (whole-(whole/100)*100)/10 +'0' ; +// temp[3] = whole-(whole/10)*10 +'0'; +// temp[4] = '.'; +// temp[5] = fract/10 +'0'; +// temp[6] = '\0'; +// // temp[6] = fract-(fract/10)*10 +'0'; +// // temp[7] = '\0'; +} diff --git a/src/TempSensor.h b/src/TempSensor.h new file mode 100644 index 0000000..940cae2 --- /dev/null +++ b/src/TempSensor.h @@ -0,0 +1,28 @@ +#ifndef tempsensor_h +#define tempsensor_h + +#include "Arduino.h" + +#define REF_PIN 12 + + +class TempSensor { + + public: + + TempSensor(void); + + float getTempFloat(); + int getTempInt100(); + int getTempInt(); + + private: + + void OneWireReset (int Pin); + + void OneWireOutByte (int Pin, byte d); + + byte OneWireInByte (int Pin); +}; + +#endif diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..750a671 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,9 @@ +#define DEBUG // comment out this line to disable +#ifdef DEBUG + #include + extern SoftwareSerial* debugSerial; + //// the following must be in an impl file D(SoftwareSerial* debugSerial;) + #define D(x) x +#else + #define D(x) +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e25341e --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,487 @@ +#include "debug.h" +D(SoftwareSerial* debugSerial;) + +#include "TempSensor.h" +#include "MegaSquirt.h" +#include "GfxDataField.h" +#include "GfxIndicator.h" +#include "GfxTextButton.h" +#include "GfxArrowButton.h" + +#include "Adafruit_GFX.h" // Core graphics library +#include "Adafruit_TFTLCD.h" // Hardware-specific library +#include "TouchScreen.h" // Touch screen +#include "Adafruit_GPS.h" // GPS library +#include // SD Card library +#include +#include + +#define DATAFLD_TMP 0 +#define DATAFLD_RPM 1 +#define DATAFLD_CLT 2 +#define DATAFLD_MAT 3 +#define DATAFLD_MAP 4 +#define DATAFLD_TPS 5 +#define DATAFLD_AFR 6 +#define DATAFLD_TAF 7 +#define DATAFLD_ADV 8 +#define DATAFLD_PWP 9 + +// Assign human-readable names to some common 16-bit color values: +#define BLACK 0x0000 +#define DRKGRAY 0x2124 +#define LTGRAY 0xBDD7 +#define BLUE 0x001F +#define LTBLUE 0x94DF +#define RED 0xF800 +#define GREEN 0x07E0 +#define CYAN 0x07FF +#define MAGENTA 0xF81F +#define YELLOW 0xFFE0 +#define WHITE 0xFFFF + + +// These are the pins for the TouchScreen +#define YP A1 // must be an analog pin, use "An" notation! +#define XM A2 // must be an analog pin, use "An" notation! +#define YM 7 // can be a digital pin +#define XP 6 // can be a digital pin + +Adafruit_TFTLCD tft; // 320 x 240 +TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); +SoftwareSerial gpsSerial(3, 2); +Adafruit_GPS gps(&gpsSerial); +TempSensor tempSensor; +MegaSquirt megaSquirt; +SdFat sdcard; +SdFile logfile; + +GfxDataField datafields[10]; +GfxIndicator engIndicator; +GfxTextButton setupButton; +GfxTextButton exitButton; +unsigned long time = millis(); +byte rpmWarn; +byte rpmLimit; +byte tempScale; +byte flashtime; +bool showRpmWarning = false; +bool showRpmLimit = false; +bool showLogo = true; +GfxDataField rpmWarnFld; +GfxDataField rpmLimitFld; +GfxLabel tempScaleLabel; +GfxTextButton celsiusButton; +GfxTextButton fahrenheitButton; +GfxArrowButton rpmWarnUp; +GfxArrowButton rpmWarnDn; +GfxArrowButton rpmLimitUp; +GfxArrowButton rpmLimitDn; +GfxLabel serialLabel; + + +void drawMainScreen() +{ + D(debugSerial->println(F(">> Main"));) + + //STRIPES + byte yrow = 30; + byte c; + for (c=0; c<5;c++) + { + tft.fillRect(0, (yrow+18)+2*c*18, 320, 16, DRKGRAY); + } + + //DATAFIELDS + datafields[DATAFLD_TMP].create(&tft,230,yrow,2,1,BLACK,LTGRAY); + datafields[DATAFLD_TMP].drawLabel(10,yrow,2,F("Outside Temp:")); + + yrow +=18; + datafields[DATAFLD_RPM].create(&tft,230,yrow,2,1,DRKGRAY,WHITE); + datafields[DATAFLD_RPM].drawLabel(10,yrow,2,F("Engine RPM:")); + + yrow +=18; + datafields[DATAFLD_CLT].create(&tft,230,yrow,2,1,BLACK,LTGRAY); + datafields[DATAFLD_CLT].drawLabel(10,yrow,2,F("Coolant Temp:")); + + yrow +=18; + datafields[DATAFLD_MAT].create(&tft,230,yrow,2,1,DRKGRAY,WHITE); + datafields[DATAFLD_MAT].drawLabel(10,yrow,2,F("Manifold Temp:")); + + yrow +=18; + datafields[DATAFLD_MAP].create(&tft,230,yrow,2,1,BLACK,LTGRAY); + datafields[DATAFLD_MAP].drawLabel(10,yrow,2,F("Manifold kPa:")); + + yrow +=18; + datafields[DATAFLD_TPS].create(&tft,230,yrow,2,1,DRKGRAY,WHITE); + datafields[DATAFLD_TPS].drawLabel(10,yrow,2,F("Throttle Pos:")); + + yrow +=18; + datafields[DATAFLD_AFR].create(&tft,230,yrow,2,1,BLACK,LTGRAY); + datafields[DATAFLD_AFR].drawLabel(10,yrow,2,F("O2 / AFR:")); + + yrow +=18; + datafields[DATAFLD_TAF].create(&tft,230,yrow,2,1,DRKGRAY,WHITE); + datafields[DATAFLD_TAF].drawLabel(10,yrow,2,F("Target AFR:")); + + yrow +=18; + datafields[DATAFLD_ADV].create(&tft,230,yrow,2,1,BLACK,LTGRAY); + datafields[DATAFLD_ADV].drawLabel(10,yrow,2,F("Spark Adv:")); + + yrow +=18; + datafields[DATAFLD_PWP].create(&tft,230,yrow,2,3,DRKGRAY,WHITE); + datafields[DATAFLD_PWP].drawLabel(10,yrow,2,F("Pulse Width:")); + + //INDICATORS / BUTTONS + yrow +=20; + engIndicator.create(&tft, 0, yrow, 140, 24, 2); + engIndicator.setState(0, DRKGRAY, LTGRAY, F("No ECU?")); + + setupButton.create(&tft, 240, yrow, 80, 24, 2, F("SETUP"), BLACK, LTGRAY); + setupButton.draw(); +} + +void drawLogo() +{ + tft.fillRect(0, 0, 320, 25, LTBLUE); + tft.setCursor(75,4); + tft.setTextSize(2); + tft.setTextColor(LTGRAY); + tft.print(F("RednelsRacing")); + tft.setCursor(76,5); + tft.setTextColor(BLACK); + tft.print(F("RednelsRacing")); +} + +void drawWarnRPM() +{ + tft.fillRect(0, 0, 320, 25, YELLOW); + tft.setCursor(85,4); + tft.setTextSize(2); + tft.setTextColor(BLACK); + tft.print(F("RPM WARNING")); +} + +void drawLimitRPM() +{ + tft.fillRect(0, 0, 320, 25, RED); + tft.setCursor(105,4); + tft.setTextSize(2); + tft.setTextColor(WHITE); + tft.print(F("RPM LIMIT")); +} + + +bool setupLoop() +{ + Point p = ts.getPoint(); + pinMode(XM, OUTPUT); + pinMode(YP, OUTPUT); + if (exitButton.isPressed(p)) + { + if (rpmWarn != EEPROM.read(2)) EEPROM.write(2, rpmWarn); + if (rpmLimit != EEPROM.read(3)) EEPROM.write(3, rpmLimit); + if (tempScale != EEPROM.read(4)) EEPROM.write(4, tempScale); + + tft.fillScreen(BLACK); + + drawLogo(); + + drawMainScreen(); + return true; + } + + + if (tempScale == MS_CELSIUS) + { + if (fahrenheitButton.isPressed(p)) + { + tempScale = MS_FAHRENHEIT; + celsiusButton.swapColours(); + fahrenheitButton.swapColours(); + celsiusButton.draw(); + fahrenheitButton.draw(); + } + } + else + { + if (celsiusButton.isPressed(p)) + { + tempScale = MS_CELSIUS; + celsiusButton.swapColours(); + fahrenheitButton.swapColours(); + celsiusButton.draw(); + fahrenheitButton.draw(); + } + } + + + if (rpmWarnUp.isPressed(p)) + { + rpmWarn++; + rpmWarnFld.setValue(rpmWarn*100); + } + + if (rpmWarnDn.isPressed(p)) + { + rpmWarn--; + rpmWarnFld.setValue(rpmWarn*100); + } + + if (rpmLimitUp.isPressed(p)) + { + rpmLimit++; + rpmLimitFld.setValue(rpmLimit*100); + } + + if (rpmLimitDn.isPressed(p)) + { + rpmLimit++; + rpmLimitFld.setValue(rpmLimit*100); + } + + return false; +} + +void setup() +{ + Serial.begin(115200); //115200 + + D(debugSerial = new SoftwareSerial(2, 3);) + D(debugSerial->begin(9600);) + D(debugSerial->println(F("megasquirt-lcd-duino"));) + + if (!sdcard.begin(SS, SPI_HALF_SPEED)) { + sdcard.initErrorHalt(); + } + + gps.begin(9600); + // turn on RMC (recommended minimum) and GGA (fix data) including altitude + gps.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); + + // Set the update rate + gps.sendCommand(PMTK_SET_NMEA_UPDATE_5HZ); // 5 Hz update rate + gps.sendCommand(PMTK_API_SET_FIX_CTL_5HZ); // 5 Hz update rate + // Request updates on antenna status, comment out to keep quiet + // gps.sendCommand(PGCMD_ANTENNA); + + // enable interrupts to capture gps reads + OCR0A = 0xAF; + TIMSK0 |= _BV(OCIE0A); + + tft.reset(); + + uint16_t identifier = tft.readID(); + + if(identifier != 0x9328) + { + D(debugSerial->print(F("Unknown LCD driver chip: "));) + D(debugSerial->println(identifier,HEX);) + } + + tft.begin(identifier); + tft.setRotation(3); + + tft.fillScreen(BLACK); + + drawLogo(); + + byte ver1 = EEPROM.read(0); + byte ver2 = EEPROM.read(1); + + if (ver1 != 0xff || ver2 != 0x01) + { + EEPROM.write(0, 0xff); + EEPROM.write(1, 0x01); + + EEPROM.write(2, 60); + EEPROM.write(3, 65); + EEPROM.write(4, MS_FAHRENHEIT); + } + + rpmWarn = EEPROM.read(2); // x100 + rpmLimit = EEPROM.read(3); // x100 + tempScale = EEPROM.read(4); + + drawMainScreen(); + + flashtime = 0; +} + +void dataCaptureLoop() +{ + flashtime++; + if (showRpmWarning) + { + if (flashtime % 2) + { + drawWarnRPM(); + } + else + { + tft.fillRect(0, 0, 320, 25, BLACK); + } + } + + float temp = (tempSensor.getTempInt100()+5)/10/10.0f; + + if (tempScale == MS_FAHRENHEIT) { + temp = (temp*1.8f) + 32; + } + D(debugSerial->print(F("Temp: "));) + D(debugSerial->println(temp);) + + datafields[DATAFLD_TMP].setValue(temp); + + if (megaSquirt.requestData() == 1) + { + D(debugSerial->print(F("RPM: "));) + D(debugSerial->println(megaSquirt.getRpm());) + + unsigned int rpm = megaSquirt.getRpm(); + + if ((rpm/100) >= rpmWarn && (rpm/100) < rpmLimit) + { + showRpmWarning = true; + showRpmLimit = false; + showLogo = false; + } + else if ((rpm/100) >= rpmLimit) + { + if (!showRpmLimit) + { + showRpmLimit = true; + showRpmWarning = false; + showLogo = false; + drawLimitRPM(); + } + } + else if (!showLogo) + { + drawLogo(); + showLogo = true; + showRpmWarning = false; + showRpmLimit = false; + } + + datafields[DATAFLD_RPM].setValue((int)rpm); + datafields[DATAFLD_CLT].setValue(megaSquirt.getClt(tempScale)); + datafields[DATAFLD_MAT].setValue(megaSquirt.getMat(tempScale)); + datafields[DATAFLD_MAP].setValue(megaSquirt.getMap()); + datafields[DATAFLD_TPS].setValue(megaSquirt.getTps()); + datafields[DATAFLD_AFR].setValue(megaSquirt.getAfr()); + datafields[DATAFLD_TAF].setValue(megaSquirt.getTaf()); + datafields[DATAFLD_ADV].setValue(megaSquirt.getAdv()); + datafields[DATAFLD_PWP].setValue(megaSquirt.getPwp()); + + if (megaSquirt.getEngine() & MS_ENGINE_READY) + { + if (megaSquirt.getEngine() & MS_ENGINE_CRANKING) + engIndicator.setState(2, BLACK, RED, F("Cranking")); + else + engIndicator.setState(3, BLACK, GREEN, F("Running")); + } + else + engIndicator.setState(1, DRKGRAY, LTGRAY, F("Not running")); + } + else + { + byte c; + for (c=1; c<10;c++) + { + datafields[c].setUnknown(); + } + engIndicator.setState(0, DRKGRAY, LTGRAY, F("No ECU?")); + if (!showLogo) + { + drawLogo(); + showLogo = true; + showRpmWarning = false; + showRpmLimit = false; + } + } +} + +void doSetup() +{ + D(debugSerial->println(F("doSetup"));) + tft.fillScreen(LTGRAY); + + drawLogo(); + + + rpmWarnDn.create(&tft,190,32, 25, 20, GFXARROW_DOWN, LTGRAY,BLACK); + rpmWarnDn.draw(); + + rpmWarnFld.create(&tft,225,35,2,1,LTGRAY,BLACK); + rpmWarnFld.drawLabel(10,35,2,F("Warning RPM:")); + rpmWarnFld.setValue(rpmWarn*100); + + rpmWarnUp.create(&tft,280,32, 25, 20, GFXARROW_UP, LTGRAY,BLACK); + rpmWarnUp.draw(); + + rpmLimitDn.create(&tft,190,62, 25, 20, GFXARROW_DOWN, LTGRAY,BLACK); + rpmLimitDn.draw(); + + rpmLimitFld.create(&tft,225,65,2,1,LTGRAY,BLACK); + rpmLimitFld.drawLabel(10,65,2,F("Limit RPM:")); + rpmLimitFld.setValue(rpmLimit*100); + + rpmLimitUp.create(&tft,280,62, 25, 20, GFXARROW_UP, LTGRAY,BLACK); + rpmLimitUp.draw(); + + tempScaleLabel.create(&tft,LTGRAY,BLACK); + tempScaleLabel.drawLabel(10,95,2,F("Temp Scale:")); + + celsiusButton.create(&tft, 190, 90, 40, 24, 2, F("C"), LTGRAY, BLACK); + fahrenheitButton.create(&tft, 260, 90, 40, 24, 2, F("F"), LTGRAY, BLACK); + + if (tempScale == MS_CELSIUS) + celsiusButton.swapColours(); + else + fahrenheitButton.swapColours(); + + celsiusButton.draw(); + fahrenheitButton.draw(); + + exitButton.create(&tft, 120, 212, 80, 24, 2, F("EXIT"), LTGRAY, BLACK); + exitButton.draw(); + + bool exit = false; + while (!exit) + { + exit = setupLoop(); + } +} + +void loop() +{ + if (millis() > time) { + time = millis()+100; + dataCaptureLoop(); + } + + Point p = ts.getPoint(); + pinMode(XM, OUTPUT); + pinMode(YP, OUTPUT); + if (setupButton.isPressed(p)) + { + doSetup(); + } + + if (gps.newNMEAreceived()) { + + if (gps.parse(gps.lastNMEA())) { + gps.latitude_fixed; + gps.longitude_fixed; + gps.speed; + gps.angle; + gps.altitude; + } + } +} + +// Interrupt is called once a millisecond, looks for any new GPS data, and stores it +SIGNAL(TIMER0_COMPA_vect) { + gps.read(); +}